puzzles-r9872/0000755000175300017530000000000012161170564012430 5ustar simonsimonpuzzles-r9872/icons/0000755000175300017530000000000012161170564013543 5ustar simonsimonpuzzles-r9872/icons/Makefile0000644000175300017530000001332312126131517015201 0ustar simonsimon# Makefile for Puzzles icons. PUZZLES = blackbox bridges cube dominosa fifteen filling flip galaxies \ guess inertia keen lightup loopy magnets map mines net \ netslide pattern pearl pegs range rect samegame signpost \ singles sixteen slant solo tents towers twiddle undead \ unequal unruly untangle BASE = $(patsubst %,%-base.png,$(PUZZLES)) WEB = $(patsubst %,%-web.png,$(PUZZLES)) IBASE = $(patsubst %,%-ibase.png,$(PUZZLES)) IBASE4 = $(patsubst %,%-ibase4.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 = ./ base: $(BASE) web: $(WEB) pngicons: $(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 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 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 -map $(PIC)win16pal.xpm $^ $@ # Build the 24-bit PNGs for the icons, at three sizes. $(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. $(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...) $(P48D4): %-48d4.png: %-ibase4.png $(PIC)square.pl 48 1 $^ $@-tmp2.png convert -colors 16 -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 -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 -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 $(PIC)cicon.pl $^ > $@ clean: rm -f *.png *.ico *.rc *-icon.c puzzles-r9872/icons/win16pal.xpm0000644000175300017530000000056010544315553015735 0ustar simonsimon/* 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+@#$%&*=-;:" }; puzzles-r9872/icons/blackbox-16d24.png0000644000175300017530000000132412161170222016563 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(USMOTAX؏vO"G#?WHs *peYX}o=5:UJ7Ǒr sN)gLfsoK`vqӹO1;Ïb—;ιpBh6";̣]d"1}]15VwN^dF/ S"ڳsj D?,onnsFQL)l̿/.\N&eY^˲5TU3` Ƹ.#@"\Yc- 89o|jcaa,K3STڨUooG!cf+"`3@s)QdfUU9RJUUUUcLꂪj|xDJ93N'=3R2XTSi0^YyR?jU)ef&YHXźT`ˮs9qaI\IJ"%tEXtdate:create2013-06-22T01:32:18+01:009 %tEXtdate:modify2013-06-22T01:32:18+01:00dIENDB`puzzles-r9872/icons/blackbox-16d4.png0000644000175300017530000000061712161170222016505 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(}R0P& LI)qݤ<8 h0EI&3 "$EIobڴcضm1>~q+EDPI&.ppEpLܚ$gC0'߬AtKF"F)=؎Ȼɧsk]W3Kj.Qcœd~]d|rmσ%tEXtdate:create2013-06-22T01:32:18+01:009 %tEXtdate:modify2013-06-22T01:32:18+01:00dIENDB`puzzles-r9872/icons/blackbox-16d8.png0000644000175300017530000000133712161170222016511 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(UOOA_W״؝-{0D.r& &|?U4޽?qw $,;f5NU~e>~@U5gș4Ţ  jw:߾}DD锈z4UGsoN>P53 3kZOOeYVU( fv1=Ikkox vpAg IDATHǥVKoEqv;.JKB+x_G88@%D1ABBluĎzwU3}8 ov›7 " 3]BD`L @#Yxt+&`fc@)/Zw03Fv̈o6IWF;oig}Zntj9"s,tIDrQVGKBȦo|o.7JDt:|Kz;D3D(+ B1FCq >$!1F(_bX,aqQO?J)4`w@Z^~.)L ZZ6z9ey1 ^44^ʆ~ZDҪCsr_V@v1n0ƘMMN.ADIB]c M2RX@{ TDPR Z_%tEXtdate:create2013-06-22T01:32:17+01:00|qz%tEXtdate:modify2013-06-22T01:32:17+01:00 ,HIENDB`puzzles-r9872/icons/blackbox-32d4.png0000644000175300017530000000132612161170221016500 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǵVY ys/fL,!,J{_#h)-af;rgDP-!U8D "]"bfXA 3.c- vpAg IDATHǥV[W>Uulؒy%Qp(y#HH ^+ D`"Hxv;TCg}(F}.]UWJ-sE'RQUEU\Jq3;oteQDQD6뺞f{U@ft4CBhŋWUzsgggC'''RK);|x8RDdf"1"RJ`0ӟo?_{?۔{꾀PU>*,dC1U_׵iFD0lyzz6zO shADUR;r~sΝ"Ĕ7~7._~SG!8爼s\QT)W^snwfLH,138mrΈ(ˆ>6LMֵ3wYD:Ҟ-3׆ :ڶR *:bZ:c uT%ɿLpk bw )(9bϢYދH)%o~tjwn~?fu]fJCa̪bU_ogߺ6ýCι{c+1+&DtEwޭbtGGG=b}D"땣5vu1@Dfz?Ӧ4pa=1jy{2j-NJDjWU-ohW/H|\r޽^K/}+RGS"4-ˬ꥔rN۶9f&_񳻟~:~9 ͛Cm)e<]DZD}Ѫ<"jto]D\.⸮ӳ\ŘRn`񞚦PתJO&-l6`!VwvvF>)%"`R{k>Ѽb=uoɎB E CcQqUk>D]2;U郱r}Z;ߓG}$I)y{2{q 03ri%2Dsζ !m*ESJODFQQ}xbD$"-u,RQdpЇ 1[cbDR:@^U5}yJ%tEXtdate:create2013-06-22T01:32:17+01:00|qz%tEXtdate:modify2013-06-22T01:32:17+01:00 ,HIENDB`puzzles-r9872/icons/blackbox-48d24.png0000644000175300017530000000372412161170220016574 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXYmoG>ޗݱEIZ!AUA7*UBAC"%)B$ jJ'7Nl6}wga뽻{8XI?p/wgf<1R=̕?Մ =1Y}(3`J<e"jEB9YbDDfff@Ya+Wu:8B,xVхyҝa襋6avg B(1F)uιK1EDT*38()1\!R=uٹoX0Ƅ9#""B<4ҕDDVg;6zO~_FBfҟ{?ò2I(MVSlޟ;|)I(Di}ԋ|9{gG$tY!7.z="bf oD0hbwKӔ{"ZetL; , Z\JYuyFfDTJi~===%?.c$ !}9;99齟/hr_(Dq$"0#jENc=ztɓq[kiiNVqn{\)xs{^֧~RB-gjAC Zj{B|a\j:xT;F˗F[? 3#K) +Mͫ:ӧ_~gN7GZ"J)9?,B9.`b|6v>o!Dh?ztĉ5'3{$ zIS0OǏ{{sh!CsZ&@sRJi3B{ffmll3F"bYROLL00":c)dVV֖QEQommol|e/-݉imݻE&LgvNUꝳy sX4MZDeYnjuJ)evBLeGf33C-Í|j{r3hm%tEXtdate:create2013-06-22T01:32:16+01:00q@%tEXtdate:modify2013-06-22T01:32:16+01:00[IENDB`puzzles-r9872/icons/blackbox-48d4.png0000644000175300017530000000120012161170221016476 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXW] NҝE;y5E%q>qAD&ZyVse 1f/ɠp?Ú"bUzP`Xs  4Ht*Z"A/FVY2_>e6)P ٧ʲ5HL ]dXP8G %Bgs|OC B)Y1 :ґF>eCd.ɀC[zU?awf>BFDBZe!ܿ jJƸ]s8$*anֵc?oATsR؇y!bzQȈ歰҇NҬ2S~5X!,1}.L2V+q$l@*3lpDH ztzCKC% Jk6oV5C%u?*K04%tEXtdate:create2013-06-22T01:32:16+01:00q@%tEXtdate:modify2013-06-22T01:32:16+01:00[IENDB`puzzles-r9872/icons/blackbox-48d8.png0000644000175300017530000000466112161170220016517 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXýY[\GKˬwv±#ym(FB"? c ! E@b@"R- |Y{oޝsN_yckz8kU_W?~S!"̚Mz+l,>XgZ'O *i9 {f@@c3hŪ5{ !R,'S1yЖu]@QL@郄Bh7|IJ(f(u=bgTb!xͻeYc6`檪NN㜋sJɓkڍ;ikA)%u!OĔ<ύ5Mݨ:uUU!km:ZcStjk1{_ukƘ>$% 3Çu#O\s!R6RD$9Q,Y!Qk?/wΟ?{L*KƩ*FFiF>xBo$#hwwd{q0KʸY3 rBpΉ6(xɵooyIE2R1 0QEF_ BX2c˧|eYh {,]2xdعe l[{^|h._lꆉe 뤅Rkm1!V(sBĦn[Hsn8:i+;9!7MӑN}tBz3h`m!fe"QXg^\\d慅,1YB++mqf($R%3#RS-G>AenmlܱƬ>}:ϳGDķn.<=퉨ixfe @]קNPH)NY>xnd=Ï>w~w8#G ;;EQMU1ꜛ,H<lmm1$diWϼ6;͋%ND<{J)8sDmmm5>yr n\/\>!uvy, k(qlLfDXyUpRu<31@zS7kNYaJ)3 @-<!dYc^gw3r@1c6Vibi)fZk7J:4 ϴq: 9R$sz)O]:W#{ѕ+,L&!-f&*k'ݾu셪U#yEz3 <1cLQrDh2,<}1ks:\UU-{sM['VZgD aIoXULW^~L&B"k-#/R n /=:"'tŅR7y5@,WVbk]9AHo)cSםLYcy8 Z,"3$6Dou Zgl{,UUX7jGYz[ iNS$1z9@/~F2@\8S~{;?oĈh=0w?2ɹ]O5zz7n޼sg3w:+=sB1<;ˇ}?q9(&-AĪ ^gv:iaKHUS7u^D[r0 f+^| ገz0q䙏`PuC$0;bOi@:åV(.녧_1B|x<͹>z G,"ǻ^2]eK4O:孵rX;˸V9rG}q!Ĕk'ǻ1{k>z>w?n-e9RsHM'{􇪖6wS }пV˟1޺ya^e՛7ϜB 63eZfA@\ƄOz%1ڿs#&)@ E@" HRb}fM7j_qU;*,UeKzhޜj=8K)dO} HR)@ E@" HR)@ E@" HR)@ E@" HR)[\F=9xݮžo@֋&GQRISԧ~}E @ E@" HR)@ E@" HR)@ E@" HR)@ E@" HY*zUXL_7xݮžo@֋&GQRISԧ~}E @ E@" HRޜ(UVmٲugΜu8c w-7ұ}EKh;Hr4c8ϟ߶m9Xk_VX >Z&t=2dSO;?9uJZ鋃͞f!DzzZ@!g?{I!u@-/Yvjذ?|kPNc@>R󍅋檕YY@]QZZd}OԩSZ+iZ>Zb^^ 0@*t1_ ;@J$&R)@ E@" H,Qr* &o#ݣ/.}|>…9{4T6|UϜ5 Gp8 /Y$5?YzcAA ϴZA: u@OÁ@cMMMYH$/n(KkkKӡK hiiPӧ7S Bжz`? ^̂@}---Po!" ?߲w>] ~HM,.PҕHL|9|@IعOrcD@ |'T;B'Ns! -‚VQaA ~\| ֦fkǙH@^E5/zk㑗EOCMc@?tpj i) N)jfFFG@ q8T#pyc4  hĈPHW0CpNN‚fAJ$ 1jȺXjr8>Qݿ fΜngfLNI)! RSSǹ]gnwݩJ2 kڴclRRRfiS(\$,2t.#V]t/ D23F^f yw96/\JJʝ((ȷl=Vf+;vRR5s?Z7x WsnwۃPv3sgEZZZuvv646kmk~ݞtfIN)Du~UXL_7xݮžo1Ry18+KyMdO} )@ E@" HR|0CU[lݷ3gφB/ 0rԈ[np7LfᢥgKkkKkkEԮE@&3n{9Nm[η{E@&~ݚ`0B)_LzssGWS ןV^Lbի^8q5kB@&zI/Ny}0Xn5ɂEKoxN竮>c !8AZ|LeǕw{rW"<!DI&MS&R)@ E@" H_-SSSsroڭ.]lq L V*lٺo߇gΞ B_0` ii_꛵bի^8q5k+ *><ϲ_\FFz[ۅ~~+55ɓ^~!Dmm'20#co_-[Vr_Ř8Ķ`ђ68q?Bk&NPVxӍ7;VϘ~]߬$ʏ++?+xG)gtbWYy,$7W;h!III)))G~sт͛6+_KqO8!<ɓ雵6')@ E@" HR_r KwVOU HTYYhx%%4/.O}WTwΓ /\^vLIOK7z3D[ۅݻO>cZ?.\gKCeÇ_uiiiql `0X^`0e7?YzcAA wO ^_-dp8=4L!'Fȋ/n(KkkK]@KK높>MbdB۶^x0z۶o{ZZZBv#B-{Ŝ'v #Gj /P̂G ?}evSR0 % :A|'T B'N]`|?4 -‚VQaA* cjjk+,#cQŠ֦fk0&UtIKyUu׫&GN:*&K/TLaە4vj °T1t:LPA"==]y͌ 5aL?4 8*':ݞ7FaA* #rB!_ 99 TQRvp8FYW_WXQ#R0U :A!fΜn/6cuJJ,0n@sbv۝+ !ĴiS33rФ́ӦNQ̂'v2twlZN~f;C"9qwefvVle]|g<= )))wο f$˭Vf+;vRR5!bcb!VM70q:u۝8`(=| ]zVtm kZ.z>a8yycrrrL^DuUXL_7xݮžooc7o1ge)#BlTx>o__Qa E@" HR)@LÏxǻ;#D1AfUZZݝZ/G@s˅>}})ʫΝ?~[7&%%=/b*" O>l(ֲoH@&??^}w{-0V!x'xB,;ݞw$T @ 0KgC)&tLR)@ E@" HY:*,UZyλ+-@ҲLMMw/[=:{%n?r!ă|?޽D|lݲ1-@_yܹsǏ[Ͽvz P7&%%=/VL:`L~ɧ BZ4?oLۉ (??^}w^bE@ !O>w/ %B,;ݞwn} H1AGɥ4ן\֥D3L ÌLku3A" HR)@ e"P>g}C1\@x vpAg``nw*IDATx]lSeg¶+ Y` WF4{e474㥉^3c@"8(ke t =ybBƀ{Nz_s<7p+83'%<c[{K.``( ``(Rq~k0 b[{_e_l6;88488ϭ[~yuJta҅;_{1}XkX$-\p[o0ƌ|^Se۷'&j?cyya+[fDa+A^; =&zJ⋎?/?s쓏?Dtf߾/D[ \\a>:ys};vDP=/r[Wjllf;/z^xem;uW팱=l6EݞEޭCP00 CP00 aF|>?H2 ( ~EHLo_ IYaad2޸yݳh6B!,,q˲f_hjjl&IvONӦi1MshxgGDOr]LNXki!z<̶݇t],[nqDvl*RjMmΝԙ3gEwݽ{ʕbτ3Msl6+z >kYf}?m|.?Bxt1:um{Q^hD"x)IdRX<fw5{_a5cq`t1''ʴ?XrҰ>"θAvQkNQ/TEqir 41.<,s?f$= P<`46ԋ6ϷmljjEA=gŋGFV4I.]*aϣ+1ֶ/SyEćw'[V̿(---`P@|x7clݺh|nF׭}J(x:4f$iٱby:cW_ܳ+.(=wVki^ ;wlojjey.kE$Y77ؾ-{}1I6ojo]wNSŲEmӝB[|"Lsn 4-74b1΁3000r/v|Za`( ``( ``( ``( ``( ``( ``( ``hMLv ,@P s v{``( ``(5UIGr#_LxaۮHD1-[7 @Za`( ``(zY/ƌ`rGA+ 01JV ``( ``:<g輵?<]P00 CP00 CP00 CP00k $Z%tEXtdate:create2013-06-22T01:32:05+01:00'DkC%tEXtdate:modify2013-06-22T01:32:05+01:00VIENDB`puzzles-r9872/icons/blackbox-ibase4.png0000644000175300017530000000157112161170220017174 0ustar simonsimonPNG  IHDRh$u oFFs[s pHYsHHFk> vpAg``nwIDATxaR0@}eiFF-X( '=?kXQJ6M~U7!)%6zh5b0 `01`8'WoQMXι[i<ͪbVtE0Vǩ2[ô V䜗[z?4Ҝj)3.%:d]w`AΫXsX01` c0 F,N`1#Ë˰`'Y>;p9LC ;qڦ7Xgx͢;k:Xm=R~ꇭ۷a 1L`-`cӖև ' E3g ' `0AyX%h0mq}L UqiU`Q`7/x=͋p~y7 Iw62 lwZ> ;LD֏ `01` c0 `01` c0 `01` c0 `01`0cZ4M=7c0 `01` M)%}U 2nr/ɑG `01`<׉}' wa0 `01` c0X\T`z`01` c0 `01` j:RɅ%tEXtdate:create2013-06-22T01:32:16+01:00q@%tEXtdate:modify2013-06-22T01:32:16+01:00[IENDB`puzzles-r9872/icons/blackbox-web.png0000644000175300017530000002416212161170205016606 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge'IDATx}yWyݪ{zziF-ɲd[ ,[e68B`I^ @ wxbKƒ,YHHkjneyw|9ݿn~߇AmYB^J[ ^؜)h)%f5kRRJ[zf2w& DRF)q7͇R=}oݺ^8Ns/\(ZdI]Ja{~~?) hzgw>pK.&CPH$v׾,cGG?'Lug}`z\ߟL$^:5^BHS#iccǏ\S;vP(Bɤdo۶^.54MhJ&= !J#dY0b([u8]mfɤialy`6 l.בJxtvFq|yxon KnNc1.O(繮8! >RLt _ .И`P GX\T,HѾ vqӯfpfxLj@; FKݿԊŲ5Kw4!9A#ѰUP+l!15dD>['l1wӝ( h1 !D\ 4ҩa咀=[RÚǹ7:: {@.[IJ*lRؘso|P(RJH!y7>>nY <^~yXmaCCC P_.Ʌ/2O/m.xipK[ ^*\V႗&6RH)1²f4z EֿcB0!$ q"s a'. {o Y+n Pj?_&ݎBɑi*9yUsH'b˲\ץZ1R#Z, $q˲ԵW*ιm;UᜇҷZ+ \?(TU˪o6$cTQT Vvf<+ʔH3E=^9x3ƶnݢJ鸽mݺE4uLFu0" Vʨܜd2fK Q!i^rEժn6 ilذ88ŢPsDf7TX+ BJY<g*R @Bj DӴxWP]pk+k۶.KpmuժtZ"sdT Ua]RJ5 0Fi3̈́ijVAӘZ"a.D"vوbi !4 SaWOӌu !9Wa:i+u~m"fT@Dnþn U७/m.xipK[ ^*\2>Apd*Ld*"ABq 0$9uۯ]qT(``*C̝p]ϲ,!8.!Բ!u=qeKJ5T˖hOL ?Q؄ժmYVTX6!eU# lx pq]T*r!ofnz{{)W_ 0"ܞtF"3εk,YiN:֯dժfd2iFq"܉Db-G1|>6)Bm`7cS^ 9j$XZ"l{A6Ց6c\M6q.\׭VHIJ8mW#&!8MR DZ[!BH E0ST+F\MSkמDT e0>S.3N8p0 5s|9622i]C|!ۿ uFyPzlF_l H ƘixryʧB2[|*,S08TFvH)p8TL\ө|P(+$ *|ds{ 1|m.+W؋ܺoڴZm&u=/ϫTVOTCi-RSxwr50$BcTRMq#uviF\=U8QJ"߳)LرsPۖ-[b\S,  :A4yR4K fBA"[?q~DzJAC-? +5/ѠTQ'K)eSacOV{жZ(岵{wmKBH>_x~ T?OLWwo%'mN`i854T.va3[-B{1H,Yvɗ[kb db|\.4yZ#Їc8={mLp¹H+ê;p!\s^I>V BLGXљN'0mq!n~rb%&m=˗/38\&jT$|rGy^]׏\je#U.KmZ8TjӦM;v707nyQg%;JŲu.af?џ`Ζ͛ T wtӍp6*8Cժҷ :ùWTUT*QJ+ !X ޞBVHo@)SC:Tt#/V?MLfs( GG$Ff2 ۶]vJ\PHȗP[ %JqY i ab1$aE"NcLc ;Mbڰ e꺦:c⺡p 5Bә YKxmEOS1?}gHxmHU:rzlh BDF(GS!?`[a3qh"p~4=}J))~Wo 7I_2x;w>c"|(U8clYM 6n _M*wiI,jMTjǎ#+V{sw=='m/!T,kMw]y-k1HBy'BJ0rjeYgSoh*BY[A B/g*BpF^ݘa`HRUΨ!$wlGA B q1G:Bpι- y(Hyq& O繎(.6B*؏ڵDw;&?o\ثTs< g*OBXTJ#ƤX,IJQ7UeZV%tV*u@vueJFte2BT try!kYzzzXjT0R$UЋ/8=V5u]׀\q9ܼulɗq'-CCgGI繗_~ѣDzc*By[:\.[ 0s_z閁c1^kRɤ)/sΫg2]Qwe,kddTfW*K=DfXjmWGFF CQT*VZ-###|jն|>?22ul67::imb1uo{mۙ핺JDbgW ð*;qm\*gT@Vm0~ؿ|3O}-\p=ܽ}w~afn)k#c_ue:ږW* ⵆ7ᣫWv]OUz?q}o?|mQrE7t==~Hd\v?~3y6am({[oc?F|;C0N?Z.]]?L&yu \v#c]r#vHak{e[JK.\8N 1jWKlx_Y;\\supRX*toYHg-{ zЉDAȪ[|UG|ibc* @1?7X |灦*GU%.8r@hƓ{0ZT^݃\>Qn.*.3F(yziBp+C2BCTqUNj$c HRccL #!0t': .5)OSL#v>󛡡+_jdd1 YgR4-;wwW?QggmHj{a`v,郃'FFFՑo0&'Dn<` ɗP+ ꉓ'A`M9ujbBN JsCT/_wR)9q=MBy 5(bC]*KyZhW>r\jw) {jc u%"-${ yyD1-]$Hp1H܋ޞ /f-8-^џB݉DBlLB ]\>+Ysa8皦y<}vVKf:;"Ӛ W W{,FܬZBBy˗/Y{8k׮Yj%sa&Wl71htn33@|mܵPW^y }ׯj49nзMiC8!.]T?}U]tQjãF ~ 1Ã><=IH0ݭZ_K(aB,Z{M7RJq5!MxMCЫD|˝#G'&&=3MsIƍ;oy._bLc`{cfQ#!cT3`pQ$mNE1fY֖-{3U&vuu^} aTA;xä) S]KT2b1qr w5ɤu])i:OT*^= ёm;*5Rvt,R_)ԴLR!idF2W*dVjurOt:FFS('"hhi~5>cT*?0 (%T'|>}&) pλ񿾥5Wor?m/R10p?y/}^ӵBTGն !Lص{ɓ'?~]٬[-p!K_{moO]waMm/;sM72F׮] 98pUj R#Z:awo[e*ꡃavX;$T$nE8)ewwHXLrXtXO7ÐRd@UΜq!aSXa BDVs] [1H!`uM׵0{&M4M$bcHRRM)S#HTc :6BHJ1ftR6Zln )ƐR41U~`ttlb2SG@"PJ"56I0ʘSHBZ3 BRж3*B !)!K95 !'ia"VXTR,g:|`Hd6u] l4 FNLNIޕ!<19Yw; AJ`l&LggMwŋ!ڇ2$?l`*|HJ.\ W;gV`2)$B22p]Q+ o)ҷGP5}P }Vo(D"0|VCTN׎=}[EXz~=wtk!jݻoա%^.g̹c穁sH$zzǃOoC-D!?|9rT-pV2OUH),m?xoc ߾GBzK6m0*D}o, \"G|ïg+b9<ϓuBx(p *6 mpk- 0B,fzPHÝQJ\Hy` 5szRM\ODuZnG;6q^QRr[}^ۘKxbX()LB݊>AIcPdC0i?(R"JB#H:ݑNwֶ̭̐h=\8/#WrƟ¶.m.xipg/a(%tEXtdate:create2013-06-22T01:32:05+01:00'DkC%tEXtdate:modify2013-06-22T01:32:05+01:00VIENDB`puzzles-r9872/icons/bridges-16d24.png0000644000175300017530000000147112161170224016422 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭtIDAT(uKOQ;3ws;eW"o QWF?х|D@-@+icx1铁MJ RJ̤gsn/(n,--,T( sR9Z(BD T/‘. 2!{w"^˜(J>_ \J,*jY=ɹЩz\45\73k-ͫ!B ōU9X>_PUq3Wjv%tEXtdate:create2013-06-22T01:32:20+01:007YC%tEXtdate:modify2013-06-22T01:32:20+01:00F%IENDB`puzzles-r9872/icons/bridges-16d4.png0000644000175300017530000000064612161170225016344 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(mR0$KII=eU vpAg\ƭlIDAT(m_OANn]`h O&L>JZh*mB",-PMPO'7'sQf#qlŒ1UU]E+ťZ>=3nyqz<bURJԕW(BpltEAVv]0ƞ:˫bYֳO/,.HtX9:>n{ѻ ׷rNӑ0X؀m`AZ-'(\?T Ka&s0$ \kXl0/'?SJctzwX;!Ƙs2)۶+Y%cerpHשrszɈ8oޟ50FBR('&'_x>eT¹Myt!\h\"r<~+\1MZ=%{PQvs-4 Bݒnha~v݇i !ΗhxPICp;I$Iaqs9eYZ1tJ WQvsR}_(oBB4EMӊ"!h^+t"%tEXtdate:create2013-06-22T01:32:20+01:007YC%tEXtdate:modify2013-06-22T01:32:20+01:00F%IENDB`puzzles-r9872/icons/bridges-32d24.png0000644000175300017530000000377012161170223016423 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg 3IDATHǥVkPTWs} 3_.3@$1QA 5 X($&7ͮf Qb$@G2<Qa!y2{g9"V_w_w_4A0E PJA ȲEQeJ)BzG0cm6IHH(W$L(*PU5ּS[VA߮NgfHo+N󌱉`0`9s.\p8w)S7n:qĆ-^#`N !` ɲр!}}J99UVRuuK pocl o111`4xAA>"0q^2R 1JlgZjڳf@KKEQqDQק(JbxH(`ófeOivcUUp`p:h/5…޷ Z1!o_~e޼¥ngG=rB8!DUtދ_\SyoWXKP0 ƛSSmj0CC^1`\.Wυ^AGdLӽ]lEoj`ρ-+yM{>x~vq0x#ǔhC1m5i.NiB26!dԇ.mP:gs=j`0pj II$%V1vcD)u8n ϒQڹ^,%tEXtdate:create2013-06-22T01:32:19+01:00,N%tEXtdate:modify2013-06-22T01:32:19+01:00]IENDB`puzzles-r9872/icons/bridges-32d4.png0000644000175300017530000000172112161170224016334 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg  IDATHǕV8o#DN܉P'I%Șk߼KHA| uw9>ƈ{73-Dwyj Iq&ݑ{Ճ-68I>l73Ãj{M878BR-U]M!c,& n΀S~ ;)rrdU`z"(): W=p8 vpAg IDATHǕV{PT?{w.d eTc)Y^"["FCU3yHfMFGaF"jZ&MfN'cKAy Sx{z.Ħgvvssf'A(2!$Iʲ,I4 AQ(YaN]onE!r.M&M p'~MDMv&\)>rcx9Id1EQdQ?MFq||\$ Dž;$1?/71Ѳ8а ~n箽OLe6==6vwo2 C!mOW, Çnw***\Wx}k^/JpsϽ~}|q{>X+Ejk=t7=ִ4!vGFFq5btǏ~}himNgu3 N41 C4P-_mٲ9ywYy$I p(JՎOgkmneٜ 9Iγ, LPdiYGP%Ü$IB$ qq : dYV/J}*i ,P F]S{Nr]t@Xqp҇t$yHXRV 0۶WVV̱``Vڷm߰; [-QDxȲ7\_de-^~?,[i߳e~_P˲'Ѩ"I2B^* /򕮫W{ZZtm쳮_{|A$gEqhh866h0_|\`f9be.//ǥqMQ$I!$˲xoy^;:~,@iuNv?e9;Z}5x.jjv=_7n̲5:iOMIfYVs <$}+Pʊ1<~LsKsŲȟnj9~d֤@ )˲#b8\/=W55ՔV2Hd2ŷ45Z~?M!>bs1^?wtTjJ24(Jg/iZ@ @QjDI5%tEXtdate:create2013-06-22T01:32:19+01:00,N%tEXtdate:modify2013-06-22T01:32:19+01:00]IENDB`puzzles-r9872/icons/bridges-48d24.png0000644000175300017530000000613412161170223016427 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W IDATXYiTG^^ <4.(1 * j\PGfADD=w%%%щHP _3S]_}ޯnUco'#p, G p!@8cf m!$Jr9!8˲,ˊMZB4C3 P(rxSB$Y\\rm iƳG>BE;[񼢟wo*,Nn]!!NNڥKFvОyaH$xYm6`ZIܽg_Xhik\S3gb. 1_JdtP/SI"TUU]V0sfx۶ͩ]^(bߦ}Ï4ƀyQ|+4;o)// pqd4!~B2Q{^ $.OZ޻WܾC{D"e!X󼣣С{@B"<A7nܜpqiRxk-OFiϝx>jt9j4Ph$fAdzmܘI{}ylH-+-^J%1LfaٳdizAP( 5\^7wĦ쓧 c4~INL@uuug7/^H$Baڠ ]p($؛~[od6ݻmܰA8$I|İ,EQz}ʑԧO8b0ryII EQ:aǹ fY.>n\d‚녎ZCGF8N&NIۧw)}!kH *zcƲz[,! ZޒT?xL&c얭;.\jkA-:TVe|qt>߳ag H$//ϔ XY_f [l\.pˎ~ZjVw_֌A BV9={ldO7a0qIxDT*K;!l޾#Gtࡔ‚+E5a6Mm׹k?t[_A= /~tTVvi &My.'O~)GRc+**[21/\*XRfH$0Lqiddȑpxӿx,ie@]b3ߙj9$ 1]\ޙMQTkrIeW <?/>>>/// p9&|Ӧ-zV @6MҊE$׭߈ݰ {+}\2qRѠWkt%5z#.jN@* ligL`Y&`0={pP*?^ܱCGGGf̘8$I c1UtM /\0LFzѣw@2y݆ ܵkN*ڼcΈ0 EdH,aJKZ/ 8p„I{`mY+3-: @h׮˲ 8^/H~!{A(l6ϚSVkQRr-;F{ ;[\\x^=FF̌c7$a"&2g=yT1E\Ϟkб1sGlgs?ݽہ{4}c; Є$IJBpj5 ]#Aptti;~Rǎ>sJnWaV푔@ɤwq!0@I׾K7Ouç몪/֭]ukRt&N>yz5r/@ZZV6~BR-֌Ƭ-IWJvrұtM'NS&t9 zMq"jD;=IqLD*zKAe)Cq&̛7/?JttQq$Z\WUW<&; aoJ)<-=ʕ-\@!#ApWjJr9I}::!υFNj 9ׯo\f0E7 +--k߾ckΦL,Nvھh⢢.]>w-NzoڶuӘp uG٠ mF4  5ui9M.8~VᨐѾ>;wli:VhBRJb0bnݾ3cC r<1"h-n ~ka( Nd}yė%0FiӢ"DXRz&V Fio BѢHofOaz%tEXtdate:create2013-06-22T01:32:19+01:00,N%tEXtdate:modify2013-06-22T01:32:19+01:00]IENDB`puzzles-r9872/icons/bridges-48d4.png0000644000175300017530000000206212161170223016341 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WmIDATXýYm }܌px2i tm~Sa|.f&񗯽ѫq\y>O`ѣI79#25⋀qm̖` 䁼7:9ImWՃlRPJA/ɼ{{wWѰH)֖eA 3SҌp֪BCJO1Gqđ# ~/ܲo<]^9Xx <0kRT.yT4[dr_('Uq $о|YDmBF7hO:xJ:T)eY*;WΝ٫A!<0\qM>u¯TZ+%A6AX-̞h|Heiv?) ٗ3Q(t)_K%tEXtdate:create2013-06-22T01:32:19+01:00,N%tEXtdate:modify2013-06-22T01:32:19+01:00]IENDB`puzzles-r9872/icons/bridges-48d8.png0000644000175300017530000000620512161170223016350 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W IDATXõXyXG/ #CEX3b"rx!A4@(91q5Ǫ(xFxn<`QthFpgzfƦ?fz^z]Nwn]B8a}uBD}RA^s]CUXx4d aI2 E1f30r\*U:AAb 446( qYV;xpHpPA7n^o, F8N.￿x}$@&lΛ;o ""? 7|xX 3vޣPȅyPo{{;۰! I()Fch3lͳMZUE 32=TTAǍ+V7;˲+B.oQDCX,4-Jm96vtR{훵>:}֥|XP[*2 uv2AH$Z3f‘M{:4̜zU0 EK7ѣCeYM@QYؚhfaF@>޿ӽٺb8@0pvv&I}`e(y{{dDxLr{P1$ɑQ;wXQmٲڵ+(.c½Ƣ(ƧML(DT*#9EQiv%](lϛgLmkk0[FĺeH?z)++DMZcFt?]lyw.Y8`?9ݽsk'Oo)޽Fc,9P|p嗫e23‡m\. hLNګj+Z͓JQhS e˳3eH0[32sVZqxnh4><㸤==, B#FIp\+b)|xXu g8k5~AbH֦Rl`YJET<'!|i$bH$&i欴Ʀ1c$8^UU)'`w8thV/(>q'D( yzxH't/_|lyʳg7x؂oN QSS 7oVSS# 7oJ;fL&~Z?BaGiE #H߅aXF/Wzǎ-6L$NkS7jxhX,رCdDĮ]{t:]RQkBCC،iM}"#%tBD>ݻw?yܱa ;c'(JX2qr!P5ҶN...G24mڄx̟ΰxSH}Z񸹹lšxɓ]3 nzZF=&7(;EE7~< ۿ H>2޽+`zM*[vbS8knpT"yayJN3xbvF3=Rր/ RPPqLG#m'JAIxeg$N)J5UQѭcxT*yݯUTT|0038G7\.T* }Ɲ9]9#BCCڶmcAǎ Nv) ybH$ ųkwH$~KPVZnV?_ 3>JL^t֭_ܹӵk̙1,[h4/p̍;(i3`%3f&nCyfGRGW0jrq=EۑeFRkwڵ?4(=uƒNcF4׎D" j:3a+an`'=~$8LJSͽw+  FP(׷ŭ[,n~"{;,z-=}@ |gG/߻x07_ܲ5M(nOBٯݚ<3C)Ey9мy3+"Z}.:iz/z?~777b+W敕`fܵZysgvnh,/ps{dːHMWn͝;w۴i=u$R)?Q*U,f!+Sn(Oh"?`k8* BZ)v^LkXxQFb'a2 ye*H۶ٙb&2GRƮi_h[5Nwr/3V & N m۴zTXxt:]I߮|5V91\o 0yJ"~ /ӯIP(߯5 YtJMTjՒ%_0H4lXݫ\]]P;sD8wl\d^ޯ;f_ Tjն H$}V<78ggC-#RV%[[MYɫqgwMHkw:aIstd^>5g/̛ȑ M\85u br`vJ'a BPX[j}^3f|W(䲆Z띱kkkE"Q7O>xN0#(0ǃSMwx؂oV*q Ľۼi}M2ɽMqqɤɉ٧s@$yzz:ţLxB{-Z8IOgg@o ^aUϟ?[ty^[mi%Ť?x_urr 0<:j`fM]\JY~rLK(!v=Mhʔ / B5ٌ#LVSSkYP< ?5~S8;;oMJ|: س0p*++s?~ EFJ^^11Q[ y|e۶mc=;@y &ݫT\b77Wؿ0 q7Lث0xB * T 0Pa $@ H@@  A* T 0Pa $@ H@@  A*  sr33>\aRݽWAoFj"Vs~aBR Q E0%|?x0y꼼_BT&t!!=̛S_K(Θ}:ҷo)jqQMM ߑP(`Hd] 8⡠P(>CН5p@fMrJ.-}u}ϝ?H"qڶm37O~u%*j@xPwwT`3gfcC xwF訁76oT*GXqI>>q'DЀdԯ yzxHNt/_>^C ¨yw܄~ 73Vw~ft:Pѿ+0s1@ Xp1R:>aGL^Qcݻw?y>=FIii\|Ru)'/gZ-myZTXвe +f'Ot`wKux;jZpqq9qi&.ŷxBcZz-Lg*FKULwT*uҜl 7ts&LѭG'|cWD:aפr95xK?_38K^ߛ/Fog_2kn˦0fT5*(ȿHّdž SM/{PD=c &uF**0r 8;;grOl1/~}pbXl:3G?L9# 44m6(i3`%  тط.A[jim[7lvqqYbمs|~Mպ;w@pYdr1^)E 2*ȿQ -mi;l:ZdQv3Ma=v\V#\\t'=~$8LJS͵#FaM6bР(Ӄ={E qqqqnԨWH-;H$1@Vgf"bJ"WFLh۶H$j޼%ǏMa_मXRRi>4C|aP@8_ܲ5M(nOdTP(eM6""iؗ۷ ;'~ۅBAī߻Cix90~SpZz~@(??\f^͛73V`4^?oNM-=T6quo?ӓ%%}7?_8u3g+n-#D 2DR/\znJ㩮^ ^};?83͗ 129~pʔ7|?8S*Uu };f7FA:q?86(z˪5L7Ji`CiKƍp-[|<#mXuюE//pG())N LPYY9i4DÆ}> xrxgܨчS2_r)7+ڶ5FV-Y}>sLe m :Sls'Ν3?ז뎯%iZ-mG~~dUɖ:kSV*,q! f\h4޽?ݼRdN6œ~-[B6m2"a%YбwX(1@Q={a HP( Qѯ5u br`vJ HFLh҄gŷxFs N{^kձCܳZߛE"˜/"g~efoִ}x0c'kkQc"}G+,ע4tv8uVK]FSl%Ť?x_trr 0<:j`fM]\JY~Z]]$H8/s[p%&BX9 In~kRVxG+V\xQ(fd^s2F7o2n0:vM'UR/**5eT忟2L'?xPk4TNߛķ~YgX^j 4]cXOeSN]~;w,g;*@&?6XSMM;wׯA_NN[l:8$.n-PxwQRZA8uMJ& ]*0 ۻgmk]0ܹ9S$dD"5(iL@…KSNo!ĵ[>31z y8ul64~C8A"oiw{wJO(__iS'gG OuB)ڵůZڶp}v?8ZCMnobGA2?O~*qu|s8* j?!&Nn@hcدS?8i?C7i_<[[~*h $_6wq]`?[4(osx/P-7N_S8Vآaz,'Kf°O ֛d1g^2XLC&G5lޤ^[ ܛTKp Q}P n.|y@`7s%k1P{DMfnZ*s/Yc8Q@ǎ?[XxWv{ha8QK/ Hs/Y- GA9*uQK@ Q}P s/- d 001&^205pTB%cC/Y- oo/||P@%cs/Y- GA9*^20rS15@ޤ^2^8f;P{ɀN^ǀMwp%kId 0a{#\z?\(l _Rryū{˞=aoyl;rXwKf 3f&eΡg}j,!{l;rXwKf T'&_|Px,Jo~*v.XLzEZ08Ye$޻gϾ~;vxQ#|}-*K[i9NK/ ?51?1JJKJ%KM8}ӛ)P5ҶNnnE1O<k9 74Т}Z\\\d0wG=pmǠ9tjHR'͙AL4ϜʐYux?+v{u 7۝UUW~8czp=64n\.WT@63IYEEE׼$~*3K"@6IQT֯[c:?xAR9-q,_\;H_/vr'-猶>?|O{|8\;opRo,)) 4fHsowT?k5Z}jڵog%~ Z/lM ӶPp/dz*PK1*z-=}@ |gDX X~*&.*Fp j?^)@vQeO'b +}PxY8y-,Aq%a ,uWK/ ^2TAIikRVȤLd2wXUa}=#5* DKv¥)SHحYdKO~c ^2vwǸw$d.@d={vߺ^U^a1 8eJ^nm+b;0J>yvrںeBH$qqlz>aΝ5(C㍐H$]td+|c CLc4z> NqC$ҿx/ߥ[HpMlK%wX3E<pl64~x@`7Qch~cXGK24L |`o7+'//5ZYTt+a:dSDcМ}"Ax1O$ݿ`Ku\vN/_Vu ܪUKv0,~hhVZ*Uf)-mianzΝ]+ĽI1,wx'jQ %[p͡χ9GT'ۗ](J4(Jnrb#}wF&mF$5o|ɒOǦH$AD 1CMnx0|Z-\\vgK P|nݺeEo/Dv;s S(DڛD 1L⡀xP[Tk׾vgN)jn߾k ӓf:J^h1 nڛD ^xha%2m57ԼP+˲>٣G'OΚ=tckS%Ozz˖->6ڛD 10xf!HT*UƮwZ7ߌ8x`o~'烠^8Ŋnޚѳg}|mԴi͈7b)a+pMaxx *.)5- SQ|ϧ];o9Rܹ eeI?9Is`sú?eee5 q7sX{hRO֬]UZ]]]WFoFFmi6jPs>;Ր=}, jft9grejo5^0xf}rZN߻ r+W鑲:h%ieVh=tNR:7??+V f>J7ic'7bI |?Bb+) #O.A=7ZmW[%#|}}Pp'Ow`Y;=*, IΓx0pɟWSS3yJ⩓KW^i:|&y^t[䲿ZdFap}> "υ78'`{Xtg1ϧb5\1,bvJ^0ϥϧ!cS?8i?x0cop {J%sui%kMx:;K|: z.mlx***ܽ{wwܽi߾ݨQ#|}l1n|>%<u;C*j4E"'쉓 :ϬCG,{ȵj[W >rL|BX,R'O-ޤ:V<|SQ`P,BݺpҔwl$qm,OGnRIss<NG7;;K΍hf5 OKVawkw{wJ»%J>`lGEEE׼$̽dx*j/cW% n?~zV /W{@\(l _Rryū{˞=pGvN`;e~*a2Ē 3f&eΡg}jJñݲO7lTԭ0lrx_;(?u( y xB°!W3:$@y3? _ 1IlL g~* &Gu/[:7-POEs(I@_b{?&OM2; ڛ ;xpJVݸ~]iUOE?yECc?֦n@NNN33=?(fhmmT";v2yc.> vpAg IDATxyT_I.3IXk[)æ V Z{ťV{^ZJ^uhBA,j]YkUD 2 ?NcfF2 Csc|73?Y'ovYn}~A\.י7PTAaK2Yh?$IHHP줉cF* ~y(p^^~ss3Gr6g''.;M RhҲRn޺uv a>>}gJJ2q'kjk "|̖/7psT*5ni08Q?<2)xő;_0`ݕB]nޞ$3޿~&Ilۺqw Vp߳w߂ Ao s/՝M.)%IR*J6=>P(3gNK;*HH]RyV^6GQ׾ שՁ => R'}8rG]l0/z=k;Cit_3wA@FJIw  A$mȼ9[t#.?sv7fv͘>âC^S]ֹLRھ9#"Vnv!_m|ƕ'Dݝʒ؉uxyzT˰E[K8q*s19/|R1er8va`Ey%>Ϝ| rYY ZÍV}l3z Fdzs珐{]_mugo [KEmqZ@u+W}}gg7q hk :;?zPmF.d߯i9%A-N0 &kܲ-fB3gLH?8$IN=hk ^J1[Q5g>}7)J1B16*a`XX@O?WTyŸ`01UJo@jk[ ?&hAQ#dx`IbŸw]뒿UTT &&|tu;zIH\Gq㹹K E'O݅ & ~A樱1{cxf JbNJ#<V g |Ş ͍O=۷ϔ)q>^ꝅK,ـC]1 M܌̳ſY;Vp ~VlY)h?H4B IRN VιptFyEFS1 svv  .%ڻ.X/ n߁(޽76m\?k{J>띋J:zյI?',e ;sٻ+t6Q uuu=_^9G4.=A(Z ~wa^r?(=IK.Q\g($I677S3~&-B;d 6{MA"#?^BygD$AT k]07.^E Ǻ`˽.` ?w&(hly3u|!'gRr{zyu|!{qՃj{{f Xb9i, ۑ͍7 vpAgCIDATx]y _ I*vbI JS̯A2y  pRk-cY3JeљŕR˲VJ˧E2gwϝHq.\*3s)Z =9зk΋m;Klr\kIb]Cd1;RJEF<d1۶I$K%pyf@8^-'Ҷl;~RZkkv^˾)Z+0yl49x(:w'2wB7Ӊ`UE/^AcӋ1,9gI zCm`[WYCܓQkbވZ2<Of \6Ơ@щs[ Uu2۶\*Ƞ;;1 gY|Kg[ӞxE!q0%uCd$Ԍ hu! !l bCyKf0j%go9;AƠ+o'*  M4{~']`+0Cг#xjgg5xC3CY踮."pk)Ku>) 2iOL"D:7S1s3=q3XZ ւ̓FW$MNF3Hdqrh&io-~1!˙;_冉0a27٭$$ PP1xHPxQ_UtAonqՋ! vpAge^NIDATx]u\U0n 5NFllE qu]Epw?wߛ93̙c)D$IB! B ˁ$ !ۘ G BI JR!BoDӴ(?\$Iլ[iʕYvo]?~kf5^zݾ=;/1EQ:|tZ6?v옫[xpD qjtf[$9z<;;id5$ϵb 2";۸fΝ:˰,BQA(St/FD\_Y~ &ݿuێ&^f,+O1IFf5|( ۵)*E$I}c'.]*$\%& , cuܿ?gmڴݫPޝVkuӳ( -<~聁C8D$I 4vơ}}ʚ͖h(6m޶`N !T* #Fef֍*ScNhݺKF#$Ifg[ In6tW<}Mƍ6"ի' yT~A͛5 \~#5aM8b AAA&Mo$ VF0i*zQG֥s%aFۧ)$2EzתUki}aai!/OO IJs.q}R-ҟ5+*l6+%KϞ1z*fΎ$IAn'3JT*]\\DQD%V8p} (J~AEWWW <=_Gl j钅&N.&A{pr$%%A$heB#""yGʄA J Na0$I5kVD }@k}lQ%Nn&| .]hܸRDBx.doo/"H6w$3237lܩcK"DQRp!Q%Ip}v ͚5Yjf$$_#BFIVAV\b嚡M4>hQfRŮ{xrmժV60*7 ;iVg q6ȳ&K0˲6Mb!"ФIӋvnS JVrZ>J*yѢK  IHY-ٽz9z c/g$I8Ǝ6d0(i0LAӧN5f|챳,YbݚŊZYHHk=z آEɓ3 muKAժW|.|Bbb//`oPTqsTU+پe0eRJΜ  b0t:I&)*qܽ׮]1GFoBqXpcO8xHR+: 1R!l۶ǧܲdE42I;Vw3ɅeY'jָwÇ#. >muzs_E h=70Q*ŊHA"$;;!TPA$EQ,,IRFF&ykSjgLf48(&IElZϑ0llED޹Y!u&sC!A"$fp l^eݑNI}Z?Ǒ"E?O?MU@(I9w5/ͧ$!Mq$IH$'ZքܵMI/rćV0aVfukI*0 1(F$Q.(4-G J%AfY}Wq9\Bj1 !i_$I=[/E]JIM0٤q~K.e6lY~>V{RdXXGVm:7_j繯Ek^OHHq944'tڵj7lPNmR](0##SRR8stt8!t\p2229_HewArrN> (T=ާ$8Сkڗx(]an$`ESD $I3vⅰpo Z ###::v}W\+Y\G$IV/Y|-z~kyz&Ib<~ĉSAVժY/(<{|Pతo6 ԱOY|||;֭ݻΟ7\ ˆcE,?guE( @R7+W[oN80l~ ÀOggINT/:~d֮^Qje$)R*Jh"͚6 >͚@(C,XL۶ll$IP]*X0 aχ^(`|cM&#^>m)"FR\&x*|(AufY6._EsמY4ߦېٔ}YFӢy3RM&ݿ}Z*5}9nݸRV |vتP(Xiq-Y׍0Νգ{Wɔυ(OwZzz]l6ۡ{-ׯ^G*hv,:8oܴy t8ޜoI3j+ w)FIENMMz^[z9xb9ay!BشY+#'g$ P9l!##E\ ?пivmM! |EQY3N>k:z֜_CCzbݽk[-۸9u,z(vvvv={ޤIx{LPUii[6wrry ]>yrffclѴjJF5fg (p%2,++SnjпS33S!IF~fbA;:{H ͟7g-Ǒ֪YZ*G8zD>'sP|yrfEFD^ [zYҥ $ PG@Sg>|TZlI|$ 7OMMcbbY2uF\ܛu}͝T*%I" VǎLLJe6!c= lBW7WWۤw$999 /_U x9'v$$j֬@:kqcG9::=r\>ywqq)W6|ljy!9=CyAȹ(5L~׮\q D$yB̟̔u7*hZ0q upp:|e+6_ 1T(!rb~|oe)iڛ7oݿYj֐tB q+ a* $J5sbŊL:yͺI ݚ$ԋ-SIRʊ"ܹ/^,Yd4Z2e\zQgg\IRIyV b8q\DGGG…7鴕+U|+|V*ʿ &I2$IT;w~ ƍ=uĔT؃fdfIⶅI* -kYYeU0u5ܽk;A6Ɩ B-Q›$#G3$I?B{wl>j( R#"" x A# jFcN>[ϯR}l"Պa8CB E o OK6jժrgʖ0CܿIVԭS;33K^R2_1 2 [l@H腦MCm6ۜY3޾U(χDSdb;wUT`0ZOEa0J,_\hhQ+V8szuR>4-9h 3C:|Tum܈O/"3H!4i/5==Sլ^`̌#ը^h4t;w޸UZ%}/gK$FgΜ;|X 9h4"FZywޟ8q޾Fj9׸ϵb9a }/~ϋ-bX5jcV-SO>{&&& 3f9QC^'w}w/TՑW ]fh9Jڲe+,P@1LɣUW7wQ`erXbEHpmR|uh޽k[R%23(8n2fF1M}ٹ}o- BG0,BE}<|~͚ZVV!033S[M2ᣓ'80h_s}DQbYVGt:ɓgF_^ V!Ct JJJMMurry3OcGZM YgdXgΞ] 'h eInz'$Ι=T&|~G<}UFFI9%%533+++;++3[T NyV ĬVjtW^O:=*N={j6itV͛wާ2 R8˥F6 {{?}lhhkҥ8lVaJe4,X|իM68 z=B(>>1٤z 6Stа7o*W8EFX&gee-^ǧ̟ }*39zѱ_^Z(^(?r]{S&O8bjϞ=0(2ʛ7r;oNתYC_2@V6lزxr@4(Q8aIaᗎ=n4;u0{tQUJeJjjС#aᗢcv(UzV-6m,f^!'M5Mm޼i iyTVuŊ%AN<}\͛Q9{iJ7nԡ};N˲ 'M Үm~\]]A}|.8\y̞ѽ[TR=nןbs׷k0jz .aYHFvw{u0~,B9?<ϯ^//۷o/Fp]|Y*Udbp7mMKK͚59V /g8nٲKݺuH 0u9sbooXv+ p_|y :Ǐ?,QM4ڵMXDv嶦jfKǗ.E&&EXoŊ쳲ZmX!Fb86vV-[8::X f9!!a݆ͧO.k{{o|!EZ%˗.ED>{8V[z՚5[, {ݻn];MVY|6i5m[7V\jt;w\~#3# 'p/ /YMD! h"%[4ov\0B㇥TXzch4\w`reO<& 0>:2nQ#eeeWQiH H88eYR[,kWCH"I˱VHUk׬U{Ⱦ3BAQ$@ !AYcYVޟzp_O߯ BQ$I&(M?qjIh,)4E$I@@yc6a?q!$J"@ExMe4Mmڸ[agdfoצWgQ(֯sަۚ6m\P\zp$jZ,o 9a,Qeԙ35hPd2?} *Vѽ|9bXl a/5hd4r8(gc՚h}n-l?xx&O/mWkmس1#9yA<&r: p^HDN6 I߿?uLѢE:ulVmMˎ~HR cذaC 'O^!xccO(<!e+cc : >&`OGʒSA>fH2,"Gn!EQ8xh={t=y4B !i|>TވëuliӺJZٳ~ٺeÞۧLxR'wS»x>W^{=MIɐ_ݿ׷VBYӾ;g2ET/AA C޽{R)%采O%&&!ʗ+ GeZx4i0Hk\\(|BN"J.a/ >}@P`+^dZmxsd2 PD ZZ6A6t.]J 'đIH$$LyU_zM4>|$8a^|oco]Jiߡ fL >6H?Aq ҏ[7k!|E 'Omڴy`$8\H(Jz h4ۧ@w#;;Dqd6%}KQMӒ(IJ%B(11&E$Aȑ9'$$tZ7`~E<ׯW!tQYYk{eY+5jHt>$0 l*Uiԩ3#T*/3z'ul^Pȁ*QQwW²wY޿ ٓYTSRRdSBo~LJUjN>!d1[O>PL/GK(˲UT.\Ў{ \h A0^f!<=$!)Y/YgU@Nexyի['Bŋ4n=㗩e9Qjn%rǮ5kDTSyK hr/LE(aF^߻@||B׮ܾUOdXL G^D󼽽'N:rÇ*]-?"i_ٵK+\T&IRR8 EI$ !iW^uޢ"Ez/V|:Ae!.)תe\~0 3]t:r-4no4e샻w/[~`,-AֈaZ>l6F`A'!7Lm۶оs[h߹+?OEe˖7nfexh4ǏZqOP*d27vTR]y^$a~D_[j zܹ$/D{ݯsg}.94Bab;vӦNd67Qj1 u{۶]kmܺuǕF9૿{ sfP(غu͛8LK:l4jתp\;Ζ;(J:Wl 1-9H5$pPժfs.0hH>ME1:&>|(\E˕-CL2xouVM<}_ݼYӒ%!oޜ yJ\h~6X$Iw֭;>!ѷOΝ:(WRj#+XXχ"޺qFz˗-3v³'r>=^zݕ>oHqcԫ[(&w}>¤ӳKnѢYN磢N8_Ţhi׮Y,֜v\]]ڴjٻwO*4my.|zzF#NנA.U*f^j;oѬh4^ :QVE 绸8Hw 4􎎎sf9vȩg=Sl{9ȉ)?ZuS9}֭Z4MG]rmyog2o^Z&àS  |C ԇgd"z}y<nwȡF]rk/)PII0cMU(cfY\W5ZaXG={Ž<~( r>i.̚>ckWhЏe9ccdue~< ;hUժV)Y VHْ j5M¿I~eV sVd2rik]ru@[n ("]7nڪ/YzY3eꏰc6yAg8s۸$IС# o1wo`-__AtFfV o)'X, !g|'Uc$n֬+W:pO*͏|Oe K0;;;|^ܹjU*^,\xy>>eǏR)F2eJE^&>^~OIp]FQyڍ̬!98 éw,V~U)Uѣ3 q= χK _$%[EEԩc9~i۶՘#6Ȩ}-[471ѱrDAaŋj!h _j'\.BhPV]ߝ?_MTɮsD͛a+W,X xxLf'K iA,P"ϲ<cB$Դ/5!6iz ˢT&'TLH|ǐ$}UAIQ$IPOV IpK_!BQ5ZJ|hW=nߡ+qӦMNJzk6[p Q4a΂ %BEQ|!I"0ÇhͲ'ŊQP`/\Pn߹+Je+&)33KמvHO  3g9;;WX^##A$IjܸBCBF?TJ[URCr2O<ߤIcCƏdBŗRCC8ޡ]A֬ۀXvvvݯ_>{ڕ7o\qvvbFع;%5Sv;9DFʡƿS & TTTwwRmYӰ3'ƿH;īWZ[&0Hk*IAyY*RNRM#8怀F ^(^QJBPTJR9rlMŊ٣!`7jZFق8?+J5&YG X,󂤵BRT*ZRk Bii#Gsuue /]RVy EQ?m޼ O{h4V5VJIΙfW^ Xl28m.^p\f%%%ݺu;򕌌 .ȿ^?B'V%&%ݼykY{zz6oS.+++W^[veK4mHopEd2]xy /_ӻgm7̀|HJJNNޱsϖ$IʶSy4kdprDT*>|H9kN@ʕD^*[3||I;wٴyh2K9q 7pm۾k/=rXFڰhR_WY,+W~aظ1 \b a;8~(I$ Ǯ߸5i򴄄ĕ+nh4*JJq>}.^4/p𰬌ByV\L2u5iZ޾}Ѩ]\]۵BT,YvA.Hv%I8N%GGJ9t`u̟aXFF +ܪTf͊.Zf.,7Ph48Ѽ93B&Çf$HW7WM׬Qm={[pIjU 06ͣ`QWAU*rpuw鄄})7idQ`EQp(3y/YZj77oZƚEQAqq 8NQEӒ(,_JRM4 9aZƭjޢ!#JeVVV2G yQZOR1 Zm6lt'p|E;tб[N)7lH`rr'5uN~lJO,͑||j0Jjwm֬dͬJCǮE)J?ZS( BePiVhNogÆ /VTUw۵{_6$QAi1,bZZ\D|75%l~}>wVZM6.gffec X!PyH }I'G߻o1ʕ*Tիu-߿~_}wS_7 ?2xwuuVs322 *հC4M=}o>9| ÆDD o <*WxbDVVOyB`XB@b̸ :t~-FC/ IHZMW~ۥkOY7o5[6?u,n 4d_?y fcl;^>4_dO:ٳu|Q(hV !<27u(4l Nk? X^̋EQ,[̹+zBlℱ ,ØfIJ,$Q1}bZNӵ7d@at8#3aXƲzB ]aqE\vϯ=rx$}b4Wji~Ã+i]q\-_m=zJ8\xt߿9k͒T6׬^ޤiKϭ$6o|>t@GGG28p AMڹ5.BA^HLLӻsp"@YmjU8999{.0Nyy&>~R`ח-Sd8Μ NNNiۦYy!0EiӺc'l8x[x61Р!@ƚTnG]]buR%Riƍ5|Ę.Z8OtaJJj7ywXT(:ul0!$cgᢥklu,ߠe8d^}&a5Z}6vWB6|>y6IÇ zz}=q"IF(*׻vrܸ߿7D"׫[zÇ896j(c:7oǍUtnrɠk]t:raNN%4I{~ꔉŋʒ.BI@!`0L(,_ /˗-Rtnycc 0;LZfW^2mC?"u,_dɵ70YYY4MSy˶]~K玁97 V-YXh+W z|lQ*I{a޾sWVkPtCšGhl}YYYZϬyh4؅]vR*9^#xyM8IsZZڲ9k4I9>lp^=>f g(JjÇ%KW8y `;8:(#G ѽjh4-Xð }t:b/\'OߺU{/J*u[Q/]~#;H+͛6 hTV c/vvvҹcd䕋i8UVqFk4L6rcVq҅ݺvz(:88E5oެ~:+UtAN< ðٳfvЦtS |ĉ3g5z|rrʠ8y_s%IN r Zo#^VeD|$Y*p?O2W4M.cdit۵9thFڻuZݻzx(h_Z>|ƣة/nаA}=w?3@;)R>oD;ٕcNDYVޮIl#2$ 8(eXqj֪' ׯ.ED2 ߰Trܻk7>|H5wҩomr_CEJeZn~l{r)SqΝf_Fݚ+F?wѭqFzl_ػg;wxyX/_)Ǭj/F2cw\7oVPAQbc~d_bbbIoXhabbbXEYxYjICt޵]G{n4Cغm]9wu}!ݻu8HHkvq$E-\k]?.VIH|xu7,P`mSH.ap[yz6uRʕuzA$'O.\MXDĿlPTxw))}ۻRp~ѕ4u5kX,y^I)Crr\|ҡCN8ߠGż ~Iwi~#%I:{x.=T*oH͚t:֭[)6$Iӝ=w~ *ٺec *EQ2T( jִqܛCJe:\8lW^ϝ3s %M`X5Mŋ;{|͛5QT(]hiR%;w/3̲Fר̟۽{/XoC >gv&''[_(˖.lcl(z۷I҈cwmZ6bؿ-8f:J5ktww7~RppliִqvmfB1}jLL-ۛ6m\ǟN߉ݻ>xkN={tF]~igΘ<0-Z4}Ŋkv7qXјCVvVfV&mE1tȑI)'O.8g$9|(Imڶ) @3fL=tȹ}T(9VN$Q:""ݻwXdff]%Jxk43HԷo<3{μ+پO-Xm'O)ʁq86lD1&d͖ٛݻwSQ(}Cr 1Rm7v4N[6@V.]AWf]D I@b-^~kn$fEu~&y>jRq !^ty {f+_GVݻs{@$I[mzBXxUu:]vV @ IVgۼy[HȅU*,k2&Oo of?|to2-5$Ie xz.StDdGGGن[x^RVkW-O0,Osrr*S?TBq\;~2`P.=yμr!CSRRr+x%i|V5?55-33/E }8Y!Ih 9zS^y?O%SvvJLOO߾cR_S]`kټ;0u򄬬lᢅ$Q$I8mg߱swbRR]]\d@I>*/^5z|2eʔ$D2%J'ܻv݆eʴmۊal4M+(JiV*AZm< :dfuKz]ʕr{yB!K|J.͖Ν;>z|۶>ʶld6ר^nvFԲ嫝#(AȘ$IHC”W87w7{{'O8yڳpRJBH1X˖.:lN[v{$䕧ѨO>;s6ҥ7 {{{ L&S>޽ >r% !D)))iO^sg};E^TO={6ҥJ!)f;vӌFETdXt:B(1!С.bTFbuvq{پ[jQR.3*ʈoirVZ _.(a؂=]˯~];;;z0Iϝ3.7^QZ͕+&MWWfM, 1&.7:nZ6X*26&R*[3.2*;٬ym[7$W[w޿sn9}B&G#jLZS)j57liݲc^A}ܽg[hjS(.En۾9reѭeq5EF=GNN ]hBQ%˷nہx6:vh&;w?C''%ԯ_hq!,zx;lٲAA^x9eڌ5ՋrOqJQ\fU+oXr՚_L;-[*U`X4Z-Bqqo!I@2KS$idDh (4P1N[Li7Wl6N]d-kת~ U~7 ۺuQcq۳k{ >VM^{ǭV'Bvܟ8y (rs`pIHa88ե]3f9x`ONBҟ#] b"9PBu ׮trDn֭kmHHHXº5+K.1ƀ&-ƍ` a)5͞Vk֬)jڕ$rׯ_~cbbVm߾Y3z}G r3g{tY"Q/^ܺW߷nC3u\\}˕+7k֜~߸qXmٲm)7m cX;mZR7oNPfϟhٮR "ď1lm!ڶΛn̦$IR*<{.Um]pĄS~Kb= թ{I\cUܩ̤Y.[e3flʕ+5j \!"k]LOOwppȽ[*K"Da8f2 YUcQG3aXHtsu~xduͪ#G$< $길ׯ^Mf1iM_!>#rB{{3j/h0 ! "ӎNnH$"A(~K玓'(鸸+ל=w@N3ͲNWر#˕-c4" _$al̸ /^.>xЀ&M{yy!::sk׮7[,N?f}WFݏ JeXΜ9~5A~ h֤N_a(>rLR&zW^3-7n:pЁA:IZm4O>{!,٩_֮͛]SvܻwĨ>$hެguh46<|䨝^|٢ Ԉ\?gɧN۷eK NSߺuSi4 wT!E 1Vky* g''d(*33{IIW-6|<xN։c$ =;J.eZ0 AG& j55==CW*N$L&Bҭ{ԴukW4js!p|߾]%egs燏ݴi3O`8ɳV '$>~ IJNiw-VBFf ,H$eHI?]!A*meK76AʌOH4<<$43#`pDNѪTJ[(0o4Uشebܹsbb<ð;w޶eJ={h U0EdvQci1eh:;"0 6 hhW%=-` 1YupϣY/8 ܥA #G cc8l6V@HZׯ@:|g/("r%Ji߾}'!(RԢ(uպm۶C ._ڮ}gIi-ZXùsUTv($ 6 W܊aKݽ8^B^"oI(ڷ[0R}ԱVsΜy`# {$ #_=kߴ^:!80 W,7zx5S2 6xMwmkJ ɤGl֬Sn߾VvƎvqsXEkc;ԯWh2_hR:uꀐ$fdm$!d5#RSޕ*YGwBH!I$2Hu زy,cl٘X2BÂ1 Z c3#C1DFmZ.3;{!tq@*U<)BhXZh|>С# ?oBe$I9>1!nAO>3zZzx?߉x!IB߿OXőƩU{$$ VP*K=z#bIf 1F^2d(BѾCW$XeF 9ӷ J5i8MY~_ w9$$QV=z",,\ z={oÇv4aee0!I...K ߻૗/3Pwͥ˖ :j͟7IF IA<݊/g8VP9qEF6oW<4M5k7عhtZ-B(-=y{{ÇޕyrOO#6+"/_88ӴT@e&NWNX~[ENK4M<Xĉj֨U&/IAJ'W\ aX/&j֬QP[?;'HR ܽw?<⍛QfYTTP> Z*Zd6?8}ݹ{7,ҭERVT1Q*Uժ?C/Vp$1'1%rOK< b\)g/0LV?2Aɯ^@q~({1 >a]οA~Sd$F?Ķ=Wa_Cto.f%tEXtdate:create2013-06-22T01:32:06+01:00q%tEXtdate:modify2013-06-22T01:32:06+01:00gbIENDB`puzzles-r9872/icons/cube-16d24.png0000644000175300017530000000135012161170227015720 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭ#IDAT(UnADGϲ/'SB?!ß ~!A1h];龗ORJ_A LZBDʀn{z$cH7-8Ʀuٌ9iێ*ڶ{K#-UthN8cY,"7!R"*<~3CX ˺L2 Ct@D]/sPjw\h6E:bUU0TUS@aQfS;BU$MSP7"eC}UUu||0133Nrx靏VUl @}T*\!zNNv oʲiF8$c_>3tZ ьaԊH]W,1eksq& ;7aqLcU51c3m;$a&;f"Qdo>a"{[Zq{57,FP%tEXtdate:create2013-06-22T01:32:23+01:00Y%tEXtdate:modify2013-06-22T01:32:23+01:00wIENDB`puzzles-r9872/icons/cube-16d4.png0000644000175300017530000000060612161170230015633 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(mQ!|L *36,Q9 f@U@VUǓT1͈[(BmD.c!f2ѳ`v 茞 [V=](%B6[㵋TRsf+TaLO ^KtRE0~טH&? }={*ȸ{ȋc&&qg$C̉ \W;%tEXtdate:create2013-06-22T01:32:23+01:00Y%tEXtdate:modify2013-06-22T01:32:23+01:00wIENDB`puzzles-r9872/icons/cube-16d8.png0000644000175300017530000000137512161170227015651 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭ8IDAT(5DEzn۞AN.ɮD  9d%؝hFvW]v#`8:9?EaP.֐*-2xq~G}1 p"\b]Xc O2/~9Em yj5+e)e)H)\WRϿ}vc1fǮkig~a}8lBםڶi[j\aU:UJ+iz:byo!*s HceYD x>ŰpsBÏ cM][c,XP?>M]mY۸D h&?r.̪R@)Oǟ]Www,SZnoO_y8) fc <]̳|Z! D"%s<<80Xj1ie a"cr^g!d&."J82WD:M*֜KJiN89WsӴ@`fi=!3sPT"T]u-3p_@Vr%tEXtdate:create2013-06-22T01:32:23+01:00Y%tEXtdate:modify2013-06-22T01:32:23+01:00wIENDB`puzzles-r9872/icons/cube-32d24.png0000644000175300017530000000271512161170226015723 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǭnEսcgvG!§"x. %7H+p8C"zILݝ(H?]]տKDfxF!D9@sñV'Hqe{HGADQt*B)>v;DY<8$FEQzHDy0XkHD.R'{"= L `);cL=bU)"AkGiŪw9<D>s9=00m@ZA_ODD"B{06|Ic hɝSD/h8+5kD^"`ff+ff\0"K 2! NN, V`8{ #kMUU{$Qp|tw1"Oo  Akf9E[YkzUdu {m@"<+ߛz-LNn81Y 5 /Է1s.``MԽS"Be֚td@f?HQbZǓ#(@׮in(hf>\q]fx}}ҥbzyo?X`8l:A"\@lcVޫL?{χck{w;a6(% ` `&gJ1v_El^[[z⒩&G(kk<yژ`-  XKQ@J|s|8EMZBlA5& dZsT]Veg3fa5̒$ tL9D@=}:yۄ BJ?,K1efnRyx/bJ!Fw/|}ce0ԮN6Fs}0w9QMp`B|LdU,g) Ⱦrw#j vpAg IDATHǵVmr }f h62ע񍛪 8L@ڹ^$Egz禅5$N"5K3/qUecodjj٦^E x'.}tSԽZpGӜ6f=TJAɩHL!?M ZksrF<ySUAqY1>~3!stG+ t7bHŰ֛], ҕy*we/:]dEƬ cLUj)0cT; ?6l'#4S8R=TfY:6IT:Ī`lznNZ}5HDLLB3sǴ #wGE0)!sN,fa}RtS^[Xpً`f%_S㒗bwQ*rÙ%޹O蛧G̦~T-%tEXtdate:create2013-06-22T01:32:22+01:00R%tEXtdate:modify2013-06-22T01:32:22+01:00ћ IENDB`puzzles-r9872/icons/cube-32d8.png0000644000175300017530000000267412161170226015651 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǥnE3sff׉c"R U@  oR n!@QEӤMeg=g{uRĹXϞ99cן->jB"}= 9;IBH)X6!_I7S$vG:kCRC9tcSZ)98a.B.s|O+w !19/`NZ!#: )K.9Z+c"s$.r(FvWJd.5@R,pKMJ{iZhw:JcQHQv渢,_Zd``4̜[+ AX"hh)RT1fJ:]`mxQd9>) ]VY۔R&WyL}LsBX181F+-Z SLsH)fRfm/Dn+{8ˣ7ON{>9ys 3k(.oąuJdSnnn:w 2ʢ<>>~F(A%ƨF'!>~r8|JW|QUUۓ۷7P2laYCn"Ҁ}S3wJs?=.7yFy WUBRyLSr1:w9 rv!pLJ1 1=o11R3$N膼˛Rr0,uddRf6RbNԣwBK_~+B cJZzm{ï)mH~3RRⴿwkdKY/QX0BZ@R6_ ƳYÏc|wqHDUE>F$Gk뽞˃Lk۽:QRz{,X`1Zu&N)ZMggN,tVcz.,KxNgrm'@sq&N,S#\ !4OB󑍧a=g+G4oeWDDym;L/K!EQh"jH"*K!+/>iH\b87E1OѧLv:c$MEa'Md-8秳i(XB%!&~5h9`PUM˪>G 8%tEXtdate:create2013-06-22T01:32:22+01:00R%tEXtdate:modify2013-06-22T01:32:22+01:00ћ IENDB`puzzles-r9872/icons/cube-48d24.png0000644000175300017530000000337612161170225015735 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W9IDATXYo$Gޫn7C,Y"! "N QKLjK$ "cǻ*"d%OuM{_'PF{~?~UM;,Y?0LBDTT!LGJ`q̦s{e4M$fo D[IM$&d0wb!Ң(//[>B0x_EPzSeVCCԴMC?Ϙ}NQuIj'uS%Y1@U7!Dト '5 kG9t,uڑ1s6oC.p'} 8`FU Nb4b_ c/1*k.NNnL]BD/+!BΎ0)HU;*UQb126CUcicUcؼZC88ܾ}h C4ƃJ Suko,zAIf@EECF>B*`R%J]+513Os5A^XWNR"R4+lܫHsYc,%&"ߣ>B0>|XdY 6|z\VK*twcc*aBҹ:/E]V^O Ek_y"q d~Șh aA }0SYEKC=QE.Nj@2Q M"(T79=ez"H ]e(ƨ1jgU&?ܴ1R;$ܕM`UԳ,ƛ˲SPS)Ԏp}9)uh Q?qX9>O`ץL"_x[I1mwΦiږZV*7E=Y`x<|w}?kCdXs&!4eja˜|O!" T$q{1#֦~~¶BƆ1ƘV٫6e>^Q`wQߣjt irc7,>\*CRV+˽4uND wͶ,1fo6+PRA(9oe9hzF+XL^ǽq:;}eBݪww?=Z?\Ǭ71z<^ncz[:xVpj5IS7UezD|PlxVW>>>ͦzQm?x@D/qЁ1f2tT ! vpAg00WIDATXýY۱0 UB'vgKND'I'ap_:g5w29>/CD{O4s{2(E"" DQ=wxLGդY`w2 gёA 1Zȟu]EL={u+9oKa5>!S*%Mg~Rp$IDD)SXm+9fX0@)z Љ$rj1mDq:@z@ijwTH2HD{`Q]9P3Ѡz /s.<&icmm[R0sq:d1S{S`h7AmC S2y˞H!0ڛ :I՗QԾRbzs:Жhz !>Eg/0s7myj!RU[?jT~#}LD ˿ji6e,&bCH[ iw۶2 gP{\_pŌȇ:U#{psvR 1ƻ !b: SU˜sE QL*䇎mA9hƈԃm u{/q<5ΰr,qwtTZBJ;t K:c)"QN26bv!:)Sd> hj꧜ӽ W*Wƻiz^S_]3T@f jDg' ?>;%tEXtdate:create2013-06-22T01:32:22+01:00R%tEXtdate:modify2013-06-22T01:32:22+01:00ћ IENDB`puzzles-r9872/icons/cube-48d8.png0000644000175300017530000000500512161170225015646 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W @IDATXíYK]GY3df''>H(;40 \}BR_,_}5ؿѶ-6wmǏ`23}0>POjvkܟ!ҋgg3%}E3*"r `F@Y̜87jbڃ9Ua"rz:_.WF >h $F1F@D3ܹsr0Uu]5]H4z[˒K ܹsmvTm-Ϻ$v"R3bT3n0{fY.1v];;FQ}e޷5 =E@ p3c{3LXB ΩeNq~ѶR fH%y>$p5`"۳ҙG/꺬*$'1@$vXF lO# e\K<}d8b!BM5YQQt]wpCC93oB4@q\4S3NJQixHD.G/ݽ{źvϿ'.<ܥwC]Ὅܵ92$U嫲h!HYV;; 뢮ˋ/^zm{I=y֟tR&UF"B\B3.,c3t]CfZbqz…C̄]zڵ7+|65Rc~FX|v͌fn?jsk_&v }_\׃33,QӴ{{{/1f!Ww͐:")V{xwXƻ'DUAY#\m۶O!h.7n#N*!H[B4'&Xe1"R lYBg9\(Gߐ}J,%i cf&FԿ5qޛ,=|b2I"G"qa/I(3$lqrO{ouxJyj—D9nU09Qnx,ӣo};~_~xI4_yXEQʲUUUUU~g*_xzEHP.;c$WU/W˯|继"OVvơ''>2%fZTY?*2 SωL!g??zk#q$ 8"{GD?} !*i;'D(F3PUooێV^_?4 dnH4}HWU1޽{x<<9wxxжmΦS+˲d[Jzm6Mu9tšf$ɄmaYHú.=7M`0i˲<̌PüAwÆO7s+@y.s2Rd$AS 1[DوT%MS3%YDʘhS<&CB+Ǹ 9,MSةM*I)LWYaFT6ahV4 ,\sǏnݺG3{ۦPF(&&SKϭ)"/\B7դdX·UBROR"i !tɚ'nكm=[ 5Y* }Ju(3u?_%tEXtdate:create2013-06-22T01:32:21+01:00.H-%tEXtdate:modify2013-06-22T01:32:21+01:00sIENDB`puzzles-r9872/icons/cube-base.png0000644000175300017530000000221512161170206016070 0ustar simonsimonPNG  IHDR7~bKGDBIDATx1jW%5I6U0xE Ti;IFh/Ja)B>T9<~ v@7[/3 A"hRMI4)&EФA"hRMI4)&EФA"hRMI4)Of 5crE̴cW[3̠n'N{˫1s>{gGRMI4)&EФA"hRMI4)&EФA"hRMI4)&EФA"hRMI4)&EФby }ws}OSv޾yfӎ7_|ß=QМӕ4rӂlZЬaJiZЬgͪݴYY48_ӂfgjZlM -3[sįtz/مMi3۷cT9<~ v@7[/3 A"hRMI4)&EФA"hRMI4)&EФA"hRMI4)Of 5crE̴cW[3̠n'N{˫1s>{gGRMI4)&EФA"hRMI4)&EФA"hRMI4)&EФA"hRMI4)&EФby }ws}OSv޾yfӎ7_|ß=QМӕ4rӂlZЬaJiZЬgͪݴYY48_ӂfgjZlM -3[sįtz/مMi3۷c vpAgEIDATxQj$7@Qu\Y!TuƟUq ԯT{IФA"hRMI4)&EФA"hRMI4)&EФA"hRMI1k8v? ۔+b=sϛgs;}(0g:)$EФA"hRMI4)&EФA"hRMI4)&EФA"hRMI4)&EФA"hRMI4)&EФA"hRMI4)&EФA"hRM뺮) DZYަ8-h[RMI4)&EФA"hRMI4)&EФA"hRMI4)&EФ;[xH%tEXtdate:create2013-06-22T01:32:21+01:00.H-%tEXtdate:modify2013-06-22T01:32:21+01:00sIENDB`puzzles-r9872/icons/cube-web.png0000644000175300017530000000621012161170206015732 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge IDATxmpTǟs}dMv "(hUDO3 "B@TMj}3--c?i2*uQf6ɽ< y#3=0ܽ=z@1!mH!bC*muw_h=N8ZSh=N8ZSh=N8ZSh=N8ZSh=N8ZSh=N8ZSh=N8ZXHL*9?6_HWSZDkDm(HZkfFMRʡX2@7Px T}maTB:U)̈́AP,yLy<|6Q =!V*g:HwHjm"r]J501y}:gbs{)==aҰxVК^K^].iK?oURDQxr# ba5oܰa]6!45M3iIRes ,Bd.<f2tcz AO񙮊wԦ-Z۫:5` z{.7Ec?eS\D, ߻g'6OX`QsꫯZ`~4x*Dd9ܜkoٱcm\y/m)}}QA9s[fڶ}v\xaMbT&J]G~4Mr /*R'OZ۫VuT-f&^xJ:ujE{ΣG5A_t /J#j‹;ݶJ_sc:&DֈT FJP[jk55+/4>dm ϧ' 5g--0 )Lnm)~P-bks0`NJBDu[5{Cx>T*qS ڴ|!2LaJR,-ZOyԚ b> BxJ)KNV\0~V.=SxWt|l3D{Ӧeqͷ93)NaM*ܠ>+<{uub7o;emdxN' F"")7f6p '7`+!x~I"/պ=ݓN@BD)v}kֶ `+ߪ^gJy{[g,:狪< pu in{׭f&wA#29tؾGH\+MDDRH  BK6!`f!L/x BY(V'^YձU k"ژnd2RJsl6y9_sTlgV_Mx `_?~ {Ӝ9mql*`M45}ʗ^ڜLS=_+jg3\./eN 1k% R(ej\@RBJ)e$c杩b"FȤ5Xkf̚ L d3sQ@7b؟*]zd!㍙w_h=N8ZSh=N8ZSh=N8ZSh=N8ZSh=N8ZSh=N8ZSh=N8Zψd\r* 13QembЍ7I0B$I)ahC B= ܑRaA ̃ahD 0d33A=,=ZRԵ~|nJ'6bf>)A`UJ|RU=`!`)eO'mmmJI r_>c  J(63a$I} IFÆPW*T8ьa.@u(ԭPԡ(06mr u*Tչ(M8?C_%tEXtdate:create2013-06-22T01:32:06+01:00q%tEXtdate:modify2013-06-22T01:32:06+01:00gbIENDB`puzzles-r9872/icons/dominosa-16d24.png0000644000175300017530000000156312161170232016615 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(MKkSAo̙sNflT JNnt \ZqiEKoдMړHƴ9̸w.^tW10iB|GDDekoL}K)a(9Ht?xF ::ܿGbCR4?04t0-v^]s}ߧ`֚[ z}biOOw4d2X(J%!۷A 5];Y/s y_*FGG#xk"J.74Mu sD9J}+=6A ùB뭭-[(rcǎoZ| !"hmi,ΞRMۈh4֔RZ)h x~],󼇏{D?~z.McG+W-RJQDtghp5Ϙ)m[JYni4"BviYdl@A ʟΎ'Oӣ8Z/+7 I0~W*]A06v6V_Bض2cZ|ϟ"))e_o_q._(׽Cp=7M6rj4Z>Z#"cLJIiFZ)ռS0^ܔRY%tEXtdate:create2013-06-22T01:32:26+01:00Tv%tEXtdate:modify2013-06-22T01:32:26+01:00%IENDB`puzzles-r9872/icons/dominosa-16d4.png0000644000175300017530000000061212161170232016525 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(mRK0W9Z,"V] $! o1!.6IHP6l`/` ȇ;2J:InDFr"␴].}*ɖbΙʝ1>s'B'lS Uj iiXwѣΞdD~yB2Wc@mI(@X'uj$4_%tEXtdate:create2013-06-22T01:32:26+01:00Tv%tEXtdate:modify2013-06-22T01:32:26+01:00%IENDB`puzzles-r9872/icons/dominosa-16d8.png0000644000175300017530000000160312161170232016532 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(=RoSG}/I8n*HUG"QPBQЏKşP $8C ? ҦBKMX@qƻorq43hHP H)q0# !Z5cB=vg8HkԱGRklO׮"!ݣTT,{{?CZ[YYM+JRr`X <+VL?/1??RsP)˗:JT_j=O 1Zvd2IX,AggW:',t]7ʜsB`|⻡˗܃i0 Wr홙z}Z˅B;~|ZaADcL.DQJD",L}:8w?(R~cl\n濵u7oZxx8_tu۷To@Dprr* !fc)֖?>j:;d<=+!j !c''NpKy]m ?cJiB`r/J%tEXtdate:create2013-06-22T01:32:26+01:00Tv%tEXtdate:modify2013-06-22T01:32:26+01:00%IENDB`puzzles-r9872/icons/dominosa-32d24.png0000644000175300017530000000367512161170231016620 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǍV[lTs;/{k[1#QZ!6ŴIZ"B~!D)VRABG*_&PQI(JD|`J6 6<؞ݧǾ:|n9k`a2|"*>O'c (lE1}J2a2*Rӧ?\0>4R;o_u]P*"2a R_o~+ ^T{'?B۶4M]p╣a9Ӳ,!d3+J߿ɒ%9g{۷ a0DfǏ߶mೝ{ٲe 3ZɁL&.X~a # `ҥy۶PFDG#Eg18B)ED˖-P@ .("DD$<rG1 CDg.=S}_7k%0$o6_t_j1d-WPa0LӘ3M:+8Ro9JUB?z 4M9JC>r gɓ:MÇȶx7FMUCCXW\[(\yU(eYRJgS;{ITΝW^#DKћWUVGFF2 c16H\T Bg_<89ݦ)r\WWMMGi-N&K̈́FjnnNH!"#JB?I9hhh,+<ښgq9g@(XDDAZdkwڵuV2J%S' Jc 1L&xOgx15"RDֶiӦxիIv:rܚ57FF1c~axc 3,klllŊ@)GnM"(>|5_Z|RCRiCZBaܳOZ*MضmR !ba ,Ji8S>MMQB}jz4 E|gb9Y)eF>E"R&OK#:炽0|L3QN. y9aaQu݅#>pb~%tEXtdate:create2013-06-22T01:32:25+01:00eal>%tEXtdate:modify2013-06-22T01:32:25+01:00<ԂIENDB`puzzles-r9872/icons/dominosa-32d4.png0000644000175300017530000000146412161170232016531 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg oIDATHǕVݹ Nd*[ V2@'I%ɝ!pDAD,eify2 G2Y@UEDzG"$QH{5D(~%.D.U3cfz)/kf: 93OU?+>¼z<KRpykMdj!Uu pԗx6PI""Q/UMyCo,@ҥ |G|('sNE-1pdh|Br4#wo|r,z@D(8Enna3s"d89LPx$f7F)UI1v03435;$_XT5BDD5(j V5떫g JGlYw%Բ.+c6"rB(b:=#o<[DFĊ!ssD'Ax6+Ik-gp<@:D * V@BoP?fȜ%tEXtdate:create2013-06-22T01:32:25+01:00eal>%tEXtdate:modify2013-06-22T01:32:25+01:00<ԂIENDB`puzzles-r9872/icons/dominosa-32d8.png0000644000175300017530000000326712161170231016537 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǍVYlTU=\8Le)EA[(B"p PD}#PE 㳈!$-K;8t˰Μoׯ8C)Odz9R䠰fi ,RUu(BM"uwW8REQU0-keՊE _p0#iZOUM˚=kg~84_ncw!]/ΝS[<mkU!wB9e&1"OJ֬ZYro&|Ld2ijj @&azb٪A!e0~\XoD($D!( *-RVZչwĉd7o]x188i 0M3qZMt|q=-X0CC,((hRF)wo`‘#]V(s;'>v\2U4y۶ƀB-Z:.ʲ/ m4Ƙs?"-DpvEG~-,,dzz="QXv_q u˔s^V~:'3$ZDg9iJi:b_#&4uع#Ifjjౣ_mq)9sfGeG@IIqŴo@xz !嫖P1_iV^ԏ-* 븛)++&-'4Mv6n 2$֊]6lX__XTѣdbUP1;w£mPw ȰŒ1Dk(D}twwKUU3uj٪U]]]״p8̷mDZOb%l[%I$ 8EQ\u綤rh[k2`w`7jk MƐ`alFG&p{ï6SN{;`&I$SJiO<{..`Yu]oo^8)c9?g6C|wMPq<)wǗ쏛.]G"ѶTrBYιaK,>z`2Bx﬎$wp%uvvѣp^)U+}.e-[*"Zx'N` oc/OZC!?E)o_ %tEXtdate:modify2013-06-22T01:32:25+01:00<ԂIENDB`puzzles-r9872/icons/dominosa-48d24.png0000644000175300017530000000566212161170230016624 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W IDATXYkpUޯs3 CS ZKI"Q3T81SqZmg &B"#B*M7sc+ޛvt!{oou]}K|$R~S.! ksu\!41Ɨ \.!eY>/N35mxa3g:{7RBrKSJiO?TYYC~KEQByo1p!D ugc*"zw}IƘeYyEXw۷۷s#xnm9ANa8pB-* `3/>мyɺl@B{+y6m;hW]lo=cjclf(TtםJJ2}T__[RR2wvSjPO:|(uN9sc:v388b 1 $׮bnB*~7jjjJJJYPśꖾ+7ڀT*U={/7tat;BZZ޿xo͚UhGnزeƍwӓퟬwraReq<ފ¤DT*k/! 80j__~iZnUVVG )e4CHB;:T R"G}Ѕ;gΜ.H)cX-/G@`ܯLuDF)ݺukkkm۾CB߿em{oz=U0tbѢ{.$ Y4⊛jp$2: $0xsdꑇ7ٵ iYnn{bk%XFUURt5!dYւ [ZZz{{BaZQjxA p D:4͂MӤGK5M ƒ3MsTvvo! As᜻nA-@Zr E/4qٮ.5MC+]Jrad2G<x</blJ,xܓ&M:w?XBE`4\R3;E^Cu"1cEBC3MbMTHyOGaa!n4 vpAg00WIDATXYA0ĮKퟡe]XBS9,h,"BH)֚뉈'fvhu[Dd w񱴀o͙ H R{J7m}gfRk]K)Z1,Dhl4Wa6YD!f)/svk;:c?=݌,() lUO#8۶f_VCo vpAg00W IDATXYmtwf̰a4@֞#@$GP$ Hx-=*ЪZx0R>Jۀ1@ZC,iv̝nLg~q;;4jƘRJ)Bnº( !!1]+<1IOdcD`[T?zK:D=뮋ZؑǮ~.L_|t ,Kw:y@:JZ/\ۻo/`/mW]m2˲Y3&MKz*FKtJXW8?hQ#GVWUjyNg ̽wnu}F3oMFO֭9sVIhxu-)+ϹYAJ̋7V#FTWW?v=$h۔FKF:tӤ?t$ ƻ7C Ǖ^wk>hx9|޴a>rtر<n;~XO P`!$raa8?f̘G8466=YTwkkkϫZn'n|ȑŲ,;r6k"$kס+d`}k޷ucڴ;wإ%5Y11`EEEqO< ]l:11w Õ%qEmRZ߶mЦ(2mۆ1( iO{Q9!_/@#,ys۶v X@|>P__BnfY~PJ%I HO>2 XE۶3 r[(0tJ)chӧόvƶmEQڛEx`ܳg[ݪF}===(tj).'<|g8P'JС|šNd2'ZÜzw^f)S<`(7oGe_cyi,_0 YPmeS?_EE+bEEg[ZN^^`0xl?عkw"d$ګiKڶ,#FD`T `􌻧ozys[[K&S|Iiڍo|񅆆zp& LbX)1 ϯZ߸x`E>!D%]7R4sSi(^yǝw P4Zo_2RT**Q7LӤDQd ]AejϐLU1Ɗ `Gcr0 gBB<7$%H$4`B È㊢xbjkkomvq0!IR"hkkw( F%pjmsL#-++]XO>d+ZVVlyE1H׿Q՞Ph菟vl}-[2F dY;fX?oLmF4;$Ps̮.U,c膑Ht3o@2!'C1c_x1a&HBE˗jf۶# {T?zJ]769L͙= -f2Wj_bӧOsgt:s뭷L?j35xI ylR`0L 7-FKWԦi5MC\.Iv۶|,  B;ۃ =99a Z[/@/ܴwdq1 v`cd, Btݰ,+LvvvaXaD@ggfaԨ>zN y0qc[:<`du_+DJmSVj \n1MKel..d;L&S Ο;1æs qmO`, JMv` 3ϲ{Ʒtt|*XU  Wo4n[JMhUB'[C\eQ8"^yeq|17?aXoo%tEXtdate:create2013-06-22T01:32:24+01:00g%tEXtdate:modify2013-06-22T01:32:24+01:00K6IENDB`puzzles-r9872/icons/dominosa-base.png0000644000175300017530000002200312161170206016760 0ustar simonsimonPNG  IHDR05bKGD IDATx{\TgB%&x- @I#eK-++ݟNKs4D4skQDK"h*^tĎE =  kEg=Xk J @ Cp b$B D!81!@"HCp b$B D!81!@"HCp b$B D!81!@"HbgSSS!C3{i縱͑dB0TSS3^:ڽW׃{ ?gZrxn{e'o4\s-{fBB`.:gOsQ=d3z7@o!@"H@,n_7TM6)JFÜhRu/^P(vҮ]oTU]]VR)m 9sk{{_~_HB⤺T*2eǏuFrRRҊ+!Hf`CR Z U2l޻v K.'XHSRRt H/_^UUIpl8;zF>@Ӟzꩪ:{{{5gϞd빻@ee%4fjll$[|ĈT*ZmEEŞ={Ȯź43-ۋ!ܾ}{ĈMGG MMM}7MMvbxŊk1HdPV9ܹs5=dO>>r@/}M޽{W/'&&bGGGgg#G{zzYH}=oo~-))lqzbqiiehԷt___;;;__tHj* +z/ ׹Fu֭GѣS B";{ZQFX/ȋ rvv ;wSN~ggѣGWUU)eN讯dgg[3rR駟NX߉:W^5Ɏr|^E=d9rHTT갰:Nlޕ;uJ~}}}gL?|pi~XX͛]h4ׯ_HdQ-[vƍRr3o|LN갣+o9B D!81=]B5sx8L4Qwu&OhR-gYQkIc+j{iH$ru}(cH_s-g0`9 6eV)++~ɯ[ѻF[[1D!81!c <"/Cr 6/Cr 6/Cr 6/Cůi6l]7C"ԕ@"H@]ӥ"|P)*JKo|>e d!r͛75͛7r9GҮV׮|]+QCC#^Z:J}DBO-n"#$&4h zӦMZP[~`bP(2p :V{3ȴwOz7MÆg"R_&̝;wUUUjĉcǎ%RLּ\\b"H? (((J@4T$X4\XX#V._fcCǽaXHeI__߇:ujɒ%/_&RV;JZÇ[ݻ7TTT+/?x?I5uuu/ZάFJRRۿHmD*?iT('MTg@k}]\\? Z$,Í6ϖr8''_|!˵T^'RS/T*UCCJdЕYYٱCCC~HOKpclׅ/vB! !R_@\L+ @@ B$me jo~ݣGw.n4D(J$\_~}t<^N(W(WYԧmҔ7v4V|E1SSc!7K;iߡ>g Ҹ1@O=н{yNW\$o ໦+WT:JrʘRFh6&~ofmLYO);Rcb&f|ܡ}fNcXȟ(7]uY81!@6Ⱥp |Bd]8$̕=CCD"7gʬ" P7v/~3~:PduxuHP@"HB:|W{{"GN=_##<{hdkңhg##<+4j#ó1!@" ]le~~~~/V(4:>|H7Hq3iuTzǏw >0|v7zy?ɇ$Fu-,.+ѭXܹs_|Aanɉ'N8Apb1DRo |ȹ 84~Ľ{_744o]\dXX___?k֬FYc@3񇛛[MMMii׮]{ 6NYkUVXF;Unccӭ[C.ZҥK'X4N2))I,.[F})Dz'&&bGGGgg#G{zzYH}spzaԨQ'O-ѽ!H|Ν>}KѳgZϭYV=[RWpX+eeeeee_-7'ӭ/._g~G?u֭Gѣ㳬',fee]!u|?·ll~#u9<ڥa!ߏe!i7Ү_PP0 gggPsN8u΍zd,zNg'F;qc@eeexxիWW<>d|?·H~XX͛]h4ׯ_hb;|r@Y!@"ɻB?2"^@=_#Ⱦ4?ᳬju8iLyyZ&XH(э#mbBMjʫ>$XS&nF[#H?C"+<1WfQ5_{m4gه=b"F!p~#GT%?|.m!daֳ˹_| .umCvFh1!@"N244tӦM׮]jeee? ܡe|ˮQ?aGuHCF:vxA!oL'w.-1c'|zL6lD>}O*N2Eodeegfurr!Xvߦ鿮;|"&fUKTp<ܹs<<<̙cƌ!~-F'O|dhʁK|ӱ%ܜc@W1pY N2$$dÆ W\QF7-3̷-Z{̈{ҧO }-l vU9| 1aΠA +|藖XQͷ vll/+c?:dh[~]]Nj7663g&^Ǐ+i!NuE@~޽{DDӧ?$Rv|KՖK}|&}F};wNQTt a<<\VCCCRѣ5!/2{{^\-Bgj1lM#p|M!/^l_׹|?K=d\}K=dm_[[{UrĈ}ݭ[kjj@!,!d CvFhuH$B D!<H!b$ $?g0WH$v#Dnn$̕.{6 Hx(cz{[Sz{?e'G:$B]um81!/'K7~}dz:?%i!+OuFگ/x6{hi4={ѫO[> $O]æ~}dzDkÁD!81 )JjuvvT*%UvI.߼ySܼyS.h|EWkW.╨!D|ǧܹb &=F}ȟC:ѿѣG ?~|&bU[+Z^Ǵo)Çmۺޓ8pH$˗/sqqqrr$Uv$cccJeppD"Q*QQQSN%Rv"CyEс>t*r˖-ov\\ͯٹ(ٹ$'';w^~7_FF'Mw^ݒI&޽;##c„6w2~=zjggwsq-OcڻɲNڐW#uK=>7QFn޴5Z%Rj{  =99`$% 4V R0D;_x}$ACK%AP\\Bjt ))iРABW^'N<{,|H#'i߻wo(///WWW" WQ4k..t ==7==]$ZZOr_D;_R}n!(t +++@&TJ%999SL"5irpi9xkJwlO}^}*/>p,LN.͟AA 78}]XXH>deeN/+/ !=x)x\sk_P~~C7;9ckk㓒DΟZZrD" %\./|Eiot<^N(W(WY7=lll9btỺ:,,Y@0sO ;v,22p!:Tv}Ӛ>g v-[vƍ"f?I+WT:JrʘO-6&~ofmLY4us}1!@"I*C]4I6|BoDEx Z%uHR'Fd]C҆ש6|}(3(񚯽6xM1poF{C"+"|Rv'iiixR||Hd]~@=ߏwqk=b$B D!$~~~/V(Reu@>ۧƗSƌ0p5]͖Ȯj>^¼(ow9{Æ 3! .:`K& Z!#"""""?.++;3󨓓SMM tI;.]J*;UHU` 8 1AyGR[*U*L;Rɓ'.Y?G98Cf0͗/_& q`9|T)d2s!O8q kYzݻ 76Nk 7E0񇛛[MMMii׮]{ r|>Ættt;ꕑ3g?SD*C>|ԩSK,!(mG\ <`~#7mllu6tE]tVǻSV^XSSSRruCʦq_uw"iC}hqcc;zv`"5; I/DX>r@/}M޽{W!7}R s'R%q'Ot֭ӹG9bI׭[=zԣGC3utO$ݻGrvvXxrΝ>}K;BϞ=+++kkkM66 C\TT gRj?h9c!+F ϟ, u:uH}o 1Z]__b ॐԧNYi7 002<<ի3ۧ]T#"[-O~@~G;~004]iS'~9̇|H#<!Cp bv$yװi|v$CZE-dzju8iLyyZH$rumF>mw__lI;!g{H6HCFI7DYm<H7DwqF D !@"+C*JFÜhסOANNNr͛͛r#;|Hv"^"J>3,|Lc;(P_ =z1Ǐ=zgIhjjֻy͞R>|ض<_Lu>L&p{{{1$ō)̙D껸@A.(Ƴ^|/Ryxp=R4''ɓÆ ST<_TTr¨_PPÇ/}]XXHp-C@?pߒ@p;;;=_i?-- rD" D.[v>T:VYP+ū׬ qDOFڎFj(}sֿ1(_Xo,'nرc iС|HӚ>g Ҿ1{iۿܳ7|'ljj^rJST+W!uHI;R lژ vpAg0v DIDATxmPSWǟ$̂DTTVemub*: jg3bYJ-nu ERU5bC R IC0ˢ 9x~xIϹ9tMZ>|g'@A@$*$!P! ITHBB@$*$!P! ITHBB@$*$!P! ITHBB#Ǖ~ϤO𬖁7 ;"h_E};NuG׹ `0^QAWqzGd̘n=y_WqzGk zCg@$*$!02,,,++KZ,ł/=FV(շo555 apg zgCe/9#BQVV5Y_TnٲѵbtD644䄇#؞$r_T*ߎ="8͛Q޽{w,j+CCiyst4|]xQ<.cu*ꚴ&m?{>~Id./ ̅`5ӷs'9,|G >MMlooK.LK.Fdl*9}v6I~wh&Q{uVӣ6o*YK-yx}Guޔqd ˯?C߃8lX"DR\)NXxvz-%0p@  굔TD= Ţ'<C KՃ8>?@$*$!P! ITHBB@$*$!P! ITHBB@$>R*lm1jbAUVOh4JqǷjxB|hD۷兆NxQ Vxi቉ я=:nXP񯯿yw|+HȾH#Wg^V^DjcɄܿ;>?pىʕѣGwuutm۶u=пN>5}M6ׇ˿GZ_n͚52L$1' ܺu+^FY1sZ[[Og=:+fcWH_,?WDžC /w9N GZ"eٜrۿ;3g֯_9bP8cƌ}ѣGg'qƂ@{{{LL̅ zKۚA~jbz^OzṜ췅B!$&_"TX^x)GCeG1^^^d2'%%ygG2#),A$*$!P!#O~$>SI~$eX*rVUuygy܇^Z ^Z f#ROL)qC`41g9u"ԞDb\V;%)Co? ITHB]IIɒ%KqXm bS/_qww:e+/EN|ȡ:]3̙34% EYYYgg`Y?2o[ES/.Z$33G*XGdׇcWϟ4v~'NXVUaFdAAAzzzmm\.7LzDfEGE|YR:%brΝ3sȃ-=#R.X ha~mgaF]^XXpd~dgg_3{6 #!JL&t8`~$\z4sc:bFBnܸV\;#چE4aBޢ=6'0'4aoYdP]]v^?sfCoo1=_JNXO D4c~s <#aɝnb:" ITHB%$r>Xb#bj!:k%85*%tEXtdate:create2013-06-22T01:32:06+01:00q%tEXtdate:modify2013-06-22T01:32:06+01:00gbIENDB`puzzles-r9872/icons/dominosa-ibase4.png0000644000175300017530000000312212161170230017213 0ustar simonsimonPNG  IHDRv oFFs+" pHYsHHFk> vpAg0vxIDATxQ u,x38a5q0v?M5-ec t>_6HH$$  I$AB !I$HH$$  I$AB !I$HH$$  I$AB !I$|ι.snǗtu/%8W$_ ;'"u;oG  I$တ9<<Z0o%Y8dx,aP*Fdaľ g里!/?;Fd!UXYi A纮/ %;Q$7" _69hJ\KD#!I$HH~l_W;>R U)^ !I$HHRY%/Z_v+")O{¹n?M ߑU!k]OtVĽeِ}td]O$K[Wmm,֝58dw۳C> J3\F\/my&,Æ32~c{ԭlBHV,!㍻-Kӗb}1-5ؾ#_5!j=!oO|5 n3ڜ b;afK-,geQ I$AB !I$HH$$  I$AB !I$HH$$  I$[,(&Uvh&櫬D.NnU|2Bj4Nst"~ 4T_,c`CCydq~g9`[8[G34 G"۟h>]82>V|P}d$HH$$ 2 pbx~jeJG= r5FJV'vA#}P3  053iYe$HH$$ B.G$ė9?<\s{TYE9cVq "㕇^^z6~ƐG1q`yTKYJ4Xn8"(s~䪉-YBA{ԕT3tm&k|]UI/6Pm3;+_,F>\Ɗ1kyL" I$ABl&}A "[_;4%tEXtdate:create2013-06-22T01:32:24+01:00g%tEXtdate:modify2013-06-22T01:32:24+01:00K6IENDB`puzzles-r9872/icons/dominosa-web.png0000644000175300017530000004043412161170206016633 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge@WIDATxwXW8~le)J'*%`آ&Y1c=V쉉ɫ^"Ҥ첽eٝ|x!;s{-v@ xމw"|zx'·މw"|zx'·މ%B@oBaBoȉPu5-!VJ`Xh&0 IBj:˜f.0"}`Zm63 EY/^X̖L&3LD볲^T!HlXJXU4s!B$9sm^ Q ܸq*d&K#$V^fJU͞5iX`iRy`CF o|"aVZlߚHgokV߯&NE߿o7AsBaMQf-MvB$"BaFS!EQVq0 M|h$B(A¿;Hˉ,!X4MQBA<}=J͆;&D"ɤXbKtզR(Hr+M $0 U6t(dQYNXvF9 5hXk Ð$!t[ʼniB~?I}mש]6S%\.[n?nשF'JAE{ʕ'N8uٳgG xLzǃ2e$(S@llw!A^H9BP˖-SSSVZP-2e޽{cccXE޻ׯ߼u~;4k t!$H>bYߡC l4 <ݻBP\\Ν;/^xՄ;Gٳg\\\BBBDDa H$J$IV^=sD"qԅaIR uԎqc`|8~+WE=<..ҪUȫWxEMNN>O$I ҨQ/r1c\|9''Gק"Ulڴi͚5o<6a0 #H,V֭;X[HbF@f|co?L޹KUwcǎɇڸq#Id7vXRyY*xGD@9-ph ={vrr;wd2Om+88e˖ ?e2n[V$b]zRV|7OFzu7mުT*YxDQdF3{'Ϟ>~ҹcO(=<89ٲeƍ4(88IY,̘1c=zx1`@go'NZVk׮AjՊ}  g͚նm5j4i/|&Vgf3gw(dS<{׬V}qqVea-"`0,V+o_٫W/ZYR}RRR=zT*KJJaaaA,EEE&i޽bc֭ 6tRSS;vxFYּj $4mNws~'Æ`er!&Lr9DG%sNNn˖۴i}ĩ*"!6+OOO^_z*@@TTTCGJJ4I| bW^^}оd  *)BaÈ֭Z:FfA@@ :PƅbUqQ [ɤ*BӌVi  Eq0"I\FkVNAPRRRPP7쬠(!ï*d3O>{0>y4U5  jO94P(v7{^_2FC9(Юb&bD$U\\~zuاf!B݉БJf'>n[( I7n^pd jl`7'BX sUؽHBa0 sg@b>~1 B1@ [8yv!Bn4MRe2i)'DTjs 4@i|JOcwvPt,?^?af[OqRKPlܸСC5k\f͚*((ظqB(.oT*=}l.1\ M2…M@>XvƶG5jҲu~X,+ ƉRtnj޼9p0Vk/^dDF6eȆ ƌh49=E""?yD pXqqqIIIaÆ v;a?pUnn}vO7lH9L&c_ޡjgΚ'$<;'g!{>r䰟sT*T̘1qƭ[^`7= ID٣S&ueРdFcXwb ؼyc(;f_^&O|QFEEEs?::o9yɓ{2ߣGSb1ð3a<==_7njܪ!z% _]lټ9% իWzzٳԩs1Nq`?=yyybb@LaA`Dl4,/xxxjHd0B!D"|R(INS\R+ M XgΞۻmh!dAujX,bB|3rH"]34,=Q5L&3bh4fdd]ѣG8][ ˏ?9`ڵkGGGl)F Zf.>}laaԩm6ݱ Ǽn۶m…B/8{l~~>wpyyygΜٴiSJJʜ9sV\Y8䎐Nk,V ?AD\3 4͚j5O W~@0H"O8CQ'44EL^v(߹k׮ݹsgbbbZZ``~ TvNnJ…K֯߈+Iz }}}03H$dߢ;%%eҤIO<ꫯ8*##cҤI)))-*;Pl6oo˗nu&}ܶ^GV~z?֮nݺ_J$BM4v/kt V eP L8ǏWT cb$I!jyBA3 B"Il6_ꋗ,3wEQaBBOذ]ٜVV|4JDŽdEfd29Ҳl~ߍiVNV(Ξ ntɄz0LJL _?iA8{o)v^ݡL&*)!@:=Y?JkOd vY KYY[ XBf (oL|O%!ݓ$5Zmp$00 M36F&M_Ib,tL-{Ks(j93oCCpO RUMZ:MZVU(o1%ޤ׾ Ju+}I;u7ʴ)_ꉽ{FTٳ%?//))5k7#筆r"D/\jM;|M뽼 YffڲuSbP*B|QTw󧝶C{1Oc!!޸q카bgBa#?V*[#8l>hSd|%nX.}^0bc''k*g[,V$,f 15(F85, : ~g(ǃ^ HH$0RT$9Bdff&O.3 (>}!@ita55$$H(0 Zj999 I/&c5j$Em8!s%((qXAхj̈́|n߾}ff&N\~p›7o޺uk9~{ܹ HX˗322>'BK6lܔХG#l6sضn=zwԽg'ݺu[̚-Nѵ{=L2ldQqqMʉu2޽{o߾|۷ XӵS ʕ+l٢h8ut,ѣEjڽ{7Zq"/NKKhر/_tbgWisg߾swǯ; K !;wطٳ̴}pƵ׮\X9X,\]hrki7o`5k= йsgպcǎ4RɇK.c?믿xp[==]t[nBNϜ ٢C(h[PXتu=(ݺ&ilf䰝kfmڴ䴪0cǎ=zhǎ8-aM۷~*(>>UFӦM{nݚuyN.D"{!I@7$Y(GYVJ)4-˲@QTAAa-x༼z^"ߐQPӦMfN +AVևBJKW !z?iSwܼE!D"lju,v03\tB@(*=/X]Rb u\oy; ؊MV<3F??_??ߢvajxO* D"ZrP(0E93VM.+Jζ31A"H(pl6~0/}bXJ62gm4ŕ!w0LqE b1|AA4m3 4MjY)jz5W^say"dRi~}vM0 RU| _V뇏1,((d6vw~KS Zr ž=j%^w!oWXgr9v)78?v^Pt.77w7ߦ?J7>})eRm&O"B.DOh4-."˿+SB;~zA?P( $ ;Gz ==EQ$I($;"? g eX$I _V(HQTƳ—IB(B>n@YA]~E08gj Bdøk2 4c (!bJMנ+ϦN]GX{ĵfP(J&$e49 *;L& E Ipq4M_~Ν; +֩S/>.xNZbbbu0VLLL͚5rrrر_UvIFI6BF=vDQqq Dqc7n=إV8K>6mN0iҤM6Taj駟bbb>|(J߿GX(jqqq֭2d9qҩ:uիWONNޱcI>>޵k֭W9'b1cf۱cm!aÆ={\rLԩSQ(iiuoq:1gL&!_<;lʙOhl(wիK4^PXXCdFFFHHHVVֳgwvy`bVuĉYYYf\_ &N8d#ռO:{!| !DRZ5P 9^l֭[Ϛ5KJRټ|r< %5vnݺi4///+١UVժUizܹ%KPr%͚54M8p`ӦM8͛7DR455uСU)TԣGݫH,x.Y(X`Qwޫ[N7Yxa1k]Y!ɓ)S[qǶM ][%q˖->WF9vBe25f̘ݻw9r)t7o>|8Ν; l6_pݻw7n|%FR/={ܷopKWAhZkԨQNmPTNڵC[nŞN`8aN 6-$$@0LQQ_}\.8mX.--awp5L&\ cz,Fn:r(rӠBs35H+I{tʙC:oG$ ł]@&)a&i! %Kz݅\ҰҢ85M!@p8yT`h$q)Wv0!SC1 GPSc7hJsRC=;h_c-B= N Waz:O,nQfMZ]9R_zc;iرbakyz9"2.^W-[jgu6կ>r0DhznXܻLoԫW^=p[_Sv5,u,!p5;lX b ._]! P\e7fjs$UTTl2݅P^<<y~t㼼0J4/>lauݼyQ66a„7oܹd2a/0j;v6s|r҅ l6US=5j 3gNՍFcnݻy}Fҥ_|jUVÆ 7o}ѣGs7nL0s0 # H%?/] I b6fV;Р#G 4}Taar2&lHtgϞaox={vZJ\.OHH<"hĉoҤСCCBBCVi,P(y ER$A7]{VZXaMbJJLh-3F*.]422f͚=z􈉉A@6\.ZR@MX/^$",,,007'''ܸ-Z(F) :{lttRjne0//ŋFG>s `5h1VRD}eXS'N􌎎ƥY:mJ~xXʦ-ŪM[<=!'Oܽ{3g333-[*\#w~޽{WZ?caɓ'7oŋ+W7nF=`jժM:n ũg8j ѰQ˗eq[?zyV6;'l8wիcV̝;Ν;MA)SyfxD͛~=q`ׯD fΜvZ/_tӧOBCCU*Uaa! ]OO8ѨQ#__gϞ5lBK`e֭NKIZCv5kv*G*ba^:hSӲzR²TJR:C 0cvh-{eRi]f3bo۲q?)4cS^ob2 78U6I:~yyy8pl/sP(A<>{^53? HPTfyqYh gt8-//YCخY ( ċ$Mc/ %rrrEj2V]b!qgk؃B B.b\òjh*Ip`+^$8rR\}&׋v!z)\.( F#uIq{\|7RFlf7Unq8÷mۖ~޽ŋWV 8H,O0իsn|{>|ɓ''Of͚u֭Gݻ766ֱyB05菉{k4e&NdܰqGm;4 iyB׮]~zϞ=?KiޢLeXs&M ȑ#R (=z4gMF|']:OW\Q(YYYGz_TTٳjСCϠ vl9~⤏n&"oܸ%J4;'7%`a/]$G;Ǯ=F'B![a|] n?wԩM6+^z:p`Сˀ !֭|_zm۶IFq%&iD\TTtMcc9Q>|~/fIÇ}ݬiقâ+q'(((***<<lG* WG'{zz-YdBIPFGGrniّK%>ʤfVR鑑qБv:?x.s}}}7n\fͤ'O:݉P"/\sppgju:\`/l;wܹs{&'':tFf0WҪU+O~~~PPӧO9=Z~ HHHؾ}ȑ#]f:!* RI@/=ETQ >mO50 &34$Ik׮M>yd 7|7iy7n:vDXf8𧤤F8q"00M6SL<FDҸq b,(e˖ݾ}ӧzQT,PHHԩS:d0ƍw5/‹/ݽ{`48q*$$8,KH MӞ76iyֳ 4S~Ϟ>}F|c׬k/_";eAc'EG}W>|j-Zn:NW{]pZ]|rzi Ra;oowݛ3ZRD"p)>OX7;oݻk֬1~m>Zˊbr}6[``}~۹KO~|4- {WXm۶gffκtT*ݳ{'B( {M0gjlpTȿxb6moW1Gpsp,3f̘1É,t::=e+"7mI uII;Q:oס};FIRVuܸqƍ \Cgz,@=R#)x*/O8B@ӌӥ蘛v)=~ GyR(u"\ik$ksʊ$BWG)!Bvik .+{yz-j> |&{}||Fs-!0Ν!hBi__ea0#"Ng;Mӽ{lٲyNNn.۴i])<X,^^cnjf`ݻ=m} W_4o0;K ;c=Mĵhb98A/((D.\wwӫi&fuz{, ¾o  Ú ""֭SY!SKuLiJE@ ZnYnmWWW曺 K@M2Cxw>낂*"TzMEEEG߲6pw}"waڶwVkʦ~"^^V$%{ H(((ܼeۚ_Vbɟ^Y>XBZ׏dONNal1RBm68[AS3;[U"!Z֝Mw_q/4'P%΅ h4K (+#HZ-5>hpwa@Cv)QT _+ Zc (&jbT") $jX,J%|vY BaIIF B2v"m۶W^z1kĈξrJtt4:c Iҵkfff:PYYˋWjjjfffnnҥKq>!ѣe˖KeHl6/O+V5kƱ-zHOQc4m)ɓgR)Z,a#FhqӸV;u]C,oܸqƛ7o^nN`7"h2eJLLLNNʕ+ׂ:qFEFF%r6mgڵcOgHH̙Ӻu뤤dJ {QV\?u⟝m\]dRnߺs$tab0-5kر}ӅS{.^`0(8W*:wq.ck֬y>}.Xݻw߹sgvv?w)**+*{00͚5h4ׯl߾W^7ofJÈDCN'ɾrpV-n0ssg mo;>LomJbyy*zgI{ WΞM㼱h0bZ]Bb{RT\ܵK'{._1L,6ugǯ^zҤI+V(3R3fm۶=}4RgF1cƱcRRRFcq=y/0˗/[l R(77W*",KZp $hts;wV?TڸFD15M+JwNi}=cL6mڴ糧Fb9s&O|ȑf͚EDDY8_~Yz]7o~lN;wg͚5mڴ?Wv7b;{lAAAjjuo ̞kEGef>|6:ۥ:p`&C_ذA,77!.)zȺuj{zzh-J%Kǡ ݻw`/+ ]v.]ܾ}pMZr%0}̙3qXH$Ν3qqqBԩS̓={Μ93**O>pt  FuV\t;eÚً:w2̿AѣFpF{gd4y{?SM4I~rϯU,NoM)(,thnTJQ#ǥRjh|V \$A-VVcwy:]/E?]9^ǂuA6tCR/v,Ρm'b*i%!*:`\s`68zB{ӃK4F؛\!F&ժUqW$ ׫WW7ˤRe Pcsl6sdSK08û|yuaz4b_1j2p|#\<'9^IҺuܺ} {1h4UVZC͚5f85:}NyFӪœ$aF$5RtDV ( cB(H*zjMY/^0U$oAZ0{X/ŭ&~ P$VM>&2-U_l6{5X,(C)-ރLD7EU.Bn |-׃g2̈́oމw"|zx'·މw"|zx'·މF"Cj%tEXtdate:create2013-06-22T01:32:06+01:00q%tEXtdate:modify2013-06-22T01:32:06+01:00gbIENDB`puzzles-r9872/icons/fifteen-16d24.png0000644000175300017530000000124212161170234016420 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(]R;sA ;/I!3I0ρ $vژą>$Ql>{!">f&BbJRJ]DADu];sLDEHB`8”RӬDm[U]7MӨ !+b2懙sf}?!1|a{3c3+bXUs10Equ{jDĈX 3##U_el$K 9gfR۶e9jӍ{`{?b`vb:8{Z,YDb[lcqcEI ,y2o󧏈 ("),Zl>_7٬iGfMsR3q6WUUm6-3,+|lgggff[gŌ~vtg8D̺F{ff=)G0k,sq7ts%tEXtdate:create2013-06-22T01:32:28+01:00 %tEXtdate:modify2013-06-22T01:32:28+01:00uBIENDB`puzzles-r9872/icons/fifteen-16d4.png0000644000175300017530000000053712161170235016345 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(υRA!NOg=A4 ˈ,3Yv:W>v{t26?3" U٬F02sj:V=XA?A7 gql/N]vv\AW9?֗B;%tEXtdate:create2013-06-22T01:32:28+01:00 %tEXtdate:modify2013-06-22T01:32:28+01:00uBIENDB`puzzles-r9872/icons/fifteen-16d8.png0000644000175300017530000000124412161170234016344 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(mRMOAzp6!Ճ`o, {ݙ靮 dC|:իWsHDSDlιOmFZDDt0 UUU}Ab!*mQ۶JD1vM)_Yk-01"}e2P*G*87! "ft1E bJ ]$j}1B C]sΛ=DBM;;;9uXk"|>nnn~ϝs|97 C ("қz}tt*T58wumۇ?ggDxCD9gf!mn\1>bf6M$ê&ƶy= M s]W]ߟ[My7M0]@Uޣ1֖@9ҺRĢ0k568%tEXtdate:create2013-06-22T01:32:28+01:00 %tEXtdate:modify2013-06-22T01:32:28+01:00uBIENDB`puzzles-r9872/icons/fifteen-32d24.png0000644000175300017530000000265612161170233016427 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǝVMl[E4qDEu PL/*HHEpBÁH$$z#!!*ҨJպ ;{oΫcb'V;;3|3<܋!"QW "n\@'IBD8^ՆwDDDFkqWVjJ)!@""CD%u7s^*pGVsHkyHvF"phc==ʼn}u 5Ӏ1|ػjuPgi=~߃ ff''gT]v*يdI)+r__e2uB !1f+鵋l6{o~֭FG·aCwk6m۶J%ku]$qq Z Kwfh цX(Jť7nrw@Ç0ŢRlvDq<ڵk~W_o.rGA= RZZo(Ԣ6GƁO͟Z?<9rVyG3sι=n/lY{f.\8uЃ;dΞRZaWWvPA2Q$=WyBa0Ӗ-z92aI\~V#qg߾ǒD%I25HRJ!`; |>)AERZ\g5;U0dR{EұwѱVB&%ֵZMkuD#bAH}}#Jmlrܦi>Z"aqGQ.W0Ӛ7V&$"cLQ)e lT$h?>z#)%tEXtdate:create2013-06-22T01:32:27+01:00}%tEXtdate:modify2013-06-22T01:32:27+01:00ūIENDB`puzzles-r9872/icons/fifteen-32d4.png0000644000175300017530000000131712161170234016337 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg  IDATHǝV &iqI&Q&Iz?R h1~4GK!۶=!;3ef&물!uՄrqAD<3H7^]!_>!hj(hM38!UCΜ}<|yx.jD>:{9F0Vm+q h|A_.ڇC_?*2,A]6ʅ8&Fl ]''tRWD\#CGlGoZ׽]B$s&"+ RJ)m=R^˲Z+%]Z6 R2TJٶvѲ,DRoAUz{<ϙWs=hk9#DuHeg:M۸ۣW?!Ҟu%xZ덈r/o9-2>X$@us@Fa}kn-bJؾEIO ^_D/?$ vpAg IDATHǕV]heW^?{{nro7M'N2c-w) V+>* ""Xt(8:bS3 In$gﵖܓg~8sk[Ϳ]:iqntҵAD3ifbbQnu3PUs:݈!CĺG{Ռ`="*D`i<Q,, S#Du͛!Q--K;;7$T"PTk퓙wUM"h7hy_AR3"Jމţ7h{9@J 3hk_;88ga?ٷ<3Q T9>::~K.:/?p8>n0eT(EQh&c眚ZB$h4ݾeLL{{++g"e2s0SUDt5!djlBsҡc@ȍЙNu]WU!4S%"+YBtoHz׫??cdfD1@*3yetcne* IKynn>^ޣ#FcMcnZ]}pwֿJw4tTON8D=,B-*d]}?/ͿO=εMq` ) *2SHٹz 3%5UC~ۣ(sU3#W!pS&LD;y~;ϯ_^\\\Y^zU]t=ExKuW% (J3CRBO|# `n+_g0r pݪ&BUK϶*!a9:@r>upQEQ033M$ !..IdaNn7Oӈ71ͩʞ4ud; Spf1QG%%o&f.ubjĔ vpAg00WIDATXY]W̌ǞnIM*@4-CP%* Vm)H<4iU姨oTgݖ&8nc=㙹p7c;qa-{w[GU#xI)$L!/tB4@s!21Z\.ZZW75Ҧx} F ~i|%BU A)UDhcG76 URvNFi֐DH HgH1f>LJ`9$D@*}QCYsWBA$0M>nmByw٬)judyӋ:IJA\xIJ͞[>=>5۶^z{4 e1q0BLouvщ=mY^"(-[g2|o̾{޽:LuJv۝0%RڵZ=ʇ!LHtiA6M˲\UVc# ^HidR4RsBGJy@uf@U&8BI z]4BX)*yl4W@hyiZ6y۶rU;}RB)4Och$>'Oe>tIFn'!9/.mmCg`uos|?(K=]~) PJ)$Q2cqcAebwI)0,-,;֭{=^ tSJ4Mܾo1:+{/p.ŻXlk?|o]j4yx|wyҥ#۶iB|~,[V.BuݛoGc cOEjE;N p#Bc4_ҁyp>=?FJ9334V(>`<8&1MG!vwRrcǎ[XBOt'zi.Ç>l諚1V \ks%eH"LorfaL:BH7 $eV+jjjDslS]̔K%#c4RkRB߶m vpAg00WIDATXY[ ՞KvKÕ9d &/_+[!ȧ!9S#WsB'Xd YK2Bë~'!ɩv^/?2_S3aR׶HzPJ.KBL6UC:Bzy℈T4pJ,!cl&)!4ii:`?<"J9-؎">N$ړC\iח_ O|| ʌdDNO?6C{ vpAg00WIDATXY[G?׻^㰛dKR  (P**QA*VٖPHH+UU+  R$-b4ήמ9Çcdc;g;̘?wc7;Z_!zED!<chġLf0( `0s*JZ!!ZZoZ`ضqhhmc7sdR,I3pέ1b$$|w.OOlZ Y !!DG!7@GJ=f!"XpMlivt(ruptqCh#0DDY9cl&BSN_zFW{?x,"BW,hl6VVVQ='8gI\62``#G$މm\,//9&?20y<%knnfOoȑml%pZNAL/-[ՊA(O)l6jE~-,\ڹiR3ֺ+}m;1FՆnlEbq 0&3Iaq,#bZo-(T*RJT-RrnR-//Ab֚>H)cPJA7ZT*r RJ;! ^Rjn~!ܪp}~PK#"҃ (ESL2PJpa "Zik1sއeГ~֩'y*2dP7m7Yk]Ҧen%4J!R=q "Jkkk-\GtM9ZkRk3}7~f+wW*.֫Z~n&\O8TQtqhU)H~D)pt4_,oMm81^۳/{ uPxVֶ] ΘVӷ߾k .jk_u#h jz* PPZK)Z^fh֝+<^;mr3;wܛ> !' ! ֶ[#@zUk׮}u9-vo9;yovp+GԾSCwCc09g5=Wo׮?~߹<~Pq nR)8+.^8?o]|+TqkqKKu`үsYZF0oDTB8WG].}i~k?!Ο;A-[d2ٹy6uGJ-\VWj[) 0 8q *δ֥ť'jE]䢡 H)tM1RcL^[NZR j5Ƙ1V1/و(/ `\PoI!~x] pUDԚ#WIXcF=~h^՝Rg#]8Ka 1kZB6ԁOƥcU!c ^pY45$t4sWפ  !cL<*'Cbrbb)pݻww 4R_04pQ?pu)4\WЧݨGvrFst=nHmSh)nBA?[ }%tEXtdate:create2013-06-22T01:32:27+01:00}%tEXtdate:modify2013-06-22T01:32:27+01:00ūIENDB`puzzles-r9872/icons/fifteen-base.png0000644000175300017530000001122112161170206016567 0ustar simonsimonPNG  IHDR7~bKGDFIDATx{\T38"۳јЏqV_[[gf_ygcn>?_f:"9b8`"$ OxS&?P.jF՚+i00;cQGfΘ֣GwTjaaٯ :eB[GTa}HQ#Y0D<{ BWPh \A+(4pBWPh \A+(4pzhZmu<-IrB_OJAĖ$̌ABIDsf2Ex)J#߁chZu)BLğ)4'x͏ %11ZlDžcipXhxBWPh \A+(4pBWPh \+zK`]#1Z aۂ/0ICvQ}}}Ա r.]K/. 1^SG{!qh/]=V( ʂ~cV&tDThE,߻_.mp :d9Yigv h$&y+6oxҋ^rWQhFӦj}vgǰMS]]_QyLٳT*H$fffD*#<͉О>Vaaaѻ mll$I$[ N+yx/}wUU=]]] B6Pnc^=C73ӜrTO~mw$)WFDs+WO,7;-I/zݻW}nVtDQ':;;d2 ;(%5I[["^*88o ZODy?Ο YYD)xc^=Ri=C7QffH:(tKff"jjjb2YgIIIS( uӧ2G]c %Qz~QǕʂ|ruD4ÝIɍ-a%%j4tK$cD7/w&MwMݻ!足%JeRYfULL:9.K@ner{Lh%}ܥKoa,ygW\rnْ0L% }dԜٳ+d2Y>2_LH$KܼkW)}N>ԏIǚf5Dy'ǏJ$Ǐ;t/lH&anÇNΝ[YyhKhȖ)4n>:cbB \A+(4pBWPh \A+(4pbjim6&<&a }=)YT{y [03r % 1EqYh Bi;ݩ﮳NyDcO"OK=DŽnwb;N; \A+(4pBWPh \A+(4pmTXX9R|Baa?}l};wgJJڃ*ӧ.|k`O*U]iS,^_,B|uv[^x-reMMM{Gj_|=>s}?߿o0?MMM˖|iIIß+/r8 "rvvzwɢsg0[ˆhBӞUUzkwa?/'߸ٷO/OegDۧOWy MDE .;cpBO3 O3BWPh \A+(4p|=Zo GU^yeI6G9::vԩⷊ+^[  B3=V՚& 5 8AG'޳zSsrc{0QtEEYo22R"k8vP v-;ѭ[؉Z/<?Ltۿ+֬]GD#-Xlr#|n?}jăKXԑ3]*ZXX8;;{hV`̈́6|GxQ#G, D/o d?#~[ip@<ګ#SRJJJe2A,`" Mk5~udCCCFfVFfVCgΘD]%WiK` ګ3Ǎ}b]Vaaaѻ mllXrU9ڲ98V*UF)(_Fq]֡DzCP0n(t\]]>ZA\lLV͌ȣ\]]6| ͳjr&pѺӏ\:="|))i"BY*USgg׍I LLBDMMMEbyTcƌ>u"Uu_:T666+ګF{& 'N~l̜~~\0 9ZO,P* fULB ۺ]\܅sqt/̽ ݺS'Ξ=y{h&jm 6|`a Mg1Och)'4wŔ;Cb P( Z8BWPh \A+(4pby#3?nPK?>FQ(Fi&4 "a <CSBWPh \A+(4pBWPh \A+(4pBW1|ᛱIENDB`puzzles-r9872/icons/fifteen-ibase.png0000644000175300017530000000303012161170233016737 0ustar simonsimonPNG  IHDRxx oFFsx=E0 pHYsHHFk> vpAgE>IDATx_L[Uf("Zx,C2Ynf:݂3i187gdc@"'5( q*|XZ&>d_w}>$x"z8Ews}:. { :Mck^|A%(=]ǜj>=33c6n<~2KPe1>h4GDfiit{5Bi9{U_Ns*nDKWCq#e(M(}~n2[@h%l|Iƕh$(qfS@FD#AHZ {PaAiu$y {UDDshT)OH4$ FD#A Hy>Z~՜!0'A݉زu fќ@u Au `+'Ξޑ7DQ3=wW>PQ[D"Ftu{60?'?jbRpm4nzp[Gv>򷅅K,!---0ffl+؈¾ۯA"TuD]FE)3ꘜr:]P]Nihkkm)..ퟳ.J9JSM'rû?TP\55[P(r%EKSQGer)k4|IIDo;[4V5ssÆ|֥)McZJ6>3xL*سQ(:F~@f#$H4:D˥jn.jsr [6y.##C4ꐋS`0'-;}uhiL|K\n=wd*Dը됦MϖZO77Un}ȨF(JHIIp8,-ͮk[=&Z7B]lxQ]q |E[w1iX.MoDHm4m4'x ꣑HfZaL4W+y#iv=Y,}&O)G#A Hh$H4$ FD#A Hy>D5'$zFgq vpAgEIDATx]r0g^t4|}rL͠~s쪈瑐&!zv=/>{c='&BP4! hEC(BP4! hE?#ֆa(l&I)eZDGDPY[4*&MnxsEhT̞9pɰƪm!LtĤ/kZ]劉0WV&B8FgF>r#&D'DP4! hEC(B=N K Şa!NKkSPy]Jv:爞WK>twwGi3~(Ǘ. ]o~riˑe'c$!nOS#U=; EG [y #W[Px=9AG/ǭU۶P2&@vM2v]z舆l?N38Rtل&ZX:  hEC(BP4! hEC,:>WOM5crrLS}IssN! hEC(BP4! hEC(BP4! hECMq%%tEXtdate:create2013-06-22T01:32:27+01:00}%tEXtdate:modify2013-06-22T01:32:27+01:00ūIENDB`puzzles-r9872/icons/fifteen-web.png0000644000175300017530000002152712161170206016444 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge"IDATx]w`TUֿMOfҋ) J+Eb/*kCsW]PD" `ꮂiR 陙LrI!f= k~f{m9pϮ_AZ3Щ@bE [=Q!%;w*/+$=A)>lD޽9&Iw)Bڷkסc&B "1!D@QTj! !1ċB) 1FAQ( I3DUQ<9AG 899 95G{bUOq:ѦV6zգM$"cq8!:,819B)h)@XVTo1 bˌ1v]$|>A/bZ0&p8Dt0Ij$ @e9"Ƙbq8@ (jZ^ˆ֬߰qӦoBl !e돟|Y~~+uGzZ9焐5k>ݱ,'&$ Ri7 {;/D7Wi)))ybc[w+0Ɓ@னr3srR0k&&&|9O}1oM \97Ln_*(8_o>[f]1ƍYj=wOBݧ!K/>ftÆ32W^cZu` x睷\oqwK}+))PQ1!D&qf{٧1(D#OKKݴn9&4r@ xN&$6-a(}٥mlס_i~Ƙn\֝wֿy b0 AZ,8^o BH"g\wաP!='m٬9r]wKnS={32{(Q,+B;R9T Aq u{<5?0<0#.C{8pi^w܄+>ɷ\[E4ZlŞ9^yr:4MӢf13]3N'033#33CQ2,`黏?f~c_pZUU )n1cws{$:q)))2QH)u:˗sݻpΧѽNMOKSER[n/))ڸ>Z5eQ›mKbbcu=guF޹sWQQq!_dc.+/>? ιl w'u~כ9c6Y)BDHǎޚU~$1#vSز1޽Yv8c#B0|Ιj-,<ŗnڋ.(:V*$TWgϚ1чcQB0 ɲrZ7AR !e/`0BE7ևᢢ"I"-1mg HK=< +Z_)BYeYnP_)a!!Qľ#5WB.Z=TѦV6zգM=Tpa4SQ_"l nss8l롯pBBAs*@$I!J͎`%8$Lĩ cl29ARB$IdDBB};wlsmz,8mZ`Ç]CEUS_3TU}W0NgaCo8 %^zAx'={{5W6+!/L&i b{o _k۷믵Z,QS c jC**ucEyp}'MG6m|6ÖU|WpD~4 l2hS311! YV:vhݿ Ú}qԡu|BY>̎O?Ħ_u۷׏1|1B|_ym߼؉Ml` >~qѫlJqc:/>b8z]RbptKMMege76 E"܎L>}zw!4lam}b[Ҳ+W]:nݲ> qBHUu5pذ3;ٿ*J:ɡ( mx^sUfFFEe/ܭ[vrR{,c6].+33eDJ)MHHX`(tj9 rDڅs~ߴy9=$D} X\Gjĥcu7Z[tT*ǟ2'NGO$RݶY2b{/bX佥wq{K޾Is~vϿZ,Zyιbٰ~7;̘paᡖRNGBnիibK,O=lvgs]:Dm>>xЀgUVk4f~3OQJA}e+>߿ߌFe߰S jj oc,>>%Kw9c4)z_k.bu!d$<}/$m{QQqEE%BDA.BԷ(//L^RZڱcIqaBYN 1ARfv^n~r%&&|Xx@ xmyLĘ>OXOFCiBšByKKd*DaL.8 O?[^Q!\SoMͰG"ч*ҧO?xŇUWWӣou>O(%46"v뭁$%'QHBJ q͜bX, X,~cXXoqcBkϿrmNy]1i^tIUUeE&dYƎWjС/|sq0E-6*TU533c]I| _;nhn3 WaH]!c,˂oTNyeeUN1+,Ez4)R̮BpDk'D̸xǫ*0MZ6{oP(!hlh!s#*I$/` K0t8q!<}m{ _8?yQ285Cnmh9N̠MQ1F::2 U$2p9=% ) iCuf]^bSYQD42} gn &/[ BHMӎdUeuDc䳁/ORRN C4 vm-vwnfLvᅃ7ml6ކhT(6biTMl;fs{rהۗv׮]6mb1+ MbrΓ223flHJJ$i֭rseYA|qŭ hl6;ﯿ?8>>nG_{HD-7땼 '@4G{(~B왫VٹkgQ^s=ztڀb򤛕ݛC,K]1^#JcHj cԟdJ벙$BH Q`f`65p $˲jb)vj`!Bb{(r)-jww c(J$#Ŗ[V,h$I?IHHXQ[WUB6/>ROAWϼ55O=E]PSX̕U˯օÑ/tfH$&7\q~~f=4jnܴyvXOX,u(ThtLiZjjCӶvہNzEEW_u (d\|s/;;wC!FZw}kj!1MѧOå`=.g!`a x䟳Bp A2o~k~:YV\c_zYdׁn;&˙ӵfa# Ɯsqw٨K/8`a_oL z-ƘlZ}@( ,xvHII[>NֹSvÍ&m.u욚޾=Ϙx_ ^@bbB#r> 陙YY]¡p쫠XY{:As~Y0P((t>"8w?aÆ9xͷjj|?HR4M Ma1D¡0sNgx@l63ƦϘ_^x(j*`qL&{Օ8΁bNKK]ٳfHHУ8M@cQШi4Fs8RaW<8̜ر3*dedfa$}|޽{9v{P蒞1 隦#͚1NINNOO+/!LMMu9ݖeY/xh#U˗7~oMMMMMK ?UiZrr /JJK)._;?gҥI55Ňb sl3z^VJ>믽;o[pѲ+}>ƛ&M34hP$;~[:ދppʫO>tFF 浴-'p7JC`(;tҥsxkj&]>p|z=C^K"\!ej6~9g! `WL83ajE׷Tcl6"E^"'\1cC@~AdDb@ s7~UWB~ECʋ vMDBh<:1缢h9›!|ZdY ]j\.N;V߫fG\C,_]YG+>'33s3ul@cp`T?x1)K4 /%Ј(666m*lhSaǩU!oeDm:V  O~Γ1!#PD6؄!cBcQaM@U5U Ҡ8Tp#P~jC@@ܽir$'gatzB6Bk$vD\D c¨Q6 k\NgbbBx7LrDdggss;uB$EӴ ?RU5!սY$yAp LN4Ĕ}#2%ޙ邏4Gea8 '4ӄު41.)ٓF:ֿ} h;ڷz7VK/%tEXtdate:create2013-06-22T01:32:06+01:00q%tEXtdate:modify2013-06-22T01:32:06+01:00gbIENDB`puzzles-r9872/icons/filling-16d24.png0000644000175300017530000000155012161170237016431 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(%ooDߝIǤZ[ XnIC{!|KXx4`hMI܊vlu|/x|ﺮ !!c,eYd!1cy=~|tSqÀ1g6q2 wD]iSh4zdy;۽^q(`{Q<}{݈1)%˶ 3&4cFQi70PZc)X,>.˲˶dtT)4MYg?j1>4әvUYfhuߧ28c@!\Lo}}0Z{˫?<|u答$Yv;'p0𤔭<:lbQ7axF)\7l:-\M&1*=co<EgϟkږNNު9pd-oh@i J*2BjVI1uû/ۙ(zp &0SqԝZ6Χ[xrfZ)1B9BV+ݶE0EV+ۈ%tEXtdate:create2013-06-22T01:32:31+01:00]H%tEXtdate:modify2013-06-22T01:32:31+01:00,IENDB`puzzles-r9872/icons/filling-16d4.png0000644000175300017530000000060012161170237016342 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(υ0DZ@'^'؊l:Drz|dY$-s,/wY fff}ߙ [qIDT$j vpAg\ƭIDAT(%[OPvеeFedF1j$& ĀcҎv_?WDŽHӌ H0'2it8 |ϳj9}W-W7nwks+8޿RœJhEBxcg㽒iQeiK!!X؊H q)ǓoLHJIvQ+4{vJlV(珞/XpjԶmx/чqepβ4U,DTjCA^VMc?NvBV rVm-Ԣ(N}Bz^ӥB !`!DN}o.4l&YCn,K?~}5^,I ι뺎㺮k۶8aat}{3w:9?Ml0Ɩa7b8x4KY^`6B?+ x<^zPY)PH!UYe O\4 21/)Te@5? ;Bf9.Įceo[][F R1 HRBs DJ]rr %tEXtdate:create2013-06-22T01:32:31+01:00]H%tEXtdate:modify2013-06-22T01:32:31+01:00,IENDB`puzzles-r9872/icons/filling-32d24.png0000644000175300017530000000446412161170236016435 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg oIDATHeV[Uk˹MgaJ R1@(h4d$H#o>/.bPZ@ XR3r9s{N\egZ{}ւOq k-V< i}snS"14v5kBLMO !TcHRR$y Sc?{<ߟRJ c1D KzԪUc cLgɓs !"x7y~jM{:k`_ljZP.fy^<ʗws?#AaʫgϞg0"\*=sM5uZku9g1Y '_˹ 1Hι'|fvvVέ!Nj"˥>\"1`$=rKgz̊!br)by!ósszc1$ `P)ɓヌ{]J圽nڷ7MSkJ)U@GDJ))2/]GkfMOZjޯֺRp~!tI8Q-,4m9=o}(vSV!"5Ge )q|7z[kUdINj-/w ]UkRnnIq ۶msPԥR2˲+Kk4޽nk܄auh@1;w(ʜAbS%1F)w Z`@ 3ZAnsGgϵwy0F,Ib QIc$Mzb9vRMV=ɓ[QAMkzS酴R3)^h\SE[vۭB<7TMEW ښt|ߡCĢ/̹u U KpÆ j}BQEDi];rGV6B(H+K℈@"oq9p酅޽7Qf)^$wbb:u .RZ]v}*MS#B}fiRy睓V}{\p"JR.\\;x1tn fffǵV(fmws Nh4ٹZyd$ C!DQhfshP_-[ơ΅0;W5RJ.)"k?<@=ǎ92 RZQJY:'N?U E6ה@OJEu1/3Ƭ )6\1\*1I5p3Ƭ{yDl _Y _iG%tEXtdate:create2013-06-22T01:32:30+01:00C%tEXtdate:modify2013-06-22T01:32:30+01:00IENDB`puzzles-r9872/icons/filling-32d4.png0000644000175300017530000000175712161170237016356 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg *IDATHǍVѕ0D-DJB'f;q:AJrD+9?HRk%Z譵Kt KĎ|>C>0KJ)1k]wqEEERǷ) N8ٶm nDHHGpB1 Z+P`tPZmbkDYh<}cEIHKE"{3s֚`ww9ri|>D(27ƽ_B)%y8L2#c0fʄ%5j̪KбwsҘD~w{Q^K S (2 [JFmcYQ‡-V|a^Bo;X֚ S;ΛL#tDq>)DApwN=J3sB>1LcLU_٨wTr (#hmTG0%Fg}bʢ5}rْ=2G$aVqsw%R{@_'QrZ3gJMuz3 ] ULmۦm[* 6%U4%2k;ڵ2D+D\\2;: "!]T ;AIc] bf0{npHyimnvNS=[A) Aw HiwJ6"2?:va I4 Z5n8_ij;߸盬8spdQ-Zkפz"( vpAg IDATHeV[GSU]==zw8{mD $(DBűJ'H @'D | +qc坍=/{fgw]ֳ+Sۧ9]<r(B!L0H!SL0\kC(be)`(`(C/HȲV%ԏ~ SB9B h4t  iLLL!yy(*8p8R,;}{f B4Eٜ۳gg?IB&RZu߾+B (a<76eiXO=/0k՚{i4 4!<C۶w}R J(yfB,M$W/פ+P u˞@*+!Ru˳~~$F1JCj8x럭\кXeTBa!.(tGɥWJ()$R*--]՝RI6L@Ih̝=P!F!EBt̮3@pȪ"ɐazhw`EdeY"C@)u &Q{=ow#n !|!C4\  Vk'0Bap/\GE8$'I췻A:v>"Pgɡ B//JŢiYJ(;%tٜC(5M#Mm>/0As^VwˣDէG c$ev(ιG[G91j[4M-$:A 03oqA2=w`'~yrqg|E(cAN3 s$,a,Y3)BJ)|gTr5= #Gf? 57]/uBLѕ=1m/|v6*ewiZ^[1RU%Wc swߟܴiqiIi_-vMtPJ !c ?x$N0FjBI,,˴|w'6nDCJ) ok/R:vۧ@w+re]|>=GIkf,ˡRzGT*R K0!/_ټy rei+}?z^Ы }!hԺ:^Fa+&}_aS^\k 0 C ì02T(qEv<}.loUBi(Zi\^Z\z?*`SR EO|{ee\. )iFq,8o4+s_.? 0 cìGƳ#}~HĬhԕ6Ҍg)4 3alO5x)%tEXtdate:create2013-06-22T01:32:30+01:00C%tEXtdate:modify2013-06-22T01:32:30+01:00IENDB`puzzles-r9872/icons/filling-48d24.png0000644000175300017530000000664212161170235016443 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W IDATXYi]Gu>~߼YYdE4T@ Bdq\T`d-?YUBYT#U*N2ee:Mfk4ݻǝyzyӷ9N?9w? V~;+D[տ0D3Z떳i*Z2 !4Ef1˲ 1IZ7ϲbqL 1Iٹ~G$IRX}ȑZ566y~~M~rp8kJl~AWW:G(fff76 a~Swo8u'2MsǎmJR)8:N۷/BƮҚٷ6) -9D@jP"RJJy=7gTJ)_VRiaH8Lhš[;JR*4 C:0nIXY^AP(<̌m۔GUS@߈ |7-ˊMd GcX*~ׅKl6""qΞ}빃??paPs'ԑrV#q>;D-7Z朿kp^ͳRf7HwDeUO(ɧzc?ul%oψ$ڵkNNN!,iw $r ({]Ϳk#!""ʲl˖̓ B͜'@ND4?}Rr.c/,CD9s9$mn;2T&`q=M ?deYibmHBPJJ%4bEzAiZ*0R"- è\!rY%HDJ( 3$ɮ]; h"Fmѡ@u`W*Ovk ==yLRf@yPEy !L̋_^$1M88dfs;Vqy'i&5GsΒ8V|4}N_m/-3{O8^Vd,2V3!"p_t_KHƸ^W7soZF))d 70PeL@X>Md;ܵOy#reZg# Lewp /Gjfakſk͚-O _!.#R)w)+u\ZKGD G`wNWLgZk2 TJ*]aQ0^$h- NMϔ%uplr]s]$6EE1NDb7eydW\iLӜ>r$0 cvv]E3ٶ=r.c~ k"0McrrzCC[ZL+]*&NZ߲Jp @pT:zߖ;ӧ|4%lu^ꁯ}E)E@i@JիW}x3B\Fm2 y)M<0 <𵯤YMf+8-45"J)gRKJ2T*A"$qeJi'{zWQEieY9Fľ4M\.[um 9B=1Ƹ1DiZLM~?7u;m2r x?#bj1=߻z*<#ݰ~ cI0jCD/ϸ%E5貁7ώOLq3vu[6o㤡S&3oMLL!ZmJ_zr`d|unc{z}K)GFΛI TJDw"JR(hx8+z||5 |_y#UmBU+K˲m%I1ch۶m[sб۶޶4m۲-KADR1溎m[i1D˲Z7ijڲ̉?~D3S@pԙ,;Fb/]Yr=7GSa8>>?0Ee{gdEQTbdeYOOρD+7.Ge^er$aH˲N?a\S# 0:w8pK˗ 7kY mm&j4֎lڢkY(2LT⹞!4Mm! tUWl޼=ODr<4\:4f)G b*\oUZ R U$N,JxV+>{ӟ :N0LLӴP(<3rGV$$̿"ٹ&gM@8u^n`^YxO˶8iL|c_oƟ}eA\r.Gʋs/=ъʾ>i;*ϋ3?޳ãUiIV׵.&oݻvAX*9_ef 1 Hx?x;IjTN7.8Y9m陙3KKK2Nmf@(jتx4#%*zdSdǁUT÷޺wgڍP֤ZwONjb1Mwlճ8ĐmnTtsm 8ci 縮DsH]v$ɁsM<=)UB, vpAg00WIDATXýY홤0Fo I'rN$""G}]GvbffFDx@eYmDDDO L'+" !Kb Q DŃ# a6Ǭ˲뺮+@PG_m[cnJ|efƃ"7ujzL@3K_+FEkp)D-*'ȼngZ}̌ iͻW}CJLg`v@uj{؅}ģ!Rީoպy9/1j "$j8c*=rYt뀁j.u3DNj㐱q^z"+M̝'6q:9ꦑom=emyx:yCDD@o"n R嵆A.4[e/VrHM_4of_ŦX]ߔu]eCmwamr~4T䇎lR$-2;MЀ٦VHGU3|PISLjGK] kV\4補3:2;m[zA(p> vw ]9" }K!D۶Ŧ:n\mLR9rN)\ʻTrR*\r).9T1pRɥ$e6 {*iX1!bJi6j"A4Y}SvS5&md}>pd/T:o9% EGd[T8ښz };xiz ڨP/~+ *)"Tҹb`P5w*Sݫ{ ϫz/N#(F{Y6hN0>bi ;.":.NFΚSsBh`K᥂۷XL%tEXtdate:create2013-06-22T01:32:29+01:00J%tEXtdate:modify2013-06-22T01:32:29+01:00ӜIENDB`puzzles-r9872/icons/filling-48d8.png0000644000175300017530000000674212161170235016366 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W IDATXYkl\u>;s%V,ڒeʂ-PVWH@Z c5I$J BcŶDî %Li5{qv/.Y t~,wfΜ9;ߜ9,\RjFJҕ!6Yg2?3|͵o3Lku+/Fi%Xqtzz\*%gRFga%TXθjI!J)Ñ_D)M 1 Q͛7IYhӮ|(cP'&om ~#_ɿU`R9B 4Uh B(W0ܻ{q1[4y|l`@\[[ D)(E \ï1RkL?St]J} R/%I"Bc ]&R(kvZݳ: B!DQ8$I|% FcmGG!?vWB!6p~}mmXX!%;>7~wtok P@k(/%$I\m(C;#G^mjRRBYG~;e B0FGd)-3lSo7M;XC]N!Dlc&$6[o93|6Y)8gP=}&1Jnw߼taPocZ 5hM#ե!8EsWF <9H6ޏG䥩rO݇I)+ PڪCZj D]( kiQǶhZMhq\Vq2_qUJgc]XS[ XnApkl~GVJ+ĔBGF.cpnw}Ӫ PJfgoٲڥ!AwWE)q̰zh$I`;m ¨{U=Py,뺧 פL$25^xG4Iu-M$V*$F)kBRRrQNX)Jk)z#}oH3P^}x#'OKцF4%(b< !NL{I$8p[>=Ɔ H홛WdT:GG7'5đ(FUB&K<(;~D$3|}i8A#[v Ok)f]N e1DӳjㆍKkk~IzlV,hQkea-F$w{wm0Bh%n4C:R~ng]5|\R)m"xLV|uJ`c;+p'&(jKpJiFri͚5WI)5Ɩ|+n'! !&'߱X,V(!寵.˘iiq֎8.30ڵ߁fƽCjp'bZ `߾$E#H%Tv-'~<)fx\];eq=imoo﷟(v]n~;J%? #ܒ3qo?wi-"Ƙ5kщ+`3==k?Nc ^qIܠ&@Q7rm!xڌ4RP115 ʚ ȹp r ) 1SݾG QZ%/G?X& 9/x 81>Um<0D '֭ 0?{g5e`WG;5_裱mB25W,J-[6ٳgL!<%h{ccW'cYbMƧy4x|bbӦsoҌ'?Oh̲xrLF {lNkRFTqdL8iCO~ ( #1b-͋]\\}1+2uVB(Be3.=\/q J38^ru]4HDM+\זcW~ dF(RiQJI Zm*bx, |w+Id(q>ԡlen !]魷n&gn޼QFϒy!?=CƚXAuU*l]!o2|FZSPJ;3cH,J)Ã`pJ'+ A 18=MUTtyi\WK>7c kß]62HAa!:.́u_kR֢(ºy/)E.ZD቉t\ﭷH)c2 Ce^~o_9QMc ԿO9u4~RkyW\y_\|/y@ mN#PHߘyœM)5yo羾Gٖ*U2/4~67&cqk[XG?{ CԆ24MQlXWe~K0h[M^zgM&S";]pPLlւ!J<|_OcRX{{>{x񏺽XƂn-'Fa|cFwW54Qx܃e%c,RLj%jyx-#1Z 9}J)ׯ_11sW X J^(RY|]AƚJ~|13JaHFjua|*SK-=|+SVw75%(GJ9wF[mrqZm*JF/Ь ㅏ5(h-_򱴕q&Xv- ݤ7XqhFQvdB(16f+J˴iV%tEXtdate:create2013-06-22T01:32:29+01:00J%tEXtdate:modify2013-06-22T01:32:29+01:00ӜIENDB`puzzles-r9872/icons/filling-base.png0000644000175300017530000001174212161170207016604 0ustar simonsimonPNG  IHDR?1bKGDIDATx{Xu{Mb"r y2<-C12icΕ+?zQwƍ/D4iD% uU CC(^Yڿ$კ^iVS/444> }؋/b2x&M4rяmTTMPKyZSb+>$Y++ S/N._|iT6$ߛO~//r֭V0S/,<:{ތ2FUR^D4.*K#|fY{mK@EE^ttt|Bm.u+G¥ee^tvv~afG?9vcݯx!3{Ϟanr\1ŋS/,|kccccc8aagGzifGgȈcnBG a G̦)<!5\Ck|۱ʴͲͰ-#n[QeO1u TU'`zG"\Ckp !5V67܌}%-Z61Kcܣ*.mrrrO޾Vh{g坽W`3uџVXP8[/9S2= &|}|^x@5aضjȘEݭ$`Af +\Op/&0L @~~rkje%[sDTSi?ިsP87b4@`L~5Z_$KGJ*׶_2tj/N=Q؂IvWVVy{{XE}ֺ]D43SqjgM-@ci`W߉hӆB -3X.UG%",{[nѢOzqyФgभFS=,D|nC]GΠO> 2@`L}R9~ǯ8~D c &$= +Uڤ+y9xZˬ]l]{~?^L ʱnF/gTGz33p !5\Ck|5 ?1fգa>iK>Ys|'&\Ckp a>m1Ԉ>o;SO|`rKSO`{>@r+.\/{1S}=3x$_ n!|1utunViDY5kۣ v1ϢÒ ̊D ÿ04';F8E{k1GK<RF2Sa>@?,f>w׾WU3k1S=H/{߷Jb C"oreS安6?g~X|06N^| -QDm4*H 8\Ckp !5f1/a恹c-` g_3PnX|nkp !5awg'''7#s_IIV tIbLLPř_y%4et;Yȭ hI۝='W\RvcVm{c+k&G?dc_wo`uoIxd`D8*wg#z>5|O_b#Yҥ5Ϸ'30i#)O:qQ+W>/VDQQYkնZz(U|'.P?Q]]]mmm㋸yOI;Nw;Vޠ472/H[R|+@?h_*/+:bo޼edo1/!pᾕe>ɝa)M\O)~8kU*w\R_$x؛IDO?L&{Qvr0-7|#00mm%d3߫եC5Kd2"2AʂWf uu:>9y|whqB)E7\{W*+5]kK_%0] 0s|z<WI"쟪꯾FT?!|hh`ӊ ++];V,_imm2}Z| ,{Ʀre祖)2c^o@G_aggjU+YalټqfV?iT(v{\Ckp !5G`WOpL|ڣ|"0)LCXp \Ckp !5ט?[S.)5~]  '/7l4󁡓X пu}-a>74*JN#6\RY?hӶVSqq[ _ߒ0 @s[v"zv;W8#gnO~S4aΡ*Jzt*:rVXg>{]{l#i{]k5*0.N{Ǧũ kkek0韟0}X׷$%?J&|̘/Q\.K/ZY19Lj+Ϻho ^ Xuj7o}C/_ Z7ڎ˥?I>`r8K%>OJ×.zx<ŤWӔWSSI֕ccݦF.Cue;R/T b`rD^J=Z--_٧hbYy1Ϻ>"/:M&UߨQJDΣ%Q?\T)c=y*dU7jR!= ul~c~e>փ-D}Ŝ3 {Xgmo5+l"~761W;WI36m F"rsOTW!ߓHSCaCDŽ8Y| 5\Ckpm˜f?ոl}~3`$? | ;ws5@?`_rp \Ckp !5X=T[{X>Xa4'/\ohl4 ^<0i^Zq1]?yⷈU.JdT'v?޿dkg[V񉉉0!&`iSBCU*wBz֙/V'aC3Us>`{Һtz`0EL?  ^W>>ޯ?k̊kmmPMp;9Id>kDmR|>@s^h4U$<^"Xc=@k\FS^^k⒬`a> ?*N+XԋSvܺY Xc=@LJ244dӆuDTS#|H=\_Bagg嵍DtDz>dlƐ|+*$ uI,6-6<,t07LV]]T" ?k,I^ kꚔ!0>oqt+țK}w-^xTTVT?i`v|ا\onV(""/xU1;' uECHg>8sB_3]HȘ!5\Ckp o55vw@g\Ckp !5\Ckp /E S|VIENDB`puzzles-r9872/icons/filling-ibase.png0000644000175300017530000000444412161170235016757 0ustar simonsimonPNG  IHDRk oFFsNڵ5 pHYsHHFk> vpAgg܊JIDATxP;NB ɠB0J(q288i&5T;AIG<&3bX3h98ʏ#z1aMH'w=g}w/OH4^+ᑔ4.YnH? Lji2ԜoүWJ +?H\ =pAz ?;^jlln+ko2{r㹌o[uM|Xl6l2l6k&)""t27&k;Llikih-[ z5>>w~RX?C_uVtXᬃW7\lr*/ZP+ݲ+= {㲲D<u' Lk׮jT hmo}m-Эz0tK"wfЧaۊ >2 Z۽Ar4w:n 0OJJYCfw@ABޅ枧:.6$M'߸^-wfGxRl@ef>2^GWfV@0~LMM瞿oYHjщskar7mɁ> U!m2WVݴ PJfxYTP?xfcQ wuTL}>C2漽3mG.!4$4V[gݴz̘F֖-/)[^(~/Q~(.H\ =pAzE0 α`/$Ha/Y =pAz .!#p!#p!#p1!w1Uk㲎6ݮoA?pA\?p!#pQ~3C =pAz .BfcaBK?c7 (?ԷP4TcAz .H\^BvL&'NZ,]edo+޺fMXx} y1><&^׷#oEGG>xʩWL?o!#usy -=Z &''8L҉ >^VA! K~r~]! Uvۇ *XR[WPɹ g?wTC*XΒ yt&9YEwjJKJ.H\ =pAz .@vN.g_LK{ =pAz .\Dz4jn_z,zǯgj&H ݮ/~k_L{ %E$<@F0K◗Lpu{B"?/Zb~7?:8cqca9 WNxbZr^SN)[^ꦋOӏSv;GͿL.N-o`_.[ZT$rG>Qi<x:뻄],cK^V/\4_Qo?7|<ܿVsP\q3z:gօF$ (5ά\ZCCtSSq8߃'KuȈIψI0 =pAz .H\HZC_>A 3%tEXtdate:create2013-06-22T01:32:07+01:00zj%tEXtdate:modify2013-06-22T01:32:07+01:00IENDB`puzzles-r9872/icons/filling-ibase4.png0000644000175300017530000000226712161170235017044 0ustar simonsimonPNG  IHDRk oFFsNڵ5 pHYsHHFk> vpAgg܊IDATx[r EYKciZZ>!=5_EGUwl `}?Nc }}`AXECUsGF;4wGd,T5[ϓJ?)虯 MZ~_" 9>}jd=8d|naan͐Ք%=UmUܙPYr##LU%@)iw&y_/;gi{D,Y;qֺ4A!"e 7lmSo }+2^d7>, >,ާ^Ɗσ#7f}MI7%R=O\ϱ, >, `u3>«C^<ݣ+>U c?=zXK:?`ϙk8Ň0qΊ| >, >x'{c0>5cA}аq}`AXEyq3?CXZ#ήYeD, >,KhRozvTWMy{nU/g~HOtֶ?j{3s}Yi,?8SvJ DK{huǮ_4^먄Բ~ܞйO{KEZ}Q_ds1gEX}`AX}`C&1vS|lUcRUTao%tEXtdate:create2013-06-22T01:32:29+01:00J%tEXtdate:modify2013-06-22T01:32:29+01:00ӜIENDB`puzzles-r9872/icons/filling-web.png0000644000175300017530000003356712161170207016460 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge6IDATx]w|Tr$J^ FQ)ԧ?=Q QlgG A" l߽{$sKٙ3ޙ3|{_~GHWQ1W騤a67bGxT҈My )U06B(+3cSc"ئ3mk(K) |޵bTVV}amQ9 "I҆/hW )!fك1*)ٟ#I0BuèرN%Ñ(yy7$Zk˘,v=5L+KJJsssdI2=@h+[H5Ν`BcgXD0](g3L1,WWW?ȿ]0TRϷdɲ;fϺB%nsE'M:# 5]*zg\=o aSHT[[7G]0?a;rсԙoH$F³IcEc1Y j1Yci;D2p8$I؄aw8T5H&"H$7!iZ,l++IRAX,fj@M!#3C1BcSk B!!v !@7E?21"T E ŕ6) =rԄG5/GMxQrԄG5/GMxQrԄG5/GMxċhhB@)cQJ)!JF0FcPBa4%@a1ijK &dB)%@gQ+SlV՚0J~rBB1ʇNS!4 Cd՚ePjZ%IuCE3ƔRBh_=Є0B1fb6 z<vy<MQJ].w.I&&v\ڲ1e@5 XVz%;Ƙ$I`p}\a&a)ݻ/ fgg!B<s` ȡ;SUUu|OTblڴfRJE@lR&-ʙgv~Ƙnn[ߗѡCUD$l6/y?2ٳG*d>W| ڴ>'c,7'nb vy}3g/ Φ䫪 >Z4xL͈a@s1HD'dddZfW_o!IElK)u[Z}Dž?qpXp#zo. 5CεbIʔeBxl67u=;;fQƦM2qV۽qںL?++SL0;;{ܞ,IM<^elqyZaFNNVdggaX[Bi-g0 0XVυBe;A񸪪-˲@GQ8`yW:!%r2VxCmXTUEv7!!d6nV%++ z ͫXn}(:wڔlSx6BHQGjxnzD"Uw6jJ&Ix<ђ{\I&SQR"];;o~ƛo+,G_y7_-*HwRJod"YQYyI':BHU/ܹSff^G\_?{ۊ+v G&4 #~g\vAA~IEk/ͭy;?={xvᰯZfu7)fO7xb#i/#\ijC%iS'֛~%!ccg6y} CAjBivW"qcO9؁xBere Eob9i2O~53A)M;`s\1'g+JJӴ,ME$EeWpя? piFQpd*nc-ˠAǾ땕q~f3+*oܵKTJm}ޝ;)χusCH$Jcvwg<=BRAq>]!UU34+0z睷v-|*"#8?psfcC[_%I ^7믻0`0$k߽;i0!j3i4r16`n _8ei1C6&6KHCC`^aTA)D4;LIa?SP-_%YeY|Aɒ%IdSgpYqdb*_w@ i;cFV r=nYBY%,GNN:ѼS 8vDeYEIBߺaTWWb&JEQR:B)1/(IBhѻ_~A8azHe9 RJ5]*sC:u7;S->Ztg3iI=VcӤ%,j0@CH s[(r$pN!">f0TU #fWIᰪar"-1w.lCSM7A!s`Be?x[!t(qzt`[cy 6 4aBY!DO>MIPrZJԩ.11H"< DK\.ҏ2vK0McdN\ᖁdb%dyxyxv[Wϼ׊hj߿VUlذN QH^]ݗDӵM_9%'iN,RZRRhѻ͛:"dmQmx033LM ѾkS7nΡ<˔`!j.DŽB~[eW,rM{Fc2J%uoQj}ϐ;uZPn%I>TxxSD˲f~.I gxhC4ymw.{ϗof0042m g-X ٻ.X"'RRn=han]fBhP#ߙ-e L",)}\UUC1 y*r>h4t:6lشOC Aգ;'/P_ xU~ྒUUrFaa]+a=$[+1]ra7vH([}Ϗ~_w]5`B!, fef:~\0R,:3jjj}~euR *Q Ox\B8ER1-NM{2J ^A i ^SS# TIp$Nwh0ʦ1|iK@\ 6 8Bi_r_&)Ɔ)߆1楿p`C' f*~dϦeҶ J*KLH|>.!jx^_Fn&"˒iVAhpRsrL%8Kϗa*DZVeYeuY=k v=iI/F"%K%!MgpZjB1#%%K|ơwjwD#QDjpyD,YTt8JJJW̛NjRB%/_ɨNL]we a{nP3]3 (JxU58x0駝 !H$PpN5]Cڵ[&]#M׶hKP觟w:N*RJn߇B!K6sXa3 dΞu8 ԗ񔕕|ن 6Ȳ\YYH$fϺO49۶mj$Ia?E֒Ʌ4iVu#+Ɗxh'0:C)u\^_݅bgt](DcfLHɄ("IRMMmRQ*+/R)jlB$ɂm Cv9V2lHQJRQk8p,3ƜNgf_ |VKPz(0'iAPdǛu9O Pf%w 'zNZ¹c4M4RѰaqE>&lFo5J#% WVF B4Mu]pӡLraBc)4\~~rK-$xer4fJ)lE9s&b1sUjQ`ɮR-͂,3ӸQ+XE$rx"aL܆,x<lڱ˪MWL.z1KNRSr/D0$$79q>M{j3λpٻnŋCN믷祗?>4῵y6-RƛN/qQmCqJǼ>mH1:k_)a=IUU !䟷9r_xA"l e-[ ߙO/xO8!Ńm{;btިcLc&ŴXyҊ N^zy#֟md!Bj,Tpע^-#$XD&oxvzW0O%HqD3rmmO>5OyyI D:ӺfvY;gبzXH6ϒޞ)ǜ"~@L-D*J*OK̚tqKJKV4H%j_?HRГU*Px1d$>ݝR$Zޔ8N)I9w!A^O>VU5++s;=95V穪N~ (&v<}eٚ%KRM6fNJN(c!;^]ݯbBOaϙrneea3rkim\ݙ}0F6)'H+!~bؿVLV ,8.EPJBg '=Gel)mV+`{0DC \4 edPzBNWژ@Q=zvA؅f83SљFi(4@`!5Cb|8<=r  @(nxN?+kz IiˇhqiPOr1|-l &DZ,"tOfA(c^|F-_FbX,x !hX0<,"ΐG)dYX,uR"alj)8ia55JPJUUUR)AaaA*Y|d=/?MO$jjjjkRx"jj^^f5a=O,T% '@Sc$@, sRJ97R ~ TVVyRz!CO8uo`p_nxim@kUUm3B(''0;ykЦ rss[.0ڹSgBxA0q"(-ݟ8  L?(EPJuӮ]-q%v)J1Cb c@`0TPv !%0{4=htb e|骫{5B|[ez['M(^*4|x.y/\G 'x=&B2~3Ϙ&?0/z6aFΙ?&$ DB$IRT0 #I"(<B8YX"X,LO0B8 gCMbX<7nՔiJ* -- !z5"B!%o|!<!xb5HS6-V 6ShCBԚ,+uw jo=K×!l"Gڢ'fdS%[]ӕTJ! m6A"aBSiB6bUUcYՂ~'T*2 Y,]v|=wFq:kޒܼݻ zo**.+*/?~rg~0]#b>QWTtP'2Q 0{*RS-Og#Bt ^wO?|!om{{:9}hi]~());vM׮a[↏D_1X$qEΟxyE,zy/]^[WwW]{ "dZ2y9c_yzV Nw7mv 7\ru(BJamO{s׭"Bo?cS&3J?,E45 v(^~ر$I:#ܼ9sx`փ! FY1jIMA-|Q<1|/<c\o-gf:+l.]{5Wefff8Hebc:|҉"u*Bdrڴ)ǎ!/Y}ݏcן>m/~)ES"|߈L&PR lkWRS&/_U3f $TW 15\0dY]?\rՕ˲,Ch4pVRFwhQYÇed?>$Ivc!Y3Y{%I9riG Wd9E9{Y,B 1kL0^`Zv;P*tnW:ݎ#3_u^ 9#G#dBRm' /$<?7|w ZԡpA[XVV! Ƚ_<+}>_0'AŠ0FcDYY~ ^ٽ|i(=Ar;ts@d!5 ӑO^0S^caF &Sk5.vFu7<2fYV<ԇ[3<ov:Dz]7;wZ<.1 <<_'9}m.3cdzwtԍッ]vl7h FN?tq9`X!Rf^sg@N )TOWXlX"rݎ{%6̉g8BDoc#-70+ 4,8f'6"kxH lIQ!uC]@^0STaaƘ>ċ=Y,=z) !ΝXrC"'*//ISiy@j'K3t+D'uy#mEc&dfq Y9m+X3Ctȏ&Ғ #4X?5~_c1 Lq3!!7le8x0 e:ʇq~PB|_dblSLWN M?3hBqFF8C>:Nfk&ɂB&˲V՟Gѫ"ȁ1ddd*r a%IN~rVϗx˰Z3e<+,!FcU$Eɯo!ܳg̰ԓeJF`_].! ',]$(FI)u\[ WRɧ+#ijN*n{<Jx[ǍZ򟉝9*rwJ0,yHMsrrZptfk6IaæI(ʶF0۩bhǡPhv#c4o}tl֗jWRgz5|f}mWwP !/ro c,?/wQQ oo>3/^ &IrMM͓ϞuIq_m: z W=n*a<ȿ3ap8"2>tg>GSU%^񚯏=>oauV.^t/=iIC;@BJJR:w 6 lSTuUu zR()!eDMH H#HĄ u]#EQMNyT*H&9Y!T׍9~ԃMIK$8 #s$KLv}qpew |M+sN#>w-D6=KBHjƆvfyEao=H0?dJq#$˖cn{ذӢc6l{GJV`[_{_̈2#"Gn~mFd-=o8ùa=]5ʨY&w竽x b( @u`纚@F5M 2!{~2Uy]_H$8)6Ds~Ղ@6WS;wW\>OWz@d/D)lkV-oT>:Y7G*fFP \4oiy}D6l N^RD p;5jϒ"jD(r;~|aK˅ #IiڮF2>%{aK:՚np~aeN`qij'ep.uAOh4=$eTF7kp@ Ǖsf魿\8TS!kF1F8NNQ(|_c`C2n"PRD#|bt`Mݯai=*~nfϫ'n0bö[NZ ڭ[՟͟D^=ym^8G,xhV|D7:w8lg_8q0WN2Ɂ~a3}{ȚFnAO=5$Qj"MUTmW"f5ݮʱPFc{siy~\lezkF%I!Vd yoǕ&qJ;+CrX\NqٖFO@: idiXԨJUCpN.81lDbFX<u%dgJ aąݽ>(0j lw$s!Lc}o($THjPmOZ9⪮W`@>Ԃ:nao*1PgzifگqRd<Լ){Mk?Q 'O)8bU ^-=nLAxqM90JW$ac3>3xa 6!b?Qn$Q4֬xۦ׾k! # wQF?X^('i3V*h`Z$Nӹ3KX}A`ioKۖ>xg3)K(Q013)me8 Ц *d܂ eI 6\l.!,I&#tl6[ ;` y NJtfx@b1/z=!S}$I’ p˹CbeY !&j|ݶ5k֎s=}J\ov9),KUU0 t# ΛBu׺uo߸ixipb@ݧ:>jw@SU<6iN=u*vzzcdj2a< 8 ƋL0XԄ$H ]d(LZB Cp&HKBx$P($=R{ 4!B !e)eAbMA#bwDF(@(J3p4h` 9hc|()Ud+ 3mVe[pS/Dq%$vma9%$P.NCeJY22v a*kPֆ2,%{} @!Z\VEF[ڏ|再r ׉0s_Pyx3]i/xK. Bw}1:0ܻްa,޳gl: ϩ$%ņ!C|"VʨSrn  3An]Y~o[.]F1B/>y"ݮw}Muuuۊ[,į$ KqF%B1++k[D<0ft1Y|mq %;W_Dioݮ_wۊ`P n n +|P5wu޽W_ue,OR99΄nF"SǏqWiI)BOɹ?=6)b$Kfc/[orE"vYSb* Lzaw>wF+t-ngS#22yˍǏU3 CU5@1 C~qQ{7aDFGGS? frk~eKP ebRY׉@4;9;>x+F}U=wJ&'x¼'[r+N9ć>'DQPt8l6[~~LcYuob_*zjׂ+znT>n$:999@c#NdowLcw̤V-??rUS=˒52kx^xb` bD1LAFֽ{ѽۈãѨiBk9vݬ9mܸ)T.!C}֤hՓ9𢷧-E7&Y-y,y+a$00!Bc?onLsxd~~~(z7v&P]Yڴys<|٭+K-5_?>6l+Q iadggK+Wջ'1毶5ibFFHAPuPYyy/mlti׋)c?4f/|ɞ<0 )2v)h `4MW,YlYYFFc=jEߕ-/߯B_?5-6ļӦuZ:c&sbپ}9sygbƎ9Ip23uZ!vwH&>cl3 Dc`0$>0q@QaÎ3JEIйcN9I !ILY"y]@i #~P$Ⱦ Hd2I(W2񆧟!䏘/f)qTmLRmxg'ͅRJ)a hJ `-c!Tp-`.֣fAŕ%҆V 1NY"؊+(DZan:!p,IRBcXqxPB]n7rݦxg8@no,I˲3 ̐T qbֵ !D1&a)rlV`0ԡC00޽7UUw& {ϯZ]vf:Gc~/?/O\YĨ)(7iCviss^47'RNۿjD$ILȅRʡ`mcrt[FcoMRʝFjcṞqeyM >_F17ٔӆ>*SLaS G/؟GGĶj%tEXtdate:create2013-06-22T01:32:07+01:00zj%tEXtdate:modify2013-06-22T01:32:07+01:00IENDB`puzzles-r9872/icons/flip-16d24.png0000644000175300017530000000160712161170241015735 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(]nW?ssqX(HEׄKMW(MTUP+REJe&P&L Fk=s.3@b>wJ)@cmی1DL])%q6 *6(ff^jϯ*S`}/?SBl%񧭭ёr `.$dCCmz]]6JjVfsss9Kʤ2ӟ;w~ƶm9R۶/\Z$dϝ=35Uw"BE-VI)7qes7Xʮ޽\{֭޿xxզRƘ0|bВ߾j:U9~؃Ohm".0ITaB 0Z @J'q+X缗Xu<Ip!qljAw:X$IJŞW)T -2 bç.} 0%tEXtdate:create2013-06-22T01:32:33+01:00Y%tEXtdate:modify2013-06-22T01:32:33+01:00F&IENDB`puzzles-r9872/icons/flip-16d4.png0000644000175300017530000000071612161170242015654 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭ IDAT(mR٭0 MBMMI>iP$$8i~@IZk}'iԪXf81U ~LowӖlt* \YzƜDUYt؀gb@})lƌ123_zk0Gknz7W$;$A ɨ*S7}?: vpAg\ƭIDAT(%OWsffo +RVmA)VkC+>?' &t&M-)w]fvv93҇}{i u]dT)BD4MWVjBJB|ffa}Z5?}|rJJI)?=#tRpxT'Ns' au3ͭj|KRuދքsh48n4k}}qnllT~9}1&cL*5/>/kk;@)P$aA\y~Jnmͫ׋sƘ6Bq84|sccfsgrrS/^\\Zpiա$͆˿l80t0gϜ=qX*UnW'{zν[}R\cuRP*Uhè{KYBzn^22r8 ?Z[{)L._PP8##'"qr{CϨ,ǝcacLEƨby7QJ,䬵Q}27V+UZJ.W*N' vpAg IDATHuVYsT>˽g  D b*,v<[ʱ)xCwg 8E`@M3Y53w=KΌPXLswB<(1FDJR꩷P 1A`#cL2dZBpnd_ 1g\J5D S%snoϏͷNEQDnLEч @J)$c,FDD)%AdqΓjܹsnܸ9Wq'K9`2" !l8QDbq^zVa9҉mq,9gEarA۔!$f|.5cʕ}1%tǎCCySZk>Vժa+d:NRX.dDDD)㹹KbqddswSJ??oߞ0 mzZf~V!_z"j]Z{ڗ{ܻwh9zXB>}rK'km<)X6[-dEQd(d"Q*ONLa8ۆSp:[zuoqFyEMʼnswR*%7""!D$I) AD[}Dl6+ qw]BH"!R/`B(ӧdcy)0FAP.9B8-ghCKYY)_eRfaMN5q+E"mx{V{x)͆aX,czc~~…7o_ PJ+0u 3_woY*X8Fk4J TL&"!؟NRM qL{\~yupVl~'W42t:.")en<|qd굊(ROb1—_ "yJXֺ^SJyoDQLl޲THRy˖D"i?۸ vnڄBPk (lRkZ+#JhDGٓRkVʕ 9gZTǔ  !q,wyۺ3_pN)[~Wg.qJ);O'iήi%tEXtdate:create2013-06-22T01:32:33+01:00Y%tEXtdate:modify2013-06-22T01:32:33+01:00F&IENDB`puzzles-r9872/icons/flip-32d4.png0000644000175300017530000000150512161170241015646 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǍVQ c4XL&ĈI.+2݉vGr- !C;TOzی[(ʅ>R8C oȱ6 b#MC,"dפD"BsI9S&G⾖,tR>~穝;PZ3])b"ΙnUjWZN]dpVF_zLJħx]W'þU+t:*1<7\3sgNw&]9^=\p&E ٕ=j.*MW2>knq^*=0L|Q,P>-z[˩e}1/Ҳ eR)4OfiC!ړk,N0{' ѓm⡆Sg1h9gDzEkcߎ$D%@*hRW\P<q(p AB vCW2@UZblc&OK.;nnzEd]ح%tEXtdate:create2013-06-22T01:32:33+01:00Y%tEXtdate:modify2013-06-22T01:32:33+01:00F&IENDB`puzzles-r9872/icons/flip-32d8.png0000644000175300017530000000371012161170241015652 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHDžVoY?ؓL7ݾ&M(.HHH+ЊO @B|Y VP6M+؄8v<6>8Ι;;'/54AJTJ1g} " #@(㽍Pcq: i) ۶ caA1/s!)k6oÙ3zRsއss'FN|߇gY|'. x F(z>7[PJv<:}B,ˊh㍛7=sL;[[[X~tu{'ݩ$wb_zP_K)-v[x\64;EFX8Ω}}}oEח2L>#ǏT6ҟ '@E{Ne(=^jg 3·̠}yɾׄ!DVgE"HmX^E<t:=99>u$c ]=6V!$I) !R 0ƄV_,ffLOK)|WQ1A)fus޹z8o#l1hJٜsFRm@VQb,bRZTp>?_ fsPJjL@,mߺucL$>s3Jزlm>~qLf+Ņo뛩1ffjxerqtt䕱1 ۪Mmgzru*u%9`Z,.\[\,79S&'&",J}߿QQG &/|sϓuI4#!90 @ )Dؠgop{{k 1 9P ض%燇kk0ƨ[LU6K^0 F4M%yA'0١S'Y(kk`8ǘ0uXhJ*H;J,j"(ʵEbZgNv]WN)-(zf4KvF8;`B>}ޮmkWXZ bx^"cUڪoo/4{, -oR^qP)yXKNfg 3fZtaG& e_019κ8H1;V( p]Ws{NMNLGt*y>`ܫ=;}q{" w~9rSdT~za(]{Y_/U*ӧ]ys8ҥQ\׿(XƘO~BPz@-*8fQJ_DX\R@(*)ebVihUp]7;==@xa=tjkB8ZlM ewV=ؕ0 =obO`'oojumڊ#{z} /<F@)os#R2@{)zF=;=KJiiG 8鿆:LmݵJ)M"!e[^ބ,J)a<2J)TEw /4d-%tEXtdate:create2013-06-22T01:32:33+01:00Y%tEXtdate:modify2013-06-22T01:32:33+01:00F&IENDB`puzzles-r9872/icons/flip-48d24.png0000644000175300017530000000636612161170240015750 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W 1IDATXYou> 9')i%Y\HfٵCcA 0 O" p6 _`U2)"%-Q%q9>* RH}Ⱛ_~j7?+IS“,BnB$3U9HQP'Q(! !019B~B> \ 0jz՛67ڶ_} gܲgs B(^s`!dmm}iyOuM0?76:J)!a !zC=fز(n(hq񓏯/-Bi$cˈ{GP va<2(M2Gpd$VivGOMBǿ0>J@6 FqSG"$cϏdٽC~}QTU]ZZd2R0 iUkk{j(t]l2FFF C[&)wTPb[BUUwvvoeCs7/]znkV&)%Tn-"O?t: tA(4]ܸ21!$!t]{v5M\.`c|>z3|>MfAshhM5!k@B ƸZ=6ZZ^A5-˲(@q0 },۶!d?eG2UU=߷-}UU^!8ɓ͇aBʻӧ(VBt + [!8 CCG$`0d2//OL\-+&dZvlw8z(@^7Wrʕ٫W9sZQP(&== CH@)@`(wGML\i<1"dҤWs.7ݞALw#_"dB MMCWUZ%Bj0\^ZXل!cvv9g=W5MBIAc,"JSôϜ9^X,Bjܵ;+l~_2@USu?zdegg]OQЀDEl$*8sc3ƚ(0dO(}VE(F1uBuEQ\׽sGO%/} %)4M;e9c%6uA: EAQ\ZZy0"]-aleB>}^0ƚ9s7/~/{4qx-y1uea=ǛJR.KRZ(ffer.L?rCCSc^zq&nd8/bwV>nNP"WY޽ScAJif(i*tvvPȧi"Q .Q,?+I'ڠABp!jt:k6ݝݝƚLaaBJRLO esW9@!a{~t mY_ږ%NMNT* B1ldA{]0Bͧ66b]\X /~9B苖/-/4co(A~7H@cBFF \./r?( ʨ\#0Z }{mm}AT4]nu}k7w~1)Ξ}8t i6<BX\IӴ٬[9lo뺞$t]\| ONNBdrzyaضv{c1|}S PP P*[FzqlkwZƣEIqi0EQU׭rcvullkF8F2v9OSV*Wa5npNj//JBavfz~sg5]'Hjc߽-˗ޚ_NV9 BHXl>yb^Q| `O6gff <*=%1]:G}e]v…B(G@[30?Y\Tn<$@zooalB={.I^nb,MhNOOMMMJEnGp!Đ ~eLvBH|vvfww`Y&"H$ #B@Q~ iau] u]6!YŔR]7_;w\t:F*HG }σ2Ƙy*׊!϶k-,aؖFah[-O$Iƥ(DȲL'OH1\ rò#4Jiq$MISGi 7/yA45]Wj06Z&xQ0pf|ߏlL`wmZBb2SFcrrMLhxUQ!oFQF_'4^HPNa]>& yFI;%tEXtdate:create2013-06-22T01:32:32+01:00llR.%tEXtdate:modify2013-06-22T01:32:32+01:001IENDB`puzzles-r9872/icons/flip-48d4.png0000644000175300017530000000151412161170241015655 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXXY!%Sެۛ !KoO\ۖsu]cK)ӀPGPzZK)9 ~֗߉֚ a+|vxDx9Pg1h&  vpAg00W IDATXY[ovg;NJ$-ɶj[-X[rb;E4m^҇T4hHE$Yb˩|(G2IY,;{XՒr-:Ι.COO_zyy^5z eG"> !{]P@Bgd8y.#N !t:>p> }8UUÀx'|zoDc=5u\Ӵ#Ƹ鮬;/~OY$4?Gy.[qӶmQCf< TX|T*{"~€( $fm}ȲLY^=7wrPӧPGa$I\V4BLf˒$]xkzݲlb5̴m;t]j:~۞y۶qٱY)OAb7>jeML&di,;V!%a- X,$I֯iv0ƳT*% H$13ӧ{iG{ه!qɄc;<s(:69n+奥L:h4?rjEb011fWU8 nNʈHE (km @4Mha;v&Ysoν\.jOKcZ'1oJgz),Lv؎ Z]mӘ8nk1, agϾb8vFvv(a5Mh`{3b+=RbA4BaBWU3,۞y0 ]QY'cƍBƘƇϝ8BR06婷?8ix<ςyRGjvœ'NڿB0̝.?pky9˾kǨlEu}_ɸ$4Z{o;oA #Ĵ,ӶF"eٕH6MB/"n/,,LKuFYƣ/o3 oMM H4vHp}}nx\N&t Ω4;7W?ToJ1vs7\66vq6) G.N H3C#{k$aB[:vvr';y E$t?nmZfhl=Jq˲NLM=:lffD{ry󖪪\6!˯r2xT,QhO5d]Y% pݧ1 w1"4 kk޽D\u`j]7BXu.|IΜfcve3d*_7|fhh"{YMMXYX^N2$Qߡ(qq:yn$"fs9bTfks#[^ضR.AcPr<h:QUPծeY$ ϟ R;v{60P| nn=TdY߳!\^onjm|w_)Y?k4KqYCΞ9=<<>hs]w ?omn4m]L % +R,O93o^t: !$HܼyFc4>~xfStZoBvWo!np0/J##K+w=z334"I$I`5Mt:uffSPޗָG.y.0dYVU!HQVW+,؎ԧ]ZZbKi/ww )}zI@DZ,rcc]imSiYqȸOE(X?ٶS>|(O8p'_o$Rm{Jǎh,Lة9#i`'@}LzOC?6XAuz)KtzKf D">ꢨ ``x"yvv[.X.;;ggaZk4F"|zAP>H&O Yʥ['|r M URdT*U*'r6(3S> MO'T0LNNOZ<'0gL+|psOҒ$оi191>44808`BVAF0iyC@z&oLU( :Ӻ.r:+ z}4ĄA=wv^=|K"CJO,=DH$j"%`GX\s1˽z}2ozr_D_u̡=#ڙ>T:L'CAvj0 J"Oobbq0T"HS|y'?ʅ©r P5YБo4::}:k N&[bGT 3_tzo=J1/053f,=(3T,;볿~vָVx=J.3TGDO$7 JM4+9^0hNoml[#;VjB("e;ۻ|/kJMB79\--o,;N wI#PV h"P=(^PL[<կO$>[xx8#>:=9-JGGGiJM|H5,3[ T*ÃCI^>0Mme?CvW@M',-i1[JMݯQ&t1Q>Vgp83gRU'JZf+{{{O44 >VjBl`(8v#ᰝw5BleOOl42844==\k+0kdrt`hh0 Oޘ}u@N=[ٚDB#n glFfw쳕}}}>7 Daݦ'u@lqsBڵ ZVnf_-f+s/l%z!`8[;̾JR>Cϝf_UKOFϝ3rׂ-0h|Ͽ`B G W/_pa)2Z*R,-'%RTkLF7=zO'ޒB!HOzuqq/b8wg'cwwׂ"jɳrp8?U8 `]\~dd9vܻU0[{Ry0lzwvv6Ƶ#3D*QF dz{HrlI KTzKf~93T*IeIZHtEkJHTT|O$Ӝida=vzr`g'ohjvޗ$SÃ|2j gY^v%5/,-?woN%(j4$,+ҝi$N=s`؊^Oc}m3D.빳+ wqGfL#CD׺nm5zofL#imϝ]Rٌ3XO7;.gV&L8=wv{XB=nvF40T*7o~umϽ#L@w=,=>Hz%`GcXުz$Iuo8ֳ\ַFLMkxB3D-QP],?ƒz$I^~o=+>W_=-4RxM#gVO[8O 5'CQ.F,&&i$Z=,0Hk>>1MLF>ka|\8Dg^vm-6g.g5q:p0 uA=0Qd$i6zJkv6reFGGF59Տ4fsqq!oo=J}4i0P?~Pe4tNcYt#k5SWOәF,=ϩ2VOkFaB݅^l%F2V;zbX xJf+u4ꈑYFQh+[##4,ht(r.777g+[idYFM3WK,zrXJF5,]˞cdH=!$P=1Փ䇏leFuUϖ}e4Re$ԄV:=9UjGjUZqzrZ*8cdQzt@+I |id2J%)]bXe0T*Ãl,֧WIcdRzA,#Ir`&Ȳ!;㳕,3>[)N=|² u=-f+5C4[G4[iI=0slezVWW6eq'XOGm#1sf+M JoV~x_ez3Ҵ̇=; KY{4rU!M=}9c A3{쌜t `mϝ]팥ĄA=wvFXZ0hRym{g&2Qx՚GuXުz$Iuo7rt?u̡uFU԰kKiqƒe䌥D{t*f03X4EĄxq3m\XR[[M:BB@3.Sykjr! i6 !Y^OK 6[il& VVcGFw5BleZMB"B!`0BB79ұ0sns(v]Pl;~5Pn@ B!`0B! @ B!`0B! @ r&\.Zm- Z>"B!`0B! @ B!`0B! @ B!`0B! mcc_ѳIENDB`puzzles-r9872/icons/flip-ibase.png0000644000175300017530000000605012161170240016254 0ustar simonsimonPNG  IHDRLt oFFsxHo pHYsHHFk> vpAg  O NIDATxmSe162&m3mhYffwf?E3"4;ә~v?acI`,+/ D+J/]t=_'K1DQO2.XX'(@E/z][Ꮴ%_"BMf9?TRL\~sҢdjۥR2>?\hP511r]#-PʵѸrA|/*Z_1쒖)zV.j±Lv&$q:G}>0fP`3L]xL9EQȹli2\ΪgL踜Neg>\('ZdY{Nyz=n|y3~rrn7cbLd<5q{^iҗғN8iê5 &a)׳M5xB=::QȨ垘Ǩ/ 0IȦ)IJ:MxDh$]!YUa.z]H(jxxxjj2 8e,Y*J%3x'&oݺE┱gJylhDH_,s,*.kdt!n}b+&vdxtMd Cs#Tf$Y:,ϯt>Ģ؍ŞTz/{N,VL 1ok&-@~.ӶZ ,LEoʢzA(XbRy`cDi!Ag#zTfq2Nk:`7_zyMϪgz9-ob RL:A߄%MY,Y<>:fYi-ob:&ʾDt7MCit'ԬǠxSF6T3:2e"SěF|\tw"H0D) hϰě'fF}>&ﰫLOiۧ&b띛 BNmIMtNg(4 y; y#zof3Ldt855D G'@=o7opDϺ5ѳY^/apDZO>&l7c(}bPZ( #oQ :_PSr VVy<YqQ(8=,&%oR5SFD=&yV.Xrzp˯Iy>m3FUzl,DZmǟ0^ c=FmzzV`Rh>bݻ}nZ;ۏ}j݁ߨGfϮ7^8u6bZ#=< *'[?@oĨGgwt*ÇXn F#hv"W7Gg;:fs/_B݁h褒{cQm(hT:} +߄CnF3zƲ½Ϟ!y]O?aжZzTxBK?/Hl~ 7hI\>tb|$Rh4rYmAo4Yg,./QEQhKheo*[i{;>Zz=+BލxSVmhQA6_~u矈ݯHNb7jӣH= K>Օ)1M Z6=Qԣld9.zMhDL #8SNmHJO? v^77Qbqqq JG 3HXiJ^GYuxZ=@ӳ=]YOkXO Q^D#lLOjt"U'_K$&:Zmw'v~E,+;T oy?7s=FݝnF|Z#=?{ovlfzzG%BogǛ_ݗ!ֺqpGZ#4=C\iFhzMwU@ollMEQ-Q۷:Z#4=7c2abscx_O҈ׯ^B%>^xORk>IDg$Uq<'>I= g$0AȠyO3h}1A4 {O4>I3뙞~B=@=syF$YO/ [+Od,>Ig$zA4u3D@T|# !Aܨ/Q$վ,l'I+yOV Z$),>IA%Zˆ:t%Z,zt5$b ϠIA$<'I !'>I_o`6h}j1@듄hϠIg$3h}p>I8 Z$zO@=' A듄DϠIBg$3h}Ѐ>Ih7ZyN顣%tEXtdate:create2013-06-22T01:32:07+01:00zj%tEXtdate:modify2013-06-22T01:32:07+01:00IENDB`puzzles-r9872/icons/flip-ibase4.png0000644000175300017530000000305312161170240016340 0ustar simonsimonPNG  IHDRLt oFFsxHo pHYsHHFk> vpAg  OQIDATx[0EӽLYYW!˫X:H~@ţw',;~^+ma"We@Wy(fth 3Txa g2 ,laT:,FsVraXΦe vVxaT(X`VgDI:Al¾cJwq_}M3pwGj#r5̙#LΤ0)LÁEuF *A}vF AB}xF2joF,n01TYl%3* $ۖY,32;GYHaЖԙiUYqMXi3QLl#x(&_˲L hOn̄bmu_Aϧ}|۶.Um~m)({g΋C497ƇH,3 7jw-b+Dsm [ J< Πnn InKBTA'#U/ϓ,%!h$@LOHI6t5 < 晓dc7 a"PgF>* %tEXtdate:create2013-06-22T01:32:32+01:00llR.%tEXtdate:modify2013-06-22T01:32:32+01:001IENDB`puzzles-r9872/icons/flip-web.png0000644000175300017530000003224012161170207015751 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge3IDATxWsdIȔL $4Zj==3\[4[[r{fk|5>P3S**@jutD܇Q TOײY'Dpn?_oM&n[jK3MzK ?_!BHE؋ϴEJ}p}Ar}Ŕ1!$8sE4->aeEqq\ R!poAb; RJ~_wwUq&BƘ((bAw B~W mdZn`aܺ@ H2ﺺ:/~q#eޘk=@ʕʙBaB4M/ʂ ³ia;8:<s&74cRUV9۶,²@C1F 2B c1@[4c B8mۂ ɭH eB!B11v>ܮ1duMykVo<OoE[Ԋ{ؚuZS8]ʟ!Z[Z2(ȳ=q"22O-iRJʥ2+1C7HgD0Ƽ^*ʨm@HEDRq2~ȀBcB:I-+ͺ BA$I:7˲X]zBri@A$Qb1UUUY4!BrXʲλ#2ҭ u C&'jf!`t٥F:'ӽYfŒC`+>k JqI4qCRuEQLMMQwwO#<5!ζJ?_6k~#cAqTUs 9=>:j cA477vs"u|248(JSn~ܴm{pp.ZI)m q;wlnxDQ4M;}.@K?$vb}}|[ КSR88熈a1!!B!3Z8!1A!c)¨˺(RA*eiM}kZ1YEQJ>=BT,ěsSssiz^0l۶,K9JiO6%EJ( sm~Ml۶mQc@`k{)$V*d3PHeI2s١.r6}t: E ×iλߺ}٣ E40KdYV,֛vwDQۣ2lQ BtyotCGã#EQ&wvvDRqB?30B+O{'ׯ|u޻1QDQ(4B?|ݧu$Y,mn"IE%&1feYe3#c&5?!R0M۶m8aͭl{^,+ E$q~ %˒"+!UU!BJ%c,˵y݃BAÑpo*J]1$M&a!e)4_~Y; p%t (I<Bg?12Qa)CGG(_nq¡I À.öeyw3r#q!D)U!,[A |aooɓ QzR  {IJ+|f_qṆ7FBx4HV m鵵ݽ=]$9NOOYJo&@75M|,Ev9.l|,{oE1f2Ϸ&~+Zt]_]][[]|+_(B$IDƗcLŹ`08N#]j͉Rz&'L8յv"\67_y}>?mKB۶t]E0͍fQ7](mnK3wExr`ac4-˲,psQvnme[CC7 F%+Qvvv]7LӲ,0LEQ,˴,~r fEQ4M s\ﻢ>OE/tyL~/FyX׶l`X2 uf 6L ۷}z%60o;v6EqiiX+tVۯ@۲Ԫ_|k!#!l3MZD\>szg@R4!Tֶvl 4}{{R@N>Z6SUT*mmoB CCiV[0A0¶ MN7!\sz< o%c <~1&JDYGˋhٖئiI*$,`(n""?n-˺YQE)0\Fޞah?h|zf>gM:BOWA0Mstd qx8Teffj}c_qpkvBM|tYuI(?<~"aB1N}:_ܦi޻}}N#KY?mF}Sp\.vr:pS٢8 c 쓋8o#!PʮE_%Ƙ$IuOB98!띟UUղǁ7#nK!c8r ESB|@{z1UQUU9Gz&y^(zs0BX( >.C Ufrsc#dSPc\TNҧ@JŁ ð-)cLL**Bp?ш_11kk T**`o(>@Ohs3dҲmYFn;q/~;I~E𽳻{g |JY4Sh0_[XjAE0C1VVV7=/KQ xOO40.d gϞ={U.W(p "f:#TҜ;%BdZaE:oMuwwg.SArR/:p}}syy踦i+/)wqq K2ͽd*i `q9} E~c^(%\./ˮ>/Y;ہ'L-W{O<{r @`[0y!.J{Olmo#l.qx__ߵ->".]\~b1>ezYPg3'1!˕~xWV!D77ruQJeYךgP74MCq,a]j$*btnR_,S<==/DZc5+H!$>uh4 8L$<#LS??!tx-ۯn;txx([Y4m}c3==^N؃ w;;#R,?ߺJ&&)~Y7 kjw[0xZܪ|`ᑹ~B%q\. GQ˲A(S3c͚];|Zlm$1] ô,˶LӤ2DQ@9蘛Ilm*BhcOةiuѲKMm;h Si X8Iw\6cY !@AO,y| dNNcX lvmucL)z1V* uuu'R cLj;4=X_㼞^0hc,i,YȲ, (``,ON! `Z]^^m{hpP˶BʨQ1X/_תMPˏ !@pjz"K% øs^SΤ姏G$Q|7mwwGg}<c\>G=} ϓ}IL$ (h0I<>݉a,$ϟK448pH <=JَmkLVïB(qx<0r-۶dYY[[W `* e52: G"I%Q#y2F)c=UEI-e\YYT.$!<Vk~Q="۶щ1+;Op(/c@q]7x!|OOMh"B!qGQ۲ BJK0tM%I5˶gUTȫ"3Y!tKdJSt+_.llW*2+28y"swpneu\.ׇ* B888x|>$|4ccc%!0}R1 `P@)5MkvfpddZ_"Z\;fTΓˀOw{X)1]zMFqEQ|0xT:qUD8Ԫd2IQ%>:'gϟ_W$`_` Eʘ SS(he #$Ͱ1r2;M Ƙ,+#grW_|^.<<99e LO.GGQ{x3izT!āvuv~{K # n@[Md  /. M9a^k%㸭U콄 PJ%IP12㇟|amu2n_ BXԀ |_M\a w0%=4|tc8JRU۶AFӓsCC~Cly1+apE?Z\C("-\"!LOMrɜ tvE~..FDQLO6=edoo/Hb,ضz#C SSh(G?p|{ s SAa=&J)B c^9||S ~E}N`@dנ~ ˘ $)3H#DPJ*2`ڎ޺8;;UT/Gg*bYԭٙh[:Lk'e {J-SȁpT(SUet<q8x/_p1*Bx̱룔/8ZY/ q^_@61Ti=*ɲ" 43 X`gIvEw\%pPmY\Rʊ:;400ՕgERUlbk^SFCn)Y l)BJ,渫{dWW72Ãp8~)(v:B\{%S8ąOGvhv. GF;>ǴlK\7.] kn@hq^wwEKo:ح:YHBX!,<ɟCm+Q'mS,/648X.%Y=ylebɃ7׶0\U8Uo}:Y#{۬*um``~EVR6ϿВEI$TU@<LOzc^4MQ5Ms^,gW_~~CE0(QI5~ CBp\^Y~l[!P8/ b; ۶)e 4)ao8,,!C b#SˣR/p'(8/%gy6CoM~ w/E( a‏B:$Imgt4D?)},b;ɉqJ)F(JWkƥI8$I(vt&?>N~3OƘ \5M0G|$m;J>y=wΊ8<:^)ka"a/˭9 $Ik!н{@ǻ{;߱d2e&DAA bP$bHT}z{ vdY^\wGLX tt 3g(3 [@*ʣWTUٮnJw4jeDVK/,݂d2ۿx!kAQdGy$IDG)yZu*DQ<>: zjE4Vx*ML4 P,"NJi.{:44`Y\:bY;=TUSdI:H$X!mTZ}>@ɭ&eYUo #|9xcWʕ`fG(ttx1R$#ݷ_CJ5#i97|w(7.cд~YomK0u~}ZDol}b,{?U <1l6ѩO+Oўh8_jwgR1/?7㓁`PӴO"4 af7NH-0v^ "6RΓRlV= $؅gBqֽ|>XH%q(;J5uP@mA!C)͜rUW@20K#y>yJG(2! RH7~ZjgvwqΓ1ݷ߄BRؚL~Ds{eC.Utb$Jh ֪GǍ9\.DySjff;@tBONNrīmKy.ݺDh#4*DuwG[kzv fz\5Ly,GcZx+'˶ceY%+2cg#rVQhSQ}ˊRl 󹞧*A(+<%Ik5Bc*DqgkK4=$ѲƫroРl;` C33ӔRAKr#qz`eiR!9ERIJ7_ t{~a7>{1T*<S_2Kn+Bw0JE[.9F쵋P흝mqB50h`/Lab d2E|Y!cw ,E1t=>6>nn֍et޽wooTգh| 4b}]rܝZyjSa*㨪stZT @[mZ(ies98YL &q0x<ݏ1 :lVU=@!8h: H$ vx<>Pc/Q"Tq<;N:z%"q"p0`0(2FHE˶á!m7m]T*B>f2j[*b9=4B!rjBXUK3lbӰ A>ƳR.lAq~Tw1?܀11T41B_!Aɿd s]4sԸӳ*R/@6 tuu55@,G"k!ا5s1<{^ !TU_AdZn⪪~/_D3ݽfBx8z֝nna5:lh;shkh.Jم &B.E_G\ oo*xz+7ފ"|xz+7~4*%sĵ, :3z!p)#n]:XGʳ j.:20 B>!de=`y;C~ƓɓՌ ?b*x]Tht놑J&]cLUX,j5UUݵժsKD"g9!xⵢb !:G.ƘR⮿^C;_pνXjR6l_ۍ7RwR ySđtuJ7ފAv!t%tEXtdate:create2013-06-22T01:32:07+01:00zj%tEXtdate:modify2013-06-22T01:32:07+01:00IENDB`puzzles-r9872/icons/galaxies-16d24.png0000644000175300017530000000146312161170244016603 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭnIDAT(5[OQ>Lt"TQ"@#DnY@,^Zf3=Cu=d|NӮ){7z(SiۯF~`Pw;j8zmrzRJ[;7?2!z=bvkkkV:'Ax/z=H#@YY!E"*i^'""SSSfYIUU4lq-.܅1 hkk1D ZQ) }]vZ+";Ή&*reeYkt:"~8,oT1l=yd> "jf+߾5A-i4h4F![W5*,C][yJ(G͋yuNrׂ M?#Mye~ÇXZ4qݾt,, -..)uO*L*,^)5~33"VU53[ֆaq%tEXtdate:create2013-06-22T01:32:36+01:00#v=%tEXtdate:modify2013-06-22T01:32:36+01:00~΁IENDB`puzzles-r9872/icons/galaxies-16d4.png0000644000175300017530000000064012161170244016515 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(uRq1 H%w(tPX&`WEn0">gIDfg&|\&Ynد݉ݻƨow CwafO,!""TվWU ze>#сf+$f=RuRdku] y J6ђ^%tEXtdate:create2013-06-22T01:32:36+01:00#v=%tEXtdate:modify2013-06-22T01:32:36+01:00~΁IENDB`puzzles-r9872/icons/galaxies-16d8.png0000644000175300017530000000145612161170244016527 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭiIDAT(MIOQU4DC3$ 1J?.524han@z}.Y|Vsh?y&"@ľ `bbޗR$In4UJ} G#CG}GD})!bJ1c$KD2I,ˈ"sEa-dvh-9爜% 1qom/okjȘRJ%9cLy>՜K)+WtNrd }B0h9}hQ@ ȒVCD;;#eP<Z&zc jm}cuQ}0p$\  !rDD#V}X%" Tts/^{Z,NOO}c(^ҲRc56‹2jzzjcY;'7>sq_EYyQ< ιd ODr r^%K_n< Y縗"k5c?70 DGHiS g(23 %tEXtdate:create2013-06-22T01:32:36+01:00#v=%tEXtdate:modify2013-06-22T01:32:36+01:00~΁IENDB`puzzles-r9872/icons/galaxies-32d24.png0000644000175300017530000000360212161170243016575 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHuVMlY~{KE* 080AmbfD#Jb\8q ½ָБ8HdEK(Щt mK9uq}vw>$"FQH "5| bMTD"jo޼KBz~d0zݮ}ED }]̬(yۿ%IJΡؙ֚Bv7'&(:Ƙ<ϣ(}`vᡷY$$9w~Zdf $IIXkK} n\_/|"BDLԏ=gu,v3Av};{T8aXaTLjLPi@Bzk͆ ޥ._ټCLFcMQ?Y<+ &'o]뉈'?: @D)al\XX8}{Nj<Dxz3"n۶_Y344w8g_hZ@FawnKKK<ʆqj51c̦8\@ϋ,eZQС(b%kZ(օօZN`湹OB+^:@=66655ѣmG u0 1c ƘZ-ð\rǏ7~ѣ=>m6ڀ1;33%iey7&$mۺey] F1`.kDT/ [FG?= ԍ$IHD 00w@$B$BWO"$FiӰRLJ+++Ճ3" vpAg IDATH}Vѵ żW[ X0N'Љ$AdÃpADΒ/isItErBt*3DZ}>4KLql ƫ*"~Vl5}cmjD5X"({|yFߖ2.D,"k/wV@JUr@shxX)"V&-IY9l-DJhN]"^/"i n"נ!n_beU6[GOUFo,Clq.h:^kAج홯5CvCѲ>#?wr&@iZkvZkf[攊j*ESRUmops}BlǕ݀S*CV^ksNbI[fVe佈ݑ9"/ &i!D:aA1ik͓8]%"a+*3? 0UZ#H'0 Rfá5;j5ȺMK8H$'1ğiQHHb_kc{%]lދwZ`jfW!|m.GC4f>B7s6 s R*$+jJHx#c ] l).?;f KH%tEXtdate:create2013-06-22T01:32:35+01:00l%tEXtdate:modify2013-06-22T01:32:35+01:00ؖIENDB`puzzles-r9872/icons/galaxies-32d8.png0000644000175300017530000000341312161170243016517 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg FIDATHDžV[>UU===3z嶻`^ 0BV^@BqyHD$X(B  NDsUe.bBX fzwf{vOzk۳Rs;Y"|QBꉞIrrjooOmB~TJBDRvBPJ'&)ss_+ ;%0RB[.<> ;P03'Uo(GjfN/}_yg=CvMa,Dk+WQ 10v:썭 cǨT& T!:I zk9bǬ;k#LN I}D)qᓤ==o4:'"68 M]R )B#e|aHq (*tQa+j:}^xJ'Yf2 '''wU*o\[:50bk2 M<.x%pPT*5ւ><>GMH^#G䑟&]hRB5&[1bVQՊ?7y?™ܻ? û͒Mh"]Gj m$8R Jg{j!MkRUdR1vB\@O]sBWF61fR "[_эpctєlu{@z=alFPKj:>sbx|*a/&Rp1;;SOQ^68s?8w(B|_n'h'F#a\gy:Mr,w||c~ZSM͌ R_R vߺGb+/$:88y^DmT,TƗ&2ÐP 5 1MUU2z=`7#8w7cnB {CE:s.tڄ6yaJuy"clm-վ(=!u][p9w TƱ&y^Tw80jeҹkRS0^:5"J)5~MK+i"uJa:TՄ雉 !7Ë_0LtŢ]Z½po555=kUpYU;::gs 3g50裏MOOkuSǂsKqv))ؔҩW_}-v.֖b GFv?w\S*az^ fc74]_wݨeڌ4\W2o4M@ZcBɚN@tߏ1{u&(LPiʼFsaToYWmחfc"w k]&YW pM?/~{1LH\~}6RƌQ yXI6 {>n'|e"*!8BġnW颵-18lɻ {e+Dv"69m[ubl,MK]1~d늾[+QF [ "AX%,&!D)eL?vY <%tEXtdate:create2013-06-22T01:32:35+01:00l%tEXtdate:modify2013-06-22T01:32:35+01:00ؖIENDB`puzzles-r9872/icons/galaxies-48d24.png0000644000175300017530000000504112161170242016602 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W \IDATXYkl>;3{S8 Ai5ІG*4I(E OVV US~P*TT(D+Py*B T`D4&-lvv^up zdYs{ι=CcxRSQ(e2(\ )u<& éiu=hBĔeU*7a!(UYpڲ[o&#J)YJcL^z饦i*%0ΗR!ML褔2 >>~P(}d (p q9cD400 A~ lrqBi<m#bc,9#S______d;"c C5#'e+$0B *E"QS@cQ46ng"RW%b1-~7?k)s ^+򅐥R}Ү/uta(% u1.Pm;3<<|U_ a)!r_=򋞞nJXl6k~'O}=ut:mYqƘy71jiD_z,˔R-$l)dKK;p_X ˿:߃j牨P(\.zjD}_g"2 ՙ5NqGgfZ!ȑ#Ü󩩩={hת oN^r]:|-[֭[iӦ ˲[^lF/c>mRJJ%Hy9;;[j]yRsNvZ7]933cY=zT&"NT*]o=8Jڶ===k,t>N!PJzm 0 C)(;a=q +>gYVt3 }x}쉛v)ggg:; P.2LGGGmFyg_&Zzw~kO~tIY tgϞ]R\;vk3cKk\=2Wn񩧞 0rʄ3s%,T*BPv $T_~TT*8qԩSD(\.ꢞ.m۾[BV[y^*` 2af2qi'_(WT*n6*_CaYa>݊[!d6dl:pQA=וI6HgbcMG_L, jElF-%tEXtdate:create2013-06-22T01:32:34+01:00g%tEXtdate:modify2013-06-22T01:32:34+01:00~ߨIENDB`puzzles-r9872/icons/galaxies-48d4.png0000644000175300017530000000171212161170243016522 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXY l8nR:M$k mJ?S(  33s R +|e0I)GyݲY$8crm󖈄][T1 ('4R-DT9q9=C*#{}Z xtIRm9_Xe=}INd/j PҔi3(t^Wpf14 J3x|KCӣLDP۶Z4gfJ:۶93suBWzYGs[ J&b3m?e!  ջs(cM m d2tf͢>?E'zQSLmS !,>@cz| xr*gD0RQD:f 9E ;[uCK8QW|(ߙqڭ! {8R?|+ˮ Hl#!YqSwǤeYM3뻄Vd0}ߋN'RJ"¬a(&/sv>C1FLFD9UD1猦 1n. P6ޱ 11ӨA+cJXDH0)Ω1 Eq(F_>2^UlgęK#wJ%*axJѦ)%F>hY(W533 93},1тMO/__wP/~>W3v$8VC\y17"GY# Mι?M &<q%tEXtdate:create2013-06-22T01:32:35+01:00l%tEXtdate:modify2013-06-22T01:32:35+01:00ؖIENDB`puzzles-r9872/icons/galaxies-48d8.png0000644000175300017530000000540312161170243016527 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W >IDATXYkl>;3;w}$Nb!$@0M. %AR[RğTUU$"VJQ+h@ǎ ;v{gwggg5_YW9 rREq-[wzcAgq\=nX{]uş~2nYV*ЬԱx&V3 RqROQO5bE@A,50圛3?FF[ĜG n1=qERl[}pH{{:tzG(PS<fz2\f |zjzy_o9O޲aU*$ځccO96SkRs/X|?tp;m-;ӟZi&N]uY8ᙙʲ޾=188SOR{bbi|lDXB΅3yY6|1{OϴsSӭrصdΝ,:\TE0 F Lak97b뺗Rq:7ip){t-]yU)h߈k%h:M@CHc#~xJuI}ߗ2,+Se6\J^_ejT;R'rJ0MkK)cvRůfCK['T_xn^BB)w֊H,Dʰ$WLk XZ*@Z.~4ɭ{ \~XZ?9&`۶mw<j'c(8=Pmr⋷޺>,̙_x J)d…2t84}PpBFQm.|A)5Yz?t;tcQ!|zg?}f6w)>Jג%Rʩ鎎vMlE:giJMM>rnl0~dXkzIEFF!] K< s?Rrn+gӷ9\R\WR#`5;tޯ|iF k6;* ~/%tEXtdate:create2013-06-22T01:32:34+01:00g%tEXtdate:modify2013-06-22T01:32:34+01:00~ߨIENDB`puzzles-r9872/icons/galaxies-base.png0000644000175300017530000001126512161170207016755 0ustar simonsimonPNG  IHDR  9bKGDjIDATx]LD0!l"p'DTE(],Re{IV[oxC 7(Mm+qWB``L(ń\41D 睙ߕ!ǩ?7 @ B!`0B! @ B!`0B! @ B!`0B! @ B!`vnهItCp =E|_ _z%9mh;v~ !`0B! B(?䓽{ڵk޽%%%?s(R4Cb]__߾}~Ғﯯ|Jm>ujkkrPNNL&_SS3;;ܬ`.}}NG.))a\pVVVZZZnF KNNvݍj DQ4 YYY@O'O|1ҵM1JLLt8CCCsss+++sss7ot8x)_K0BpRRRdp b0މtMktl Oԯ5??F!}i]۷nnQ{.IҞ={ϟ7~Et?qɣT*᳾R9٨_իWZuɡRRR^nrG0~oyeToPHp*_jeNAxC@xC@xC@xW*: Uu^2 T7 T7 T7 Ԃ7<*_bL&(]]]SSS󩩩[s:Npߋ!`0B! "2~B:F?Fq` h~jFĆFQ`vm"F4zc?MB! @ [?oj׮]7E{cҒME)N> rTa0)" n뼩haۀ:o*Z6`Λ6  n뼩haۀ:o*Z6`Λ6  n뼩haۀ Λ"x~QqTtaPSE׼#O< ..꒒|3nr}<00 s,:i,gˊ4^?X,aH>ߨAUi~`C5}Y_QQi?biijܹS&z`O+=I[+|H Z=k<--h4խG|O*R^?PSА^o<}2FůO!!!3.=i~wr( B!`0B! @ B!`0B! @ B!`TRG=hnD|-bEэ3{ ";H7> Z_㑘/KOF󻢂M&! @ ]D׼#O< ..꒒8 zzz<Brrrvvb퉉ƯC(翳_KPH++9tKa[766~}}W<Ώ`=ڭ[azzzzzd6ŋ_SHxommmW\mnnVB{cbbBӽs#G2ǜrzߏggg0./S:<F{?,99|ޱƬ6ؾ۽vWWWնxƬMW8p^gPD(A{ QT6n;6MwIr:2[VEZF C\3=n$g#lg륤Az @ @ZF X3[:nKoJJ i=6`V IGdMY@aKg}$ֳnVTN褑A].j%g#b.t7j(cPDn޻wNu X\\\ӗnbiijܹZnX6}+++;::նPPiѣG~;BSEJ 響)##l6֮EQ{nggRW_aLҒBC{oyY2Fů_fl6dE+\OJJJn  5gNל9t!<뫯/..nhhMNN^XX$ի.]|'Ԣ%11p8Ư+GL EWj\R"BiJm0E+Wj,ZRm_a3ZR~;g5??š^n/|tI#gz6_BАY_ii _ nr,ZR dJ ` V4666lW󩩩R l}@/m)]z @ B!`0B! @ B!`b]eyy};<2.))++h,1 H?,lkd(&'/>+oR@G^qu֭6ge^xY|9` x7&&&"̳ܿsЁ?tty22,s7j(cPne}EEŦ,KKSΝPq0A V=z4<+^o49) ʌFc]]yV>BSEJ <` =}͓ςN0+h4\Q~P(x@}Csxn|-0AjΜ9sZBA닋rss$IzK._, /Pz]rehhhf(BzߏП6669|.Mnwcc*ڀƈ(Afډ'DQdPPCq:2[VEZ1-R c$OI60Ft:]08l `lO\z A].j%@2ݻWW/H|||{{bٴ?#..Ym@cd2;wӎ;vy<' vpAg  OIDATxOSg".6ђxtiD *BFeEQ1p7 141MmqS!TDZ5`MRNGyy9yyx=y޼>bP|iߴoZ7-M |iߴoZ7-M |iߴoZ7-M |iߴoZ5Vs?ߴoZ;ă[6<}=W =RyyyM |iߴoZ7-M |iߴoZ7-M |iߴo L&O:o߾UVlӧO'lC9wƍ''';&›7oAhkk ŃYESs8qę3gBPiiih4v]N[7nرcdeellRbb>W)333]]]~1)֮]gggoz-dZrdIIɞ={^R%v\>OH\%.C ޽m6JKKѨ}hpe<;˗/3_`0xbaɺ{Vr6HdR~ٳgx|LUMYhuח_PoJjVh{>O#N ֗co7-M  9uZ#õϾueu-Spíokn}87p;Hd?::xr"[sK/>v?a>&q}:pױJ~iѾb*v?jKU;w̰>]^^jpHg;պuVkKKpν{kXK~}Vm / vpAg  OEIDATx[r E*v+S>(S=l!+N5 LqnZ[ |ko-Z[ |ko-Z[ |ko-Z[ |ko-ߦ;z֢m{^H)YknyZ[ |ko-Z[ |ko-Z[ |ko-nڟ dmBrI)mιーQR:Ufw\{MZ뽏1:rWB{>.)‹81ιC ~X2=(jZBl=.Y[iz1y|1)~|9/X`6g_~\#5Ehx~sq<xm\?I~r"#TNTNd=.y"ŢH^J{hER Emb܀qE$'o-Z[ |ko-Z[ |ko-bn}pAFZ[Bsnfo;_EZ[EoY??|v}ᳳr{Y9[>;nt,{ks,-t9{|/W>뱬ᳳ|KggYwgge[gg#W~wece}:Wօޗ1B)Ϭڜ(!a緧\Y5CUlsf},Wk8W\ ӣu&.B])v~oK;=ߺe4taVF! ;Ykdb;ϷTO2@=}犨Y$9,'|>=Te-h+eqZ Q+TdZ[ |ko-Z[ |ko-Z[ |ko-FٟKZo<"5,%(G}s%tEXtdate:create2013-06-22T01:32:34+01:00g%tEXtdate:modify2013-06-22T01:32:34+01:00~ߨIENDB`puzzles-r9872/icons/galaxies-web.png0000644000175300017530000002351112161170207016615 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge&IDATx]y|To7d2$@,BY"a'$lj?`XB[,J[l?KADd` ;Ad,[qǐ ! q{{u!Ԏ ;Ў]jG#(*kM<=lG#$)5,!D4!BTC!B4"mY (ֽD04EQTd;Q#DVkxXHyhԘq,Gԭ&iqjE:bYvח[N(\HE{j7EQM%cY]!]xrubB(=O$IQdA0ꥅ,G1{(tgΜq:IIIz;LQQQz 34Mmח[+n]+ NeD,'$8{֪w}'#c!|>oY+))EoEIxիWnn޺:t?xbuBߝ:٣g*8pf [(nfF ! x^ɤBHdQDUEeeԅv+1ΩanBEb4M?zB`PQI8>dQ|U{ܵ.bQM h  h C4#Qj|  040 [WEAmc0LH}?{: 5bhFk1]~Bt~~ F]wݕ [.0kgcLQԪU/^=hР̜9YXX*_p7PUFڿŋ_~eͦwټhU\h aeYNOO{>#-ٚhg!BUZu֌ T-x̘1.\pݭPBZ=2< +**ZuWa2`mpeV8Q; pKOO?r(?=Oeϟ?p8Ei|KwYeYUHCTiBHFFFQQY( $8\rر ôNxdaY(zW\TVVDsر9steذaux77iӦs!RSS'N`رe.\P$y8qbff&2E5CeQ/^$ʼynn ]_Z,,ȨfyQ&C($\.IΜ93~x 1ǫkPRV7^ VASs ^6<++f]pa.]z(-\LKK[|u^{+))*r۵3p싍}BGJKj Ekz-ٔȲv?M2TSQeD\U !(Rfj| X8{|e9,$cty]=wF0xii/.Ի:̲LZ@zO8Akh4?~V[,f5]V=nﮝ ƒ$izAob(X,1zoAAB( IkDEVBU+ SO:|g$YRTwFQXk췇gGR}tWTT2VO$_VV(ʊ+td(..:{eX)+w8~TW[cA0~P3<%'ur=:XIJ|ԩ$2;ᄏv䤤NKy~K/h5<IJy7svb l~{CH;c|ȡ~ShP3!E^%Q( V,+clON,+`hW{EIECb$IWQK].Yf3x|G@JI-$I 6z4C#EQ{@SG:++%@t M{=@ `],8!~,I7w8)nJ"U5JG`l|.w.PR/wp8еa\Xmz . ݨK=ê:3 "˲pMuG3U5k~Y93L߾}[E*1+**ˡǍ=zܸq3b j>jcǎ?pƌw}7?#FjUKC/رcg͚bVZu3ghy|u-[6d(n ꫯ]-Mo{'Lb6aի_򗂂={B'Nh@Ѐϒ$egg_5T}}޽{ j {'vءh>,H4ntԩ دj.Y'''|[`d2Ր+iD ̢EֆfdaBBB~~>R.\ =BTÅ(/BlllK`K#GܰaizƍFjF#,_5|Ucˎ;:wl6[DԦ$b4 f9** = !F.eWZ%"U6|4Te٨(,B2c8hРA5˲۴iy4l\$Й3gy9%!YYnXCDy.]sύ9f\]v̝;afm˗/_pA%ֽ{7B.vj1eʔ`08{#Ft9۷hv)NJ͇FX1:w><&&d"lݾsJOn5L .}jMHH;!Bջ ک. -_'=Z{jа=ȑ# }"\ !,Aڟ!q$B4|^n4#AS㴱 0 \qq 6LpLʙ>J$ɠDq8`a"S9$3L㘘ڭsEءCZ۷{D PקBc,$II @ b?g߯oBB,s=?Z$J(骨$)?nU8"\Dz4z'&<;A;u 9pϯg NGz=‹Â(hf|=w 8H  >k.[ꇤ 0U]^TO4M{I' 8o:x@0 #IK+v1](JE2#G ~px?-I7=}0MS(|vՆ&iEQ+* R4sM,~(DhAg@m,lhgaG; H51Bi9el( Am.)8cY3H:\T{ {4>`Lhjn8-O{ 4 JKK4Z2Iuܙ2WYa0c9*KII@6(BT\#ϲ uU! ZUAhI \x<5AQ`(tJ_?K81GVV5:cz1]v(Ba^z}?(j䁢UZz1VQ娨+? `0.\E,)($(x#[7c=!+!,])*)-9h2DQkg D1>} !$+I8jr_!I(5Gcb`xq\uƂ0 KM=zd=NeO$'$8oWbXHiQj70̲W_B:!\i$I#YO֡cW{L@DBQO0!##nT'dɒ6܊";v'?4{;r\ǎN(2d֭[Qi؛<))Fcr)Jtv!K >w… EXHQ~ u9|}] Eq< !]Xq\RS$wbYN{v|y5XNDQÇtl.ʲ2j5kS^^xq.O?5rx377يr>wT̄, resRnB EQ˗/OMM6m?K.n\[]-Ok(ƍ^"Abb,V{a訨pc$6[A0 Bˋz+Sju-enn,ӦM@\I/\vکS6 o%嵲Bahh %%%vA)[1!к"T3(Zo|oQbn!sNС++8gff.]Q#9Uu<̵}g(l}#(Iu&nٺ}ᢗ5N\,xI{$JH8!y= (FcSLrvGGG7 AfKNLp:~vG!A0?&8nQ{՚t2 ַOz OiVH:x3j a pX8eB`ѳt^",ҹSN᭨bNLLLrZ.I#mo)˒Ƽmv 45 X]Y ˱ddܽW/_²Z rQ-66R'XefhX FF4p 5 o߾4I T77nj] )g-A4,o>+%ړe2Y-D$Ǎ\Bƌn8p qx/--!_ RM[huu>4 nĺXbYF7wP嬣<Ϟ={…Ǐ4iR||| ؽ{'|T\CΝ_{>hҥ.bta-Z\5kxޜ~YZPB.¶mԳvPԘ;Xu`͊knGhgaG; ^EmF;Uд>L8Uv;qڃ@ɪ&AȲ (~&rB}`0Eټi{~m9EĐ,+GHoC$Quʮ/B!]5DƑee+V_^~=6x<^O B5[6̃r9$%Y`EyȢګYk9Thb4xIyix &8E6mfa2$\A竬H]XXXruzZe{\_=l7iX[AQT(~\NWサX,{|g4+r]_f́Bx/n{ rFyo6&LG! (("/]?5k<ӟr=-y[ x򊊊Jχ@_H#aEݨ'|r+Vq#T4M]}؄9tЬYTIkE{DQk[Oe~yl6dUԃ|dhK,BZkqo֖-[ؼqtRqEMeI&*+s;QK0j)mAS!TB{EEW4nc \rkUKfw>G!bTz]יXN+fs$ @嘘GѺ"v;E!7U@x%Uml6V/h1[嫬+ ysd+t%cY]%cͰX,zIIE֛> UGf8p`3LȽhlڵ(˃9  ǎ۳&3gF( XaN?~WTT ۽P`LMz2\1R (6.$ݱZ}f4t:#V;n$I Zs!E{؎FЈӭipo>?+*{0L%tEXtdate:create2013-06-22T01:32:07+01:00zj%tEXtdate:modify2013-06-22T01:32:07+01:00IENDB`puzzles-r9872/icons/guess-16d24.png0000644000175300017530000000173412161170247016140 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT([LSgNO)E*8܂$fwDˆ}%#YyYF|X|Yabe&̌s#%f/. [ArŖ C.c#6!ŕXEJ_gt2FnB B]Zoݮ\U躰sןhrۖ3jp0yxbR3fS$9WyUYͮd7B;75-~Uշo-JZlv "{J+1١?_Ϸw:}\Lh.v-MaΡ ]=€MHxs F›A&v (h7,~x'q;BO x&I F.߹1MK~.ڋ}n}}HQo1?vLaC%1ƛV-?VP{e2ҫk=h*sE9wD k6JВ?h3U}]³FLuOx8sXώ^%~t6LSkuoZnOiBH~@Kg1Bwn<~sumPYBڿ J|v$s ӹ~M&|dyf)g3,38`1`W'ʆF3s%-ˑrpa'Prd)ӡ 9#[3r+ՉY' JE⤄C$pq [,0p%tEXtdate:create2013-06-22T01:32:38+01:00 `%tEXtdate:modify2013-06-22T01:32:38+01:00AIENDB`puzzles-r9872/icons/guess-16d4.png0000644000175300017530000000073312161170247016054 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(ύRq1/)ĝ҉Dean& /I$#Hf&T p$UH$@_I&Ƕmw3k^mnۄ7{ݱ$%9%63#hI@2 4h# 2df˴Ycû1p @DU\C!\rg:_b,3߶J 4ڔty^ǒTHԀ̜NEmTM~1]$ bc%tEXtdate:create2013-06-22T01:32:39+01:00nk%tEXtdate:modify2013-06-22T01:32:39+01:006hIENDB`puzzles-r9872/icons/guess-16d8.png0000644000175300017530000000174012161170247016057 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(    %@C%>C!lT"!Qjjf0fHŦ vpAg QIDATHUyp]~{{  " (PѶRQpڱ:ڎN[V}Z8:n1n1, !AK޾r3g3gs>狺:av$83j"eR #$@!84sn{᥉k~Sλ5IJwܸŵڱ֯V (vdSn9kswak,H$3p4rTpNm=Y9yã˗:u.:!(-D_.{\ 'ߴ-U%BТ8! g.hjJmhtᰛ PjE6)U=th #τ] @bZd`A` 6j2{ \Μz=!  Uͫ))M'> `[V]ڜ3Kj-0(5B!XL$@9y`whh$Uuuj]PR:f[Qk&wBOyx^J2R侊1#tOT̯<ޢl6%H@Xb<5xtgv׳ڛ&Y˓OנpD o=tY̪"BpIr!1Ν9ݶaɚ%|J1{uןrp}Ο>Q?K^JZ>ύBt֍.jܾ68yK羣\Nꋊo @aV2m+9LѴ%RIϾٺx4:wd2O@!:@6 \8`Wۧbd3 Sia2wv]>z=֯#!BcSy}{l:+NۈWU`k>{$ک]* m"MSWP:ۅJm2)mŪiv }0i |0ݰ!" ^9H?{H__ݴB!K0fPM8@ۋ3j .ܴVMsO o#U }бŋD̲}~`CCUU$q.(D2CN V/a`Y:y$a&=L;yԄ EQ8Sr:5Y(Ejd" x0 jeJ)7FvLH@{|>?IHL&ι0ro(>qW= Bg/[N[76o2t}#-jm|Jј [,|r!~@` 4[I띏2hs1Xi 0:= 0^ZRS?aۼz,6gٛ 0-"P4t|v}\~Qt=zwj9Z]sWiU컓;$xΆ@>z? / Y3S9ز;j-[oa'No_XXъ𪥏N L-&q4L)$ iJOƝ|f ܼPve ``ӆLs`Ln #%!r";7H<׳$I yU̫s^ОNgRiV[vj&hUk7ulے`ƙJy箅9טj8jвEp4rp4q@E ȑLV[tpӞ $>) j38Zs0Xl`y޹K壦E8bdsn57ݻa?=?Vˉ%o>g=Kė>vͣ|e];owYMU_z@}Y9dm5+6->TC`7šƥegyGe8T5_Gѷ$2` ŏ=ԉdY ~vn 3N^@`Ed7Mv#+GZAk$ؿo,9s-++ucUԍztJY( e 42p *-ݠ!% ˌ|YUSO>Ͼ"s} v_ PHcqw%a: =Rs,2ydH t\KYƲOz*'M^| ͙]ǹ@̯X kZS]A%ahp`1yb"\zR }ER-6UI3?]s4u"4.8$Sc3u0DȲ&|\ vB!@4n!0!dEBQ;6Ta% %tEXtdate:create2013-06-22T01:32:38+01:00 `%tEXtdate:modify2013-06-22T01:32:38+01:00AIENDB`puzzles-r9872/icons/guess-32d4.png0000644000175300017530000000202412161170246016044 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg OIDATHǝVѵ( w"Nn'*} T~y'ш" Zk#/DRd.$Hל}Kz`xzGf/5I;ѕP%U!* \tZkG`#!RtW"^,b1?0=VzDtŢؑ?is0 $|̡Zz<#^  $I'hہIvfwDGJ$8N07KӇ41褈v0cF%fE3FX8YYws#\x&~>~~~?_B3{>f򺮅u]unW_JW{zbQ*ai_ZTl(r͢ʑL$ vI>>AfqId0뷬/hOGk݃D9XII fGR&L,Lmm Pbs_>+dcSZܥf#n}m-Y>F4" 8If^+SW \r$i,Z:A0|ҝ3I$\HSr燙R#rbz܌5rYA J.+BRF->yتlipG_eu)F} fnsW*}~;vpFATթi @$s&ÂO,Jd(IhN c}E ~af$ݥِ G6oo Z#I:bڨyr35ZzѦDeعAXZt1ϊǙ4'}\bXۧ%tEXtdate:create2013-06-22T01:32:38+01:00 `%tEXtdate:modify2013-06-22T01:32:38+01:00AIENDB`puzzles-r9872/icons/guess-32d8.png0000644000175300017530000000512612161170246016056 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHmytTud21@Xd)PRrzz9Z[UTNEP+-]@!$fd^x)z~~~?b}03(Kh.$mP2P$!2N(f\FAhq^>?D1 Y}ZBYԒqkjlhmO7DcDu`魹#rv^/  BiYLrxM~ի1K~=u]qzO&&69I>tK];)?vigvL @h^0s0 EOH>@zTq7771 ε8|z5^Pm:(čD>'iQlS?zx{:;ynl2쉺O??.-v?C'Ox*FFHPBcdrae^#W[2T#>OUK3ϹX}u}m={SS]]V4`łz@CB:*@TČjff!%M@NQ}/iO HadgPiUhR1TS-e$e vZ_`I\QK@xB[mA726q"%YٜucW_H]/S |َR#둿4wor,8+QQ`TDxhyuF5͓$Fǐ8NMiYGP裶Uhnέ+A^jX&e8Br8{U0D!ٜ&.*EH c|˜wB9maYQEY8k+"6| 4?f@A? 4<"q h vpAg00WIDATX͙ipy>.@xyH"m$X%Z)$,9rTIrT9T%i˱ٴdH<@p_X,X13!Je*vjAAo !@@! Bt!SI,۲[b!dEALƶiA0n6D87m_Ŭ;z !(B#jqq XIG v8zYi ?'@TZDKfeUI dYU.loe^˶SRp.c$0n7]C(>Èdeu?'w=+s[ְ7 'e#ɱGU+0JP…͔t/aGFGGs@`?0P8<{c?m륂gm;v/gm/_U(6M#˖FBzG 42㎳>z"682>ED;N5%W(AN/%XFo!By߿8Br:í 1ƌ 6ʘBH55o(>wK\.ܹnLH&'@s';DbTX  #4j! !c+ BX\j+.\j$I6b)G+P1Fs+KkBr1 Q3 oI(p)c6dׄ #cp,B,eq˳6r8QS{0ƶ@լ*|]1u紐|y|xֱ,=sT^q>5Ce?)U0F,ˊ1Ry6n 5ljqyNhpts x2ݮG~gt"h.~9e,cضNv]EOG]`q>V:E-:L%kt֗p~qy2x`r2v!3kCBA͝}yy8cJUmi4겗T[c >z++ec\g#_U\qX45MRad  A2?qtr>#<9Tñ}cj=D/=Bsj[5nMַ;=us5gTθ2vK>3αҹBJ !I3ﰵk}Kd4z #۶m !f:Y ӱ!m5[ @I%ebԵBk'L 1' +s߽_Za}ɜVXX,gpC <# Hae ( ו-)(IH)ذj`U`^p.(`Vܺ!J1N)'!U.N6574ww74j:9g}^ڧb(mȘȖ>E-x~Jll ,,۪<<!oiг\a! ˮ_Ys%%%[wled??{}M5ʡXv 0XXnJ~'E?or,H,mۧܲU?Z:-+jE]wz0f !,RcddYnCp^Iն#U[vXEEΥY,$ըEJ @ uˇ2e|>Hp*9kV9[r9@HӴl0lv\OCBH'zvu+17{6^~7hxjK8W^7vQTpH {S:yzx ԉ՗^\5o<eJaS^SIlRjio>uz奒 8ih摊Zkצ^R {Q5 ?nlLT(ؽvu/t4Ƹs9ن$Dg_-1%0p怢D.^>c3]| Y'?X0x9sQE^74Jq 4nw޶u,++rus!8clӭ c@ u$lfx,|.A`dYfڸaEm{<]ƜCma2% 2"za>0@Ti(^] Bq{*#("! *秚zS9%Nnstnw u/YF19bu5K>K)6>UOS?>C sهyFDBq*C6 a!$OV270}>J"4H^O \$c[H *eL3㇐e9~{vͭo=ze2wu_ІϽ]]Ք}B%|w_j<~ ﾿IWn 7mr30cJ_}7jhhǞ$vR˲n!X^O^ mUedypj_L?Tw]fre"Q"I!/ymWV؅.2˔HHA`tB],_Pq=|h"wvM,IpR_f[̓%wtvT/i!l1^e`ЏV\A-0dѴuZw'zo|Yb>?eE Vf}̪:"(ce^|EE.Mm۞ί0+|C2!DH2'ʹ]]K!IJxd]sˀs$QA)HoشdM3L)L R13a޶9:6q؉g [/ FFKJ?ڻӛkRXl~4T9/r?m O>@c2/l;m,"[ 6N$W;4U.;S^^zfe Y5y7D敳k\ܶmK211F1s&hXb' "6/&pHdràm5e%v! 4u]@>5$8Q^UTzHd{dɂ蛴FV؞{* 9 !c$,$3+1Fs`D &\Q$`ieݜBpYz{ꮵk 76yGM40<ɗ]Sk [| n-WlM;^Y?[ ~]{|#JMg!HrKT$!ʛ?gگņ/?x+@۽uYgٌC%dl 0Y]ԡ4G\sCC SS @ƈ\r`K٫W:o?Bc66B T ÷\j ADMTRAfܼuٚ2JT"Gm"ymT}%tEXtdate:modify2013-06-22T01:32:37+01:00O 5IENDB`puzzles-r9872/icons/guess-48d4.png0000644000175300017530000000263612161170246016064 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXõX3) l@&Q&LԄh2q&w?Cf[wZ 3C_LD*;<3vz6&333UD`&2<)9M{gaT{S}g2Uuy)%}qB~ uXc/@W&޴^R}΀ED Psڻp2~ ){qجe¨|l8)qYZ=9i9B4 69<%h %D}+g0*bo<(zf"S'J_sR)j[!ljzۣǗL8Z)0p)"uCbu,&HqㅞS(~RbƼ:cw.3w&o(3l!? thwǙ1 "Btáz8c0{Kˆcke"Xc>ZE[/2N[*өfʁC>Ƕ:1̙{rMʂodMp'S&[JRSɹ)%&nd %+Cv9brR s]ؗT}%tEXtdate:modify2013-06-22T01:32:37+01:00O 5IENDB`puzzles-r9872/icons/guess-48d8.png0000644000175300017530000001041212161170245016056 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WEIDATXíYypUnIò[e'$@9HGr Il%eZR;URق 3,,3$@bqbGlDze˖%Yuݯ9JKΰ[׿ާ}ku pdghX1?}4 h@#p)'nc0\=,i[OroNI/z#I-rZ6eNĘgL"fK'bb(Jsvx I28)D(2x6!1@y_q?˸TM`B N|elÕB ]ROF)al'oTT2W/οԸo2<t;HzAcac_RY}+ye3wg nMܴkS6LWO kXjN䦖L?$YB 3O|Ib  ZLx=K_E9j{+.q%Q$HZJܽ,bA19RVV3ZBi1ڥѨjL6ōVN`$Nx/$tJ$!5S 1fy@(2yrEOҮd 014ݡp@ ̏ͻMkşyC*zV GF4#8>g|o~z/̟w1\.?< M@#C!wn |zp*04B<\:f0! Bav=&zG1镫7m/j4Dc[`Ó ;u|uy#Kg)˲Vi:yꐛٰџxnFh4۹'awb;v /\ǟx(`պP XZ5k4p̓pl2*x;Fn9кdF32aݧ}6~̡Tj1NqE5[mnϝ_ś[VgVpdnˡzws_1-΍g7@u0mk5}6˸H2 :mOG:u];Etmc ܚohCI㳵L8@[ۥQh[ۥfzozx{7.Z޳Z+#{kkq/B͛wP)ߜƦP5ܢ&AXq8ʹmX4dMZK@^x߮B5&Cx:(n ;|'8Ԧ^xJ{" MF]O-ܻg̥n9<q,࿒BnZ*Wj4H c%_W\ +_"I <<S9%kme^h* ȹZ,ʴIDN IA-?@^/cIL%1Rf8%$HR/ĭq@#$gTt,KJ2)&rTL=SlR=+nfHbP* AH$Q%INbsq 茼_-\^sE[~AHFA3I<3y(o?[ n<./9%tEXtdate:create2013-06-22T01:32:37+01:00>T}%tEXtdate:modify2013-06-22T01:32:37+01:00O 5IENDB`puzzles-r9872/icons/guess-base.png0000644000175300017530000004570512161170210016306 0ustar simonsimonPNG  IHDRAwbKGD IDATxy@T?30,læ2xMR\䦸uo\p 7B5Mkݾ4q4V31Q$ \YΜ9s9k}B>ebqD>bȁPU BT**UPU @ TAU@PU BT**UPU @ T1 -IT*ByʜYll/(=׌'RT |WW)~_0P/^YH1A" @2JGGǵ[>4i~? ca/66 @STV½{NIV::9]AJ$ب兏0l>r$pAsHQ\6>O!) 9^C7b *22mjm]T⼚8dƙr$ǽ?<590}}1B/ 6)[6]pih] Zo ÆMsZ> <\M]qWsrr+++srrOuM+WS =Wޱ>,ő .wק'*AZ&I$ݵs=}Zz7Ҝz^RRٱ##VUGӆg7fo &1Xsxnʘ={vo͞=֯F"?Ay˔JRf[P~UmV(J8:P[ֵFpLlT i6@TzdO.I$%1+=rDA]XZZVUUU2?W559 a㺺:g4k<,YP7w>!5ud#`pUH6 F΂Ij AA: fwvĈb?~k+!0Q5ܺaOa~MPFjP u0bDU~~~(// ]0bx0ՁGkaQ"=.Qtԓvss=uO>{O=V T56&Fj${{M nwm3cpWP2_ 2ne|-$  &H\UZ ol T IMM&H_QSKr3n// m 4psq 8#n..Z>n\t΂zR^^[]Y%utveʪ #s.s8Ob=K7AS\nime H>ݶp]GDIZ DFׅ2~\N`$ *Fyƍ=I҂LKA#ƞ/[yvx?}m;w5NΆ]x7>,[$0Zvv{'.r%LRˍ?rfHI\+ , p7y;x0оmno/ Wdevvo7FO! p,_Oyao\>uP 6lͩ}`0X,QޖFp<<=ƌ^u#ͨSTte@"N > OѫV}|l[+H̰0n7mϳl::FÆ_Eb;tiިe b1,d-lpFz"cv Aq bcA;{mtEF AU99GJ$ f00hHp(N[?8ot,D2w.383lm  9s1t~Q^TWפg^4I&&BwiBgd^kjMMPwws @U@bOf@U@PzF/**75ֵIl˺~-"iT&BY䊹4tY '$R yl E#@^t!"qK{(21 Sqd㇚4 TP"D/.r3`]R)oH)t{V台8 p(8^bb.z9(Ы"#3-^:e0p]O*5 Ocߩ/kSl[buk02M8siVPPx~#30gp^t! cΛ/d0>x`0X\&|B^^Lt!2><\('+edmi-_b҅Ϸb!dWֱǟ,=qQruæŏX[[GGG[[[IIɥÇWY "7n!豒a;>9=/+tWXKZ hssiio47<=wtOΑx0'Ϟme?,c6m hC38Bڴp%rVKK끃 ăVELvq5}5d62zOm{cʬ{2w|Y)t*dUdʔ*A`>\ru `aeeTCǪ:y_;SE/Y7FiD@??DQ*K##~p~P էvN.:FyF{-;Ile'Ǝٟ/kL|Y 4(%*b]ӝ- zZ o;y@b.9(ЁKdq<ɑ1>ٓ]`%OsiF=,ϼsX5,}Nćk2hP \ `9x\r!r=7kaGR!.I)twB9cϫ:J@ۉDD&7{dO b.9͐L7<}ZB3s}{֗{}[ "?,+b13v!&梗ӌbPUENNHm-91LlYFfƎJ)!⊳ӖЗw,AR"aeb<$sWNΎᚋ4 TE5mᛙ fΘD/Fzzj"qqSD`"]\.梗ӌPU YbPU @ T1^NKO'R)&s|WW)sQ `j"3+ Of -pA`MP *!Hvƾ,ʂ*D"ݵQ?6 X_T9.^ф|Y!zUddfmۺ%ʷuD9͌WӗEYA]pCo`hńJd)ˢ,Ȁ `k6 B)+Z[Zs8> \{{LJailq4'8ڏ×EYZ[̬GM樳>}ˢ,3'z\d,,\<zze\󡪮/ d/|q/j~HwՌ&O+0iko_eAF6W'i'(y|Y!Y3{1B/ݹ 6)[6]pI (oؓwIXa:zˢ,۟'dbm>> k;;>lںmg/[㋸2ؕ۸_eAo$Vmvĥⓔ4( B0#@p[#։ |7S%W_x2no/4wx fjfo>u_eA6H<N|i{;)>Je|]ZH|j=z'iF rmz"Cb63'nĀ|Y! `x+~Lu??ƎțR >-ۻk{YqA4,D4Ȗ~g_8#]Eф|Y!]55Ka$QسR{nM99G&H$s=.[۠* #c&}Y!!ԮO*O_8~ya|rUꚴbqqM]`j"໻ˢ,D9}IOt1'F F )"TvUM pQJ"8m[v?V_z吔+*HvE=),_f Py,d!M``do5?YZZ{z !1˻>i ) QL`:(=<[hm1GqjB}ͽI牙\ ""<)/e'?$7'23| uLYYm?ۇ/> 6 z?wlРOvŰ6EFRamcI`$fMQ<{Vy"GQm >/+z3-t8:-ۆٳ-_obF 9|mdMQ1a\!\If#oC{vNˤI|\>8Eth<}}cl*I'?JKIj{e*X6Iҧ7J*4^1'Mt~|14cl}|$6.TX]c"}AҏWؿ0#@*h[&sؿM_;4bjjj"L@8Qa,q RYf]\`,ASPHsx`1Cx܃L&5L2L"o'>O t3@ mP@5Ʉ ]a"Y'.dL$4aNՙoϏ+p 4I?~/1mDl~uKw+#5(H @,#C7bt&ܸvϷ /p쫓99Bq)FMQ,x!**z\ `G ;[[ - Y(iIeD~H,kE T7b.Lj/1VY jY]L՜ʜ_]TCFI%afe#o1eּ=_yyʜ˗:پn\3NM# GQbz'h}iOggg;99ubkk~Æy/ݽ6dҧkܾ~LTBY,5vے/[{Xc"+2% `JPJRAjB))4=)e"WG͉_FGo.n>(Ŀ~eH,{_|-Xnn !!m[>ٻiM. `M5dv4VzyztNj'Jj*xtipw/2}#b#zGͭ[gٳg5?KpM$T4FFl];B#"3) 2.TbCB [o6o[߀:꭭kk> `.Do…KÆMsZ> he'I1#e{$I('7ys;TZk,--kjj|g %% *b]ӝ- Zt IDAT ovek|RΆ]x7^?SDv Ifnhc^yի}Cj%ZЁKdYkIy#ceI\+Ѡ, 1m= PE8.rfwvĈb?~k+~ehb"օjsp>)ɫ3t`\>[kQ[d\w9ʠAp,_fݼ s7:u[͎f 4K ;-[b999'N,_ |w$Fu .~t)7]dfey kL\[z*jƲ}v_ -jsx8v֡2>ܿ4vOzAa[ -Je 5usYCgrń{=z,>u;577xY}H@wT 8o49ꎝڻ%FLÄmcGA9( `4^j*\q'LB%SjE2T `T5^j*\7i\-[#XKAk=ͻ@Kݼ ː4Qx sU )HBqz0$ `4^j\J@+A2T `LMeщ*16s" iXPpĐLEL2\7P` 0\7A f.2"x7*N|y[70\W3R1-aUdѓ99%xKe!Š3W2]w平oS?DZ#혹LE Y c!18 eb.c1he `Z `ZO`fxcOYx >kVܹ21 hfH䔫o?|o'ʌ䠐\UUߝ~y{S7y~\wg| -CY)s8Z2MLN4snTbzgz0Y3'Y^ڒ}Osܮ^++deڜշxmI!`L)J ̥4$>UD"ysq? Vю}7R;@`_Un}w[:5Q&rt\dӳ5PxI"D#mU eH0=[3Eҥ rEAӬaؤh>ײ:2Q&?b, 3ߚY/e!7nf..Gfӳ55^j *\I.ff.g՚Y1EEJ `S@](s ̕Y@řÎZo0;;[2 E4411o|yퟶ^飬W'۬=<=<=4 `nf^/7HۛM&!u2Wew_$٤}DY%+Ԯ /]vUPx@ {y2 K^]S]Q^١P(L0#ggok-fMPn(@i3kRk5{9 :P;Xmm]aaakk+C׶V,&`j" n.5Z/)RT9orR cFQϝ+ݏ$ w8NCP}EMmm. f/=k#m=ʿ.+@.'bD"# W?PW{ &5jBFM㥶+,xDryVryӑ@.?x뱻.x Kamwj8OokkGAǏc兏0j/::4wSCDz#T*jj l/ASSS}#iߣ+ʆ>vDAVgG⁸KmAAgGvtvY{oD8K+== ;edEL2`g;*2JBQn<<܍gedN`$H0}Ώ8p`79i0q|.;#XyEe4D.WWT R1pD"bՆ`XLooiu9SGOKhԩY[Q᠞'z\d,,\<ᑃB,5^~O0utyk޷(p |q/ȁ!xa!lSy3 A:.LrUό3 3koذa( _3kpU2s}NQ᠞<V^]YU[Scl#䪨MummR6ofn:s66joKJ.v ή׏{%lWšqHѿ &'9(n>p{he{s^Rw_^BVdyÉ;>5Me/=6_sLQ3J@pJL=' ԍ~᜜} Bm-91LlYFfƎJ)!⊳pdCA8g{knrEk0)73 -2ATlтŋ;-ϗE 3K@?_tˢ_ uˢ_POhbKKO'R)&s|WW)MC04hP72VYy2׸`/.L]V\{6n@'Ttac#,(ȗu3Jqc_Tw$1Q^(Xo& 0w+n+ M.^; ԍko.W[Z*h[-hhxB_iY?Ii{a,4Iqn~KԖ˗.irĠ/~A]ߚ۟N\;ǧYo?PPٚgʣ>|G p8鿷DnC0'zfQ:@텃F~:%e/H `~v|?Ջ" z1ZV(kb{4ӓ_6:ϗE !b[ sJ?>w)PP@5O*m;5(^8݌ `(h(vUlZj޽)!|^1c$ϋ@Of>5A `UQS/w/qPUG[&]͘gZhA Eo(yci;|SߍϗE P*-͵WEl,]%Fe/2\k޿~:ϗE !b?H\8nY@e/H? `^lc ۦ/~AzcpNW b@0>}qOt* %D A?_@ `cO%gw^>Gv3شu ^vO2ؕ۸5BӉԁ~,ugݟ"W2ȕ@4 f0gM[&k)ߔ١a[ 6/88f_byl 9J&Ex{ 7O |Y F `_}8gaur8M?" 36;q2''wT\f 6l`Ɔ6l H~¹H2 3IKOf^kM9tf2_Kd_@?_cطgw3κ|)@q~4# 36hNNnb<]ق#mh'O8w)s@0sf7}5iiHkMu-&\ [)[  - `,ZiI~,霡e^s uD?_T@0C?_z:sQEu,D,}Z3ΐї/  F5]+ҧϗugkfԅkˢ_PfV/~AkffV0yXxկbD^^\Q AZ3KuD'jk͓j`Ə'<D8H܌s%,WїE AUe/2PU~,Q B}@ BT*u40e/_dBACTx5VݰX,P1ͻ'W$`Xƍſ~7xts(*: "T^^ݼi\P(rYy9yF>/~Ah7J/~Av3h7*/~A] ~,uv3 ̀"ˢ_P7h7/~AL/~A=o@?_TfpWPe/H\UϗE cpWPe/Hd:e/'Fe/f"ˢ_P7h,EϗEnКYꠟ/~A]5A?_@kf)~,Z3K1e/КYꡟ/~Aˢ_2D1*~,Q^ϗE @U@bϠ@U@PUC@ Οs z2@*w_10UUif ?J2>X(仺:OO#m bIDAT?mZn_HDZ+Ie|cdQ̬H$AAA؋/ p$'SNkW0iDM(21 ^4TN2[ttrP$OK {.P:::ݢiСcI.-8sLۡ* Bqrtܴe&AJ$ظa <sHuv?00@{ 8^BAQ; apD^1DvE=),_f P> Yw:e`do5?YZZ{zEE7I.DGǔ Cd̷-E8.&]8*|+^LV*}ekIwE} StPzyq1R+rso~"!3חǧLŝH2ۀPL,-_ Xڎ :|H"4HW}ųg}#)if~ٰp//(7@Ûl0[(H0h74΁W Ǣw_o:"@2((ܗ.(0Py8uJ9&(Hq8@$[#։~Gǩɫ}EmqOi$Q;$eNNE:xeo>\BQ}U )HBq?P(J7HW:V9񊙅?3HbU>'e0#GbkXi  t8&6N*%/b#{9MIK `"=ID *jjj3Ȝs,xX𸮮ۡ+H YPgPQ19𰰏"Kb+>R 9Wii遁j< AAo|FZ:f㹠t-@ wH։ 0<UtUŭԠ x 22~ՌTL AX vwЭAjZKazt 6 gƵ[O WMF+Š Ek'"p$=UtUEMV +)hA=4JUu\ mn`QרuE\U47j;5G _T,-de2T+;aii5ં3Jɟ ޾,1h!wX;9-70jjܯCU'\UX[+*ȟllz9{|BY k_;9`c+R` I̊i5%?iw7מ>rsqNsG.PJzPHHV *^2e/SR8~~z2wLS8zyZ>Ŕ7WЌS9I*13}ˤO WSSR>'B))i*AR@ʾR@Iv(Av_b(AY J✚cpUaee~:sòH T+ݝ#ݽlZ\U>I LAHߊ GI۪A$?u#|r _CB [o6o[Gl$bU1[0bɓ8ttJ-?b, ƍ=IL~<9eHJfh[A@{***-_M&i4Ov6,X [,_}h X 0`вKol8_(how@^ğ9C>JVu aa=x>"1iFrrMD&7{dO |A^cFf)Q*q@"N +{L{Au#lş_$;}?zՍI_}cE"Ι306Z.ӧA$$_V!}'Ooǝq ~j9i0q|.;~Wg=?n"'y;vзgw3κ|(/qB>!m?ONNc %%sؘ1`k ffɜ+WΎk>899q$%\&[3FJ<$sWNΎO(H2\$s8WJg'Э%Z"x0&`G.--POp vpAge "IDATx}\w/odBhxS*X*U9=^{w۳ *b$ȓVA)z{[k+hVZj} Hk$$sHBH`L&>L 7x\QQe;1 jQ! X:&f6Y*+(=0L.B!$zu'H.k5.4dzx h Pf!hB!Z>*˛_Ay|v}JJإ%_(0/&\q@Q@8Hpg[Hz@x ⯾ Nt:8sRR8~~ai)L,'h38pAn#Q>h-؛ٖ@8k'7jk>oKso0-8}_9Z T3v&; W̬gϪw$)S`._3'+\E/mKLZo_~cŊ%&Pf$jqOv$W"e9@ aт('B-I.nNd)jiiݚMMM19Y7IC{H_[REX]-+V{㙜(y?I -[+V"8Zx|wbcc<\?ؘNbz'r,7?lO S:{}Ess67-ZE/W֬Yc5k\ڊICL,w)n'u`^/:?M% MiŤ!&EQDa,J  |#%R)Ƣ+.vd!J]#ICR)kh\Iv껺V$k\IipaEYb.bYْVLb,***׍kAY+ZM+od2srs7o~ۊgrT^(q* xշSTUouxUc7cFb–肂B ::z{V349olYCcءilڗ}ɧ|Jx?CCBa>ot9Hء::SdАܜ,2_NDբo01E*-j&>01E4xY\\0;..)`vhuL 1މx6e'^wLIcSd56twzA(e"80.p $81EwtÊFFh~q{V;84jw\PY= cŋkq8o['zj-;wEh GݻF~Nt{#~'?i񲈍YV Qh-*%6PZJuh8!ɻt:]݃z+& 1^SN\rӠ B"q%J "aE$a9 VLb)(JA@=@nOOqdQbJj*JёZ!- ݞ"C̼vNgŤ!&pLчD 2< _r6lXF.p1oos3:WnCSSl=,E< NΡ!VLvs9`/_(tZ,7 @̻h6_eyӕ+Nڑld)2e Q~bٳ#<=<4q8t-CHo4"ZBIr%E+c 霙~i'mײ8>@ s E]!ZNmj]_ϝ >>nAI y" @(0d!++**.}XUGC֬Y.NۢL֨Tb}}|PEE=EIJe 2yJ, &&, &&B x\^å54z16uL Zrql2 Io.6bӺ.|!:Zb(HSryǴWt3_774Ю=SV"M~FRGf+%S&J=}0Gd mSiXf?!]Oec8xzn>+H Z\ % ;h]tOgb$i,YBQEjCJIlH]{t> g&.zاں?oj.S\,ܶu9$xDΜ-ܯXdߜXN\bW4'^B r曳i35_k vb1{/OPC_i bn{O//q$**)snY ojj\Ӎ񑤤wgEVp~eNH %$+`č$c=]SKKkTwx/O6I"9i"yOV p|gnn#=/lX ge ?*|VEPpH֘K+b"OR,+X>33_z@|UT(IX*hhLt@uu A=qFն{M:sH[m)"7[&DK!f|y ?ieqIH @^!H{Gnb&1Ud8rVLb I5TR?|u&I!ZIuDKp#f.ŊZdo7guTX'H);(IxjL gwN.k..;yýrj M` ]n UeH!|~A]E1##=f##Afg!I$' 1M&yGPOyFI?F(_rhh_rl6,\eI,z{;ROLat?y >JRo4&|!2_Nd_L=EhUZ1UDC+ ٣4 GIrA1UDCw"@/ ǨL^ {;Hc( IAA8F=cwG (IҘ*5QmW.؁r+ Њ$vPeFEPJJa.zاЊ$)~zƒB!ZI;Tch8"Im둧PD}[ޓTihFC5& I1cJ3͑BϘPD !I1RdHRFޖluԏ%'-%@VR:*$^oI1UAd+nS?ZMcyBr_mJw# }G!IS=x͏rXL4^BON՛ E UJ'#%]tOABmzb?r Fk7*v/Yb9ιbYGD@ZIB n28p%Ic$:\'{$ |7UYttqȌ}VBiҬJ[:'/<:b(Hmj |ֲ=ss89z[=%=m~FRHzIZq$qJ8y?@b]tOb$+z{8U+QK WVU_j5)W+_Qn=]%a$&&°'LLYLLYLLb$򋲆kQ X:Z"I țH| >whWv(id> D"QBͤmE d&$IEi j8p>Tr2"h+bq31ooFfB™A ^YEȟ;1Ϟ=|C /vB`5}pkB/)D[3$)m?rKt:^OtzZHp8(3!IKZ$ Y#O9GHB=HFWW_;3dHR{{wT gN3)D[3$z!.|odFoF(8{_4"y}syRgIߢ#REbd)V|)D[3$zoO E[̓BQ!I[QQ.ĂaA)D[3$YzV\\h'*M V InNxxkī߈p19lef{1mEkH!T*8G3g %2y&<'C53 l=CU%o>H=Y2 =-#ٖB{35gr{A8qJ9y.79#Z Z=Okng/̳D~rc"gΖ?poWX,oNY,'`+}D/GXXȄg~5 ,^LDE n8tE_9qh&%HW!Zr^aa>SO #]kxWt$򓆘^ybw\"{"T/r]t(0s8J?glߍz_s,>:1v (/ #˿j.HIC?d[?l솺ѤP};Bg;.54< D?6|j^HI,z{;RoLatr J/tt8.ƺ q#I'w"*'g'g~(+FҢv0|0EV CѪm:xYy!P=`v0Ecq\71qs>7nw"@/ ǨLжN_0 v]T))}kk>oKsoX$)Ǩ~pNp6w` ݹ0((014S^rnd_(ۖbKׯhoo~Ɗ/mKL:h&$iͪso@oꕋGⒸs@ rc\EMn7Wy]--'>fΝ>>>cΚ~')@z;~}O{il2RBOi^DO+9 7fff÷P$)~zLBKBeeH$)4$~H7y۹yy2͛J$i'Q.UוRKYt龾|pkQQC,7\8vҎe&]4C~Z!;X|~ޛsyO_QU(Nz뭷v'yzH׉T_ٽ'K03U8+r cGs_#U}&=cOJ 'kPT9&?jYQ דtErMr̲5~:u0t9w/zP/Oo|KRxMT*f?p0@"0Ue݊]!3rYC IZ[k%|Æ *;}93R']z Cxy9F8DU=ӠyO~_l)>NvVOIO߱:yIv-~=>w.;B{;ܺ%%牀Q'.~FP@qAn?rn> n'KxR߯XoU W\D=T}I&kT*>U(DCHceEeWe }]+׃f $1y°'LLYLLYLLb$6۪ X'Ŵ. _Ee$;$6Gf+S&J=I[H!IEx'vA쐉$]{t> g&c$:$3ߜM; '^ /"ڨK۾ g$z$Ĝdul1ܓ 4"BhvfQaÚ?S` D]}<_xac$6Vۯz7$iD(Fhr%cL1HI.5xQ$HmvQyE%/b1HP$f]*P/0HHmv50HHmv>xQ/$x $mv UuRgI$.((8?KHRI7˗˻{zjMwOo[%˂6h8J9/?KHRnA׏\_O8ޟwp$D~Hmv@5^DnSUA_Bsx lWJ-ȱ!Nt: Ann0qTO2_)L,;0 ӍlNN|3weD1EVQt5Վi%tEXtdate:create2013-06-22T01:32:08+01:00F %tEXtdate:modify2013-06-22T01:32:08+01:007β?IENDB`puzzles-r9872/icons/guess-ibase4.png0000644000175300017530000000650612161170245016547 0ustar simonsimonPNG  IHDRi? oFFsKloq pHYsHHFk> vpAge lIDATxmw!IN,/evBv; -@GjjŏyM8P"= AD\ѩ ԮY-䉞l+>(7oN~HPjM"4dOT;FGs{R#N *5 Z kx'Hs"j*Z 6F[ v+h)Ӵh%r1 ]X1t$7UA$rШ ʎ*}?2Z.W =ְzH7Dî:RY'Ղ\o*[W$;}AeYdgi>[D R,1fRH<(ݐ,?G \.ڰXIt 4z,P`moˏD J7\KH)0As4#߱#mWY&;wTۃJ[-Z#D4-:p3TzkeČ "Ja|cK~nLJ7&2#4I~YbR h+3I22$ZY'rIR2IW`b%i?7&%B&IƨPo} :IR!Gz+T&^[*wIۍI Y_dt&Po{cH:8qꪔID8B1$m;͆ma[$WX g(i74 nXܰRĩVi$ٮ#dZ*s=#C-Ľ-t?S$i; $鄠vI[Y0ja"d&I'$ ^[ $i{I,?s6l-Mg5IB%==RH &IbaGiaTS /#$^dĎ "p˷{P\9Ҏ&I=G&IH؎ƥԸTƥ֠>5lɶigPI/-LZY'Ze zL7Ngh$龢8?6&aG°U$i~\L HIԁCy$gulB&IA~dۃH(r12i]4MʶME8SDPj0BSq $)bonEǙ-r Ɛ1IB !l +,JP3'rA$w%bmn"f&cIde~ߍc~Y~z=w]@Irar_D\$i]B-Zm;̊H=Acr4@=ԃ WX T,*cvTۃ-$.3I2| !&BV-L$鄠vUjGS,dY1e-p.3/L$^)Vp4}X4~#*8 R@~ySɏIwQ$Q i$5"!3&3IZ}I`TRHΌL#jq^"5 12u-o1ufT*zzIDXv"]t|S&Ij[CfDo5IR.fd&I1- 3I2iȪu"/I?rZyb+4IGNEйMNjjaE=^H{3IbGѓ:E= *2h2q"kID8wHسoJ.3cF$Ƞtwӗ$1?-]A͘$ydyMȉ$ |$5U^ JT9I/vq8_f9Wȓ>]fd"d&BV-LZdg#= 94MgVJhj U%tEXtdate:create2013-06-22T01:32:37+01:00>T}%tEXtdate:modify2013-06-22T01:32:37+01:00O 5IENDB`puzzles-r9872/icons/guess-web.png0000644000175300017530000005026112161170210016142 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAgeOIDATxwGu?~{WiVͲe7\q0&8B@B R 8c۲d{YW+i%+1>z՞{3st][ 6z?K㜃ieB!T!gKzBΘJ Ji:Y(#5U0瘦+>YBpA5B4`̶m+ RU?Tym;TB sB9D"clF;;q'g'Ƌ b၉WwxhH*JXs_/holfMM~ce` 8r?X,WQTyq9F`09,K^|>a9"@~ ?t 9/!]]y;|;|r.tEvW/u?}%f(=/ P9@/wvR[g ###igG]00\QB/|cڞD³,){%/ Բczt]omY0vرBH[[+BH;p'O\@ Q!?i4 !iƶ~Cn}c!!Bh 7\ ػX6{B#[vUT"[>us[!46]X y?οpff:=3u>/tm{x+/w]wYWTGx뭟CW{듟B0$`' <弍cU8U!=u 5=C+q 8GlؐcMo#F pRW? r]Gꪫ921>VV^}1=[>-/ 9?sZ|U! WJ|cݿrAs1` ]B= ::W&eyuuP4aa% 0ƍR)1Ɩu3BEU&'+G;Yq GSL0&$9o5]fEV5î;&&nD^ s~--nmlllH˶kf߾(KmZ1G7M8aOcӍ\Rr8w~(ZSO P c`L\׹'t]b|~˅& !eea,kT C2?` B}`(%8$1B D*TTBY^ DU4W1F!!JA0#8phhhfMg]]#e/D9[UQ>{%&dp [7_YYQcXP{XU$b;('#$0yO-LjmfSJ8'!R25%{ڮ 9q7xS1cs)۶罷:8RSJ1d)rTȉXSUz[-K@B`)7إw Ej`4UW[ͯVU _cB!- eyyzի~? $0!3ψ?rUnQi+Kp# Rkt- @p }e/oϤh$w޻iZP"~r$Q^uwۜ ̙u+~*%@3-̻om3<MzՇ\+<г?{/\:;[LRyy՛nr q<ϋ" u WIOԩ#<Eo~Mg2~TUnE6JW S mޕ !@ Jj߿}PU{f B >{)| y'?k_\ 0Fq4׿r7~񉿾G?nXX pUUr;{}|~}/n/}rTp*91` \'=>@yM;3`x i߿w,?6:WcsFȵRD43W xBcy_zٻomǏ߱st'^zyk..kiRM5DO`/!8,-{e_yvvd_.^f6 ?7쳛S) 06/%NcϽPu㡟lTu0{~^veYyuuumຮ-áBUL/4&:fJ∻%@f8@pp:gÇ'jġCSR 8''a!|[>7k?X,7/g?yro eBയH?"8B,|6shIŽ㔗!ڶD-!o#MT\+K"s.(8A?n>*乃 fbaho,B8%#m0=1vҜ]$as箑Qp5m;w05sx"yb!7XT($/lj*Kִ\ػ(0? !@pnLX& c`lO<^}Օ)|!-P24{cL(R5ubbT*MMMA766ۯBJOffmU#j8W06&%qȇ?{#{MϾO׬nKkR77ff6_^{M( "yCPG?p8|P4MqјD"iڶǖs3<sbYN~*HYSSm[um߱#_^pEIGoşq7_Oܕt=IM;?c8P+A= *q_m4JƘH !UU--!DLB~8q5ٙ_޷H(׵+/ 3<NOn}W]tE0!B N=K( ptVp6PBP_}`mqXB'WHض89w9^6VCBC9d ]vQ*b/Gj@ ,Rk};̴l"%~곗Ǜ"Bv!^yۮO]ܯG'&vHӉ<OhZ7ۅ/_oxc~q[[k0\np3B]SR3bT!|a1in{~r[miPT"@D9D'~("|xtQ*c!qUfsԴB)iYqЙsחWer*U4AcJ*s&RVC7PIip_]H?B'!B!]=‹iSl$ `aMwqyw=CySS(˽;sP)zy l6[&84F J(Le3FvT[pڰPg  m+:< 2ET1BjÛ/`~mo+^P(YPwf<@$2C1i:oP a0 PJba#G4 t ®G!,j f&r9JI>*7+۲BPB;v`}J4ZU+־Diɠ6Bqde^} 8;a8y q^YE0p"'#M?i<!鼪@`qT!y4yl}lVQ(7lXo߁t:-(7_#Ozym##oH;cwuc~1jt]F1 #'`ܙ7f@|q$o[f~Wm_ J,M؎W^ eok 1CPMM ڎm4-/ض]*u]u=/lnΘ/S} 7" ɟGj1WLTTL}hb)zE[?S)i*b! s '!>Yr\OxT1!]uk>GԲ`ʶm!D]]?௩us`,_`iFaLW#!8ҚKЪ.e$/׭ꗣpbXNILfŋ_qXSlRQ œ# ĵUoc=|*%rBg 7gKRpK.~)>t3J铷i?}]8l9M&D3^^^HjkkVv,iu{)1c;9[nBR#SCRHM577Rs]SLNcGciʊ殺c9̹$s7m;#CTLQ/D@`h&:xЙ"~cժ&u!7cKT* SCEJ躷r 7*k>2-Ox!sC^W*EîhɸsNy((t++PY"^x|(0.VU%QUZ('=L`芷Hg\i0'W 9/K[_cšU֕Rmۮ_we̳|64lv6{ϖoaEį=_jSozPȰ,1";o+mEAE5xP=|SSSbQӴ~*˜%L\ Bt0v睩;((5{MooK_(_rJҲe4gBfv* ;B =(g=͹|wo(As@#T*‰WF^7ݶ6@u#ƵUHF|={@JMM=;|3PJeR49VpB!΅c0mYr0F^[n}IaLȸ=H*&'MN^^[[] 5AW/OsڮP M _xǍ;re%Cȩ'RС*8Ae\%Qo2L1_v} IM3m"YI[ /?#`WP@{=斗ۿolt|}lr(Hx3^!4 gf Ch}o<;|4;0TBj4$oA檋 K~DEOúUu3K޷n]i"3ߓOgҜ 0&DEã' OkZVj;\c]92!$/JroU 3RNc,S\,_ZC\T GP(~/ɤj۞ק?ssϜ?# (]to58Bl $Fّrf?z{F60[vr,tλ?yMuδL(i>spx˖ 'EρEFIy 6 sLOu ~$`3[KXŧ&0Ox\p!t>ںooW<Lg?far!c1.NsdzX4:&lny22%<呯4z=i<O>KY#fx1ݲ><ǘc,O};eŦ Z\Qp:msksNRkh@'MxDpS؏Ri8s[yxީB.{%ҝV6'',1= ʦ'CCdyyccmZYYz*ɒ V<瘐M7+>|r:uyg}5KBEQSʪn8=G/B8-|''5=p+/^ڳo-8zH`\dž'Z"׿h4C<^8*D8G篼2\ϻ^sLT7y]BDUE\vz {:ʙm{q0 N" a]] a;JjA\dssuM9;_}oLS.s}衟 ˭?|pś3BͧNN7ށ`$Hp-vou ]]ॿ!hkg?p1Bαsf~~j40(,0B Lo˓opk*Z׵o@RYYYP{k:fS7qq^w-i, ** #.+WUV uDZμzϷ ~ESc  HW9a96>3,f>ܶL$2aOO(oZo]5/lu]A ˁj0y՗\x6kB/q05-Oj( dtvnK.B1_kk}mOgKL f7XQ*ϧS i9EAm/_*dE_x[tb&"N*UO?i>94EףkhTuݼi Z@D+kq~l)#׎0B CшEi޲ rUsi(a&4W2J ei0!.1^ +Y*GF8M%MTUU): T+䲌y>WQL& >D5m||bjbT(@0Ƣ܅BB NXE(F5~) yg^2!0)ڨNsj;`sanBeYퟞa164t|ÆpH(TbRaxxmmڵ]rBHoo_OOF%&Dp[[kss acкuk BpPVUQӴb888Dijjum;a˱ڛڶoӓd n7jQ{K2Gbyޮ]{RieaSl6s.4%쮝 TUuNDU(?(yҁC1Uqࡡy7`߾##:TRk\.'f\ݳg#G}9tв*oU(>P'yTR 2g˟ 93F#}hh8Nk&Nn>YrhyBi66661>iZP eBҞbiĩ=*ѣ=ݲ,X,|>UUri|^uMӲ/Lcu*sw*19gq{ʯGA[j@9311qZY=SS)q B&r~c&lۙ_T+R%T !46:~qvv6@ Hy^uuu ,˱ Y{>CUZ Wr,maM4 L",rl0Lu咸HFP(z+g:FOJ Ƽb$Nۣ뺥F97nhllFQ֭Gc#\!l3b4L2ӥ:V!dy"H,1@ٶE Rv( >sB!;ijCGM#=/,sY9u= fδ~]ׅNA9p$BD"KעFJI8.trs s{eڮ}OLLlv+^`P{qġCzR{GO[W/E!鴢,^0ƎT+BI&GG4$S~~2UVV[H=!X0L&SclvUUU0Bf33i02񘙙<I[f mF_ee]*?Tմ ?/iBUduy|&XU^^$Ž`0h6Z8m^,Yfێ[S!7y ֬uq܊UM2P̶P(*Q_V^&͙ʊ@ LiNeUg@r7'-m$$<)# ?ںySiyeH2z8gښ )¹kLJR<\Qθjh$ rY˲c1!D0ذa]e\j"Lbs뺲GH&;o?GE0)Oچz(// 55MM˪YT`Vʯ.mt.!$>r\zvֶlUUX4/HB~H_Ӷt:](  %TU]QJ b:6ME|ŤC)S_~,^(P4> ($P$zV/XHD"H)€\Ҫ(!ضҲq  Bh4 ۣ͝(ys8]`QF8d@zRv[ BxLڶmEQXSSc,[NR#\1z2Y8MK MȜ`0PSSSWW 'mL5MhϱԴeلh4АLɍsmU"*/1BlQ#f-J?] bY7tj*g>۲ !r?^\Nj{1!{ B&$UUIB!##G JuX,ٳOÇON)bR9-o={K%C4BBW16 С#biffc>|$g2ÇQ*ᄋt(RQcCn|m(ͳblmӓ)L9뗠Wy/E;r#ӳb##LF׵㲚iB 291.Ap/+'[oFn驑1/?jdضtZ* f2˲|P:1L8TLerLMp.RԩP\\. WPW|--)%_0Ɩe&e===C⴫8R#il>LA;u#4q+9{,K|`fc̲%u,˖P{{[CC=!DU! 4M;3#E{um[1ϵhߙ49/+KPBЙ' BH"es\~s$BXu!!6m:CBQ^|E+ۜ{>7o;Z ][>?P(\ף]*`tt0Lι(huuYY<~M)5 رi˒dy[[qENMIc %q1bQSݒo֭z4#nEH[~Pl_V=0 u r@)q0 ~XhVY"3e>0pK%0 qI W]MS em˹ 01XB fM9|ȨMvw۷o㣔D"܎UƋo 0 ` BxOG/2 ҹ3J*:vGQTzMe5!obbR,LRѡ<)i*4M+q)%lСRybuJ-21688T*C\#L\妷?`GڧlGKbhI w04͑SU\{xlT1ι*2(qE##^Q 骪8R['8 # Otm;@ׄAw֝%kN:R(L*e)g8>c2;;;;[]]9;;{#evr.Hj~AUQ[ZWB֭[?1[r/LP0x۲ߕN];PVi -8' 3d-As0gCR@:peck5EPifj`{1pүx-FDY؝^iqc򤀢(QzBn#| !˥SR"Bȑ#ҵkjEY)O]ٳsktaO>ys?UqlzYkeS+7])%`)rA@%^mH&BtT#H4t2?H_W]]an!Jtq}t]wY=YUKpgGT_zG&Ϸ–sJիdpKjd5j-ˎuu !l۩FmE"hb4miY-_(RELƶuaى9h!/>EZ[$M^i vjЪcŦ%b2z$r.\S(ݰa}EERhDQ<s]/lڴQ.}PVVff&'wusJ"OgK%@HyCtɝU4oJH=RktNZΊv|z^b">dcL\ֽ7;YF)5Jm۪H$( qfffxx6c|ɆEQaRx(S55555n|ԴeX,PH$&ˌPT9s,g*0N`b1}3وRYUK:ݎ,"vq]7HTVV':. MM @H)loIMRE&y,q:^dJv13L3 y 󱅩tz.8'qOȗT&, / /uX,NO Cr]EBH:=;33c&4-N 8uz2U0D=< &!W8z{-OϿ愐dr.MG)drJSSckk (tdtih쨩"Bȑyq<_+ IoAFUE&838819;3*COc…xUר/'c_2x7={*T!t. c2lUUKҫw ;aOM畕1UUGGGl뎏Oh4*/!BJMWTTh*] NLLݻONNyWUUan` K?mWYHT!>/f5wm I]?Z:kWw10d|MSi >*UPLww5MSUɩa] #PRt19 ØRӆa̤}>iSS)WYA^~Ƌq^}/fsb??Ჳ2뎍r,~p]X,̜*S122J2M%NjT*R)iGܩiYXTr' 0,J&˩B(=,X7:_bBry|!eX,HhtӦ&'EHr7lXWUUF(7l$Rh^S#qOW}A5doȞ 8YovBLȰL DcZtst~!&شL0A~!n|0u94-rBHLs (Ph Z?[PT:c(`@Vٳwll!ѾsaPKꮮ5+8mqOa` 5ӎX&^ V555j&ɤLzeeP(aL(Rijjk&wm4i466[NnmG"իWI_MY]__WYUav0lkkByR[[k mvyBq\]@< :n,|h4M۶9H^ w`ɤaBD"眗w'2)ɚ9g3k<i6;qGGvihIqsc8tzBHYYRRU]z*BH"P%LJew|~E-+KTT$%p`sgrr*xɤ$[8!(|ajjJʊP(`̤SxEEr1t1300JM`S" UUfs###L.rᡡa@knnLܳgo:=GUUexNOO tooƸufge32cR$W?(NMɤ䥘ڳgd2` j}JDN7FWT$vܕN.QFw'''c<*q;vel6'^}ulP(K5>8v*qڶ-zzzDB1]צGGt]dCCêv.\NӴQ=U+-umVUU 9cYd6Msœ<"ƶmRe0̌d K0^HTJڗ?Si!/ rnll c οy޴rGx'xdmi&cd$tic0%/5M3{_tvvIzBtX,xV$,k 1F%!f2lnhhc,/7 2̩x>ORgbQBH<iB84|˲\#0q{yI\qӳB@dl|r 6V__/%NŦ=ϫU"VTTD"Spibd\rZH-QUWWo۶ u]UBhvd躮ZP9p殮Zٶش2 n-fyE%ڵ]y qMvuQJ]M$m.ommaqƂ`MM$Ϩ 5xP̙ˆ1˦8c kjj+)LHl6#3EbHdh___dHEZ]]6X,QYJX,*cv61bH7JeYLVbQ/wr% Ea|xLU8A扝B!'k&1MRF [,\!&=-\ֶbT(-*ybB2s^rbƯ-bN=i>/Tڰ\|"1B!\p%aa.\nyN?{ TPCl˕.⪮HC{3] #8@u DZ,O+ ?9%S0ł bcimʰ>7ʛkEB!D>_Xjhh8=6v2Ӫ\\yuMsQ:8}@Ն@(`r ~Hw!]jUgGs41!rf@o0ul`f"զ M!\ϑHto,"󇂻6]k`Doohk3ELS'(X,޴vMP F~fXd6V_vm3]+.y`B->s׃T =kyS` BFJ/8p0 Ƨ|^PBw)L&4h8g/uRMGn[XԄwlz䛿RD,{@mRx .΁RJc Tٌ=:2HPFj_fp!?}-G!""p= dCG2 ]uڤ #1b%TL ,w|~U(PejjJG@}C_65q4ӓ_ǀ> !ܑΚ ݹ{ev8;Gse66xhTوF㪪 gLGXNK"IM43wt8&`8CF=6Bcpxv6 ']؄@" Ñ _L~bm(Bm vpAg\ƭIDAT(]Kag晝YuuFf]KQп!*D:Eеt*Aicwխݝy^7|_SIkF^GDιR C""1TOOw{fǕJ}fwGE"=zaB $V29qle4 Oʖ-}Dd۶{ZJDD@dKkwt]჻}}l&eZ-/MKh"`GdO^TرZj !Sʰ,s)e$B{ $]9j4LaK5mn'}ަb7pfr<4=0X[te[SNLu]D @Lӌ(|DřIs n%{Ruťn'7U`vn~a ׮^ٳ_f&,3c&d*@̔f&GB ;㎙Z-8Das9U"l{y+&3Аaוϻc0l x1++40,4 B 67R8ۚͦaʹ?0Џ&\JCDeY2YhOC.nQ%tEXtdate:create2013-06-22T01:32:41+01:00WAA%tEXtdate:modify2013-06-22T01:32:41+01:00&IENDB`puzzles-r9872/icons/inertia-16d4.png0000644000175300017530000000057412161170252016360 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(ύRK!C܋ܬxx"SJ,|L433s9(ymv$^ ɯCޤ+ =IP9&A{Y]Oa,$?:H@򲻉]h*tKuFc%U9^Lr P]+dQB9w%tEXtdate:create2013-06-22T01:32:41+01:00WAA%tEXtdate:modify2013-06-22T01:32:41+01:00&IENDB`puzzles-r9872/icons/inertia-16d8.png0000644000175300017530000000152612161170251016361 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(]=lGz߭π >lMRH#@EHQА)"PJȡ"M|ޝ+W=齏,6RA0 @E5e ιBs'wE66^^V{(գZho<|Ct^8cl0yJqyE'Duy9@~Vh`h(*TBc}`iqIE'I9dYFie4?ujm, nR)%̲7Z2J+tnE0\h=0 Vr+/VYk)|m;^8~rlҒotazgwozR~~O ࠴JkIJ$ CR8Ish#+A!Z5{zkorjtjgysΙskmQi2<*)+ys"y!cvBr]عoS(9 9{rbsՇ>ft<?>ˮݣR we&^ ckǠxZ ͹w Ƙ$I{y֪/76#Bh @YQ M K[N13%tEXtdate:create2013-06-22T01:32:41+01:00WAA%tEXtdate:modify2013-06-22T01:32:41+01:00&IENDB`puzzles-r9872/icons/inertia-32d24.png0000644000175300017530000000432312161170250016432 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǥVklsyo[Z^Ply,$JD#&.Ka1Scͦ✖rՖ m)ԶH>X`~N99;σ}RJ1hQk 8 Cy4Mq Bca0!AD$k9CDC)RR/{KI~htTfAD"3ƆG0,%9wmw|nX*I) 3 .#q)(R f. / M ᭷w@PXhQ&))׭B(H)]7ēOeke=scg>{665455zG)Rٺ% #3kM!Ry˃׭]S.<{O{tegΜ;0 L#D pzUJ+3=Rʲ97fqLc k֮ rUVV.iwwI)?r[m~sn"f3Jdz1/.\شu55ܿIk-@R ?w*N8N[[kT2Lsbbbdd4"JQcBIEd*9WRrcH*$Aj{>"FB(LР)J9,4N+ OevA@ aiǶe|TVI( UWwyX,@2!f}J)RBʄiwFR΅Z2زcLJ~O@EZ) W?q;8ּ]mk &׸;wjw ӔRΝ;_~o߷skﯞ|*J0/ܓwSMmm^֎vuva{{-im[>/Ĝ9sݴ t?i{CC1Y;pwgs5JiJCl}lv_(4PRZU+^vn|a157zLeEH0" !Tgtu;=bltCBH!@hxa׷6aH)(jo_9=;vlPƎgy0r9LˌiZ>qd]JTeeX,SRz<288|ߗRKXճJiʨiR0s8C-@k߶mКX. %ݤ+# t4ch !m[H8ahc۶cۖed*ܗ_QNu]DZ SiçqS+.z܅ᬦ:Z !rٴGW]W]U᧟"ѱvzzI:><4:uhs_ `MP$h߈'/JG6l.c,Dt})!-K9)yO-߼vbsW !r!8\>ߺ%xWygѻ6+Nz7ĿD pքa8#x0illjj|< B HM;$RW0?411y/lۖRƛRQBFFFNW' c\1(R顡ϦɤRRjYVwaʨn즆D(*GOR?G2i =rEn0:;ZZ\}|>yj/[611t>mJ.3Ù[ywV7kt\PW7n~\ vpAg IDATHǝV l#~T2_'b);dHAg} w$"p_ZO/uF눨 ~cG2PiJJ[k*Q"BDӄBpcIDT̪Jֽ!Q"cՁ9[J ?9Zp,Xi[DćJ1Ek<]!f 'Bf_Cr#ڗsư{0!սAeLXd\bAzU㡽1/&ۑ'MB>Ed۶mrΈ{W^b^הR8sYG0{ ~VDȆ4̆Ӣ5(:("I7z=Zkrz5!3.$ދЩ}Q>blBL.d3ݛNX+9oz~u9@^W"2s)%F{f6X}KDơ1eT ӬA\n5Z}F{PY_ĕ0W0{tAm. \;i~McUҎB-SφO ĕe=uH < q0mGچ ʕup3q"{ eMFއѲҀذ_D%tEXtdate:create2013-06-22T01:32:41+01:00WAA%tEXtdate:modify2013-06-22T01:32:41+01:00&IENDB`puzzles-r9872/icons/inertia-32d8.png0000644000175300017530000000425012161170251016354 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATH}VklT>;3ww/^ybHشyMJ T}h%RI[UV*@6E(!m5~M "c]lYl,8Zݝ3sΜBכ!9 e`³<B˳<7YBH@_&ʘ˶(ccɲrG$l`0VdR!1BsE;$)h$rMךRp\im)q](M6gNgaMAݻK)ey!DUTL:|Ham8::&˲_l((W%/nim*a`0RUݻݥ%swnPellm/\ǏpwuR˶/_RSS]LzG0rEōBplJY*5d:e?vv<#_zߵj?mw443XRẞ,Ka=KXU,aT4Nٳ ƮQZ˶gϚH900Hrղʌ3(hwwτIA RJ u\o:K0v]0=DĈX)2("˖m0Ɍ˲>Ȳ3fJLD@1tbX:>{WV 4U+E#UYmFc<,|{ntt9uzk4/~vXϓe,"u|x:t;^MӄU5'O-x@t')rY NhHU>qe!;o^YV{_nnzJY7gfyjpk[7ψ#{FB!ƙخDsUdJPQ{ef,fv\-8र(,=RLq H$9VII1ɪ1F",+s,4mp0ݓ{u;nt"{zU Ov0w4}?N1BS(d~RCCBHUkך1,{sM[arΟu3.AJK-ۺtJ"Xrppp9ڕEi&M3jM ͎UQpby%AH$YDPEal ޾i v+I<=M'et1RJᰠϪҩI24 VNO'Y3%9빍}7*yH%tEXtdate:create2013-06-22T01:32:40+01:006J%tEXtdate:modify2013-06-22T01:32:40+01:00kIENDB`puzzles-r9872/icons/inertia-48d24.png0000644000175300017530000000701612161170250016443 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W IIDATXYiUՕgwz *( $FM l莝鬕]!i]J%ڤ@)(@!$"BA V=U`^u>{;vW7FW!1H~_pگeKoY}/2)# c,B!΅mf  B )6h_FQ`0xy!!EֲRJBd(`1fvLBSUUU3,!ql0zPE9r(cLQ)%cHJI)2"4 4{O7\ꞚfQQ nFUeM+Wb¡uk[YQ^Tdn#  T2(=>h!^;E᜿: 55oBŽ,!m}㆓'NN20 qB3n}}sߞ~c IrYr̙t:1·%Y?ugΓ~f~"viٸ#C=ϓRzß~v(/B{b#pӢ߽*=BpΥ?si@y%G4 $sB={ծzW9@ s]cL=U/#aHD ﬐hM~Bs4/45VX=C;sW( _I)ϚM)u]_'hgs& 5ԭ]"UQiٲ;H4M 8.!`4Wo8v!2dzٶ 5M?يzϯ~o߱c2~ݮYV.KN[ msI 5_zg9ض~Lmjze]?תo8rOqnz3^ePS BG ! X#lO&KUӣzҥw4-quCIiY-!KR}ԨU+k+[ZQA)}K~Ǜ9r^w-qc0(%dI)_UFƒ" _SAs0!Eڿuf<@ #?uu>㊊rsq6/Z72lh4rEEEv6sSa%xgL6*&8/&LW_(=w_⭥sg<m2)cq~0!ۼxC74pd2IoOOJ9oO@?aÆha #큷"ٲwZUoӃA溔R۶1!9./XY|ӫϡ70>7J&) 4Ms\wQX#c(f`'SmۅY.{gCk5l_s]z$u!%\7 gϾRn4(JQBJfKJJc `V0\Jp(D0 d!e@B οud2j+&S4<>ub`]u:_!!>RV^6ScX[ 5j$FaʽEi:sߋs+a۟?x}ݺu%{}4_NXG/-[BH,2v@r%8}[ſ8C=#=<(_ZJwʤMvLqI'JǎsN6pa$HO0 ^;.:,kYLj$ƸًRB"v+tXߔ@2 j\' )!Y4`g4xz4|vcк(kJI$[׷OҒt:aO%Nϖ3;B 7. |[ƹ@)N$Rƍ'Ncxk8Hhma c !E(d- cy^. B~WUt6 Caڎ''I4Eu.7; N0y<+ϗ3Ô`%tEXtdate:create2013-06-22T01:32:40+01:006J%tEXtdate:modify2013-06-22T01:32:40+01:00kIENDB`puzzles-r9872/icons/inertia-48d4.png0000644000175300017530000000151312161170250016355 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXk0{In7(F?tԒ `fö!IkCcvK2Q~ !j,oykinr13(_FSd":L0P3/&ՙp䅠kJ-'< 9G(e~ L9EtecS>j.Z|)>@"\9VmOQ6ϲ瓪Le˻tTSLc (-bfgd']. 43 u2A]kGĔXnAmnC^kXIIJ2`!gs=sl*3e |!B1FKMȐ41B1FD 1kNǽ4;.9]a0y@_K1C}ƬD jb|NC-yUᔠS=2ɬ<*kzBR $1M<m U'cYrL?h$3 [:fJd*aB ߑt]~P"3@o#zMNv vpAg00W IDATXõYt՝̝<^H֘("EoYl˖ZA]+ڣ= +ǺbVAD-#@@X@$1 %5,/cޏyw0^zN w||N&b>7_ hq!r/'I.Apߺ+'W(ɏ"xwg$IkjiUC UDOkOlܴS]β,˲8@)ݻoߙ3VՀeYpǬY{[Z"E$o+s0 4EW࣏>mw.Rb8ug˗a?ܳ> bg(elDi6v? w̺3̚(۶>2w쬙5 |>޼9]]Ic\_|qP.G)=ukմ{UUŷ~f[c#p,?cE!9. 8E$yQE(O֮[?soYYe&VڱqB$]_l( ^-f|;㼾n)J&9)YQdw]s_`C ͠e2[O;7OiH韯_ٟ ոs.m(((r)MMf:]<̲D<"0Ho/iQ5~xHBp4km%m{ 6JzS<߹s?m[6g,Ϫp?9ʲ~.'ǰhQ=ߋumWVTӲ?_tGEqPkPز,Ji4}rN}< 2JwlܾRey cm4-斉O='coY|%d2Evzhz_u $ ij>{WVWc>ܼos]4-EEQF{z^سY]aa׎DZ,KUՊ?ӷ~Ǐc$~ی\.fX*"his/Yw-˒e8wTUE4~$350}͚'y$b{>g:wl0mzcsk[iDF)mi{@g|0kc4eÏTVr2!cMkܺ-pX'O$|p/:5kL&gMH C7GhE׆;PtPTS9rħW^Zli}}QBHUU}O|ᚪLH>ݫ.tԾh4Ncm[ĨQ^020 "t&=w߳EDڪeK)\SG&;vg?7z3E4\}"Tmu7K-w&04uH_w1,kyG0e%x7GQY@eqQ&VWmF㯼ضdY,k[c~vWUu~0i`. wWUeO|垅H$%IjxJi$y߾sĉ$Iܴmۨ2ػo߲+UUݲ&OJ$*U6, QeY$4M/PJc8!R @R %#a,Z$I2SO?i_-!B4ɏ@?BaB#!r i\WWz(QQ b1JȄԗW~pEa-$ W⠪bXfm^$"mmn۞bžx拎!o^r͍c^@K]UyWze:qԞ/ ص޸qbMlYrY)a$bqB說yzx5Zk([ha:6 #eO˾ 'J0ݙvryW;.h4Z^^2{[we(6mqWӸqvr9C7jԥXBn #f!0F60Ba 3la` #f!0F60Ba 3la` #f!0F60Ba 3la` #f!0F60Ba͓/t _F3,ot sc琛ŗ.RZ\4|xKo_vljZƍn._ 0ϧffX>53+?{SHWojZ o ߘ:%88H@.o?ipUV^p@ÃT|vlkSL ponؐx`jf#gc{4VssbRʂElKRn\~A;ԝ>t諢¼}Ⱥb߾} MUԝ&6ź)BPd$1=[jiGGjm;w^~j?~eK)?' @՟yw[cXWr^}"!!9K>WJ]XTiMُ,Yc=zyK wQ 745rO9' `ĈϞ9ʪ1"vbLmo޼uܹ'Ot}֮/g_|8sKѓ'E2 *s`O1 'jXXض#G\<~ԥ봀`#1&O()Q"륋^.hziXXΝjz- a4 ((22hOR{QH0יLjk\ZmmT^Va $&Fn֖p0 fX61C/] ~-_R//I]sT_!ekU˜^[[(edŘS3(~G]26/>x9/_7>TU)&1ƌXC !k a/08^}.xtsJw^0=ooUVcNvmAX}/Xމ7nxׯ>>?w 'I֯_/h ހz"rF:C0j;_䆋`ߗ~]m@Ǵ|;pa n@ϗE&{?0 }>ip궼o ^oQ òt\H?|Ҁϭ7?l޽_=>i@D2 gt?0Ʉpat'sICO!џL& }>ipP29|fXI? }>idWtV,0COk׎%BK!'wQ0A2L|Ҹy0~`I >4x0~`I ' O0~`I?3~`I?6~`I?$9~`I? <~`I?$ ?~`I?$IX/hiTR__? ߿ ~ƥImV롌 ./^EK㳲2/ pi ' ޟ.eXg?0  {!;w=S:w?ǰ!x '7\ii z/ w} ^FoZkjN9QM)Uk5}z~M{FkkkqIY塪ѣGN<mM̜YIW CaaWm|x,Kա/9C_<|p/g8q*b<~).3 ߕUlEg}xÃT|fL+~bo57oݕ?/j( ³ƒ~òc{Q k괼/ UsUMC**̓`Q߾} MUԝ8t}%׭;0A  Bбq96n^}I,À]XE?޲%ǔ~!dVkL:t /FGGWzI8r*04T|eI~K] &ð]X]>W朥 q﫰?Uvi{_[Q*x9/_7>CbG. ^xah0~W@VvsN m>צd}b<`!+}&ð]5X] w?('O.? $Asa!=z>!=. U[UvMFZzJ Uk2{8?44$~3<6{^o<6yhر!='D疠jz^8{̔{޽{+V̚8!\{;CO^&+zO qV9>^|4,A,6ݐ qӦMژ"85&&h3`QBw-~RrJSZzFl̜%q/)kN]=G4S|1x2z/[ϗ%"#VH߿K5l?lan _د&OtHe޽ߚ2)3gӧѯ*|1qFHիRS:@lm9[||0a_> ʹ|}iIB_?r" {w=pޗvu/f)2gCKuCaeX" ݯxat.b{ 13?aE\ 3t.fu0ì ݯx-"zRZe+ߦ 3la` #f!0f~]+0+H"^aYA_ 3KaE\ t.f9 'Nwޢ+W$^w-I-oEuO<(@H[xf[-~2u}i!x~Z䆋`ߗ~ZgXb?-^j#^yZck?S}\r?-^pH(yuIrl6kfy=@Ǘ燻#Kj'lg*|c 2ϧffX>53+?{SH&y4o57'&,X_^͏_$1)Țăܰ!,ݞ?a4O7}{~faӎ9vz HA"#uc\et/~F5g,Uk $X*ä{Ν E%}--waE:1g~2Lf?Mُ,Yc=zyKHW1OIRysoΘ.咲b'ä{2$,l&`cXOI^6 k`ZcXO0 7'`cmk~]*"\et/~Аܜ~~~T{xxl;6D'1a\eX K8<||BC?R;.tlZ 3"ϼeX KxР%; +I[F\=;  (kC]1>an,{HR87S)ʦ9#8.>X<,"e?pHȘK/<"@Ǘt/ aiIR&Pb}iEF,||rE>.N4e6j)V̚S}}->v<̮OsL<\L4'צP3(@\x_Z՜3[7njooQYٞ{:z}0xgvJoizy/Y,wA/}iwfX!@o53~`wVD?0[p Fn-e6qi fX*j!g0*pC#f!0Ktnki’+4]wòK ̰lRo3~`wVP?0[p +Dh&p`# V+"̰[HB~`XyQoh<.(az?*}i`apOOnK#i*[$gjy40~W0Ϗx5 A2hşC/dZR}k;K]\RCMMW|3Tx5M֣|>53kYbLz?= x`PddpddjLL~1Z} [Mt A6&P/ wuD&l,b?Ƥa0"cLza_zjD-}.,b6Ƥa6RcE Ƙ;2=)*?SUFoz]Q z`k1&[YcMp"p/AOyv}i(qw֗QnEAFc\|ڋBn KyYqtVu֙Lej/ 3/!!c7ȍ /̰T.^/Ko+yyRW(`0~Woicw|en BI1k?K/p]迥3`u|= [\Ę aՄ`t|i?. 1(WK'/noowޢ^.*4ga_W6ݙ qӦMdca4֙LXDzA_]-?GԯY fbfg+h@2 =˴0SޡO*d "ṱs JF|Ǘ /tЏ/kb⧁J<_WeZS1nլⴛͷ[ZZTDU=z WZ71sf%%>ߗe}i隚V}y7N dI^?)Q|Ã]_R~Դ?\l)yAǺxV= 㩿Z-fPCدkjaX_!nga~]{3 ?:M̰?!׵SͰ?׵SZyn!.`=UyEmn#۟˖eF9}i10WeSfwe_WVO(ݗ?!)_a?L?!ǥ 3la` #f!0F60Ba 3la` #f!0F60Ba 3la` #f!0F60Ba 3la` #f!0F^}FSx4IENDB`puzzles-r9872/icons/inertia-ibase.png0000644000175300017530000000612012161170250016754 0ustar simonsimonPNG  IHDRL\ oFFs{ pHYsHHFk> vpAgAA{ vIDATxyTSW/ Y LA\ZYQ+WDQv9( J]p\gdYMc[gsJFYރItr0fyyy]s]߷޼~& 0 1La@ b&0 1La@ b&0 1La@ b&0 1La@ b~چ9gAgC]6kBV?AO|fBARÑR5 7?<\. .BhhlaADޢRQEys T?^%\`N \*]ЍՁA+1 88?Omy?^YUUCsq^[ʛy‚%fXdᑢKΜ餽'! Oض5SD[:;GLQiiɓgs޻giS/XƦo,- ~eL'&gyj(F.i'Ip*rIBք6>Xݪo\XGa "QԎQbɎT ô) ۞*Kt.ipp033Gx(‚DG'L{0+33gphiT&_1 '(aXҎRif'O 𳷷#W|N.G=az `ph0vt1mttF;Kf/\9}4M <==~~_mmmspCd9ڕRxivji13۰~mIq[ruk9ff 5LffY[OlU5OD<[.]ԣ,%7'kX,>sI3]ɉI)/_$/^nߞbf6iT4T ]8rl f6r\E עUV%˞ظ P;F1ڮL\|LYDxGagʊL2YEoZPYƶv_imk4tעaQE=ǂ|3!>"mtx[}bml5Jo79U 4W׮Ə|eHtL61 +VzYd7f؞ԢAHȪ9RWc@p8~nzBA۽lb q?E&ruF5ć1]ИY@`8=HC @" )i0<`^ʛy‚Q_1`؝-/vN>ⲀN@҇1rto#r")$}`#w0H07@B[jݡ#x7@BcZ1#07C}:1#x7CM!T}77,&*zQ]]s7 k0r'~-@@x=Wf#y^ ,<+>^_wyLJGii[,Z'JGuu$ 'J?y-3H>Zcճ $m}|D055ԡZb9 #&T}HBG !oơ_c~< i񈒣"$}H c8ZG@҇1Q3BH07UgɓȨ!HN&TmkcJb}|>277,DmڠQo&y%;aQ'Jz fA'w_ qz\,z[tܘQm1[baЇ'> B pUQuu- 7\@ѥJ D*TaF}%`PooxV|S7γL]4y}]?`~m_}N&?yon h0Riu .OO` r1.^, mllFX`/Kv|]) fHQ \T v]]{k8p% Xҗ;n$ h v€3a``l>MxͯUD*~h|N}=~#cA£%_߽/^@FjRȖn4#3!iCVi Q=t~74R0i 0i-6} h vbRyaѻ5/-f'@QcJIyyȀm[۟em=I}]?`K, > h ?wO 0R_ uq:rٟA#~w3t$I&}1/7{֬D֞H✂O\QXs7փx..vtpWr(~CBt UbP}Ŀ 66}}MM 7c sPw}p]%tEXtdate:create2013-06-22T01:32:08+01:00F %tEXtdate:modify2013-06-22T01:32:08+01:007β?IENDB`puzzles-r9872/icons/inertia-ibase4.png0000644000175300017530000000264612161170250017051 0ustar simonsimonPNG  IHDRL\ oFFs{ pHYsHHFk> vpAgAA{IDATxYv E>P;#+=`FgS_IRz ,,9RpA_Q`T0* FQ`T0* FQ`T0* FQ`T0* /z3#}:cW&x0 sZ0_`BPιc&$,|=0xaNdIXG~i1&Sl!j~YXuc ~`U=xjxz6={62ajQV~%Q䟂gZ tC2/N営`A*J>, "gF`T0*u kfnI8΀&>8Yv- ̘iEh_Y2g@Mϒƙo PrtoL%րµjuz%;Y͢ħAS#I c<M>,2\̓G ?z&(O=̀y|*D`*"#m 3B oh#v( gOgo`'?΅B%$ȧ@ȾPBg/ DE);4[ 7jQjXV9e)U|8m盔//&n3w~v8.vhǥ?N)-:و5D*\CQRnuPdhsXso" }X%K7IG_y+2o@߈o_PP.*go`877o[Bn+fp[tQ`T0xKEw {G˛QYHg:Xp-¯|KDD߿'(Q.[4kBM>&W}-Cg@M[8xeFiz0N 2Ɣzu/qp +;Ovsf Zxo)ZL~D{/?R-߳#MxY@~  F6R!:n(kK3oKi37k6 4t_\r6ǯFUgG W+ohkȜ?@>#tsE~'x>rAmi7h+%tEXtdate:create2013-06-22T01:32:40+01:006J%tEXtdate:modify2013-06-22T01:32:40+01:00kIENDB`puzzles-r9872/icons/inertia-web.png0000644000175300017530000005713612161170210016457 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge]IDATxg7\ٜa9H*(  TbEE%#Ir*ąes5.p+ 'ρ ;jGhB/y~D_Y)eg1,UBQ/ ! GjB#҇y5!Bca"@2c?_l(:4=aFc,DRB)RF!,){!B! 06LX@ABl3{I)"5 rscd c "qYfGD)HŠ@c @ B`yʀΏГ6C^ |%QP P%1bً1ĊRlb]H)X,iiQJ9O8&"B:ƍ ƈ$Bt*iZ#ZVUQ X Rt:7myaǰZ;|DV;W͹R^^iIFnRRJSSScKKfΚhѼY~PZZ&In'PJm6,))&X\\"IF.11M@bJxgО?HWc!RiZ0ZǏ_"TUϚ=frwBBEbxcPJi,-7deggׯx"!*JӅ0o&`tܷ?A}UUUUZwZ|eFy99ي`+**gϙAQ!>M)G*Ç|ɓ0!Gdׯ߸f("bQQѢK[j9xm1UU5PF%%aT)D-7£x㏅UUUUh4?3{μ| >0Էw/;{ƍ%$$hy&Ƙ䒄R !d2QJҸ+V}u#Gϵ'&*|c&Trrs>㯰OUUUe)I X,eWnٲuzi))ɽz8y:o'2![,}fΚ_!!F~z(hA!|> `Ify ?8Q:ucMӲ2wo7wԑvE RV׈0%n'%96K5 OY|=ݾ][ۭ ÿ!N"'$^t_FϘgۊ"%jGa^0BBjݱcCYnB(#{ӧ/c&qӇ{`@M<<'z9#~BmV뚵JK_BRR cmҤ3~4Q}DBØ0弭_W;Ng$*+hժe]/_i4)*o% {-[~a[Ӧ?b/0ƘA)Q#MX,fF)c,TL&9}0Pu7NxaeB(eITX\wQF|>~_hcewq;Bhfs]zPGPC,8y B˖-\.7_Q$޽kiiiyy W}AqwL"HKHV-[8-?d6Sr1f0?kU|1o^Qv{> a&?I/څ 7n^nCF#%`cOMvUUk67@0ӻ޽ u)B|xB(%_V5Mĸ<11$R˥jB|bY|| ZXcX0>L~|t\Js=zt*/9Ƙ(%mڤ`Y-TUC:NUk=x/\(ǧ B@˖-nxsM5kil6i!оC[[#D3ϷhLE}^!GD8&(X]Pͺfak7CH֭ڽKUU%5E1F#$IKJJߙEKLmذ)===q g1v)M]fl&HB$ՋVu!㖪 OH0QQ)@XE@չsq$]8R˼X1(ee嵾yc|Suq㭨!%%\|;wz& BK&YLFo3g͑e9ʜI\񧙡׮e0ux^(+)\ۺjgܗ} z 2& 'N\2iI$I***`hTUf{6`qM(97j -f}׮[_GCuɥ,7LW,]`0u분U+ŭZMq+[Lr8[^qk'XVRb@Ȃ񶕫Lzf<EJ@Ν:\#GlugA^fm޽BJiyE(/OzA$B.nBxn]Ο/۫gg Ξ=ǯƚnܸߍGɮ u.u 85Z`JJʪk査=/WBơ֮=PUEJcc4svyyy|||eĄm0ڷozDBQ}~>?lGKEAgloom6[TC,ӿHoڴµWMeܠav׫'O5{GƎͥk y*b"EB?AFq^z.AQ_0@&DUfXv,˙]~ TVVN[EUF=4" B^2fM}fݵ7Pn][oMԫO8{ZZ* oO٬;QJYnuc\pK`딕O|W*!byyǟ|f6{ijxa#% ?_t6nq=CP9tc-Kݞ=蝻v,Q !c9ec{T pGNNNݫ'!ҰA;\.}_' \.w=G=5`0hf4c7|ף{#=@Q/5"APM&PU1Leee$a&ǻ=@EEŗlr)\eR` FcNKG?y u6 DFV+R*˲( ee&[%}~,Vm+)-HOBr8pְaM"U\%ƝMFBXQQa9k^l6 n[ƹѮMN^RZ*`n/B0dd !`M"Uj,eR` PF9oB._BoCv$YŨz#Sa4RJퟔAX@0LJJ<_Xx>58.n012R£fJPئp&1u1QQ@ `x ɒ͞2 38WP#1fYם\@Ykؠ(_P0!4j2"C.Bnίowrͧ5jrCǚǣjbBWx5EPaqI)Fyy5U-~gF G!`l۶6f+P V%JQ OBTpPHyEhlִI $IQ #F n1V*CX(+;[T\ҰASJ 41w]a)(b FAEb wȒa*ZW,`sḮp# h 4EF(@bEaEAWTe@H/>jcigoٺm'=3~̘%%%>ژ"R4 a p~AFN+!$>>~劕3sO4lq_0NpaC{[4{FmG}`J̝jw^zCRL#Ţ\B=f0"g|== ŢYz2( !SyUUU$` ,g^/ 9M,H1PZ51_1QS+/Yz[L5̱cbNGRfZP7Ǚ[v+o5V/1D a 6o&6|rw:e!b9vlꬹ]zg陂'$ڷPDtĉG<{pqΜg< bmծ̱ب |;t~թ_wGܧOv7v^!Kviom\#co'<~|>}yO\577_cSgʫm;vk ϯ !FС#zlM`pϟ{G?6nnnZFO" VAnoy5#98vái O/%o Ҵfmڴۑ K}Ru#籱o~9bry|^& LYpV.ʲ(xj'&&PxV_+8_h6A5Z2rw1Av{g;sfh27?h2Fj9rYFNnA4Icʔ\8>>TUUQի8xBx]wܱVqyył @H 6n2 I).G%M"qiYII,]v鹢[~[$ RIJHj$'z}>Bf(+׌2F=ҡ{w7!(5c۾ſުe 1L,-YtsPc$,Ady˦,;3.!v'$[VGUULQQ1pBBBh֜y쉃^zޔԵ3fkhZ=^Bj8z쳥;Mz9aC_Ĉ(c l2\Vڨq#/2|u!x׭۹kwh2!s?h۷k(,˵{M pI`umaQyyMKM&Fliiح{wW`!}̘7ZY^. m{tU0v)J6m٫WD<_戀 Ue?o1u!vduefi񒥰MyyEA㬂/I-x`%hB`0t\@w?=}>rnwM5`!?BO|>mqS%iͲe {qŵ(9;{UYtSJen}i !F~ǤrƑL%iɬY*|hıEIi/? +A%|-[,\0烩Sᔅ/3O-Y4.+**3!]iӉÇluSLks'=3JR(O<9._}eƘjYs:w~N#V]7>OTՊ9s OxvB)bšAcGu<43˿*'XE B* Iό.u*+ƚ0>{W'O>^Ӧ[_ [l1ցRMBH֭nz[F?pFr6i2qD(|!xr\C˾2^nS(P'=3;oί˗b_{u!Mxm#!B&2{nw@UL}d2 .^DtTRJ/ظ/B!.}P0HlOmyZ S4Gn׬ٰ_hת GjfﭐazFyO?>b].~FM¢"QBBnyA&-Znz㏏FOg!X$}D۷;|ݚ׼Y(c~ќr[,_`qS~#"<5XmA|~õ#CОc6i>.3#{1V[C;]NA&/ ! _̰:J\.WB|e"q3L@ t11r8Cr@.dPXTvt89h2rQT8AG#^ XU!EQ⒒R@,r#cH'`@ 2IT.#SB^=.O^E1c`l#b`(I($IB jX}?ͺ.x8ڒD!d*lu?,ch4~?G3q³qqqG`0زEV-[,^G䓏d ?N&ѣfΙ3dݾ*o)yᰬ{5l9No~/2~b3OxC1M6oc_}-B0mW3/)%BCsb;LK·O<駻 ゙~_\d:mP˲MxiY].Y >[.}7Cy8 $r…UÃ#_qn~ױI?\ɸص#\ a士X>eCW]*D.2j0ׯu6 ;tc{m ゚qz \%8 d6?_xiV^B H_,ۺ$Q-IR"BBd޶m^0ƔҶm 6[. l5gW&_=z׋dd;lH   ؇]~hG0.j0Q`8adeg p6QJFÞ?~wxd2=No۶Os dIbFd wzyr-tG?a@ӆ0qf= [𒷻g,Nxr ;|µk`6*]SU_,;Q*JRwe8U(c JUM6QWL1&I \e@YPj  $PoWc!t~ۭ+-:n#GsfŲŭ[y}&B!=5vt=+M3c"*//_ cKw5" UUxiv-ux̎0c ;ɺZVUU9qq&WӴ6mZq?74s-lkVeR' )*++[;q7QU.y]`05k[/I)5G[nG̟z薻11$)@QH K{99~ٟ 4q?4c,pQ_b M !K|pW_2a\VRu]5L) !^o>JJKufnϝn- P2gwl~j>MFD!# E}8:(a u3 |>C#|))ɺi1ְa/>{|>ݳ7S$3\3}ziQڗ^zr`?οS\NQA8gXVVd2 _~9i& _oW5&t.%LjvB+hĴO6ܸyjRzK~<ԓ<<`')1<>ߓOFc#ڴXbEӜDAעEAi7t>3Gyf͚6Si'&'~aj:udU݌fa57N+';Q<`m*.^VVW4^nV(gRJ8XA\[]L_"8s5ш[#Fvwh.+3cwZ۷ذqokݺ;7 <=~uV}|{Ӈ<5) $ ȯB23Vkdp9&++sssSR7mT5=5 x>ؤq9!:1QKKK4il27mڬx6AZl1p1Q+d#%IepbC(b|>iZyyEzz.` 1(l6CY6\RRj2j, EP *A!qq31Q ++y%8cfQF9@=lTt úZᘉD[$8 By5Mùc1!|%$Π_fQ?`1C@)V c "ed"qQ._jZRkyW,VK @++tE D Сiii EIcRɈx— ccx9|PBS3S22clEUUߩshI'Lj⻰255%F]hK~-lֹ *böb_v#KYvS~[|,LYv3'ND]r)Ab:!}~C33ҹ2F^Bt$^ ! *K TUUmKdy>tA5䙄4M7oG~(++SQn Aǣ'w+(p\YYW&pn^ZQ迌1 pIY(pŖUJf[hIaQ+8AX:?p=c<إ#eDPy .AWgF&Dy. "JPƌF'N8ĸx# m[n^x$IW߇R >71_{.ݿn6eVCU^ߧwz~bA.c?paUU./'3kݺp1ztVVWFXyb G . d wc,/1ilSOZ7lܳO8BJdZ-;wX,s+>pPz /!nx?sr"`v?jժevvVPQg% cҁy+"^To97oIӓ2v~,nr Ci\E󐈢xIsO'o+dff<>vYsnq]wdeeAY|׮;t-BڅT17R2t2XatLE JJK/w{C@m(ii/]! p!11ap( | 3^x!%%X< bPaAlB5BI!JeTa>Cpjc #drpF1,`^ UUtb27n(\QlެOqD2Z'1b.W D]%$TVV0<NW|D~økg!!41!>#+05F7҉8y2nOLjD * 1a"@a @r-(J8;˲(`5k0;Tr#bLEFnF>Ԭ̌Kq N'+l1Ђb2U0hlC\_Č,1  ( ~d25j EQܳԔF\.i111l%С=OyTTTr.]:3B[@||\Tʗ_~Rڮ]ۑ#w=H'>?AbBFȃ1f@1 )@)A$1q BMTU n(&IU]v7n(9)R(ʟ{m:#>຀ڼV-cܙKyL͑cgZj ϰp848^Ҵi]ǟ;w֩SGXPQx4o?117bl24sNVffZZi>g}3W˯Ν+@¢%%%y@h4wfqڴov{5IO 7: קWԾ$ 001&dڳ wmn0aΝ6mpRӲesUU>$"z/\?4薁 tqB˥2nݶpF l֭[09p8WxhPy \nX\,IS?pq6lhVZZŗ1j}UZ*Gzt23(b2~iWӦ/s |ْ.:>-[9 vȼ'O K.eɉ5_ wݺ>?RAUU)/֭W{ M~رc3g1F(m8O?j%ءiNuA5۲eKL&#cLӈ [eX( Bh6?f-oyl,>BrƛϚw1K(>A(/eUnG6,(8/Jb c׸isgN(8t6kD^qt崪ʡjJr@/V(JeeB~\\\ !-[ضmY֬y[B=N:va.wbb$_O~~ޠc@@tgح9$b>Cii<%[%%92ŵ ((@[M LUr=AԔoۻW~ ErQϰw}6hP/$p:5k!_3gBc2BKNm(أNP&e]n%=Rr,@ xmlJT}NW=^o0hٲyrRҺuj5CdYZtn‚n ^X~( T3gyA7|+N0C5o&BbۻעEKlV+w7W%&&.[qF:ڗO+]ױ'}(lQ#))Ss|P|>m۾_&''GDfboڴic1!_ѣ+>MUο[)e<]c||$II+V>;noִɲ+c]t;秮]:qu[({FF뜬_}Kr¨r'+jΉ걼Vi;߹s׆ vXiiYsN<=ṿ\h]St3uqs+oЈmv8peY^v}yE kܸ{A&!!X̋ɲt+VrρK5g- j֤ictU]́: ^'#'w>ǃb]^çޣۢK+**DQE&< _^_Mw8 ٹ6n:,ؤq##7^+ bzqRT5)1ЅZ- (}^멸$%99j̓E c\UE]ό11+-+KKLվBkQPMnNKK UbBB!#ѐfp ,J]^Q)-z)czI6((8AdYN`0j!X3yM&y^GFiF(廊A]2DTc0|>dZG˒QdY3@J&P8O.Ma+P1&KRZZjZZli cL0v܊$'%UˀPQqIYY 7'B!-**NMMabVUMSJ~9ʪ*@B||pbޓڅe(mV-£? ׋*UK)ʯ:$z9Jf {U/S%77罩~_.lݲ6 ecfy׮VZsmi gH; e1v9Ci#G޵k,˿ڊRZ`U bݿ6r;Y>H@ !ȄG|A 1B~3g?|>s[FܰaC4nċᨪ:dݹ9ǎ/1D clX_UU,w2(;Rǟ2f쓿h4jL~tQWQQѠA{yƋx!/p ض}1 DQK7]wE}ѭu-_i6j. {y}i ApoO!VpS?UոYo߾(v6 ii)?/\rjq铒E3#U&ڹkנANgEIߍ$IEE'N){9N?Ǟ?EQ\(bXE%^\"ÜlŢj t:SM/|^7%%%֌><8v1}\vt] >oTv.s22&d [\2Ae*e/))MIM;vJE~7&$&ؾ=Y0 ׯRT55%#T.BEK5ԆrNݿCx%%(|FP5ki  c.f97ܜѳgwy˯'O !D\ mР~Rb"0,u1b ܺU˕֔0zD yd-dddkcѥF;w(>Cvw Qvz=z!@B|?L㪪?T~=Ń!v}/nOFF*](zvcmgs>HPJ[h^}`00B?Μݡ}:zHZ-?83!!`4`"Ck)wМ>}f;vY#vccǎ?{!0`02nf@+**DMJo`!p8 ΏxtMĈq"?9NQHRR`?v[#AǏp:]*K >zu5MW?|Ǝ=r>O2yޛxmw&'']ߒ$.t]ǔdUUPEBhff}T^1"k7[BE 67>saFH#b1s_F/^QEQA7^ 111~Æ67ЗIbIOOV5/]ǵOݱC{\JMMI 37B%UEcrUUI {*Qa>KҹSQqnԔCpݳ̯: Y,]os}W*{.rQl6+_OlZFFz?ؿ7t^(i|ۜ={n7.s~m۴q\,F ڨz(_ 1Ƅ\Hy.`R^V޻WO 㵡#M:PbٞyfhҤMoLLLt\( p'x,#,ėu.hꞻt]Y60`ΏFH0y; yWUs7o&˲eVv˷fR*t" !CbX̗BX,!^Uh4Ko7!p\V'f1ʌF!D/m0UT aC{}>d%xcnfx~i,`cd9eUERxv[rFcP t9#Z9fNIN2ZQP~I B,K| pQnr@MP80@bA1u}OX("9\/^##,۠"AC@^{’(>"x\A$I3x[( c$R$<_TYA(GT3o v 6Ν{."¸bӦ-zZ< cz _0`#BEb4gqqgzZ__xb!^oTx%Qdhn@m@moYiIAP$IvMsw:td67mAz6sTM 7b4s\. :WC5MX,/ݹk7ǿ(ytr 06yAOS!˟Rcv࡭۶f+.)ѹB޷Y/6>5R,"Nyw;wvTUU `2bnJ5}D ɸcyu%}ބR>" j\OB۶mv-pjdԨThFd2_Sg?xmE(r.6l8wP$EE{0X̄fǩAҔМF5G0BJA6IP!˲f U`| PRR} ܵ"|D 8яھu!wkZyy}jY}>xC6Ax^;E2/ޔ6o\۵kgac*#pfծ]ۭn,\:*n̘پu]wv8Y"Z ES+6nB>\Y{MQDQ`٣wf}2)B$gesA ƚq-<8ΫHF!%%C.(oԲe Y8DQ$Zn##w'%%s ȇPQVZ.\$)11j[k2FcTb"ݬiW'1UׯIP~_I|yߟrd߾xAqs*UeUU~nxl#Dڽ['ƍuݑ $))Q6z nP:i(5ܸqsaaak6EQj76uM|>(۶iܨљ'VݷdWsc ړ;6[JZƮ#!ſ+VuҫggyBabb=w믘L&M1U{>={t/*..--G,mڴ~ox=g2?njyL~x__)k2>,#:Byo\ˢߝW,==_uڋ/?w!5w1 Gx`M63y׌FCle|| 8c&rÆ,Y4[.Buv6q%OjOluͿfeeI(BqqɎ;cpV-9?k.JBM~ݞ, VHFZc߅{v ߞwNK\ TCKr%''gɷl{96LyiFCnn?rssDAVɲX!3сN+)1'`eZ,/yl1 !CU$%&LLďP#xTUMLH iA %II5'p8qqqF"#D1Jʓc *AX,Q@0> (^$''E 4I E,n aa1tݡeeN'!D)/9cBe@ ԈaDjQP B)#kQ@ NծsBf j ݅E蔐1j4y~~ه o@lDA|a4 B> !BYu9[}A#! 0UZ+ :d7ls|"ܽshå{-[D0NKQ$}ӏ^$P[ݻ1جic=‚PYYHH'PAGUQ^!l۶5cTU5or*jVVU%&3!Fb6M&ۭg,fi)fÇb4 xnₚj> gϝ+(hִb0ȚG>(cCvWRPpG.d֔qQXvw딗Wqrr Z,G\fMuh ʲbEDn>m(/R'b!+@HKJK[M&_f,˪:7miݪ%FH>д_TrCJ˵nQ)EE[moڤ G  H[%~ÆodZB*%]iiY0DY7nڲvܢE@ С}VV֜9:~b MHQKC%%Z_CŤʼnUX(>pн|~‹QJO:}ϐ>7𹎅euOwc{ *+qq:@i?W^aK޺{;vE!A XQB,ouI/MС.q%==mҤ!s-ekBCm7u02EDĬq;'nj}5BWz 0vɓUU3 O?qDf͙`˯"Aœ —&M;o!tǟx? rd^>v % cnظG umA𨑅Ge4_41vZ*-nZ-SR6l=ÆSfsix-Ho.eEQT˕8lذ0˖XjuVf`23WZ|JP= grMy}Q(5M[~ݚSDuX۷u1=wB4_+(عs7011ay9C2?gd'&$(n$K?8!lPٳqqvž~mۓSSS)il_m^y0ƚB?5kڄ'uZu6EQy $'%|>[m0 @=GPTT " GǯÏ3xBQ0۷)kq{5z7;/=m pBb҉A0M|k `ʥ)))WP{R%%AqZZ&9g0 nڴbĎAwӦMo(4⣿;#Ns^Y縚q&]- a)Ο?9\f6]r6ܜ p9aPg&4̙9s3FAبPÆ @pwmz'Oa 3@6^\U+>XX!z)X;ṿ^E-)iwm+NWaQL7XƏsݻĉQo>*G8l=F7VQBdZZZ.\ڰaFrHN8yO/x5tmJJJ~߳ό7  Vk;WrWqh=37Ծ];L~A<;ޮ]ۚp~EQfXa0СGg0ɲiڶkP?$I ]v"MZjg'&$pjړ$;onZ]P.?D1[1Ud2}?t{a(^~_+YNP޶m~o2eY<97䞻7n# +uڽB\nr#Gܟd0E=>sƅv.eHX&c,䩒$Iŋ'I BrsrG?0ϝ%I3> 1Ѕ)eBݬYF>կ_28v䢜F>X~}MTU}h͚5q:|25y<=!w/y0 {{L&O<Š/=ۢ(ʒT j EQ{<^Q[!9@YAɯLR%#=[ ԎN od0ʼn뗕7$:P[PZVV>䞻:vl e?h1ʪ!QtоrMwҒ;ΚGs{ʲ/S#$bRR"PrKX΁.Kz̸'6$ͱ0rX-Q av;@QS!>?h2F4)VϬ$!Z蛄BHZZѣGc <@TE^P"`AEQ&pjĤGj1j(/;{~pb$(9$l}b':걶TlBkG=}F6BR eІp4= vpAg\ƭYIDAT(]NAﭮvG Ï aE>a—p ߀č;$&JdL$ g3]]aH])=;"BJ)1hDttdxy܃()MkkPۓ K6qX"`ȁA)ek":VJeysxXJi4EE "_X,K"bf9驇K?V7&<yeYZkOz'cdr0?"p@L Uj3-\ 1 R@DJPj=32ɇki eY흶"bF@bv޹@a8>BĘ$I$ILbI?YgͦIV"jZkf"JԘJfY640ٜ51~vv҃w?0S33-.}xZg7zYva .:zYFw#\_@g%Tzwo Ib Z8VJnj+w'*c^( ,[_"ƘVU\yD[ۦR9?+Gk-z<}E66%tEXtdate:create2013-06-22T01:32:44+01:00yn %tEXtdate:modify2013-06-22T01:32:44+01:00t$ֱIENDB`puzzles-r9872/icons/keen-16d4.png0000644000175300017530000000060312161170255015643 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(ύRA KE~V=0R=,J($`*œy?#.fff$#%"c 5IHm`Ιow"qw3pkVEuxdQDcMADڨ2.~re: L׽!r}^{`\I(ZkAU1Tgs$[5:97w,?a"PA%tEXtdate:create2013-06-22T01:32:45+01:00e%tEXtdate:modify2013-06-22T01:32:45+01:00SIENDB`puzzles-r9872/icons/keen-16d8.png0000644000175300017530000000141612161170254015651 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIIDAT(]QjSQ]ws=7'71-mv$AA:9(8("icJܶkaXG228d2+yAD; x}fssѨ}"--XY^Zj~`f[QB!tYvmFLVJ E^lm݋cYYZQkk?88>F'y8+ˢ,Kc 9Fd-F +jyp =<%Gu$ovo9!"!Hrb>,8l 31Qd xx<4`!F13b*If$Iht9MsNӞ>;fZPl6U\RJ%IY޺bmlw+?@Ysc cLk휛fU/_3iW U56+3WŗF< \idajz8lQ֥5@ଭR0<Icbm!$DgY) ˬS*z33YI.x eZH?!Lz9]%tEXtdate:create2013-06-22T01:32:44+01:00yn %tEXtdate:modify2013-06-22T01:32:44+01:00t$ֱIENDB`puzzles-r9872/icons/keen-32d24.png0000644000175300017530000000276712161170253015736 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg 2IDATHǵV[lUE{s﹅Ki{S5EPy?Ԙ%L2&D1?51" ] @m{5"-V_}f^{?mY-- J "ޘRʳgUU%A&9Sgֶ/ȑ2Wz\CTGAD\7vSkVBD˲GMӼյhR8J`tJkUL$Zax_s}e3';;ookkUJ]s=ݻv^أaA@LB+`6++{)3g(KV# К;;N&Ӧ5! ðBK%z֭Od1PR!b>30"v@O4)H Rʦ\.~Ô̡UEhK%W!1y M~4V}z}n_9@uU\;su9/RVjR7o0y)+Eߗ|5ռeA?+Qbd\1iRD7X S]P7!^UN,qWr; QYMh)nMuA~n5%bbl¶mGh>ضm)mێ ")%3!Seq;>\6w!Ǫɲl^jH=˴\JGM^Tm. ۶i_oii>ys޼Vq\PHAC vpAg IDATHǥVѕ0W:$ĨIRc۟u$0pA\fyȯ%H@U@ʀ02` &p%@cPk5mjfz| `۶޻]%?l&2 ~۔ u]]- \fff+|l l~5#nuRThh>+"p.8n؟ٖ=nKFv2nx`dj:XU+ t)N8\MU9YNO܏:D!;w-3R2YHbbEKnۦy *G5_pag֍՟DRcQ"(};SPke4(o~_U%"U-؞%DN[DZ+ֈh,ѠTA"H䗡IgCD~T,y~4F!TX-XZzrd|=9:57h䫚}I41 XAT5bz$ޝELJ;6e5RU6i,RxŕmDfwi`DeÚe4(Uh`\$Gg}1pK )_3 }sIH~PJ!m8/c "*e|RX`ط%G W?`%tEXtdate:create2013-06-22T01:32:44+01:00yn %tEXtdate:modify2013-06-22T01:32:44+01:00t$ֱIENDB`puzzles-r9872/icons/keen-32d8.png0000644000175300017530000000305112161170253015643 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg dIDATHǵV[lTEwfvmn-[pF R DZ|3Ą$_M/諨(k[ ˥-930{+ɹ3P(s0 MSBHQT"/ ҩzp P5cCC~4 _纞jiZxsi=@uZgxxe,6cFSS7Ǿd2l6J&gΤƈM=Jٲ'~yY. vpAg00W0IDATXY{pTWs}ll@jBB˫A--R*0mUǤ:U;іNut:&FTӁ04]X,ݻs!YHvIw[{sπ_!"@DDO:1ᓪeen1"xޢ"Ұa)d#8,"(++=vPOOv,4MM 뵷w!4ՔҜ۶MӤJh50}Y EcY{ 4Musn;: /ڶ{v=gNn,Saby,~?N #BPj0&{*/ܱswQO:uzh[\9\1!)DVVW_7w﮼`H( % 7R457?u஧v;wNT˲1#̒?wl+== *l #H)UU Z}t[?pfU44D) HqmiBZeYJBh—k+BobYVtĜ] BJG8a'*!ի#BR"ڎ}Ґ*SͿ>h_otvS)ٵldRBA$/ř΅ʳ'??(\"^ %yMV꺽yu{'jB$Hn-:ѰJ< raa^C:@ؼ`-$򴼦Wul(ǰ xgB=  !j2q2H s,Qwlh<#dB]\v)R6(&p%9,!$owd+FDdq-BJs8/(o~ǹNRfqf!)% FRp>߿Gsvt^|ٿs⑇Ee΅eYPs{EE~k˖/s*JUUVT""ehzF[+W.=!0~=Ohl=jN+]=^R2oݗ^(;;+++䩎\m+_<;IG:5":34t'm;n-{82|\ ܒU|LԶmBc׊=?rQ{ԧz*5T?dsC!A"3+ b^]=s7~:iĉ&9e(%_{˚{!\8F&"߰Q{ DHR B9{TݨԔWlIOAȵeFKK[6:%4U3 EQ\}5($?"\I 9w  %a&4;x̢6C}IE#h͎K%tEXtdate:create2013-06-22T01:32:42+01:00f[7%tEXtdate:modify2013-06-22T01:32:42+01:00IENDB`puzzles-r9872/icons/keen-48d4.png0000644000175300017530000000154212161170253015651 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXXQ ^f7 's?RchYw ?SDGÐs"BrǜY;"Uȵ7v;qB,gEdG7r2jF;roYMI:e`&[e*"BE/ve |9/DLx^VddiyE X/2tJ _ 70{vbi]r!NJ^TD=Trjn!FJO Aξ9w,naQ/ա}2.zzh/W{obF#"!xbaoip b?;A|@5^Joj ̍n沽"G*ҼU&&aH)-W1Ds>LLDxxѱHXƷ$ =<Ѧc 3e"`D VhR?MMLtJdo:|){A6Nzha&zx{YU{;Y^Pv%TCv͵NrPt]s}*BsB0~[YcVƆ`}SVB!?ESS=E_\lmM|I=ե5ܟ!\%tEXtdate:create2013-06-22T01:32:43+01:00P%tEXtdate:modify2013-06-22T01:32:43+01:00?IENDB`puzzles-r9872/icons/keen-48d8.png0000644000175300017530000000407212161170252015655 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WuIDATXYml~wgvom p GqRPJP?Z D@*MJRIJiʏ6i &$CRDMH-g;w;ۻ5}w8iF3<󼟳'w_2!i3N[^QeӴWUߢ2p{[b]+.)>t"U XX(SJ)c0 9|HMM3g"UM^?Sk/ ABtrrr"=jm_sOvuY##CsLCPN_ |rC{~Tm:Z"9LEh,~NyS7Pv[h\C?~Gz;Yb>BK='OM/oܬi<04;|}x Ե{vҟ5V (Vv\Z /x}EI )evn͒2r6r\7qD8p;dW<OS ;*5C4|9|.b) VR&D "NՍ[3S;% QMSU`DdYvc x<^fIɲtҴ7e@SS_xt[0;{9~a\e:Z'xLލsNx._( -\XY pxÚՌq,{;[UYY$ˬk-!RZWWr@pciӃz7+:;~kccqx(Qcex`eJ%>Y^Tq+{[*+AER݊tJ]lo@_%pX D"p]u+v3Cq| ni{XdzȋOlyiK(D@Lͳ4te+sRDxƲ~>{bFBOש}Ho?H7]E[DST;Kv}Իhk7xߊ\TFv=K;;αҘV."3 ԙdii}_>ʾ[=˚uΧ=|3fg/J)Zsƥp<ܸ+۫jx)q:4|빳qцU[D 0'=8tdi )Éa!Β5KF&3/p6l Y=MPɿs!5Omp,lB1&2dh޾䫪OUِCCa wf_3w ʔ\>AJ%tEXtdate:create2013-06-22T01:32:42+01:00f[7%tEXtdate:modify2013-06-22T01:32:42+01:00IENDB`puzzles-r9872/icons/keen-base.png0000644000175300017530000001677212161170210016104 0ustar simonsimonPNG  IHDR  9bKGDIDATxyXSWo#.V,*} :qZud,hAQjki-j" E+h fXB#WT 9so%WVw6nKF#! ! QaFC0B(`0D#! ! QaFC0B(`0D#! ! QaFC0B1odCC FC0B2Q $f iy0#! &9@"ON|43bEˌ~{hKL\aQQE6`0 1=cl*5Ӊ yxtܿN&/sX$lڥرUD"~[En]2vM, m۶@_59ھkz~g7HS>SFoȴ%iq6FlwqvDDFL^:3+;}Y&{%" S7o~O7GmoYٚUèx~ Zg#Uɩ!ӊ` kkkSrZ-rO|{^ &g-Z8zѣFHׅ_L:< #RJ{Z@}^} .rȬXxT|ƝJK=j䩴8gl۶ ], #R{{*,ޯo? )`~Zof;ֵ,-&6.rSthH=;CC#7EƙkG0dILkMk9BL&7-z<ѫ{!=Q-s|cؾga6 k &liMMMaM;3q֯نOǍ#Z 3Tj@bSL*EC0BxFZt:rsQX:ؠKg!Y7p-X[ C|ٿs2PDooNVd2RRPQg'$ | C559 bR={ӏe)hf^LLy.^W^Ӄѹ3TyvhX HfJa8OF>̜dǘ^)| 845cGbPlE*) ȚtaZGwx{G((-D|/L7NAiD?拦"{79D7=VVpuTHW1iLLq1k0d"G9@eBJ d2<; Cch(Zg`2 S`mAFX:tDi5~z=c"d2i~THK"] t h 0kѣS@.+^LُJ:~8odX hzD<JpQpuut 1!K8|%DF9#0HA"VgݧbGcTcPo̙+ {*/G~> P^%ơ ;CG&dg@.RLQK]1lh%<=z@@i)'aEv O$1_ &UGn0zTV!%W w]]?_~t3HILc䀖oó5#ȴtW_A"쑒),K+*ݜdeN׳0tHk]?ajTJW{©4d2O台c$(?"?55PU#Z^Z e12F4e? Ӧ`1~,95_Q0l{e燳98qpv4 0ѴQE)i B^G?8c0֫v?WOi)ݴ Xiê!a=g<62WFKT3W0cʱD 44M[c|lt f.lWWq@Aw9PڠVVʕUr{- o 2nI;QZ;@TX+^2k#r Ax$G>"'j?zf{'N"kMmc-/ ͧ(1 ؃4քcщyn=$H`%ZFF;̨kՅS:cCn%o%uO|w=S%2UIfPjHRqV8m5]Ck^ۤoF։>J ?g\&3g>f0-։i Z') ! f1IER@G0B(`0D#! ! QaFC0B,\DI]vYR>d&t*=tEj:L|ȕ)$KjOvI-rϽ5S:mACNݡ>T5nOv:> OW6Gmٳg󑤓K>7n̙qƄ~J)Dm phBVF]{>XC]cӧ9xO>^|0>~/9s^}z݆oEnwcnmf;R O'8%=.,yٚ5+u|<9݅ssoC6FlwqvDDFXv旳8%X*:uʳrZ6|+hBmTed$'FL+//?,|)<>sR¡oW*niڎ;k(-ZRe&'5ţ\\(zw.%κ.q"]2lDC`ooE\/.0iobbN5TZzLli[J8|dw4Md9ouau OK sof߾/d2ټon):4$x!MX'$t];fl-u gј~ݖظ/G6|:n÷Tu׮[@V1g0h<Ӻ3+SQ?f~)"!Ec]`N"ת So-ͫ/)U:ǎ='{rGnĠG+ `L~3cwE\?m4k?Y)٫_U JX)Y#Ia#lYg9:+5%l\T@mӨ ,ל >aOXGOaY4-̕箴:zLr(sff`5]خl9wY}rrsU_xu6fb75 ¶RYbU ; K}\.<:=ܥ[t uMu. <)b7__=(lSVW}Q~/q+mv(`mVXS5Eⶡ*Ɍ>Ur{- o 2nM[6+KJϡbt jkw]ܳr؇boNkRax|J_)ȍ2l ZЗQZpן?wr[8e #)7x|GDmGϬpƧYkjkx9_0m>EM`3iY|O zH Jfov#Zuؐ[?["t tz_7]LN)G/. X|s;aҪ̠ԐJm0p:&jH7m`jŒqf&P\[|wnns~i/8M&aĻ렸1n\@ W^x5^c7|bހ#Ε{h=|cƪ4 h͆t71^abւc\|t陂[oݬk[wpu{FS<&Yˬ X *Uo`f12&)"! QaFC0B(`0D#! ! QaFC&>Q4 1 ROYɾRäafy5 97&D@<Iɒ.e' Z ! "VR2y@ʨruu P"Dm phBVF]{>XB,|s:!^;`^g~9 ݍӇ[6 K^^~XRaܡRe.\\]]ji0`-%uyo2g4 RډF`Wun12-u7~0PUDB$ZuaʹyUE5Ej^ѡq/Y(b{?&++J2U%̢"v֥ĢJOܺ_MYAapT='{LkXD&%?X)/d]#׏uͿ1OD)]̺g: ۫F6v(IL++'WRog7hJE)mofl,vR!#d=YT8t-aczZ6mFmUfiW\EvG8p>O{9?OAi䖝ߐy,p׎XIR$ K};z # y^o)me48wigC`ǔcEh.iq_؍ta&F+K$1E1>y3zM+8v[]V_\suӤ }؉HfLMC+yXNR0 fDUj+)t.ZSElsU_xu6fb5#}}TߓN0sU^Vڌ2[D#t24ܺTp?.3ztx4#)bT% _wwr+_WL rt7oKMc_NR]N.괆/֊̧(vР}99͝cE_+G3+;qYkjkx9_0m>E͑a5QxfRqs[*+iʣǬg3 4ҽ(;(Wen#)uiY|O zH JfoLrO@Rc#aѣ"ũ"$ azm(=Htp~]glȭ~c:^RikaLS&b'-녴T* J V # ct+zg{VbYN\N%J^ۤoFUĮH̚񬟳C?'E7kuI]'!#lijŒqf&P\[|wnvOq/DWs| *?342Z3NW^x5^c7|bހx$"1w'lWFgx8#&^)%Ru Wg' hݑsK3.ߨYX'd4ӧxLYsnI ZMJgw艁bL_e>ę>M aFC0B(`0D#! ! QaFC0B҆ƮEOYk:FC0B2":(`0D#! ! QaFC0B(`0D#! ! QaFC0B(`0D#! ! [/mqIENDB`puzzles-r9872/icons/keen-ibase.png0000644000175300017530000000343012161170252016246 0ustar simonsimonPNG  IHDR``mo oFFsxI pHYsHHFk> vpAg  O>IDATx{LSW V{EjQZc@a"FB-"LH 9 ز`QcLyIat2y)&ʣrcU&ޞ_s{>zs01}ܵt&b1N;h*N9єN:E-?Rɮ\.$HxR3~0! ЋU߹1: = ˻n޺ N"Ab|l;QS &v|ki3C׻i4[#u:ݞd`61:@PK۶")a%h ARsY_BYA.B]xr1k4m|N+V1s/Nj_?7_i,[S{q\?t[_;{pO 4WDD-Td0'cxY%zzIn*haǴy 4|9XVAj-# B!`A X, Bkb00c -1axF~2kmKk0//?@>?@~>sqq1oVX}TsFT-JUV;x`yZ !!_xM6Pl:HVf9=#Sd ް=.~߾]۫`z5svGRT?*x"˵v ]('$z-~:rԶt*bk8$UhˋJ"M-t굂so$I{{.:5\(**Y|HKseY  SUV,_Vz,-]fAښ!s`0p8a);detTdv։HEr$^b71бtՙNxTc:EFj1mI8k18X,:I*O B3y X6XuOtu[qӤ"PIity=l{d5{ڶ6 fA4V/<I@)KvfSӌF vpAg  OIDATxAr ENh:Zd Eo6nB*+o;""fv; Ugr{<걈HJuUښ+Fx8AǗx'Kzhh}n#c"^x:|Fx]'}t bs,dA]"/!)7 Jk_/J:{ #'ʠJUĶAdEgdDb%o dAX9Mi ^t=XA7]2hDvn!(ua2aUGkPYyzt=1| Ai1` 3ȽdAdAdAdAdAϵ6'u7 %tEXtdate:create2013-06-22T01:32:42+01:00f[7%tEXtdate:modify2013-06-22T01:32:42+01:00IENDB`puzzles-r9872/icons/keen-web.png0000644000175300017530000003210612161170211015735 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge3IDATx}w33{^iذ "*5v+XcE]AX,l-3㽻,{|sgss!gЎn H)9ݤ!Rtz= n [/P]*J߆ K,UT%A BRF3#,BSX)$2F)ɔL΅)J!edgxi6k1Bι睿;'Sxu:a)aBBHϫ(ʥ\<ъ( @<FR1RݮuoP1PR־HFY\g|k>oqX QBrrs(!m!B BZo.[O?33?XJá )`4M I@ioYx湅 u%nRܒ^vbZ4 !I ݇|B@۳xk mZ5fb!>kr8np!iYYY_~;wVҾ!ED0#''K.}7K.`j\"Ѩ<[ nܲ,=y~~y?a^G S>[.R[}NiY%%}L:}׳[XX@)˯[qa1Ms:x\93.Bny{ "H} p4DEQoR~ݺ|>u#.a`0KORQh4 Ϊx'|Ign1V[[v%%%]~ͷ9easK!B0h4ZWSӧwafgg-]877GJ G˴toRB)?1Ea1JB_iS R^eeeRJ86_}?yn]7G+mHC0J)싂‚K/>|n\pQ>YӠ%PQ1,LK?g.6p>^2 y{R:oW-\G_W^[| m2 !)cP{O!X /,,lmJk? ;wLä}̘;'qzt`$D׍`0GQxt]w:~sϛz yw>sё!>謉O+//ߺuk?sQGLvp!D8u=c<|=1=zԩ$غu5W_)w ۫B^u=<׿z=c]^^Œ ^|-[9Gg]sUh4D~/~yʹ+='|>';{gORJñ~}E׮]/wHv5} pWUO?sΙ󿜜sg剋/w]*,,(..2mi놔:s()S2 3c4'F)ㆡ:f{Llll(.-O8 n_;R12 n7<TlĶPH 0 l0HBn.JJ@)Eض %%uH UT%PaڐBos'6WSRDq**};<}tR0v/a(풰#?0VƦ8Co90=QR 6oFy/t=z-2=z~SPTcqaP\(,_ĤXk! jk>}q#(Px,h~2_ogc!ﻅ/^zӍ֥iXL3ݻsuu,=kϹ;'?Q3eoܸi5ӯ\bXO?7Kqx}~bm۫=Ϟl ʺ~?++vdB(pD:v97M@(Y][[ hZ@]]i."ץ{[H94k.vu'LdS0|b3fyx^[z[609q(Z}$EUo3_C~EQB}!C۴vLsU4\[)MLsEy{@)%q8_u_NEI0* MMQXBivB)t:.{ظiA~>.x<V.`Ɂߏ0tH!LA8Үvr,'i3CLbI FaN#@lJiH`,aUڄejA#ߣ5u8HJ@Ur%QUq m؀=qࢍ\]_CӐ.x<ŜlȑڵS,?Rhn۸92aOl{e ~is< m/ le` xO1 }[7L=YYE~>ڞv#=l܄QEχ%Kᇸ"uh͜K ]Gi)L UUigmo~gM BqCmAJCc((uV}CJ: ѥ~e!T@v^%lڄ>oũ' ;Ai~fH , yy((Ai:GĮծ==剿Uyyl9(eA1nlm0ƌk~@vvD(+aGZ(,aĆ׋pp:;gs.ӷ[W6nMر==E8H |aѥ پѼ]p>A<,?\DiOoAAka!ľIn/!c,[c!1^⻆@J,+G[J\٣/JlAVN~6+5''S[sAh%_ж{("pXlX+ *6@njkmuӰu0 t"aגl 0[-#i&z:- [*d \.L859POVymo_̈_B;XrpG-Rt eH@=5?lcb;#y?; %eЙ9Nj P~*q@=Z'^HHJt;L?%ruOOlnP;322BR6ly2ädM$MU(K$L;EM[eN{Ƽ;H$FW!!$z$bJz0lڕ2HιuaD"h4~F$٤%iΥ#*{i5,*,(׳y ֌QUbcô.Ϸ4/5tGUVV6#H3lw67!'7/WpZL{ogTUkBЈUEa s:i0d+Y_mz,y !E_n}^e]JK9n !HGF[_~]z1F&JrA{T0ro/8om!4֚eYEO>>k/nڶm{aaA^t]$T )ۭJ˲JJa?`,inYyy3{ʹ\=ʛnrKee%!A99[YR\2^}#8Wک̥UQ+b+EhMM}JJ}߽wO3G0 htEՎ^~~W't%0J)hکBEQ P*\vjh4̳߶mۖ-[ˣyyZlEu@(nl f.B+2;qݺQyC4JX{K)ƚ+.6|<3GЃMpJe(CJ(PU=R34ibk<C!"(bmgX#ڊUc*ϔرk?UU})<$) #p899?nXp &Mr˯6n;tHvv]ӳg9rsr˔PBr={H/V !(555_rE.6P7 3YYr:r7rΜc@ rbh]]}G`Pe˖\}WFcuuvxZZgQJ/UV_; /vPU$],UqEgϞqM]urG~p 6m)|zںO=zTn]M+h4q&a;sg^B,>d>®wѲdhj/[6"Na3݊KNEZZZA!E#K~ip6G,M*l}XEwr#n]':=hі;/_9k'nzn@e*Yt5BCG_1Y}*BZvoeGVz7I1PSw-WrǠyw3s}TQGD݃~5BFY^9bwGqk6,?ܽ%M'!E#Y\t)kV5;oR$4lD.{AoXpٌ' J<\Q8oW יKi(u-(7iNBHԊ6荺БA/)L!B|gֿʆ%cFY*ϾToCpaӻy, B KZPF¾BVUO STHS8齏7|:iī^ *Bwaة [N1;^B a!1t=%jFuG3>=K%nX38(oj._$NMPB:cch ޲z(w?dRL!9ҺSنƊ ӆ\y\r\rF%Ԕ:X\BW~xz4ϫ掝}dK%&|BGPh<9gMG\'~x궪]5c 3K~ Z*r^97D>JcX6Ao`>iUH1'ÉrMa dJ)݊ۧz Gov,_z  !Ē_n?թ$b^J4^qkH!hӴz_(:kԌCo[9vr9"Vu-xwhClYcv3bפ)0fŤ#K*Ga߱n϶pW '/[Bp9ڙv9 tzh( #]EK8dYV~~(M9|-c!.~Nm֩_Rt: -`9N(O>'իD"Ţ1f|.|~%K0FNH3,Rt;)H ]BKx#t\Wvuus6o4-e˖xDzrZ*$+DŽޝO)'yKa䖕b۶m6mxP0 vM 9R4qϽ*+xܟWUǣ(BRz#xWyǏ?+.Kv$´MgOMH:ɐ;{t:Tv\sn横c5~&9wޗc8eZx\w8TFaMmv]OwWRR|MWl۫v <#S񸪪 U!s>‹Nӝvr$Z IBJ!>%:Jiuu?.]>oꔋ~wٓ?e6m]Z|Ϩ%H4z1y@CQaWMګ'zJuuu߾}vYtڵ(;;3 67F:H1M͊l ̜5` s\/ɓ rsgϞӫW[-\ o5kǻ/Y[Vh~0L8~w{M?Ţ7͘Uܹ_~ڊV@;>@ 0dȠ`0u]a UE;Ιrq'\ֳ] uFkM&~VnnMٳ5\uɧ:iPqO~Z_as}5r۶XXG~ݺypoXt~CRQ3>SU?>唓vܹ`7m _tFӴ:rSN??y\|q.XfrsOƍwMyHt̘C1GU^^ֵk;~oqӦѣG㲓P9J*$ )%oZ43M[o,1 ?dر5յS: Q), M=w]FmRz7՟|x.x$ ,2#6$n:~j7( #ءH$Ҽ4LCuX4% 6l!0[y(b1% )SqHg%#LB !@бL6:಩9͈MU6#}29f aRbCGwv}!{$-Idʋ^r6荹C F]!yӉR8s{үB~1+ցPPBTEwQU' p#N!V"waeo}7}A:O]#fc-\K{>: QW ShGKQ܏}Q&8:^8kEl>v>kBP3 QBTyj?9 Cefi1KKJZ*V;0{*p{wMlRu Ɏa˾ʲ%PPBO.Y]E3mGG%s>mwYXR^2έSq){?gE|tN#oELjKnA+[p&(!13a-!dH=;v9)a׼` p0dɓNa h8jfUlGR7И{`=2)O-[~xY^Jxw?~S觰vPOv1E)gA=DH"&,7O{͐ i3dΙcncwb-t2G:7V45?!S"!HaDpF08t H)f4I^)N}nBM?~^k?*] Hl|=홪S, aY>͎[iBsn2R"Y%% c3b 'v{% )( zǣiZ J 4 Kn7bM"gOzS.sժՌmiݮI)´ \*LNEvv?xז,]t!nop˭|<`ҸAȷ,XJ圧)A)5M@}]oi[ݮ׭d%Ke|Y{M?zI W -Y|_ >i(HWY Mnqo/! ci$u|ٌ74I4e=S ݽ>}m۫*++CpMMMcX i55@ }CjtP_zuʕ;wVuԑgLh%_\Jxhnnn5;X &Q`4MH-]Mj&Ƨ/Px-E̓eYpRrSkkjO;̲= ;8dJڌ&eYf?NؕN5VDjv9qL Nz{G-^eggb10(PiL%)%J(%nu*Dg9b E^|??N1FO0aC#M wA gov;׎p#cZ՗2TZ#!B ."K%H]\ .y{I(1M377֙3EObe缹י l'jަDҳPBRPP!Bp3 U.E̊s `ӥ8eVlVe];jUU`(hbsR% %8qگm{J:7JĚ %JTk["[V ˭d( !bKjAJ70Wq6?75AH;<ϙצ@RWW@a Q4m mZQU_rBۖfMF!R]lCP«xVV?D~"$* NЉ¯\zϋ^Ā~Np.j tX1xL '66*hZzGuh뒔#,d*=b8mq(1KSPE!ʹ_\檷F_HbJY\5PBkudY*$h<7Ϡ܁j>W!^HȺC*n=fKX6-H`I*UȄl]v|-$]͢,rIRT?w&"9w2!!=J=%0NKڥ_L.W۱ \p)S.! ,ieܠmjuf P `J+E>lҐs:UѪ FL|ǏxdrIDHZi}gl!Ŕ3{z<+F{\"feCG=-n*y\Jg# bKXn=md\1PWGw}7L }kY/IPHȮmw=on'cNJp2}/OB(8IbUb3BJs|뿹.Zϻ~LoR4m+/t)$S"I~ϷLk@A?k Nq%R9U)~YV9,տ%u޶ pflGv*U\OW? 9bXIbJ3ϙ;}40<Û\u3awDwƭxF@sch K_P6(|/eJ176}JX C IeRք\73w78WN_9Ҥr:$BFXČ.9Z$D8jE163V >9TC?! a!3|t鑟!F@AoH6YzŀKCFKR5ߕ vc nlGN$f/uF%*!:SNP|$!xJ{ un >՗kN B,a| =ϙW*"4Leu%̐fIVMHz_ȥѽHt}js ~{r\^'$mxbO,fQW!pɛDŽB>l"@6G*#LL2[WFSt @C_l);hKtB2"hQ;Sl)dĦe{a,SґLԲ'M+OSj f5Hs.4d6RwvR~ §v)*H$!0 ;)e43H) 7 1z2NBiZz vpAg\ƭ\IDAT(]kw[κ|:I v )6i"jBHR \Ε 2Ƹ#DioߙS70 ~eUc%"h۾n33ɥ ; |޿m4X,~R":;y5_?di% fx:!Z!pL0c@(I]rU`5} 8D,[uݫJ{vt:Q1L9q^?|IQ1bQlfWQ9Sݻb 4iڈv@`啙˲^SeaXsZw۫xrifC? vpAg\ƭIDAT(υR 0$Q۔4E1;NEvK @Qա4x8ViP =1dz]j"`Y|yOQki;+fNn݄ 4R!U$"D>&ȈJ+Vspnf̽ K3eS"QZtkFf~eJ#b%tEXtdate:create2013-06-22T01:32:49+01:00d%tEXtdate:modify2013-06-22T01:32:49+01:00qIENDB`puzzles-r9872/icons/lightup-16d8.png0000644000175300017530000000143612161170260016402 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭYIDAT(MRkg}3w8l"$م NqpcPck'&) vRRN11$H|g폻?^1{ /r D "33CvfJ~*mn|n΃ ggn]c_òL&qqvzu|(66Otލ\uW'I) vpAg  IDATHV͏U_3=; 1WH bbHZ&&=4ᤉƋ@D u fX>~C. /I^WWgGh "v,$IbDx"ޢ+% ѩPp˗W` +(Jg"mEBИZ$ʕ#q\̾(uGm{e>W*לFdUs",4khvkj˷D,v_uQ GgUdZe@ +\]3 ]u&՞&OD4V`bc{ԢkaE K;w;Tc2 ;fme6q6Nz-JbҴ m Y, `k{*+" "Ľwr86H]}kMI ̍*1<{˯YD{~|`ns"`hE:̶'!B% =2@K">maf֮YX,d )4U~*=1971;pD&IDhxk^xq߾}^H ՔYVӧI[ȑ/]$]BsرchhhÆ 6m"2>N+V\v<a|p t ' iJA3L֒e&ɣGTeDLpZ[Vsι vpAg IDATHǝV푬@Db&ɚəIfskM1C7LYU(!M ,myv.BD|4a~_8&7+mi"33OD: L]T@#8yz4E 2#G%"cB0fpmZabHέݹ /o} @.Sc< wMLK$;** Yy*D%K+ \OĽVy~63᲼F'!]*TdEd4<f 5 (n>8YgNGdfqxZy-g( I fUu̼{$ٽ&}'3S2kkǝ}x_Pkwbau ’f2jMbOnZ4d!W;$-niW@nTWh,H٢@D!JR)hN. CT|(->F%tEXtdate:create2013-06-22T01:32:48+01:00y%tEXtdate:modify2013-06-22T01:32:48+01:00IENDB`puzzles-r9872/icons/lightup-32d8.png0000644000175300017530000000260312161170257016403 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǥVoE㛹߽T^r%+/qeG6ZM\] QҲA՘>ܖR-曇\.mK|3gΜs~gddh9ph90ZkիwS e5fZUɰt=sqP[$ذOØ(mc˯bƭE-b1jL򦎑q̬ReQZ%R9r8Wkr֭G焔Z*=eZkn\ F$fvZsRZQ ScT@-'D5E&eE!9 a8cmvx _~VN vpAg00WIDATXY]l\GΙn8#TMTQ ƠVPZ<! D(< >C RBiWrk30^ۉJVw|gFKWy/ BDǼoU @kR` BVBD˓UUCv TkÌqƸ4-H!buT5o/c{2&8^՝VmV"![}ɿ@DZMqY!UύvwJDY0{c$Y?0s6!"zS_3y`}Q|mHE8,C*Re*T(*YE%v%B0LBؓQf!JX V_eaDŽ>&G"5jCũob=Lqil=Z? &V_kƘn@xKb4Y*S0rˋjrdZWkqǾG^@ݑwOOgo]ٗJϟx R(DCX@jUݗV2ξec>Z o7K{uO$j:3 1sZ"D^=3+*V!0_&g&SSw3_2@eOa^R:cs{/ IM~꣞|xff&(RөSۻ ZZ9A1=D$,kt{@ID9xp̙.LW#n_9Yꪚ$.-Upwr[.Óo2s^5Foޝ$qr9_,Wժs[U^Z.T4ST!R&`LmNVPݾ6F計olȘI{IjAHjk2cU=$.JsssꨛW-CDP(LLG(> J(<>ď~ 塡M y'n{Yћ :;R## ݷsŭ1TV5{mֆP[SUAW;R* `m o G<=QU1EM+u٩< +U*4M\W^ $IDs{\wWW8X6[Fo2aD$16`UEF[ssBzKlR`)"%tEXtdate:create2013-06-22T01:32:46+01:00$%tEXtdate:modify2013-06-22T01:32:46+01:00ǘIENDB`puzzles-r9872/icons/lightup-48d4.png0000644000175300017530000000150012161170257016401 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W{IDATXXk0fUn=tP!L*G7 ";/f~NYD6H]sY^@d&~2dە Gh Fd7Eu9te/d"hz-| IJLshߒzX"({W@VOb{7vJtȂ睻oؑdAi?\t{pQK=y-1(]43*(R[RMc3歀@˓~191 1@M8.1 t ̚˝jU0T"c7bHI^H"((D$ƨm -ૃ{~E&dB#"FOkD>C%H)HۇD)N"Do}Vh]NW.l<|ɒ\. GOەbDsxr3꧍Ne5:{&̈5FPE` `5g} 1t,s@t6cޑ"2Ce; 2ʛyXU":^q>^c=)"q3"n^@υLܼrw]ۨ.|XT>Y>w@3Nl,Jq>V,*Ql%tEXtdate:create2013-06-22T01:32:47+01:004t%tEXtdate:modify2013-06-22T01:32:47+01:00E,IENDB`puzzles-r9872/icons/lightup-48d8.png0000644000175300017530000000377712161170256016426 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W:IDATXY]l\G:&Դ)LS)*C[BLhJ!RyA / "/@T ڊ$R֖PZ LVű"!Ҍ-LϾئv(lf3Q$, ûW_j*2ZcRT*$I}UG$=4ϸ7y+Wѯ>y뺍F!2Q$[a !8!ıI'ŶR*zkZ(*E9Yjc1JDK@$)8׼S8߶mn/--醔]KՑcD?m:;XC_"N1fL %JòFCTsbc07sOv@Zc[X@d $Zeh *ߏL: *%T (0u> Aض-Z5cGkzxC9ŲhK{,j> h*bm٪oYJts42wc){(%tEXtdate:create2013-06-22T01:32:46+01:00$%tEXtdate:modify2013-06-22T01:32:46+01:00ǘIENDB`puzzles-r9872/icons/lightup-base.png0000644000175300017530000001102712161170211016623 0ustar simonsimonPNG  IHDR?1bKGDIDATxoLSo^^F\`'EaI[,8ǨF+7˶ g.jd]`YJ٠a0fQx"bg0P[{p Ӟ+>88}s)z?Uh ! 4BC@h ! 4_Bc&㏤mzdXtx`^;ԬFIyGZ,=OGǩJNwїdSf1ٹf//Zj.~ }i@תi^?8NLMnXs)Ϟt:x?nڔI,k|x]c\\}j…~EZZC3(x>… Ï?vZmmfS7I,Y'֖rl~z977sFONx&&&رvAUUIempHMUU܇;o`ZҞ={ļ~;GRVm  Nah3fXo600yod!%Lh{dśf/`CN钒JJJnܸAYbGah@r޽{7|fk~˖-_ׯ_/w=|ǣx󁁁'-(YNC ntvVyGɂ@5deeI oߖ- 3Ҡ]x ]CFh4zK}b_?B[!֬!FN.wuJy[>@n d @۷=|PV###;vO>ܚ5kꫫW;w:u_7{#1d @Yʯee~:MLLΖp======7YY8B tgKJ g桡_͛׻w/-WϬ16Gd455Bi- 566dde?~N2gzcHד@q]ػߛ7o+,,X,fy<ӧ8~F7|vE-+f Y;7v﫬;fҥG_~9Ghza,8OcXza@%M7{,j?AD OO!?@h  w<+V?V?;זVsBп_kgffy`0z222.jF,yz-RSS,XZVVWXKz=o lFj|>АD]_ Lٿwww0ٌ{LX{<ś ͏" |(k;'CXr%y_J'&&DyyiibM73K-:o޷/ӝ-))^~6o^޽E'qqAzzoNNFSSLׂP1''#+yG"zR](.ܵ{{mEl6|>sǏwffD$_;7v﫬;fҥG_~9G(qq$?2:y/ zT/;EC c޿0 x7O S@ F0?N8'P '`>0W\OP{>_63!!!!!!*- Y[UGMMG w3@]\O|h `~XjU:!1 Ab {BHؿ JWFjYj vwST7= ! 4_q6o:߀h233^o0zuuu9s9o0.QTTb0ZVVq׷G7`2JNN7 >fFFcCCCRR_1yP|EEl&߿r̙TWW/YСC>gHK-MDY 0Uqq^Yn5iT+7oW޺uKZP7moLxbiatt4rddDZ9ohr8~=7߿/-$&&W#i" S]|YZX|yxeˤ^ ڤJ.-8qB|&iDe.BNj5 VtB޽[SSGQU(4JKnwJj7l}gdTn fsWWW 2`<| s9`2`999ǎlhh>y֥iL"l٢os|? &f|f|\:o<7= =:c}VGgO K_mjBC@h !|RuO.X@oWX'`O{/\:@`A, g`,o0@7o -7 `,o0@7o ~0hޯ].ݻM&tROOի;߀gFo,7|Co0w8I<BC@h  7uh>+OP;p 4BC@h ! 4BC@hE N IENDB`puzzles-r9872/icons/lightup-ibase.png0000644000175300017530000000327112161170256017007 0ustar simonsimonPNG  IHDRppIc oFFsL pHYsHHFk> vpAgg܊IDATxoLgb;@ra+eb74//` ^ V_oFNHR7e1Ҭ1{qYg&~mYzrzO=>_f #K AaP`AaP`AaP`9lu!3o_LP`AaP`AaP`AaP`AaP`AaP`AaP`AaP`. oD=4xDU$aB>àq M VPSf cr"=|… TY]85LNNJP <OlNV+AVgqn6 Iݿ?7xyl/}_I!+XL?Ν;A8m\ZZj: WZmܻͭIPSkpX)1M Jׯ_sxaa! SR0 M':33 v۸k.n0;~AV}nn(C\.n`2bF#7w^2 g4FޱwxFM BݮJ^턐$'w55Ǐ' YiE"G4˲ +q Q~zqvwxUZ*iYt;w`0WRR2::h<._^}D+-%GjzCCMM˒Gߢ?o޼}#b]ՓAe220gϤNW<88F7}X4X"Έlkۺ&J40#ߺ6A9lOL--k Ҵ0hrrbƤyPBLF**HE*!nw p~-g#|D~}nKk(0 à0(0 à0(0 L;3AaP`A M AaP`AaP`A)Hװm5r ݣ%tEXtdate:create2013-06-22T01:32:09+01:007%tEXtdate:modify2013-06-22T01:32:09+01:00IENDB`puzzles-r9872/icons/lightup-ibase4.png0000644000175300017530000000170612161170256017074 0ustar simonsimonPNG  IHDRppIc oFFsL pHYsHHFk> vpAgg܊IDATxr E/3?/cPYQ!T&3Q@7cESJBpA`( P0 BP( C`( P0 f.?fYD$1?# P0 BP( C`( P0 BP( C`( P0 BP( C`( P0 B݆sP5t!Cy( -Bco?1^rɅ6"R*_D X"F1sns9B9׬(EX);sUCeO\ZjvL rnB.fk~>{[-iٷB#3m AD `If*"!lfWe:;U9RM6e H)"b/kՏ6e]X;&C`8>g7|u+?WQpC`( P0 BP( C`CՎn BP( C`( CoC`( P0 BP( C`U_ '^%tEXtdate:create2013-06-22T01:32:46+01:00$%tEXtdate:modify2013-06-22T01:32:46+01:00ǘIENDB`puzzles-r9872/icons/lightup-web.png0000644000175300017530000002757312161170211016503 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge.IDATx}y|etLfr@B9E9ueQ+.ȋz"^ ..*(r/DD$$!~<3Ð$C>|tQ\Uߪow| ?O؏]xgI7"J "ev"8D6 @$QHii># Tɺ_:*BD4 ϖHny{fEEk+ "9M д )=fSFuCMTV"+l."_"J<<(G0sRm~rcz( c,-{_>;CFs! HB@VL.^tUvC}/&ɑٞ gyiEU6e@~_JN˃N;p'5O۟si?r.mRKMכUUc"y .K۟/g]/ܼ-[tʫkf B&Bi0.WI4` <: 4!$c$%gLXd@ADE(dDL\9֯opW/((P @KD-|oY#^Ũ5:dt],# Rᄈ+#Z9z/1F)I j4IIJ>zm{+ҖtSm[&v;J1w Oe%o4 U /sճ ym&͞=i'NDBy9YJ` gΤcraa-p᪻[ӷ/,`'ˑIځ;vȗ_2}aq.dnmf~i޿i&{?~ܻw .H~rUoБ#̜9QT)ɋ/>w%ŒEM&Bzr^^eY1QmXɏ\9A t]k7ogώBXVƘeYn֯߄ŋ?+Q4AN?=z9r۝܉H _ɲHjYu)v tRȱG_""AnZIF9 ;vصkE]t]wmܸ1D'v,ׯ&DtW>Z۟:5՘))-ףnEDDj:s[4!cڜ9/E %V& m8q޼O8c(1M7nSc&Lد /p9~{oIII=("ra„SbMcDRJsεW^yg/I&R1Fii䓇|#G^էOO7]p֭@NhY݇mzn؟jƌ{F=apBQM< Xl5H @ c wوDfߌ 7vnD%b ,dRYWJ#^rTwɮ9>",))0L2nyzpy>"_놲kCMc!"KH"u"e6@1PB@煂=,y㍷..bDn S^^\գ NQ?T'~w>绬Kv-,6>мE7 n%.I)!c8sS}є)Su6bI`^НHjZBL/#\p Xln"EE-zD6i 1ND{'lYv<ݻnDԳgOHvO`f"&i,C=N=|O؀&BVOY{pt]RZi"97ǓXe%-P&Lf`Z8 S@ǂA$"1ϧDTQQILͩ<\JukZMإˑoYO$Ť7ku@jNތ{ٿjذab p P_;@nPRm8U{Т7MիߎhF"qÆ?hLavjժwygs/cvR!%O*Wlg={~V"AӰM|MӈA 5M{7tYPPٯ"+Ç߷o_FFƄ ƍMEε/7k֝j XݗRiڶm=S!c %oZuܹ 8J//|Μ7nke5I)~ѢEcƌ6l؜9sh _?eΝwU]mi[cYԩ=$6/R|0}zcw; #WUUvK٦ͫӦt5#Zm qO͛vk8h|ӴoߺxG>=iҡ.]2A"do։*T&%&TW(#HIn7{!Z+__ߏtꩻǏʼnPIĤ v*&\PJ`TNǎ=w)<|أЦMi> ],K"r)%I{O1P-6 4?y< Ii4-l8ٯX&1܍09Tm#wCIG^xDjdѾaiZ.WM!cb23+Z=m!L[ߗBa: ^)p :dG~dY)de%ED5)9F[o; [q  O;a -up"D 1-I}POe=t8x$֬Eucs뵱3Gs-13)"?3R`\|XN '"dztg G8>ߧ֦spg'%y.}[RÝ9c Jxa˥+pqqBcHҊOB0Υ< TWׄBDhYWWz rᰔhYh`W%L@B Bh@ 6& I*b:U(M0b\ccC4ǙC2-ZՓHg'\:6$qRJ\ϯ~&7L1LiljM0aEEE۶m{g=O-fEDvzigps/‡~x׏7'ػwӝ.'r]P Gz/ho5U"2eJii3_L0^ $8\4`8MKɤtKi*ӎiu&::uM,+ B!'"81a P6紣93ɘJ:۵\^Y4j38pE0)U@5Ɏ/F`ɎjqqBTA`C*TU=p"" 0G[Zr _\4t荣Fmc%K|7g2!'unZz`8G9̚eeGURױWνzu.*xkUϞ? (!9Ӯ]@ Gsr8t趩S%U3wxM'jdtT*ҀM5f 裑}\̽{w/z KᅮjN4dB0e0M((Оzz7֮}㫯"ۯrr$J?DB"QGV5 1k쒔,T@ PII,ч?oS`N#Q@F,Q,KУG-^?Ri#rB;;cZSzشLMc^ݲI LK;^dRO 5uiqȼwÑKqw@H5H$]\kJ'c P0w;waw@d8|-mzeۻHJgt74 Esa cs+D7PJz׭[}; +~wa'>DԢZGf2 \۵ۆXck}[۷۪^e\)rj1i p8TSSQ{-$0熣 uobxK~(<_)tNJ.)s?HS[W>IwɒB4ݻw\:9 ;Svބ)(UVFPJN$P l93A ;c`RX)Ur ̊ <h ta !F n.)zr1Ϋ.ÆZfaHDQƤQĘB1Z@ @KI!@Xy7 nϷ/#$ #3K>FbHQdmc,a<2I /{A*(˂Mvɦ׋M-I{tP͟7^)Dio.}N7o=\$5rrr.Ҏ;VVVΘ,\-4<"d֫vvRUcy(Ο?SNpؑ17h"sh9D BwuҲL֍Nk]wK[ldȖew}۷o߶may4͉7 էxfʥGCS_|x<!o=?C@<'$S !ypQ$¶m{ E 5.ŶN1;EuubOO)w'H؞ε3>R<"d rrj~ᰲ${.?Ӕޯ6D  aYVeeUԃh-]ZvMZH#ږ- ƏhdA1Ύ%5MA.ҜY\.KPߜ<"$b#Fӧ?7r.>o+Vl\\Xp6Hv\jU 33СC6l۷oazȋP]]uBm~ˏ|GH!C?g2n2)5%TFVDƘr#;&h!c 3x#={uB ~omm0rvdsEL ּDRQ!hD\YY# ײYdv6syj\"ꤪ\^F zbPE|'"pblRUJOo 4^޽pZ+|@ӔlnB/0k/THӳc2Np hYSuzSiYZI͘n15җ1?>88C"* Q^oZ8ki2)Qar Gbg@+''cAHJ@ƀE@9!$س.sd.TQUA6\V+ȊJD@&PHA9۷[d7].tM%~6 49gI3@ g^u}*&符ƿe֭{r51(wWXXd?5Nyٛoݾ0;}hYv1'+7x1adfejґR5L(,w\u-ۥs8Ds:MʬV#M{z/*b%99))PhtE-Z8NUUrcj5V/@Da PeD M!!ȴ,"(%T,F@D t}NjGxՆs`nй3BH b(蕙N|P[D#8uDp܈fx", PGޗOs\1#+vW@X{@;pȐ/"hp-BB$LTu.ӿAD8', H {'۵kgξ[~w W)#?Mڵ{yԩȲ,˲k'|*+3So \8EN=?+(...],Go &//80cq~JM8e*liR[oj@=S*Ns5~ ]pѰvG;cǎcǎk.]r- rW^0` ,˲CgAYgeZ$HrcH^yb%,0` 8,: ~+mչi#3Ƃo.C!n!XxСׯu}Ֆe 6{9Tw<Al`vشE[kvJw}aNR^x?ڵc^4R—_Bl<267n4j4x^VZL BVZu\)RHK8QY0VyRBD,i Cy9ԣAӀs̴[jt"bv\._X0;p8}Lʉɾ+BX$#D6 ;Q]yf׶m[!Du]ߴiۘ!0 9L``}iZHjSLS| "ajӦ B}ad p]A!xcBٳg_r%3f̘>}<=\ȩھ}D8p`vv p30uы@#:u!>*.>|)-ZٔߖhV>?n(*:0ѻչQVr~zȗ %m촔n…]w߿rm۶;vժU pyUWM2%:vX\\\VW۶Ios%ڈΝ a"D0WFU)^;wN|-$"?W^{k׮>gϞ׿hgT'x|>߃=:QdT/P4='~z:c >j?מxgG6_h3:@|yGXu,B!#,j5W#z*pfw޶e>)#G{˂M|3kP_}‰4L@<Ux'J'ZuH( s fDD&Ʌ g͝!!=jTŔDK!tPhCbqKX,͞R,~/3LY+㜳,!R1Z!)URZ+{a@V(>dS1ٷ3GQԕbm{!Xi#{dD!axL'8I5^o+\# e?a}<U†0$ gvב_a,]y~X*J]xtg!c# ڶv V8aD\)sϐ!0Pw-BufffF!ZUU_}]\=jTg !Ino>}{(2+[nbh# $,gB^s^UuhsADժUKt B]WA\*)㩬H@ (5 j5[h4-s!90͛s0?g4c? Z_ܙZ(L2MH!,,tJDiƳKRhGUHc[t&bRJ2]*\ͩXi:'%jp3jcBT#!b'TIj*هH(mnIT8NLRPLR@~ 8"5k/ߘG yfoܲeKqqiZhNNn j֣P[f<>{f=Dl *_λ{ԩڮ]~޽{>E٧bv7 ->߷NMȲĈgJKdYV^nf~oiYiw~^˻;׭[wu_2dȐE+%0CW_cڵйsVQ3! ձ+~9 P(T]]3B!l(zcǎ-ZزeQ׮]+ׯߢE^Y '@|E@P,q 8"irmn)2)QڬG j90ڈ%:q@RxHHi53TyMӔ|MMJ1־4]KOO4%^/ DD[!,^F=wO?]ۗ1~zhڥ\VV' En3!缺zЯby w9j0ģi^"ƫi~^)%СC3fx_zݻws=˖-O*vaY¿4i*f*Bu]SEW41>+}c=VQQqM71b̙<}ӛ};OK2?gϞ={Zm̏F6uOhm;HG9 v_oZx !]P2c qWvDтmrBn9꺮pк;6H)5MGdei텊1L!|hyqM3s #rsMz2|dMTGD8:qΏ MӉf "KDT^^P n;YUVV;ڇBP(^GUgz;o L`UUU2=ԙjcg>lYjL:*5܍*~Rc"^{)0Q3_]o\.#ͺ|^ vpAg\ƭIDAT(5[OTW׾paH h4)__/_5kG/iZ[m(jc-}콗-8ksNBagisj5zFctfGkm׻6-+WXgDޯ=zdpp1kL1::o߾uØZ1wRBFADY?,:<6eY><3sZpo_!SZ)26PhML;w6IDfL{ٟ9""i$Iqb`[R " yEQZY~Ye /U*ZGQ$ vpAg\ƭIDAT(eR St0JGQIؤȕ<>xHjH$wW.48sZk~$if3T;I ЦbfqPlySz.t g%I@fto:KӸԣn PUJa鉲XՈnd!m IRE2şɖU..m?Ż1>_*뀄f#%tEXtdate:create2013-06-22T01:32:53+01:00 tP%tEXtdate:modify2013-06-22T01:32:53+01:00})IENDB`puzzles-r9872/icons/loopy-16d8.png0000644000175300017530000000150712161170264016073 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(5RNAUuۮnf,a+QG3ghԸ 2F@`tE\ts{%!DEݮܷRJ{1ps ͋R󖖾TCGRI`<$+Rkm 67֕J'wneN5H)Vkv_|2Ƭ~ZooogYeYR9?ndd ؽwϞ0 2F&8Ơbff…s'Cw?|ppADQAdYeZ%tEXtdate:create2013-06-22T01:32:52+01:00[%tEXtdate:modify2013-06-22T01:32:52+01:00^IENDB`puzzles-r9872/icons/loopy-32d24.png0000644000175300017530000000344112161170263016145 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg \IDATHǥV\U?ys;w}!ivH ݵb*m"Fv@iH40hH`ojKUlwΝ{ggvvwAO&̜s]'PJsdM "c q!ܺ c~'RkRr)k8IݖepJrf_p; 8x3,9?s,6oڨ!ݼyҫ6m9!ċ/\uƘF#PJRz[1A@DBj6xzjCnw劈OIι!Dcq9Bȣ1*QQ~UI) H={&.֘j0rb ߽q?slݶv\!Z|)=l6sڪ,{:Is܍sss_ʮ749J'_k䷟ %b=!u}(6o$$ֶ`0*rU]}ZvmbE_ͷ<)n{׿-[X7/jcOz9o+֌K]D #IjsN|BJ)RJ922s1!iqXe Rs1ƽVz=vCpΝ=gqclի{=Ji)UM(ܳ۲e GD.یu@$I|Q ܹKZ ^ޝ8j6R)J)GD t)cֺΞ6v5te Lk$iRe DsdBHeFR6sزu:yK9B _S'$^E@Ju%͇Ց$qΑ%a/ejyrO~EQev kkBY;lfֈ94 _TUt04ECq[V CZ4M85jbq$9@(AƤX7pΜ=6VkZ w7kH)ՄH8w>Noܰ\ ;zZǽ.ʲ73=m֪!/w1awf\433SeQ H)uk>"k-SC=dH)RRJk-Eq}}fĉ'R,aGs6yw:y߯Z+ X9cLQ ⒃_RJ)ACJ敷A:c;j z!V\`̮s(?ȑ,z_c%-"rKb p+7@J$i|;x:VoιUYkH>0L##Xs>>$ƚ0(kV4.׮]۵Ji2D񳟿tw޹noƘsA\  '&Zg]뒄! "- %tEXtdate:create2013-06-22T01:32:51+01:00A4%tEXtdate:modify2013-06-22T01:32:51+01:00IENDB`puzzles-r9872/icons/loopy-32d4.png0000644000175300017530000000162312161170264016064 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǍVݙ =do!DN< ;T>pz[q9#tq:X559TEDLU8jC@.if溝P7Z+zf__!Io <9n 2Z[)%B.GD_뿼}<<~vRU|$z U蠚qmf?.<_Ckm FljCXi)lo.8p |5齋_+'e2 TCe獩sTLy\ ";raL t)Se=q{NіD49eBnD- :T>DsZ"WmNNx1< <}V I3&P[fouNQ^ `y6sx55)z$s} ffLy.:0:LͰv3׫|||нJ>uګB(`a4sq#HJZ#afX*׼RUVԁRśjD)d G톻ͺ#/7~+>ĖPnZ)/RҭC(%tEXtdate:create2013-06-22T01:32:52+01:00[%tEXtdate:modify2013-06-22T01:32:52+01:00^IENDB`puzzles-r9872/icons/loopy-32d8.png0000644000175300017530000000352612161170263016073 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHuVMG~=3^79Hˮ;fwX &C,ņ8"@ "[J. qOD#Sq >$`N#O+ofaU]}{6?sD;؝V9kKRJ0Hy L; !AX,ZkBXkcDZVJ)IҹOl6ݏ+IjvT(1ԛoZN+PJ%I߽O "g8v82}[])Ǜ}7K"26cy{'$}0ZS9Bs$w!YAp]3y )իIdii1cG[D䎔ę$I-zo?pǴA!t:8ys1yI$|ڴgCH;ZKi !14\B4MժR @֢fzzZ)Iv !֢1X°4vxN Jѩ ,OӔ'?}}nnDJAܸqG֒C+ss{!DjB})I} V{q5zh4D@]ӻ}sq~P.@k= 8FaiJ 2APT,;cP4VU؞Y9X@H=ԕN(g'@zG[V0 i" 2g`չRt; /EQgaa>I;9.Qwk-IoH#rzDZhk!s Z 8cX͓]KHQnϟۗ]u||Vhb^~j$usZ{XYmu:D$"M޺$a}v%tEXtdate:create2013-06-22T01:32:51+01:00A4%tEXtdate:modify2013-06-22T01:32:51+01:00IENDB`puzzles-r9872/icons/loopy-48d24.png0000644000175300017530000000451012161170262016151 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATX͘}T9}3;3E]Eb6%6|.ED+*WL6jmZj` X?mLk&v]4Q)4@0;;3{O;ogw͂k'νs=s.|I?BDcW|T1""Dn h4jF#רf&p9Ǣ#"*6q]yߥ˗ PwyŋFT{_#뺮+.WJ;8g|F?qˊKvקiyÇm޶d#{*vhݦmaFD`6K4"f{h‡>eZ)S]Ë=9/6u2sTfeDdrx."QJIDs-)19`*LӘvW_~źObZ#c"R)۶=5/Iҁ)'3FDh0xu]eR,Su""eUT}j 8+L===x2Hif2@\>4^K :~ڶl4Vhll[ƴL!c y^ |jbI=!dEE;ө~spuS׵] pB>ivm[7p5xi#] 3f|d^heID$1XLHD@xc:o6o}j[+++s9 '*O]wu3g"&իV~¿ׯ{LmaIr9zsnO$}}}r++2M7W RSvg2eKk"P _O-aaƘJ9N<[JKdJV7\[B_UHMMt]WUGֶ͖K.{5M4Mm1"b \`$% \qdn 6;Pyۊv,Cֶ-[,StIT("5,Lhi]jE&)5D8~\'-X07j4⌢q{t]_hY3STQSS… 8ef\]=~eض]yokˊS%Ҳٳfnذ'._ljl&D%ZMJMJJ{zzzR3cɏP`FjrDTUa~QSp8ؚZ4)F2ѢZt]?q'NYbX!b:~|={_3{VOO"FG`/?݉DbXtC=OovĉgΜS 3l۹;ya\]-Sk"CK2SR\UQ OM6p7g,?#@ζϞ='IWq*-˪n)$@իWP bB;ٳ6=y5ӴR+1DL$nz U P3źC cN3DzBj૱H :+*ejA"j#b>-mB5N6U5w^iGJD=vtmIuP 7Tirt%tEXtdate:create2013-06-22T01:32:50+01:00=J%tEXtdate:modify2013-06-22T01:32:50+01:00L<IENDB`puzzles-r9872/icons/loopy-48d4.png0000644000175300017530000000167012161170263016074 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXýYQ ľ,djDlw0"DK1F_"z33"2jf !0sNDD9FվGX"DdF9q#u׈hܯЕvޞ0 hN02.+""!L@m*ۏ^3կ qten hR2ܩE(;bo#@9n WѤ[i: > 8NA`!w=aqg3囉KnN8Dl mT!8ZKBù :?YLBЊ GfaΪ9;h> ( 9ʘŚ5lD2U HKMDttкޘ4U6*w$8PD(i5gD9l7- ޫ"S8JC-S~f2eK1݌n9՝P~^OEԠACCOȓ,HSvp@%<'\jkRPj yu)XO Dݺ/* m*§^޶( ּzYJu)m;qгVͶ6KOWFJ"A&&)hW;vByƉvfW +&reaܲr9-wUߛϼңt־]U ru %tEXtdate:create2013-06-22T01:32:51+01:00A4%tEXtdate:modify2013-06-22T01:32:51+01:00IENDB`puzzles-r9872/icons/loopy-48d8.png0000644000175300017530000000540712161170262016101 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W BIDATXY{T?{* ("AqҪI5(XئiFۤ1KȺ ,MjZB"2ٙǽ7sYɹ~;=Y٧ soRsMsǎb <r11SJ !!J)Ww6 ОӟnvB!h:uڊtM۵{'NpεQB)1FJVJ]1=ϛk_~Ł "!D(R!0gV*0 -E/Zxz<+ BZA*1Ad]RryBk qB1[϶0$Y!c!yo~'N>#3gΌ|vz*!1`e1F;GDuA6}?ήj/QѣlJ) !1SY>&ctdsS}O%jG!GN: SkM)r1OB>/u`GƎBn!d ._mj q,ٻoOzJcL\CB0Υjuxx}+a؞T\}G'k~֏1NR]c y8=ߪV݃Cwq;Rm^#͚u\*ըc!Do勵xc'^19B N-\fÅBƦ1֜Rl=0lnZ`ᥗ~5 Nv1pw qK!*Jjv^<J.JCg,|k!L@ #\JI(du ׿ЪRs'ݽU-GmdH`xߞ`%rDky|(#~5*h3K+{z@fFvYmJG^Eyh9Zi65NF2`7hx}o]ɤ7ozyxx;MLTk@H5+`I3) kNS2v]̖pW\qG=f끔ҊI.s7p5 :ZQJ͞{/)%f єu(B\^Zm$$eK)PťRv`=ZIf.*p}~QR<1'6"r1f/8!sc9D-;';z̙3T*>ZKqY\wX!NSF%j%MT3w%zV{=ppOXmЉ&j3f a0 ^9Rr力nVm/{p[!CjZ!-[vϢ * ح6Cر TiQxvoo7~hUPJQFqV,~4Fn_67E  :RZ)-ADZTԎ2!Vp(V3\22m*f0\]Q VBIwg{nK" dI놨&!c{f@B-h.뗎{06@~H_?w}d 2!2!Eo H1x-d{_O/9--O&?O,|C=@z}@p7^+o g ָH%Ȁ{ -2C*']g(Y \@;dC@;dC@;dC@;w֟)w)x,x@J[S\,5]_뷯;iMpɳI ^?~xv h vh vhyh?8q"gffN]^~C 5㌲`>dD B斖֚WqQp ̝[qSYu(==T(=}7qN‚yqO&:^kl]f2, "pr<"Х3aµ,_ظS$ ],'gʪ+N'B}}L=.cz{=C6HDw:0 دcǎ-++]Dst`>Kz-͸|`ќwkٳΝ <[UMD%9N' }/##9N'_o:EuvƊ9'N9r1sssyxaÎ:T u7UA;dC@;dC@;dC@X;說;Ǥַ<Kp>{L)!\4I~Q$> I~]K9 __z}77 !2!2!cQ~Ð H_ax-d`? 6;s]DTf]/7  H_a M| A|7#}> \o 2`M|7  }>Xlf̐{иv =2 H_avh vh vh v/屃ߝ \:}?=77> O ,kƎxawzno`Y|:__|71@糵 B|~<; dC@;dC@;dcw}~ѻ%Q@Qaҗ^hY|$~`G^z}T}ocZ.\""c"ѿߞeW̜)V8w}'};;w}a@oO'zvӆh=w}a;toQOQ+sG~{C|>O_yiX207n;ٳgϝ;xJ;R>{,##c 6q8a *I{d$vuvƊ9'N9r1sssyxaÎ:e%e%=?`yotwͭanwx-%4BHύiA{{;뻳{s701@||6=r=; 2!2!21۟{~7OD}Ѹkwoo1%,?CBP(:j??%?5RO=?'ܜg?as؟{~7OD?0v.1BM~_飘'p̀'p0f'p/' 屃|g `?` w}oWBqwg}=Li<z'{2!2!2ڙ7 /2M&KBdtx:!2!2!ze?$ބuC@;dC@;dC@;dKlS_ǝ?M|Է \>LBߣ}}cYM  t&>1G}0XLUo)LQOl(h vh vh vh vh vh vhʜRIENDB`puzzles-r9872/icons/loopy-ibase.png0000644000175300017530000000325012161170262016467 0ustar simonsimonPNG  IHDRqqmۇ pHYsHHFk> vpAg` >nIDATxmLSWǟU$ %ʋ1ӑmQa1u$FİdYٖ% Q| N2aDE'/.0x..2/_H ЬaB/}ι>O'7rnz8OUTn!S|)>r9ŇCN!S|)>NVk!+>19ӐeZ[*6eeww $U';;pk$%IHd6løͽ|2t˻Hׅ-mEױo2utg:8üG=¡sk_p\ϮNl+[q7F60!S|)>r=,>^|ֱ7ظsQ Un:884oVßǝ;݂Vp^INZ ==f O(> 8 CWcQAh'4Fi/eY{׳Ȉ}eF8 {""wz lʾeDG/8[i,8@GMnp-;gmxxU*5H܆ӌPxHz&&&`dfl@bxS!!=45ߘz=J:Qr,w.&2 @ݽB}zw?KCN!S|)>r_ddZʟɇ@bb+[p{߻|{߻|3>g>r9ŇCNa?xo%.z-9~Ak[{k[;V|+ST]tֺϷ:Vr:ȷ]>Y[ɩk<ɷSxo3x7N= <|JhCN!S|)>rwO]_^7򡬑?А9o?|(kO}#ʺyg}^r7kx*r9ŇCNa>ʑlimphH%Qu[̟?_)3өMEQŶcJO9L&&odXxj(T_^QөI$9qXJsDG/9\T|ys`ඉ0to-B왤PzL: JMM9vX,}Jϗ}1T7FyE߿-|~Ssd[|&봢`Ȍ_ 0&&m[j//Sz<`NSSSSbr9G^ ɷzi5^J@iQ-%%tEXtdate:create2013-06-22T01:32:09+01:007%tEXtdate:modify2013-06-22T01:32:09+01:00IENDB`puzzles-r9872/icons/loopy-ibase4.png0000644000175300017530000000152212161170262016553 0ustar simonsimonPNG  IHDRqqmۇ pHYsHHFk> vpAg` >nIDATxQv0I_h(5Y"̗Kd1Y <ϓ G<:)NytʣSG<:)"j=?zPд.?>Nyt]mNG'iϣSG<: t:sz.#?CT}yrM9gקu |Oru𜿷ij"hN^3]~19f/{S~{_fxu5\v#c;&x<]gy eJ4n/*LASZRπ {')NytʣSYM]<:-G :.kG<:)NytʣmtҪ龯N9vV!AeŚ'R9D}NT$Wb)NytʣS蔧ޞ{??tbK#%tEXtdate:create2013-06-22T01:32:50+01:00=J%tEXtdate:modify2013-06-22T01:32:50+01:00L<IENDB`puzzles-r9872/icons/loopy-web.png0000644000175300017530000003273112161170211016161 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge5IDATx]w|>3oݖJB#b*(ұ 6TzHT 4,t}|Ldg@8?~ΞyysEo|;pQ6!c1V m(rO`UU5M1@(oWU,BQ]-˪@[M$I2 *ޔ1$:!4hQUUUU p'#Kp8RY(QJ+HeӲ?QZ$aJM)cU)`c`С?SR*l G.R&Җ1ac>v͚1SHJO?~DڵD_!΁0BX1壤jWG.ش~d5#G{'_1J޶1<@3ͩ;8FSUe˓fΜծmk:v0 c|r[JJ8 d &rzzƂׯwC>Rv-[t M50K|۠2&1i꯻~{6mdY"'RƘ5]v~T`N؎ /?{.6h/(rBu+''Gl6|n:ap2B4ժ5|e3jz0hm8!aDf!9l7:ufؐ$ /^={sWjT۱K LNxƍ @˘k׮[W#i޼iZX`(3s,wy1R .۷OZ5E cqj۶N|Q+88IL_v#z)':q 8+++֬YAG|DF^ F@0#t͚E 8qcZU|x$UI`zG{t܉'EZb(㨪jYVTÁI;"a`fffVVV,+É*Gx[IE&dff2 HU`0J1Fl 4-BDZd 0}Gc*&|{+ՖRZq)#0`dYeY%DeS$I61 YQJ)eED)cS~e9Bud+~mp\X¦i7lM|^EQ1F!ێ.u6A(?Ҷ8FムR/?`UWw1c&#1I}~^qUɓE{⢔('Oλ !]tzesw-!IiZϞܽ[^%ُREU~v횱1 .9b$Ig"T͏KZK[5^˲ .YDt>|$11=~dM;.xDif#G-KZjv~TxEQaRr9o^E,iFǏ?qdjժ7n_dÇ qel ݺv1 A<c?p}T߭;oᘻRU5 w?(}cq )7CLw?{'f3{P(dB9ӻW˲iZ>B?!ӴRR'2A`0/2x kIƈRzt{?(bpqҋ""G{K :|pYm0$8SisG"4Jiѷ-oɦ3n[4I†x1CqD L#NRzccb|^U_v$a(21u,hGIĄD@`6cT<*.-4m۶k͍֮7,!`J{f;3|M-!I0 -?*SyݻQëTB(BgKKkpӍ}yWP6l\͆7JKux\eY6t$IQ!m[_[P%33{볳7nNj t$Ϛ=w 1!KXp`a'Kר^4#:ϷqaoM( se )ƃYYلz feg#^Jijj3O=%lYVTw$BH׮]:ucl$E1xȥhAe"P2V|]*sٶ1۶sssu]u8E)`0pI(|*XDeD@)JiWc,2o:P}dY$dYVUY(2PP.K!(Jˎ$ICme$I|f#ngؙJ"|'ND׮YSwGˋԬYC&_˲<8NjqqqQ&$x7nǟ:|(!+.ckUV7'~nt]13:bC~;z1ٳ϶O k֨q饭cՅ_ P(dYy`ݿ_ʒ$i#o}vkIGl3yԏ>Z2jp 3P)ฏ>d3pX0&!!~Οժ|Mَh ~L|^ԹPRI{Ƈ2++k䨻X;3l߮mFF,p\իqGޞ1R=9cO>us>5dSNUfA5!!$&Ʒo7n ostf_#1! "ϲCnuk4c6 ˲vӬiSA0?=ΛpoޭWib"' U9y$:k RW٦MY8N$I|G1`\>^-wt-_M>jX«V}qok4_{ΟUR(-ᷛuR(Q { }f$)]v|1mɒ۵MII.gqwƲj:ߴy˰!G%\cg5 "S! \.W,\ԹӵO.|- C$I^=?!a7^-`Q{ݻ#U5!{w냅K%jٲa%VH>{.K,С]Ք'#-AJyOJQ:cQ18iNE*焄vġ ,:8)ƠfXS#hBdFu`<)xb eieل5i ?5ATehrƒ"DIQb)7s!Nɲ(,:irD);t!3@2*bXQ8 BHFH5!B4όsRD\eheR@W UU1ƨVA^WQ۶@y!be9ۉ4*&dvvFYÆ/)))z6!])߄e("6#TUIK㔃--K/@DmJ+`+=rO1pVP.B"A!!!$zjax<ղl2Fh_@ >$ (%9-1 fQ4v#g JgEB‚R6/Bz eBw$&$(RK4%q#G^ߧwJhJNOO@R4 ߏwWRR- $Tv#Hu !Wc"IT0J,[ٷozuר8H( nn+cMTEȀɲ|}ԩS{y DB=/:aPbbc!F$I]>wޜY\2??1N)S9''gK)S-K,O>BI_[  ǎ$=iii-'BI{`{w*Y118> Eޱ);zX ÈP0FG$c`p8v={088ao@`0W,YzW^~qF rYK E!lY[o}q.ʨȄ˯ޛ5>[hf۶2fϝssפq0c"8yiӛ6mңG7 ..Vvo}uze=:# +V`-뱯w'opuM3PXxa!nrm9'cD׶vZ]:w5-l qRbccI xgҺu봾(mso۶iIU@zlAc(%M(!zM'0F(MڥΕWד8:"vc4j!VfP^^4"r'Qsnb߯kb*P6) ^6z¡ 911% 吖p T+FIT.sS%-5.@DAY1#+l!Cg"B#ILe  ٶm۶i(T,\.q,RYU+{mJjB{CGG$O>w seWm;ر;RIMK=v,JL~3?xű?1e9++qȫ_1;85{$j$)/ߵK޽zc)US>9 2c hР:SVͭ۶oݶ]bzAB(~I_};ճG~9ϙ&*K,\tӍ}}u3y?Կ{{97!B(!!a 6~7y程"_xOzn<Ay z y߂4M@me2a\>]Q#˒a` A >Cv\A04r]eYj*> :QepR4gSJ)B(&ƧkDE%;UV}2bIN>ϳms!cO?$Iڶ)W9qؘk:vLH "囔T啉͚5GQU-/{7#vygzz0{1=|RS\3w[jG^\hOg^ҲŻ3g$$ğDc1W_}ռv !WڇU{c~ròJMӪ[iSo۴UUObbb*'c ;tlpvl?عuŁ@{.˗Y*e?c^7b܀cs] %#y˽ְq4qTMeD,toƲlJ*eȢqVPȇdY>}:NoM:X04Ji^^W&u]bC-o Æ(rp!W?v}ÕW^ޮ]Q֭[<*Fa&PcK/Ov"OEz܇(֮UFV(qJi\\ Hmy{:5&$$&&L}cG:thgYfn})]WIR{AwfMTRR׻sA| ꧦk^dVÆ V|򣏗(zM=:gs|شyK||e+.\eӷ,}\ ^ϣ=iF\ꕡ yF˥oٲ<=b䝶mִ dgەC8 ut5;"7~/v^#Bd)77W=w1ו] P?G0gl? /cq%{ ]G)ߨ^)az2yi\9i\n[Q*z(*ʭ KƇe>Q!12@UO!*ؐ1N\.$Ƙ#›#E1wO 1a9ğLdQ)ⅉҌ傲]%ƪe)cY1ilD 4U$/driz퇆 A>ciOn2хE c,ɪ͓JyyG8Ъ%ժB0Oy}S;C!*WRt]8ymۯx}eB +4=fաCyINƘʓ޽{nW4?"v1:[ݫ{Æie d!۶ !6nؾ]P)&Y-Z ;mPEQ"EgwUU8iwjՆ{lPUqA^5\ t g-[ÆG}:|~7A^^d31|'NmvJP -[ZVQN05׾>tת0" cLUaϿ<&j79Y(TF)u<۩fs#x"s110S>X{njy:6,UzOФI^=u;99Cn>l竾9V-;fxqp8֌,<!/VjZqf̘~sWmgng3يj32&U6So#$SH0fT˶%,8'N R~{#6III>uw5k6Cb5)Bn?&>~eea,R{sd9%z,3͚57Je>z=!J˘#IUY~=ɏl$B!0,0B.W69"' )chFn;))v@hɧӫkꚵ_6o>߮f͚ $TIz'&U|VlBqߜ [+ۮz}l4 <2gv>tب^zԫ[7>WnJ˥?xp;rrr~cw}xؘ^r !$6&f7߶o%Ko]̋a6Mq;iYv0zfY6!woN'3 ß@1]o!쌦0`;i 橄 $$[oӷ =FQx ch`?M/u+SRn9 B!@)@)tb@HQ)˒T?`4 /p!T= ?;d)KR;vTEA(QU&&R B t&˦- Ǟ΄Fzu 0G?[Bh(1kEq5-K~pNu}4 IҲ+z(Oܝ<-u[V5ӞCriF£GNQ6HXII$!!~|AԫW̢3W ƒB ,$1 +Ça8#'M5TU5 `T қnR "Mbձ$)#!d &ؖW]^z)n!QcTQQ#BX czj(j, G c995N!, Cߟɋ.E+w%,"Z))bˊkSrkL 1B$"R% ϹP48SJ)(ieK1HVJ-;J'Jdʘua1zzzѣGWv"G*Dž47Mns-s˭MXV(c=͚6@&,%$5r8 tL\ K 丸' Bw 5zn%]+-M HK! =}>o%\8NB|7jԤ={?^a_6&&rqIrrsg4$A &,! :%%zj5!ߑJdŠ)‚,4K@12W)R8՟Ó \ v1!11r8>q1QO7cSJ0^qq10,+ , D({)IZ"aMS$UI"qv6b zm%i^E%ѶqEfB+Cȥَ.i ¤%$*fYi^I|;"PBB㐤*4Mrgg0|zE# cdۙ2僦M)N;o7w~j>@1)c ːyݻ讝SCHNV׬9SФɯ͋E6;!j^yjx/G Bt1Mv|aKV蘻j.Qvn]~-;uС?;^U;&7AFP>j@ pECZ3xYa|C}jIK!C|>Xq 9}~76|8a,+%%g_|o7k&¶m-C!}R=! N]Wog˻tnӤqU4M:HUYR&+88h '@:RȃfAz^$> R5iv񫯿m II C:R*o_o˲4h@EQ,4Mo+V|޳gd˲8i eٲZjqn  D)9xOhjj=˒AAIر.WzUE99G@ѣ7$8v狏iYYpzFFF1%Ɛ'"ի V${єR%Θ UU5 7!K@IQa!$knY1R Id@QkNm3^QUU49?UUoO0]T"! ^kiG_-};> ҿj{_/I[l3F%tEXtdate:create2013-06-22T01:32:09+01:007%tEXtdate:modify2013-06-22T01:32:09+01:00IENDB`puzzles-r9872/icons/magnets-16d24.png0000644000175300017530000000160712161170270016443 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(5oY9j њRm]H@7ҸpB"ܐ߉fɒވUE[m|Kvj;g{Dž ?cXd@d fxЏ90OinL^߽xYng7;+ ?].hgm_UfT.M 64D `V h2 )da CBTTE4oʊTG .Xi5FMC!~۶/1vacX2%@Yv!0щNkb|wV~λm[cZ P T~׳7y,A:y} زsϟg1reRG vpAg\ƭIDAT(eY!D_uԽ,=Kմ@z~c ]7uY4K+;>[7/`lAL#ԡwaJ]vD\KCGSaF !H$H$% jaw-:  &X`;٘yRHdZNG#l_k":A(+d 3V&c5#WffyڻUX6^+3<7b[%tEXtdate:create2013-06-22T01:32:56+01:00^L%tEXtdate:modify2013-06-22T01:32:56+01:00/IENDB`puzzles-r9872/icons/magnets-16d8.png0000644000175300017530000000160712161170270016365 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(=Mh\U{9wf2McѶ)PӤLF#R ZE(QJ .\誥](7݋Qu% A[cm҄|Hy_-,9!^,ܕ@ZJW>/g2W8WVN\x~sOcL&/R[*WECZmkXխf&"%aO==IOP>gP3ϞJ)є͆Ż~wOK[soGGG]Yh1JemU:HҥM˶?p֢C[I@RI'LO>^jto.3e5N!ދZדj\F+:x|znuh$vvHGa'x&r[Pw؉3z4}hƢIdi^CnܔgN #sSOnWCm3!M?eW@2Rzmc¹KF)|1qvvebG }DtȽTʧW[1~޾U WOM8=}ڵ륝Иbg)XߐU.?U+;kk>BD*BphZۿȘG3)Ʊflཧyc 2 `-@(IeAab#%tEXtdate:create2013-06-22T01:32:56+01:00^L%tEXtdate:modify2013-06-22T01:32:56+01:00/IENDB`puzzles-r9872/icons/magnets-32d24.png0000644000175300017530000000367412161170267016455 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǥVkW~s|;envMHKKh*-ZS~11#b Ū,{k]ݡ3;}ߜfa'|g{ss "n6f"4QD,<ZAERXnvt>~H 4v I8G}O,Q Xtu0|Ǿ3b.91Zk_^޻ag+DDt @DP ^~1)6^Q8& &yjLA31f}ndDz@VXj:PIu @^sAXR{nVp,"?.Y\/hkk[f]RQ!6) p;ԝwR8![t:!x^+$X<\>}FtPz@kWntRoQ&ZqQMNZkyl6k!F(hk0}'ND>0 xS̘1Fe'8VQf!W[ i$7$` uB@0d2ْn< 1UTpa"RP¶[raΐ6dVT9@bcm{s9y /i?^-[1+ceSW\e8`:j(I DAo7P R.UFRK1֬]֮cr2OD@4)_yh[Ӭ5U8oŲVvB0e`h<Dah;)TXܱlGvwd)@ٖ4T>0zVتzO=I-͒˥ Dۇs(HiZ?#hb DTo:eޮcqhR Os_h֬pעGwSȸM=^8q1ȐcogL8+V֤jAorazrJbqt8)s NxtY7I{}fttlv=_ߺMiZjNu"9?d6?=xed|\{Yl٫- dZ2G=+$Q~nq|ob;NR&~qhcoӴ|)[/|U[`Z @ _s$VR>Fgy|ǿ1{ xnhA 3iֺF'eO.Kf ?}?V#0#h 6Kǎ}!Q@T(JYZ} ( {諛7P_1---Fk4vXԸLR(jnn}On(1sTJ j/4fY#R4)*7:ʏ;%tEXtdate:create2013-06-22T01:32:55+01:00oe'%tEXtdate:modify2013-06-22T01:32:55+01:00ݛIENDB`puzzles-r9872/icons/magnets-32d4.png0000644000175300017530000000166012161170270016356 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǍV[ W|3'c?̫tf. 6I}}G 5xcWvmI^$I楸3Tٴ:[Dܷ(nj*$I|iZk'tH@%q-IuH82͔Jq]./Fgf$wޜ^"WsDh2IQyAK*9u.J}KH fP_Lgwǵ}-my)rn.W~iGFQD<JKRDYT/LGJ2T?t$but2RALMr{ÞE .Dz^tN֎8AӃ4FQ7 $Ȓn({U9&E#ghT2E U3cg/;߇+t#y|rŮSP6]K"gYzN9̯[cIhm5Hg202iad943X@C6OU`e$׉Lcɾh Jk<%}$'㭽? $]xmeZ .uL>͚k'˹&+G=ɬኙAf՗i)o˯@)OYWwփL?V0?DskږJ/l]{Y壤VKۗO0Op%_]s #/sJ GO9AA%tEXtdate:create2013-06-22T01:32:56+01:00^L%tEXtdate:modify2013-06-22T01:32:56+01:00/IENDB`puzzles-r9872/icons/magnets-32d8.png0000644000175300017530000000345212161170267016371 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg eIDATHǍV{\e}WJ4Rv@EM|P !5DD*фH Q̢(ADۚBAݱݾJ{g;{ǽsnw?fι;;<p_-pN?bqB A/|):lq\6mǮRPv9dBWaN8bw+NK)X](k[Vydfz--9~e1666 %CHdѡ A TX@\bi@655rPJi!*zܴdh0O8Ik&C+:n←RXRKԭr0DpWV Sv DkMZf-0],&OR]h u?w}R(%I0n NV=t|yKO%[oԮ?;_\Y1w ͬDpt$pM*wsf;~zA1OLu)wnzPJtqp ۣŎUF_;OӾK~UWW]ioMLV&k `|%N0L˪_<;3GsMm_1 b\]7X]h2^heԧx,3Qlj8:ufLH5ڎvQy;aY܁fv7v- b,̕H O+%fIƛR4^-Yٟw=:[+vĻLkՉ3c+ Xp^',ӊ_hZŽO%  :nh)y²^>1WF 4X_R=t l#xRF.aRB).zf #8'e4Bpδ@05W!poi^K׮LRIJ$Z*b sګgG>\6gsюMjb߽ycr\ oss?_z!Upcdx|zfG?OVCڽS @p|0%w(*8" }6uglѿ}oofᄑauu22W 1Xx+.yuWBJ%-%ZL\!  h;֖p^^XpRFRP"+Da%tEXtdate:create2013-06-22T01:32:55+01:00oe'%tEXtdate:modify2013-06-22T01:32:55+01:00ݛIENDB`puzzles-r9872/icons/magnets-48d24.png0000644000175300017530000000544712161170266016463 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W bIDATXYkTNwy;@RQ)| D_%>Th@%F2A!" cvYfutɏ;3sXVSf{Oϣvw~7JQeY 0`A2?, fD"8@] 8@dMTHJ9t2T}o^< `@ <4Գh3g7AJu.JlP I@ʿQ }ޝxjāLB۶G[oY{x\JYo9k+nZ>8I !%MJJڃp]1>ef;E?h4v仠IO>t %vA$IE@{|yJGEıK}Kq*]q )i{z@f!\XTm O>,.L1pU!!Ҍ _|qt1\YmHMDƘq\d'f3Xv9V(͊?&cHx8 :1cM1cuQJeN@b]Љw[sK'/1l 2uqjix|inLCQ`&"z&ݗbЎq*ڠ>M~gO<˰!")RgMocjxsN2~k݊ ! U0 ɐ?uh{d#*Y@X|/$^9ΰ#տA5|·\=&Pr7%ܾ|Ѱ 0LGE2|(VA[}QMZ40r2V "'r&wr[y' dQ&4eu(:ALSYHQLm "F-=s9~j-\1d6wy"["ן<W!0S40̇K}Z rw$%BJH)G846R( ZkJƈN >0K6&\Vu/'%MssS2w+ixcL>0db&O&ϣfuiT xh8si%ׇSYn/inu3amXy(~D!'|͜ڸk>Lg"`ڴ7ZeoԂKuuUe|ORx|`ppHJZD  r9D0lL_6Au X*et˓?OaLDZk[8\`{u[mvw\׭WU O`f1JdY(kcp"LPo빌ٟD@uG0d }(v! %3 MR+Bc WG&62Pӗ}1=~]͘gΜeƘ6Sqb\Nykk{^zTфRɅUƀ9?W.OHJS vpAg00WIDATX͙ I%o֓7;>vƀA7YI' R$O)L/ 2`酈.-u#T'ĻBLEād&B)UVM s_z BUK ɞ "/eĪJ)  ~7wKR--~YȺvr=5A]FRmAs k@kiBi`p]R9ؑC4R!9 mAkJl$I]a RDV&-!J8 [O vC@@JvLx%!0մT&BY4AqFRU=A1M}/p>MQZKxOt{T*'MД[@TmPB_Bul@ݞ@8 $o,Ҕ02'v報תn#bAvtoY,йDjp掗D[%M=/q \i诙W1"&n)\Xx4t%/59Y6{3qB?oxO?< u:?SzoL!={%^w(?XFf;le] U+ZQ)%tEXtdate:create2013-06-22T01:32:55+01:00oe'%tEXtdate:modify2013-06-22T01:32:55+01:00ݛIENDB`puzzles-r9872/icons/magnets-48d8.png0000644000175300017530000000544512161170266016403 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W `IDATXåYkTo߹wfE(C%AŠ$&VJciJ*?Ԕ?VFcKKJDQ<#Ggܽ4f>}>}/9Ǩa4DmHDQFWE% 4 ,#xH:1|4F\ Zo4fvņx$A4W*Ey4].4T Xl@lqo —^a&eG@ќ9yaEb4fΘ^V?GF>]\_߷ww!5KjT) ܱm"Ԃ\] 4֬T].4w6)S T*B(# Oti/YJHWBwQ@3,\5Z)Xj.DgLe|4 Eht_{ j>8K{:_O_I!8鴻w?(l #b72Y}]?'M_5g奪 &IB\pNi/dZH8AXwۺ۫V\VlzV'Bg6mgf14PÉ?14Qiw8f%BG,2Dɧ߲33Cy{17 /b. c#_cge˖xdL')21T#?+Y EqAqiZeP(YfΘ1 8|$lff4@cXYpEQ )PY $("(v.kJT 2Vb{SSbP2˴ ? M8 ?mːr".1/RKnd*,Z__滻R @q0a[[^{݉i tq@Ȍ=6?Oyo EKN8YQeǖCsIx⋉=ܬ#} ?;]f%tEXtdate:create2013-06-22T01:32:54+01:00n%tEXtdate:modify2013-06-22T01:32:54+01:00/IENDB`puzzles-r9872/icons/magnets-base.png0000644000175300017530000001305212161170211016605 0ustar simonsimonPNG  IHDRbKGDIDATx{\TusKq@P$IMAI+Sk_=^3䢀ZVh볖YS4o۳]zA1Z^[j2KI70D@Gf31u< G83y̏{3_e9_GhKtjۯ s% , _|Y~3U/d~h4+mhNNv>}̅O[uMMe塯*WVȏ=ַ(GX}d̈O͖ CRIIwb—^0 pWb޽ήsZϟIKgJ wʜ5Y//^~ GT.i=>)H-ƬE _?UnƎO:%{R^zSWa̙]~h46]t'iWcʛ7'Rae&um1D-yNN`&z] c7+Ç S0h=.Kugx-/Z7U8FssWGzUaaa f%IH7n˅s[,dzeʡCR ΅ 1*̝6Cۼ#Q0+鴞?0JlY0IMM)+Я__NO3 Zϟ6 -um1TB][ 0vD0D0D0D0D0D0D0D(t?(Y㪮93>hܘǙ[n鬫olnr%JQ>$",<&NHA2Po%+U_Ǵ e4b۶ktV=9Z.)ӱlPHpd'?/Ӭe05%ZU0LU[XS"kup_ 8]VWWW'$?/ ۱{)hr9ŧr6\i͟v|/ @5 ǵOG6EY)*}J) ؟D MvZUUU?l}oWI+H۷m8a|XhhhY3wo`ITqm첔oy*ȷ$b@? IT+@IPOh$KBgD $( bW K{=zl#@8>CPܕ Riߌ cHYHŲld+ۈ" #6[pHNuӱ!1)۪J'755`E>Dۍee#E0,={XQ),NbQyBLʰ1MP?FV?8˗/767Y#wcѵcІV.6yv2G;>}=>|䇳oO32ӍCW_oD˅--.P7ߟ>O 3w_ rĈ0V'.+v4#]0߷P>PKax|(Yʜ' eEݱ}O^ߚ7)钆ms\w[ow:5s!Fee~?ml1{>W>tdYъ8n?6O}5h#,4GEEEMyJa<3T6J~˃? 8)hh)*O/xfm^F֔Ƥ }[RhX**^ſaJv& Ea|}~:%;GgH @y^mekϞ;NzkbbBA&=4ѯ__Z6~Wh@aDFFR Q #3#@&X.z+_B(Y1&P&166pVpP` S0FNOĄvhlScQQҥGEF=٣o` 6M8)1q51( (  Z%]{\5Գgqcf?~-x7jmmTJ*ٸ43+j ._`~/Qun9V ñU[˴䟵ZZljkXS"D@vdm/no߱S8]jhhh0ot8,.Mm%R iS)4 [ ( ( ( 8n$MIR$P[ t CU@ae3cІj/ #dV>ÒAo0<jX&׏?J cǬ' K־?/ӵ"P"P"9%DzS%Eѣ'䘂L?#߃4nLHLDOA euz9 6;tȒW|֖\W}NF-Ƹm6.=8G t,'9Y٥_ VU0 cs?owpdѶ#?7hǗSKս.O#{\dOhv|y(PWZ埩?<&# x Cg:.QizZ]aX,6m2SgԠ=2=c%TUAgx;uA?8b۩k :!Ҏ/?B)v\?D~=p{{?noxWtY:}U]VUC閭Yy3 csڊV9)~pĠ=WTJ)i=9lOϗWhllt:y?s88reW*)WqђV_ k8+^j>$ƴMM͌4~fÆxMeT]FcӎOuZ>]{apdi_}7B#Me9ҿRO|ڴ? aaaCp1]0Hca477zrsi?W*&3~R߫->mZϟ޷% WD{vhr!7oFy~d2?I!Im)C{iz1%w{e^<įƓ :Â>$~K^ ϣ3%^YM;>mZ_f,N0~Ƒ)!O1-~O!f[/_ J=j ˌS0uy,N?ECb^> Ah}N@atu)gx8CKgu)w te<֠3ѶcH,l11d ǐ CCf( @ 0ηO8+%?=.(9mvYqe"N~Slؓey_ 4P?- _|Y~3U/d~h4+mhNNv>}N󯮩>.=}Ғ/!dAvʜ5~!!;+/Yae9#)b*_ES6nlN:%{R^zSWa̙]~hq;ͦZxەjfw9IHt֖O''V0h=I텱fÆ)])\ZƏDGGO˛`J^z]J yS +#z~ⰰ0N bP8{y1L^9tHYI5;ƨ8p0w *>>nwGGzম(ݲufÚRSSJ7WzE-Jkb@a@a@a@a@a@a@a@a@a@a:wjGIENDB`puzzles-r9872/icons/magnets-ibase.png0000644000175300017530000000311712161170266016771 0ustar simonsimonPNG  IHDR``mo oFFs$dG_M pHYsHHFk> vpAgQM4uIDATx1hWNɋ*ْHЦYJmtP:R38fT:e Y2d Y2"ScC)ҡS C`%%8Iw]=w';q:}޻珏`vg$)DS-Op/ vf+UUs|[EVGLʗ.kW@lj"S6kޫW5j#Ƶl:4(+jb.*5 TKorluFeqzJ[MSi 6GZJi  ݺc*}6@L &*o0p&AL &cnϸd_5,ɸ=f `803zV)@Y!6 Bh~nyXX ryhu0n.ǚ/C!A QE1X tc<.Fҩ Ql-y^|^";"Ϫ;Öy&xPz?HPzhxp#Qć:f%'RBE{ Oآ&~úF{z-/_*j%/>x|d}SeQh+h$+רhVv &YaTVGm;b`nd_XL2陸a F_wi?gVݽy&=7߸wxm zy{kj` d$SzO>u@{ )ӶJ 6b{L{I7=nĐ̬GC [{qNq7Y_p)Y=mLDC +%{<ا+P#/F/=C~A˩!hT}zftVqh!BDZr<$I) Au~OI4r.+ۻCٯ&j[ۍGle5~\Lkm@ӛjߡ 0hb6ٳ?AL & qgP`0:&i}?AL & `33;ht 0E?C?@Wc=xA'NmD#ćÎňZ?{RDƚ}%tEXtdate:create2013-06-22T01:32:09+01:007%tEXtdate:modify2013-06-22T01:32:09+01:00IENDB`puzzles-r9872/icons/magnets-ibase4.png0000644000175300017530000000165512161170266017062 0ustar simonsimonPNG  IHDR``mo oFFs$dG_M pHYsHHFk> vpAgQM4IDATxQ DrUnFNa/q! D_gR)Ye![qyv|16c3 !05y?=b8Wuh[f|=/9D)|&ZM4RH wO!{*O"   j^k(z%A @ &}}8$6H H T,'+dAE~!"(e[Gqdf=9ovFI Ɏv `S?ȥL!{C|[ů .4V,cX=q"%idbFY=l{1? cd [& d!y_[@*Ç޳z?=k7_(S9#u>3-~3b{/ QsZG>c l%}^mkkJ:gbNm+04m.PyNc0۾Z΢5"MkGj@\yٛ!ĩߵ@IpnϥAOF56._%=Br(I$ߡ؜|"E@$@$ ;|"E@$@I<4D H H NBAuWcsPEOWA}gwJߜOJҀzSI,6%tEXtdate:create2013-06-22T01:32:54+01:00n%tEXtdate:modify2013-06-22T01:32:54+01:00/IENDB`puzzles-r9872/icons/magnets-web.png0000644000175300017530000002673512161170211016464 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge-IDATx]wU?gWF El1&Ał-QQbWk~I~i&FӌQ#(GؐĊQ(}i[}͜}!ww̙3U+>=`_vC =P8豇A=zpc{(C᠇/H>;vzvJD.)i1D~=Cό?c]B?Jw>>ŗn퇖eq´e]{Փ8}" 0`s!Du㥥%ee9JVb;n?oŶml?޸qu@3{ƘeeiN*((0 [n: >hM:0NTJT5RBвe`GjkH$y'MvR*X!DA4:}du]4=R.sgPPPຮ1ZAD;v @Tn'@`ݺD"+eYRX,VYYC#ٿCmK) {h>c(N8猳~*ADXl˖-W;vuDtηDu97޲xRSO=eԨV4G!dHzf2 DD?'x<~;;5 4?~q&4/74HvRi9x_:`#"!%1_o,*++<1,iZ*:|ѭ3L4y] !bؔcOܰqRI"'LS02ɧad*.F"@|hLmqND}J$ 4 ι\Tv#0:$P4]},;~2DDsỏF _Su]cI)=WZ^;#5 Cn>(["%jp8:Z²Ҿ/v ZFRٝNjADMMM.#"1Juln46&:.yMR؋jaԉ[7X^*Rgv>PH >x^7L U,}JIJE_cHM[p 7{R(%B4!\G 絵u :a R Ʀ:kDFCʔ$+k{!Cї"zE!UW7*nG8.LWV8yƖaY֭ۄKMEaÆ"beUuuud#(/RV٢zI$0MڰnCM-ݩ zmp9ޭim;BeiZ)0P{2l w5\=@0k$iN:\jf(/D jA"2Dv uw18W*T=0OTvMu;V/֬\mC~;yj9 ҝ|c$kۻ1'AΠㄷۤ'%~Bx\?h42|uֻ{ ˟w4WT9.UWSu ehnۂ4uܳO Rڢ<\$Rɱs4t4 MMM/?8gdrʔo~xm'N߾֭[^׳'>S a}'єHiB&>@ιӖ0H)_:CJ)px{eզ "D#Rf>S49#V 0뮻+*6ăv]7ku%(Ă@Ljt߃VQ(J4%0uqM˲B)f4~Ș"ֹRPa97޺|@<{YH$B!@J"'A=O?]~W45%`lyQGNzrءd,Cc\XKiSE4-++\/J?}N9kG[G(hFupgwM޽-oTc!󦦦iN:SLP5CuCf;ꀍ"bsx;5 xR꺞3Askld@@9DP{$s ABV:dA++- R1HDCJǶmg`iڐ!w˳BJs}zKG9uiz1TZ p8,%ڜ@DPpر鴕(jn|2@(טPH&@3,8aD!s^\\4A3d-4 # v+(ӛGﮤP[IBD2)P( uƦLz páPsCLRuu0` wR ;)IzuT);3VZVZVZ7 ee6ojv>71\'^ӴÇ}9(gNVjL9QHtmt*z@" BlU? FXSSkv^QD H8jkTB !k Ѿ0dqMG{a 'O^ :agRkj /|֕ 9ck!ȦPQ䱿<8/N֝zoβr* 7\{e"2 LٰacgaxWyatʴG[ W'1czszU]Akƶ{ okn]9w9muSѬ0B [>zyUI27olZ1ۆ tTgTƠC8yH$=#=qg|^Iŕ>matR@&\IcLf2 S?dĄ*O:L`,EߞNO`ت‰R3g]S\\5v;]]伫Z!u~r.>[]XVp.Q)d75Ax1I4H;)T+~Q}}C8{&M:"^6\YHvCV hG{hi.u]VZyZI= @ĉ:jƹqd2veR10VJ%&KEWL }?}9"ض.o`Ս^#ot8)ֲt6 ` m7v÷m۞O1D4iEEEF#ƬP:e{/DL@=z]a@}? i'Hy+nСCׯ[;C ðm;oocj4!C BCQ#FyuqXTRV&K.ㅚ8'~_8N H`0N: ( bwPb<Ms5C !cX4Z~G= <5-:/`4fkB;e2;ìuǮZ "JunxN1Fx/^[Ϲ?akA30v)uc 8G IMMdl8a ] -VA6OHa< UGBN;y3[Y55%ߗek|Dіla*N$SefҸJ766َq c}-@$87>WlP!eCf&%3 fݗ <>S4Ep|;U|7mprFzI7g<vq'ƴ:}HH$ FqHel*ʌu2tPmukO hsPcu]_ni_B$缤L\765UUUͷ#Lss5|UWf]""܀Tmmj&LDD,8W9g!ǯ2θ "prɤ#WFZ5 8kNů$b)c>Y5w8Q]TqD\JH3@T: B!q6oޢEQk;Naa=e,"7i9}8N&iED:UNM[bDLי.)+Lq. H8 P`c߮ ! c[o[^yu7|ӡ]}$Y|FiFd` kJJgzێO@:+^0cw,a)^ȔQ٬|d @<3NSWvfӟ8N+/ym}2ă@@ռh2yޙxW]xe6UM70b0,&+_s+ˌ2KX]Yf{/YeW*"'[hSכ%2{%oR]]$$ќ]Xs u"*˟?4M2d"Z%m0dHYG2 s=;c{O|i*+.7%F?Idƕ޷:.ȃ9G?K+/}pG"<i逸 #ޛӺw.8G%}4{/ި'xhؙxO[y]ja`KjLy_|cze ^juO܈_^~ɂ6.<_c#ϵ["(,xQly5 $)H걗_YlfYV/BuKK]uxs ׺n#iWͺRu%8cd"l"9ɌUkkmOA@~iZ5~~V?kԺ=*H@ c['3_u`Ife^ dr\$Hh,bm5au6w-c_wͭ7ߨ,+gVØL&;9A#,-1K~\m2`? EFkl"Y =GND%W$K}cCd\N}\@@AXd&mBoTV&"DrHGNеy ޸`Stv/~[N;k0kHDmo;1X`w)D|6{.-S|hz1-`E:G 2$"˺]o9Y!t"eUCaמȖe{v7DNK53p=@>Cjo$uiݸqS`Pm:[vT^(|`Q+y_ף? ËO O@"M[3vw3ѫbDK.{aB, Nr 4/(^*@4vmΘt DoK(M#v7ăZ,ODI/ٺő'.=hPΜi uء  `i6?d@+לε E'=}%O Hּmiw.-T%Y>)IJoPsKPN=u Mfl{ŘK`Eb%=#zʰV^< }P<{5ζm9pC !+U6@r .o [?\Vqʆw1GǤR鬱( C<ܚ5k$BiZ vM/.Ω %Eϲ8`_11ћ3[57ǖف;x 5xa,N#`0hA` T>HEE#zFGb@`-<0wuM%XݮfcڷNz:;|A+.[^neZ\gjZ]lGv@DnmDa!cmwP  8S-]0LˆQ^E`Yλȧn뺮ːI&3uKH [حm<A`Їs*]׳G xө^9!d~*ZING:6y<-5ZV6bM .DD I{Ի=|zȻr?3'D@Вok@ڠGũQW?BMiz^:ДiiSf8z(@&cg2Aro"bCCêVoXbȑ8ضh l'_YN=tn `UUWƆF@< r@3YgWYY5nd2U[[;nlg:RH$r̫zQF-[[ue,NJWXy 6U_MӃ7c̶m0X;oqI'lظλFQ2su_yɻY ۷ovD\;n_3iHx7<DvKyj?O;`0x Sc7nR2 _̞鹞eYL;w쨯؁c|#8r7Eg.T9B#+( !84vly2}._BLÐ;pD5Ms7%"4+*6miRxޟLZe[3D丮:K_cJ)+Vӟ1c >,ge+vj/m:Ymm]gTWU+}1U& 2u@4͛n~UU5\5ʢxaGDRV4N_ T0y,Vъ\dHPi/~y<?jԙH$O䧷g2ÇgUWt]T4OAAi1P(X\\8-~ze?latiP}UW_x`駟ZUU1_tjypׯ+S?. >ؓGN>"eeSP555IOxg_=ȜOz b0&L9\DIضb = RFg\yθqvs'L8(:O|^B!θ빖?~'$J&~gNa+t]oUep]wSӴ;=z# hGDm]pRN;P[BnA_A=zpc{(C =P8豇A=zpc[[%tEXtdate:create2013-06-22T01:32:09+01:007%tEXtdate:modify2013-06-22T01:32:09+01:00IENDB`puzzles-r9872/icons/map-16d24.png0000644000175300017530000000147212161170274015566 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭuIDAT(%KoTe^{Ι`c[ \ , |<n\2&H ЦE˹q3΋kX@@QhcJS/:HILU},^õh'wYnlܻ|fhft杤0mqAg}0W`Y`0,4uӶfksXG-'RcH)ƘrAX$|PjH:t s!~Rz ӃKfཏ1ٟqWfHR3 :h (,/zϓ߭3!LMb[`:%m$!f:eFCNLaNιhVb" KZ=N#%`bO)uT9hޮK9-C@2OIw4.Mw}|/ajvg%R 1lU/v=ݵ̚>״0~XV;c:c3D3pjqF&w^M+cH̰ DDϽEDYY%tEXtdate:create2013-06-22T01:33:00+01:00/%tEXtdate:modify2013-06-22T01:33:00+01:00fIENDB`puzzles-r9872/icons/map-16d4.png0000644000175300017530000000070712161170275015505 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(mRa!7):aJNR]%P&2r]"f$I0:H$p ]33SR{dXf2H R87y @fV<{c.'3>fwA3%qig&mV:Jh+1 ]>$IiA7kc>ߥjYbq-#2{cGHB H"}J"ܙve/r*cnuk<%tEXtdate:create2013-06-22T01:33:01+01:00<$n%tEXtdate:modify2013-06-22T01:33:01+01:00MIENDB`puzzles-r9872/icons/map-16d8.png0000644000175300017530000000146512161170275015513 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭpIDAT(]oTUw眙)#ΐjƘD/4 ygh3bb " LkSZi9g|z~jJ$L6I&LQUW-lCY}w7?<{:4D:Ono_n}}p6Z, 5̲w˭MӍ]3yvQi,4T&,񕱕 m]V~OuD7#], `!k-ڶo.2Җ8Z{5:ҹ+`&GDhq,4.&MDڇ}hޝIۍQȻ dUc` H$yN3\RltP qiW@ 8ޥ:ٚSb&p~O|р>j{0H#ˏzGkƲQyehH<04@FC7 xMY dFD 4==Y$yIٌMᯧ|lX˗C>OQR/(sF.a]zw):zt*EȨ"1(D!(HiEh%tEXtdate:create2013-06-22T01:33:00+01:00/%tEXtdate:modify2013-06-22T01:33:00+01:00fIENDB`puzzles-r9872/icons/map-32d24.png0000644000175300017530000000402712161170273015562 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg RIDATHuVKG>TUWw3clc8 !1"6B,XbK @@@HƖx#XatR;DDd?':,& ϤV9cA|:?;bxa-0c@M`}&o-0 ,v{bڵM&yF#*头O䥻?rMt,c3ϑ׶(0uRQs$D8NG >Z J !reY);NֈB"" FG$U}Gt)VJ7' (9Pa9߿?󜱾 Hy~垅PD!J/q-Us|@pnx0+:\lkm^kD%sQp}<{P_I+)t&81ʋ8ieTCCT8n 4@!c=n|.rQnHу\iA^joSfv# s2(r&1WZK vs0B95JAA0x${9״Hh@~&g[<*(@qg(@,ϟ7^^j[o]*6= "cTlfQLqaUY=(w C׾4YWBW{9׮e6">/8]l}%trѨF? dX`LϢƃ;!y!x+u؅ bQkK>$"8)4w;f:6htbg8鴪ĻwvDsAy<T.§6"hmz|lNn\Yx4?%tEXtdate:create2013-06-22T01:32:59+01:00S%tEXtdate:modify2013-06-22T01:32:59+01:00YIENDB`puzzles-r9872/icons/map-32d4.png0000644000175300017530000000162412161170274015501 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǝV[0Dk79 btcݼ̔1~{\Zff6F_1F%S_U֒Nqj:2}ε{CgTF?D$fv򦙉Q)~t\5x#dU'U2FJef:0 |=xYW+d@||f$:>oJ7أ "#EldfĊ:qnw?Yis0NeiA}˅Rs@l>Z*UA)"^;0˒P'cR#+c|2!u۫T[5< @7ݯ[2G=g{=89q`ye /6W2V?[7+fv~J+fYWφ$V5p DZl( .#ĘgWUk+hQe@Rkބnc LیDkV$ vpAg IDATHǝWKGԳg5 f醪NRk-.-4} ٓNX[5u=Z1n{wxtU#9E++ Ŭ*t8ĘZ@1q.BVn]1pmmok䫝V AH %@TY(8CEQXkTd_~??TgQ4 %k0ft:y07.yG$+&HD+@۪X)րAq(h|wƭHI_=F!A̻"4KT 7I3ugOg81G<`ɅKߪ$lكn'GB3h+~5w; 36X^dy;nʓyq4{T+o GCkRN1D]4|FEjWii-r^U@RH6FDNVѸ?菆Fe=ah`Dz/r:ca޸㢥bu(baq~i*⛽<$Ru0w$sIJt>xbl Z(|=GJ3ncw.+lw\iN2+2 ^wX/΅e7oDSfG7;/tU6ll]/BI|(?ʯ6}H8$Rr/ڦi)z6)x5SuXxvz#0F t{>*qhgؐ$AΠ7<3uI9'hVo,NB? xtf P]]6:&ucKnɋ}t>IFb<'VL ct2x- vXS>ga׻[6Zș~7&Z&`:aRm@q\\7NeBVʭC5cxq&ܘ\ L*qxuk}GD۫b"ILfR4ē\]|>OL5mV/7>~Kr#k}F:q_r .&e f٭i[jN+^`pطίvF k}FŏҞjTFw4x%&xr{ T^l//fl&MP2Oxck*Pd,{]iCW=nf$E[GpTmZ_WM$ L`&r^|qPAKrgK'gg$%PyVo>]EQ[_$17,˞렙| |p ^ʠ%tEXtdate:create2013-06-22T01:32:59+01:00S%tEXtdate:modify2013-06-22T01:32:59+01:00YIENDB`puzzles-r9872/icons/map-48d24.png0000644000175300017530000000625612161170272015576 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W IDATXYݏ$U?{cgfw;̮7k;8(H!^xB ^x )DX80Xdw>sxXo H\݇[~wO퟿ ]Q/ ".n}p~/>OXO=CIDǗ |PDL$B֎{/HHTfUqU/pmuOAuf=!x? V(957PD:+ 6MwH ~7\6u 9Uu` 뒊-cH["DEUDbQ7 P!tɅ|2BgPX3,K?.j*E@"G N47@QP2H=~3"  TEU !2.J ѷWn_M L]TŽcm'"|:9wo}O@<@dOL}Chp5p`c~?~tp+E˚e?z3/}{ ̭L78A_ Р C =UK"JT/7'St2~3L 39gYUs;ċ"@f{^x1 0 !i~xY Y|:u;p64K89T1Qc-VQ{Ycܝ[]b%}2y;g0ʢii8Wu./,ϳvGQ^^osX Һ/"'ˉAqIP](  ʳ"kI+U[]]Qu]OC샪p|0=,}ml N~yդU-7qDȢŴlߥl6H13{윻pBIc^_Yl- >p0LD֪*xAªA6Zr$*2־QDUUu {:Q8o}MAXP6*c:n b`U3sY'MS$hiV>~#n~"m4i^ l׃\ڰVi0@-Um|Uy|,cW13?Ⱚpyn+(y>-kEUeUUGι0U9Sx;MlyW7JYxYWa g7n1gEogȩ aeӪ" Tk:Qֹ @uO* 2uC5~/E?/[뛊y^%v<վ x("c5r@3i Bg՜ 9Oo,"R"ʫ:DHY0B %IJi\#W> ƒʰm3YfvIdAG9Hc ~&`ΨJhO/y  Tsq֥}'Ւh휸'C+>/%Aq8k \t<77&HFuԐ[~{w_Q/\Dx4^^piV2"PhʪA{n+( N޵x1M|UEʲ{ |†HE ˙g^ٯfמzѠ{OܕdŴ:XdցSg~cu2`n}!K7'8 CQ|b⋫a`U@@RcADͭdZiR/UÕ^]׍rӧE澬CFrW2Nc_uO?l{ቋ/W I30O=%tEXtdate:create2013-06-22T01:32:57+01:00;t%tEXtdate:modify2013-06-22T01:32:57+01:00f̲IENDB`puzzles-r9872/icons/map-48d4.png0000644000175300017530000000135712161170273015512 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W*IDATXYݙ $b)(eKAxO!0h!"af"Rm$m/sa>DzRry4f0^^9CzOS4Ds9H5|ꅲ_\x 2R+ 63{(QDsf{DksO#fZ?D'L8:f~\|%ӊJ-\1jeDebyO"POsLhB:k֋CQ{YQC DbסWD@rh*[Y,3଀r$܇foUzgFsЯ^u71ո,]:[ E;ka(j$>f c7Y "ỵ5y71>q9g-hh:7ʔqm)QTk`ƝܓC!5g#LͤO [v%uqr$)uT:mSmuWATV_ @*E]ԇ_\woF*_ r`2%tEXtdate:create2013-06-22T01:32:58+01:00s%tEXtdate:modify2013-06-22T01:32:58+01:00.[IENDB`puzzles-r9872/icons/map-48d8.png0000644000175300017530000000624612161170272015517 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W IDATXõY[u3ӳrW͐ XCad@BQB s+Z@% )Y  AhNizHwۗv1d|Dߤ@YAΙeMoȧq*,8F`lpn FgٴmEQh(TA` @]N$(_в#8!4&J@"eݛubDZ۶m pw+!5p9ͣEMYݨi/;.k%!D@k ">76+3/v[~W/eIӕlʝ민λιe/BxH|q֧dD AuL\,w7}i(owAh] RB|(!s^}7# 0~<2FW Wb^翨X ƍAT,ʨGVQJibSs`2\1 <O`KRwN)ƭ|e7|:8<4()ZkZk-Bc8g*eR>?gQ*+A8etP icl{{[k]5b89?=K?=t <^y麨!BHe˲4Mٳgs{GguR4kCJ}'su@J0ڈ&,:Wm LC'MLk-O%8cyXd:;x>,SJiC!`4}uxfF60B3^];OyCFFM;\%hpIxp4)zZYS׭zyOU?ݟWӦz:Yv^42Z)'$}zdR-fV?R1>)h497 I`yY^X*A5!jC(W362q #UiY*oxFKsW"S"sWBljElι^W(h2ۙ*;ϻUm#n۴mӶ9BX1N{m@`z:wu\ˏ:8?`}vn-g_1Vɓ^=4W{q@.34p.F;/ 5S&1֕zAUJ#沜d޿·jv?"xAc!M";ͩ;4!`|tfqml/&i]R(1*?jAMB-V@?4;Ywo(޶ ٙSO^ Qk2rKYR8ut>x`%EC 'S+ 6I&cd8_8b*G1_U1Uk<=jij[!ttܓ%)O͕ IUݚ}p Ǧ:H>J%*s/r|.2#0Eyu`MK^_$n2%Sua(rO? dl~t<:JnNb4$׊$IO ޝkNpb뀢_W-[}t>k=%8yqjGQq@< AekeG>HGXXH"UHDꅝ]ҴMX1u|"g'vFN.A}`8HR%"Y2B%ք`i zhpΞ=˩Q(#\֗Ev%Q׵2w9sm6?,h&N8mf7&BgO]f~Z$ѫB?/d%tEXtdate:create2013-06-22T01:32:58+01:00s%tEXtdate:modify2013-06-22T01:32:58+01:00.[IENDB`puzzles-r9872/icons/map-base.png0000644000175300017530000001326712161170211015734 0ustar simonsimonPNG  IHDR6bKGDlIDATxݿoמj,aK"Or!׉fn}#e-knc)0[\mF4F4-\<9=*̼zNO J`G+V;Z vh%J`G+V;Z vh%J`./,P5y1VcZ9~XWn\ },nl|?7[lYmrV;Z vh%J`G+V;Z vh%J`G+VD:z/CBGJF+sn&'?wԍ'. ܇CD,~X }dbʳgN>tP̜ }d$2?Bʡe~ѣ#JVPG(Zy̏P"We~I@(#H 2?BJc(%D+ %z+ e~)K2?BĥJBm%̏P&V%I2?B nH vDz)<={Dv3qmeR}}q{.afN?,n ٛ r`33ןu+͜,7?zj.6nvy%(gP\$q ;RB\F,K!e)e·ҐKeeDҐˠee|Ґ@eEedҐKeeLdҐK2Ziȥr4b3r422rVrYBTMt+ , \%\P A.RJC.JQȥF:ZiȥB)TGM+͡\>&RA.Cr  230>1z3at*>ZЃguo'}wO]܍1^?}!MHy+PDj J*@qVpH.i%W)V(Aw.i%DKZ LV(Y_.i%JDKZ *1VP4G.i%EKZ B6$:͇>o'^t:Ƙn/,MڏKs<8>W_6>?/_p۟O.~TKMY(׾.A Nʫ̜=s;O_u{ZEOK52kw8r)\h%0GB!˥VJ sl(3R>]JBaNe\ʧ([I(15r)\m%09C!ȥVJ 3R(3R>JBa 2C.Kq$p(3R>ɹJBaC!ͥVJ SJ(3R>JBaJ e\'0"ZI(1L̐K2|+ %(r)\n%02C.哓ː$C(3R>! JBa23ȥk [=\ 7R}(Ԇʍ+.//`o޳כk4[./wiMw<~PP}r*M_ˍh2B< oJvq2ZBD~2*2?Bx%FE+#A(#(VƀPG(Q TPG(QԍPG(V*F(#pD+"J*%JA+!JV*C(#(ԄPG(Q.Z̏PtRBDh2?B!5n;޳YBY5sx-Ɓ/y'˪]kpJ̜ړ;{>7*B+QrJT\""D%"@+vKF+^^K(E+FKC+.K(B+K@+|"KG+! ;ȥB6$pNg; ̹OLܸƘgN9nȄwpjZct:ۋ_.>h.~kGziu 5^|/p[=܃,TǛ-~ dy#Aá4ƐK B96F&? e\$JC+1pl(32qJdNe\&Pfh%̐Z̐ˤheF e\&PA+U r=B[2QC!#Ǣ)r e\FPC+SJ(3222-%2C.A(OF+Rz(32ҊVPfȥj2ZJC!Jʜhe<2C.!y efKׂ#B96$sglz6^=ĤYZ(Z[=I] JJ:ů~6AFzϰ=G?qH \,k * $gRC+2 Ey_ hDR>BZ)P&VB(#iJehR>B2Z)P&VG(#J% PG(J%heR>BheR>B#hoR>Bߢ^J%E+!J C+=!JuCeVO? SLAL#ڟa!n?][w4zf`[=;p eTރ̜[ůfHAʹ"ɉ8:)2R,j&ZEKBD }"7mH:n_7Qm|3\p&1K LDʻˍDK4 B4Rk+ DPґ\*n!PBW.uҐKxG(K([i%<"ӒZi% QHZi%*F(+%?ҐKTPz hJϯZqt7[.zq,EW0]{~f+Es:v6{RLkw߾<e~ڭmq Z9r7rahe%E+ q#-ZY8r7rhr7rZ\ƍ\"C+K@.F.aheYe%heieeheeeheeeheeehe%eeheUeejheeeRhe }Փ ʅܐPRTzcnRsll$ʨc榛ޓ( d+$1v~ ܃;Z vh%J`G+V;Z vh%J`G+VSv~dGJΩV;Z vh%J`G+V;Z vh%J`G+V_}IENDB`puzzles-r9872/icons/map-ibase.png0000644000175300017530000001326712161170271016113 0ustar simonsimonPNG  IHDR6bKGDlIDATxݿoמj,aK"Or!׉fn}#e-knc)0[\mF4F4-\<9=*̼zNO J`G+V;Z vh%J`G+V;Z vh%J`./,P5y1VcZ9~XWn\ },nl|?7[lYmrV;Z vh%J`G+V;Z vh%J`G+VD:z/CBGJF+sn&'?wԍ'. ܇CD,~X }dbʳgN>tP̜ }d$2?Bʡe~ѣ#JVPG(Zy̏P"We~I@(#H 2?BJc(%D+ %z+ e~)K2?BĥJBm%̏P&V%I2?B nH vDz)<={Dv3qmeR}}q{.afN?,n ٛ r`33ןu+͜,7?zj.6nvy%(gP\$q ;RB\F,K!e)e·ҐKeeDҐˠee|Ґ@eEedҐKeeLdҐK2Ziȥr4b3r422rVrYBTMt+ , \%\P A.RJC.JQȥF:ZiȥB)TGM+͡\>&RA.Cr  230>1z3at*>ZЃguo'}wO]܍1^?}!MHy+PDj J*@qVpH.i%W)V(Aw.i%DKZ LV(Y_.i%JDKZ *1VP4G.i%EKZ B6$:͇>o'^t:Ƙn/,MڏKs<8>W_6>?/_p۟O.~TKMY(׾.A Nʫ̜=s;O_u{ZEOK52kw8r)\h%0GB!˥VJ sl(3R>]JBaNe\ʧ([I(15r)\m%09C!ȥVJ 3R(3R>JBa 2C.Kq$p(3R>ɹJBaC!ͥVJ SJ(3R>JBaJ e\'0"ZI(1L̐K2|+ %(r)\n%02C.哓ː$C(3R>! JBa23ȥk [=\ 7R}(Ԇʍ+.//`o޳כk4[./wiMw<~PP}r*M_ˍh2B< oJvq2ZBD~2*2?Bx%FE+#A(#(VƀPG(Q TPG(QԍPG(V*F(#pD+"J*%JA+!JV*C(#(ԄPG(Q.Z̏PtRBDh2?B!5n;޳YBY5sx-Ɓ/y'˪]kpJ̜ړ;{>7*B+QrJT\""D%"@+vKF+^^K(E+FKC+.K(B+K@+|"KG+! ;ȥB6$pNg; ̹OLܸƘgN9nȄwpjZct:ۋ_.>h.~kGziu 5^|/p[=܃,TǛ-~ dy#Aá4ƐK B96F&? e\$JC+1pl(32qJdNe\&Pfh%̐Z̐ˤheF e\&PA+U r=B[2QC!#Ǣ)r e\FPC+SJ(3222-%2C.A(OF+Rz(32ҊVPfȥj2ZJC!Jʜhe<2C.!y efKׂ#B96$sglz6^=ĤYZ(Z[=I] JJ:ů~6AFzϰ=G?qH \,k * $gRC+2 Ey_ hDR>BZ)P&VB(#iJehR>B2Z)P&VG(#J% PG(J%heR>BheR>B#hoR>Bߢ^J%E+!J C+=!JuCeVO? SLAL#ڟa!n?][w4zf`[=;p eTރ̜[ůfHAʹ"ɉ8:)2R,j&ZEKBD }"7mH:n_7Qm|3\p&1K LDʻˍDK4 B4Rk+ DPґ\*n!PBW.uҐKxG(K([i%<"ӒZi% QHZi%*F(+%?ҐKTPz hJϯZqt7[.zq,EW0]{~f+Es:v6{RLkw߾<e~ڭmq Z9r7rahe%E+ q#-ZY8r7rhr7rZ\ƍ\"C+K@.F.aheYe%heieeheeeheeeheeehe%eeheUeejheeeRhe }Փ ʅܐPRTzcnRsll$ʨc榛ޓ( d+$1v~ ܃;Z vh%J`G+V;Z vh%J`G+VSv~dGJΩV;Z vh%J`G+V;Z vh%J`G+V_}IENDB`puzzles-r9872/icons/map-ibase4.png0000644000175300017530000001116612161170272016174 0ustar simonsimonPNG  IHDR6 pHYsHHFk> vpAgIDATxQv8sQ9Yiiy`xE@އyc$>n^_ H@+ڴM+ڴM+ڴM+ڴM+ڴM+ڴM+ڴM+ڴM+ڴ{}Gjms|>L7x뭦wp6hJ6hJ6hJ6hJ6hJ6hJ6hJ6hJ6h!QJAUCof1|x?D(JmByP*lㄒiqBڴ#V! % 2>M(JM+rO(wDX?;%:3dY3dfRm?3ލ3o']w],sonwI@"ӿ fm&PPn[%![Vnr K mVnr mVnr mVnr mVnr KmVnr ^+7†r[\BBCM.!n&DPnkrK/E([%%[Vnr !% V\B0Bi&FPn3$reffXtܪr: (1EpYƳK*N*dNJasJ֤oٖӴe/6V2ua+:i%p d}KQ|WJJ(ۋ VREj\}BJbJj)R"I+)g,)KK+)jɦ, +bˉF+)m,KO+!wkR_|"fH[ř,F+Ǣy838%[k>~f_E麳#u|E+ҥG.צq# tUiry\咴r<.]zr=Z9\.=rL.K\V'ǥK\.C+CҥG.נQq# @t2<.]z25 G.KK+#ҥG.ʠtˌ2.<.]z2 M.K?۶=?AO׹QS1KL>ϞkNtBϳ>pV0V62(|™H+s+XK&LT;J-PrE RdĤX#/r dᥑV.eɦ,(,VŖC^ZeBXVi,VJݚϒzgHY̺UmT.^x$=Ϲ<^Y"R;Z9=xb+|sߣGO+6U cdeCgDs˓12qʓܿsiyqr uĴK{*#Z...I.LjˀJ+/G.Lj˘J+.F.ǘ˰GI+/D.ǘȇH+N/C.G+/~A.G+/@.GF+og'cܝ,E+ HM.Ǹ/V(s\qG.sW!)\;ZytDFs9{*7ͼ}!_K4xW;~c'}2gA+Gxa,z*~qwRwyM,Zy?R+oIyr/rwYo]\K\j-OOK.yH.z\V\jžzb2>^r[R+tYx%oKLϯ{䒷ΥV^x%oK%OFK.yk\je \z.? /rd|䒷VʥVtKZ&Zyƀ{KZ#Zaw=%@.;w+53@\ΐ)_wʝ~cz{y2B.y%$0q&RiyS{^#c.Kc$}Sg0B&|+M-vb2߬rnwj N({;[%~Pu~sr˶ݗހ*oVn*P^+][P!wHq~idUm }uZeU[DBywVn;P-vߤzʑž[T[0B9^f+}j@(g x~mrVmʹߕ[&BAx+`=F^N(s~oi e4AoVna{j뽄PΐHg )_gdȦBŒs~&?{%aZ^Nxͳ{eߑ%oiryrɿir< vpAge+HIDATxi$ue=3=s̞@&XRvALJ%ElBH "-ĵcvv;GGYU#{kxсXtUeW^#ų#0 |$㎶ BQW=[%4)!cmFBHMjj:" "q\qySSyuEU CB)J8GsDB) |/RE%IIs] \4i;mJ e%C1s4M}j7_]a7fy>TgHPUsK}m@+s0?w~GtbݷqCJg&wKZQR <ѣŀn#B*allq+:"*cw|xrT}'DUв D/g~++ EE< @Ѓ$ Ja@]͍M 6 (Ͽyhs6s/V]qeY)xc s%ir p{n>rԲ 덶[""<3Mx޺oueeS8۶S;?kG[\/}m55'4tWJ!B bPQQC}ϟ&dqD(~tB—~^tM쯾2/"ox1SSw'"<Iˊb$ht9d5eqqQ508c+ Wią._+Ȋk*Rt(lL]MS,MQ$d<^>^k M}/OɒZ@TT|#.uyII{ C]_o֋Di"i~fuwvZK-Eq7[W1!#-:}-QJmh ~ޣ#1aL8}M7,qD0Οy#M9il]$"09u:807~(z:ԏ*TS$@uS^|0/]ŭxRST{|9áiY''W瞻Y˲ ʊWK@\MFT!!,MMG=1 Oօb5":(gsΓeYV," '-c,/@}B٪sL:'j=y*["RUHJ3空a 5au6R#5v ryRѥjlj-"ΦϞp_$/beD"+ 4vww:]Q(lAǶWW6Ho∦a9>Ia~~[hnιѕw^{1vPOw0W*{%o;j  ~d'^2Ʋh!!ضsMӼ6YC8jjwuP}ERI˂jj9k_6>:p'gk;LrζJ\~}ށ‡jg*~#3ˤe/o[rkk;Awŷ=;(ڮUeiy',qݏ/ )ͯ1mM:g;*WE(R.K$+V`RݍL 4-bpMYy.E2OvP7NQ$43)b.4 #L0P(9Rt%'"i~)o{yNf[ϝxp.Z91MUBԲW^yO7w 2{NK$T_^G01(H!@l/t*Qy!y#7 VYƞW_|VRSז;!F9pܹ{ۿHfu}ee^ϸD$J @*I?|ogN}|a6-㉈$ȍ"ٗy۶n)pzpdd1O(+b UT5HA$=cý`~<5E^K?sl,dh*Og(-96H>ޞD"7z ""Ly[#}G #mVLn;͏VV=i '_xTWy5c'U]T $%W_{I$*}s |_Bt-a_<†lMN/@nPhQd 9.Ћ'GFʬOpgdd]7u]`pDHׂҷsp*jNEs]  zz _ߞghtYJR􃠹uGjpW-] ?߻JoOSݬF¿߹Pm:\ϱk3ayWvڄRj;n1TECߴ2!CK,I(]h1ANH>?)-(L>(ڢU]sqqI)rG1a9Y-15) DWCS9uUSr L)?EUxHD͍/㫆uhbbSN?~X./ufU!bGLN/xPsQ%?L9熡XX;9磣Z q )RJ~?s=)SSXW +RzQT>efw.>P(D^сN-nhvlO>/](xoLDU>^kA9uu5}c>3E}yQvE^vs(yYT*yziɷ'xnR纪w͌2 ; +ksa6?,x從9o PY8=s@n&lj D9ŵ%d"&_z>luwufxх_>uKJ]_%1?+tEecH%A\oBlȉsף1VA2'ϾGvBιi7Ο;:}/2wʪ%IE}A#yŸgWחn31`27)WPJhr4>28ԍ;s}][,򪈐@B4E2q`k$l%#D|G>Pss- @ =/ذ3 ɗ $-̓}*RGTU9fdvv1]Srmxe'1TA݊[D(>‹mʏrm.7qr7C*t$SNO+Z#RBm;@HM@|O}ޞR< ;7tԲR@H#HIJj6 C+8 GWL>ѓ{B,K D뮾yU h\"$Q 聁~4CuםX,S YQϿ#~$E(>|讻"@ƶSBVqH夌 SW !n?]r*i84[fLbb^Qͯ(7/>_?RL=ߞ! .c$3gJt{Ca=7$˲x"4tb#`p>If5O GE g i;Ex۞rlyGμ~@8CC>q{[7 *ia]a1s6:Zޝ\47̭(3]~=ihTW4R~^k:r BaNtR-fCD]U!Ac/|o  ~w&noj/ 0j|u[Tfw9G+i_T]G\Ukߝ$uwnoyUYuTcgj5emCZZ/374ՙ"+g vٹB)8դj2T{ j}w !P嶙{Q\YCMW3P)-+[䕵#;B8ǘYRuWNE,dcCk{#$ } *I4/m͏l?B8:0=;_f-^#m"{z?8)i~Es{C!rywHHyпF[o/EAÆ>8;cIT(#?0qޛ;spMoe\^5M.C$@eu e_\J~s%)nDYKZ,i1V?==5݀⎖nuR2fjʿzG6nP !:)Ɏi(+\K(NȡjkzQQT|[kgSֹ l9wίƭ&04%!M555*rӓKVX= U7TUS4U5UהM?Ww8[PU~wjN3+r4šo:`&uU8xX[v(C"4+|Qє+ + T*2%Nz:"\ڵ;SADMs(/:Æ>3Q{7gw#E9_h@H$Gb rVg%hO+@o{Jes*4MMXן/-tcEE"" g)J1_Q[n[α!:n*v;'B uS XW~9&,O{mZ{{벭^'ڒ|̼$ˏ8%KsEG^c.J7,]w}P ^"/y_pB)]/ +_7HZa nz|ɖ#L3DyaiįvZR۶yTwH*}_nՏ.[sn0Ӗ2Hʉihnu?Wl@Hdž#G|$v3XӶTv ՑN?R]e=+t&8{!fK(ڦ j<5 fٴ|x~TlJu+D}OUU˲jEu]>K il(RQ=8 y_^[qѭ̣(ʩs?AUgW;o 0 $ 䭳HK˼SE Wn&HQ0|H BB8}m@u(ګ q,B1utG~-"-R䈺"/%^--!?]#4SnOd)]Ǹ|A$Hy>ccoT5{45Ѓ~-Sb+=v끐,'sf ;"Y,) V&MM=VU|IiuB")ֲ}J]Yޕ07ϛ#\TYikҩɡcWbHY]` XI% 0#Ǘ $I,?"-Fs9d̊NQՃ8XinG${&`qɟ;*F E3Rm $\%IE#^sY.4SƬ5Ijo,]PƎڵ]mMW'/tGZZ\+3<7uuQ D+Fhlvσ3I3t L vۓMmMa-\vz|GSkp]_lIzarm!hMۖė;ƻ '#!S*[rOo^o}?u&h|'a !"K{"'NLފa-o 9GC5R A_yh/^$G b8l菿}6 5SL[x hls1ڧko)BR?>қ㚮EL0R1v,%mćَL㳟֍hЊ"n%b||Ͼ_Z% vj |z$zz2jkL<Gɸd%[^8A]-en+c)^ZuIRKDlAS@7g)zR\'*BEUE\6?M}?ȱ}JlF_ B)WJzT,BQ !͡(+y4U.LS#r\"94(X&=Npiq5dL5TfGqiRU@N]YN8R1z7o]׫yY ES?ľ_bCQ[_p9 "n+ux }g޼"njSw/RE:_Xo>XXU1/zkޅՐVԏuruaY+1?3|Ƨؤ.~kWgW5cEJFc܃CM,yc~c{`|Qr~d;Ba$K3~p9miBL_qWjmk7T(oSUC=5-UlYPsRp]xd`\?moaԥn9Uɟ=0hyQXFѥQ^|Ӯӭ2l0ǫak 7ڷa6o?L*)2ojкXS|zJ^࢈Q;Cpm. 0/xXrFJ ۙ:豱<~;IQԎ){[ClK#Cߴps puUZW`>Debz΃4Ñ)‘ 4Gl F~]Q%skl*v"/ވioojXlؚt]|bw!coE:*Pp]5:y%IVcMCԕknkϭG:6;nP{PHgߜe|U L){:Z t%R‘91xb4DP_>{CogcIB 7*J;4TRKbiԲOE19J*lOQW#aQKhkjb-LXkc{ eDzKMCi,BCH-"2wesL)#MUTվOx^*~/MHJRF DzYB7͋I3xlK;I2HƫH"Kˉ+*RST,{hEDE?Xw ~OzK0 "j|e6jC=I RZx7O#_WW$)5!GPIi]}< h;3OMϝ묌bE"j?a3d!`ʇ?q'ۛ{?BD)Yx9' !e9ζ*I(處kf%Jf|;纾|rVQ~/Z\Ilݺy (l[|e{uTMH0/kX^ijpFO E'IaPJ&-FXjշ/Ii"QUy0Gpv|J i{xyoׯ<ؐQ4R$•I^*U.KqkW[RpMH'D‘=93| $󬀢JK#rDQcJ a\^#r$y=l([UZ64EWUuM!@Al ) arAp i( Ut!]U4xC(uu1lh2S(6tUd9*ȦNB*R-+QeRSH&![_)r-UYYX]F$@&_]Pel˸کk+sv\^Xv]hrfqAIwmϮT ]>3pxNtl_]>gQ s*JeKS 'y"e%Uzjc '9JA RX $'#$iP U}cXܬ.bWVugĜ-^rjXFn)N MR_OI*[%tEXtdate:create2013-06-22T01:32:10+01:00Dz%tEXtdate:modify2013-06-22T01:32:10+01:00ȋIENDB`puzzles-r9872/icons/mines-16d24.png0000644000175300017530000000157312161170277016131 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(%MoTU=s{dRPP&|ҥlXh$l\T61C%NK:8g&b$A .xaλ$qR"cҀ`y!~||2N_D .Kr/W۫_K'W qkkLJ(FfzD1x *ⓛO,- i(`uW5k"nnb TZʋ¹H Li`ٍ#HAͣ@A~^vx·eٌ??o5m-_ S{Jx+䄆tdcg@[Ecݯ.q nӽGg@p{[q:8q$ble! #F l vpAg\ƭIDAT(mR0~Klt31=ȡčO/y`faf oIwJ&g=N)>ݕRm2smuөd_.VNBIåm77Kr櫛-GY)p!"Ifff T(\JGM5QaDO6/^:WI keww%tEXtdate:create2013-06-22T01:33:03+01:00V5G%tEXtdate:modify2013-06-22T01:33:03+01:00 IENDB`puzzles-r9872/icons/mines-16d8.png0000644000175300017530000000157312161170277016053 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(%KoD3^SeHQ!&EH*A#ʡ~@H= .VU@ln+>&d]Ǟgá#y$P,yF;H.\u,Jf뽻^C{ks~{vӬ)k [dyxY-̳JKZ)ӈ3..]*R^]AɿWY Bskcb*PBVT wsߍtk~Eᷫ'/?~2#?@mj{w78x> 0}Np<~x7}T5įFHcG/[[:[9A/`?w?[z՟_^˵&$ EѤa8|W/l3.Tѝ/+J:m T*e!ivQ mł94ˬͅ I!a"#Jk pX'%tEXtdate:create2013-06-22T01:33:03+01:00V5G%tEXtdate:modify2013-06-22T01:33:03+01:00 IENDB`puzzles-r9872/icons/mines-32d24.png0000644000175300017530000000446612161170276016132 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg qIDATHǍVil\>y}%! !QT J*VEbQR6JJY -"ԅH&1؎=c{3o޻鏱ݣOG}81dDdYa}:HXtHJADBWD` pNBٙd2ɹ) BdDȑK#WD$U]]S(䉈c"h,VJ8ABlO1-m,#FR,e*FDc(CcAD"lxGXL$s-W=ڵwb[~T~"]'kle[JҒ+O>1x RF/--{O=fȄe .ranW78;x"ye:T[-wݺsgct@wOzL0 RsgadO#lK#PlJlzh鎪9#0kxW~vm.5:@@Dhmm۔QCb Ȑ"HN~/~dQu}]&XA_=rĬp!D!౽ ξG#.]` @R?#G7;qDZ37nzpAgl@dfJJ)~t)m\nJw~t JTaznmۄBDH,jН~Xcq|.a}CAXh f!B$2nYY^vY64qdbW\R11L6\nHYlnZ.' WgSl.k{ں @?޵Pig}/o9??73a׹=}cnNG=[ݧ#޳=;54b@ 1ºKD ٔ8?&>!r!жqϣgZ[bPr.bfBƄ lV㨤5=CB!-D}g߱[JޟZ<;`9 (_;x`brQor?8VWMJQqzxuX/sǵ/LÁ]uB(?<+.-C}dx09 M6S]43=~~ls#hKc+V KBwtrݒ+[''FR8<75Y[q%\ƵTZFfvvm0kcޗRT*rKoQUE׻ewUUMeUCܰ򶶍\RFn\_We]-:]:U&j*T6r:]kjbLIDS0`9~h0{VDz=}'-f|wnl?Pc!BǏݲ/e|pYUN_21&zLIv{mr:\J'7o !gH֔ۮgD9_ؑ=T+0[TdLOw'sQ(Cr.(*Gb%Eʵ[}y6fws<{'[-e|晇˟{Ȭ)QyiQebzz: aCǥ4ZCUMmG'NuQ$ ʊ;?׶!ƛnZ@i'%+ P\>Q(~֖%1ZJcVq"""N546Nh!rlҩiY51>A +H,KO ~, TR) ZVm*E@R[%ow"c+[.VrnD"l""Öb"Cl)*b6E##l&"el6[Sxl;!(~0׫A@0X>F"r.+%CZ1* Z镠`T0?\ċ€GZd)>?]bİpgG%tEXtdate:create2013-06-22T01:33:02+01:00 !>%tEXtdate:modify2013-06-22T01:33:02+01:00||OIENDB`puzzles-r9872/icons/mines-32d4.png0000644000175300017530000000151512161170277016041 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǥVQ lS{/xdB03 `wH9o|?/sVz'ZZ+T*q@DHJ%?#ޗa$V(r՘xCLҽwծ1YQr4] f~F̅YsNGT0C6qRUw9VU]DZy2|YmyS;C?X{ K';kQؓ}aWi4d˩KrP?^ňpox}<}fcJ " ZkŦ)+Q,%tEXtdate:create2013-06-22T01:33:03+01:00V5G%tEXtdate:modify2013-06-22T01:33:03+01:00 IENDB`puzzles-r9872/icons/mines-32d8.png0000644000175300017530000000445312161170277016051 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg fIDATHǍVil\>7g2M6ĉSH $ U tJFP@ ( T@&AQ&6e I؎xgƳwwM'{aPRrCjZ<)Eͩ.g&SJCL T:6MxB0 a8LZ4o4T;DI0[P|h?u6Fz~aYcc,0AJkYk cٯtcc&]s`p3wo3T*ΔS+-wɅt\|͏چE3kg5#x*_5j#秏Ro]y˭+oɋ1J&ԓe=v ]r1h62ƩH`(>oZRDKY㉧꯽6vKK¼<Yؾp&!wmp᭭ :#G~|x>{˖QoL.\w zc߾{altW9Li%`0ͥx4jFn.ўHzйeг; W(ʔ1FIK[JĆ]sM6<]L&\]8`2Ϲ)Sg2YJ P,C6[ߙX鸮emJLڴL(]MtMUJ@0mnjRAq1oni_ъѳnMoxscȶ7^ў+7 jj6Z$ȹJ)7Tij֡)gSylЎ }ѱap=>3mm{\,?r+6]EYD>%?OVo͹K3[y]G'T__ٴNYN .|azӫ["{sw | auoΖKd(dm噽^__G-˜PBƢ}|gףwFPW!Zg_.ROg(srk7yk _}[ dz8?=xuS!|uϞD‡JӹԼҺ!]T麘T(QҳlS644i:oj̓C###1JoT@0X#AG,T\BbJ@k3Bk.,S*϶wv4%Y3)L/2fS`Y\*RH䙖Xc[*p֊ɉ5aD!_%vtl-Q́RўD$(|y<1r)?7OZ=Z^~u\^EEVɞ1J*GIPZ{J`J`}V¿L5R q}i5Tdd54.,!Jb{lt$5;K)"-mW=9976:iY(pȴ˳16!k!Jȋ@@u܌pK%yd5ֈ[ / -/w0%tEXtdate:create2013-06-22T01:33:02+01:00 !>%tEXtdate:modify2013-06-22T01:33:02+01:00||OIENDB`puzzles-r9872/icons/mines-48d24.png0000644000175300017530000000751312161170276016135 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXXkUՑ{snihGqHD,G4hƷ3YL2hfLXLrqHb23KQ@$7*}{9gW͏4n1u諾kשڅmOiVf?e3 `ǫ. Dw<B D413"H)K `;k@ID'(A  3JTk3F`y\A`y([up8 W$ `MM °YumJeX*!":4ei#>5PuG0(`IюC\uՕ@۲Gp(\mhl01hƇUfi5 STSdf'Ux 63jϋUWC(LY2zm~MMMy(4 tǺ/ƪ)qծƮ-hU۞=z[_?ru]Ոx\C<$=OW6kwx;55 0"zUX;Zn P{(1Mb+^TuaH^>%M3KkFz Z8C ΄ 7mf ( 1ۡ_1鲜;,PK|G>Tk:u3D"^0@`fSC@TWwO"~\JLC Du˴ֺ:ZT @:D@ 41ac_s6"E",m#뺍MS'5i(8`ep0x"dZ[3 B,f"B&jL˜9X`La(UHM{FP֬\aWRR4tg QU{O7ZjcR"hTah: DV8g3}Bi܋QI{ZtB-}g|iRʷ6n mnU)em֞E"Om 7.lZkR Y!~Ch^(l*X>B =߹u0Я)lDg8'Y p<ݵm޴eŊƅJIdFOOomڸi;~>FfqҁY'?1Qu)$nN*f&ipf묀)͛Ņ7ހG;x`0""fǻvTDo;`E^B˲2jõDbrkV81 4-)#PYZ3ޱcP2y͋AJ94Rf6ns]sgRCx F|f6Lir 3B8%'Om|HR*J!H8iG. #)Bi ?d2MqƗA'=>Ҭ8fžt-K(F~ڹEש.S e&Ti1 Ƹ&03%"rGZu̎#ғaf"eۥrmS-!۰JCFK{qdSsLYx謋GCic3qӘ_=a,O-|ʗc2=g!F6D,k|B0e eXV<˄~eDjZ43'GMbf˴sܑv˴i/0 sC (Tr t"X~D5dBtfXQḅvR@{JsaDP;SzYLz(iOIm3:u*|4)1{u<8s#Ypiv@HjGt\ǁbLMY?9b%tEXtdate:modify2013-06-22T01:33:02+01:00||OIENDB`puzzles-r9872/icons/mines-48d4.png0000644000175300017530000000244412161170276016051 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W_IDATXíXu:K!ӉHe.%N2GBd9{6qAT5\kjFUEDU-DMDLWz^ҺM萢5s!5Yה2FTTZ8WTMxF)ADɕ#Xj7E 7JPD `d3K)*tJ8-\YEW[9紮`Qٰx֪Z٧) j`!nע]΢ sO[E+u`,-h00fy7% W^7la395 /wVXU-a!uvF-O/Qӌx/8/zx]$;>[9MT:yOwץN,>XYuk/D(}v-0E]T[B:  V"3„QHݶ7g'n ^uEݳ+#wK\֝Z(o-*^/k1 JL->{J)k!gĺߧQUE^ ggR?o2$mqC,uh?}s|J}5Scgbtɀv;y^w1nӏ_ueGu}&ak.]Џ8ynTsDd?|C C bwU.i=4&"~j|=tzVe,x}h?;},ݏ|c'ּ-L/y0tֳ~nj^6_eyQGk%tEXtdate:create2013-06-22T01:33:02+01:00 !>%tEXtdate:modify2013-06-22T01:33:02+01:00||OIENDB`puzzles-r9872/icons/mines-48d8.png0000644000175300017530000000755612161170276016066 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXYyt\u[2of4cْ,71$!6i(А$!4P4rҜ5m0xñmd,[ȒFo-x$dz?|}wIЧ-=V|H.!`\Ř* `(94M4v] P},h`L`g˜!%%1T39,}?C4nY ƙ{$T ~б :gY!%FG넔ŀ s.AWq]ǩUkR0BDSkoId8s{]Sfh zbnDѣ>t)QUcn[7[E'=Ƨu]w;kh+ me--[DDk#$W1$ism;֮qR2ۺh߼b2*x a!$Nſ}Gߊ߀, tP_Fs6_%k=4v W )Gm|7cvZADA84ŏ8|.K4M!5ʐ0Ffykdc`, ׄN֪σlϙW5҂RwB!_ 8hkAʠkj Mk^{ݜ٧ARzَo\sYⲴPd]{ 'V85MRկUuj))z;BPˁd2<,oy~֬i$s=Jeb:NX ;~><&sU}vzWˤ?<481BOfڒڸful[^xӫ~#7|A/,J#\,]0''9ObEQjE scsb1WOole2 oԝox x($ JpIO1(ZF1ΘbX*K(U"@@3?5YfQB`eRC3f0jb gMCHEovH80,0*MRDuVme XD?Ln./UBȶzMr-BSLpjбRHTt\Vs]pkIb!'D#AVAմLzּ6Uժ(d TΙ{muBױPGFGN` LiԈ>82k w.]< Xj\ ۮ"/8sֽW-R F`+bU/}ϣp` ɞ=tx-( = *|iey5BB4jۡ+|AQP8b៞K/f{ ㍞555ƄV0{v c((R[u˛zMbԡg6tmV۹L? ##M[n0 eܴ꭛oYyչ\!%!{]\q rۯ? 62JRl&= ~zh~b}pzSPLΒ4L/_j{ੋUMO3d*7A! *RkFD*aR^ KOWUZyJ1%fҔBamUM7{p05=1A3Ư=]7g4O(6SCt6>21B"7WUFƪbT9,)BJ9yo8 iD0`ek޾k*'z#ywӓ@%{̲_>kW@& &*%*`ғg]+:#93۞\i)+hLb@*h_D @h TbHrVzҐ>|P b[*G:Sb'd M*'Mt]S!$$8L8LcTaLO9*m îJتS`Tl ZYBT ZhrcTusRMIFr]7pi) "u#DU8lj'/1!*g,룏̘ ad2BpSAv`dOcLpN&}!87 TWW4Z!"Dt&k y%۶1)5ПMӬtxӌh'͛wh6/w\ L41s$_ me결Coɉr棶 ) a#}0`U jR*Ll˧@( MbO5Ƭ+'%tEXtdate:create2013-06-22T01:33:02+01:00 !>%tEXtdate:modify2013-06-22T01:33:02+01:00||OIENDB`puzzles-r9872/icons/mines-base.png0000644000175300017530000001100512161170212016257 0ustar simonsimonPNG  IHDR7~bKGDIDATx{pTO 'XC5L>:@P0_qGG!dsvc &ж/c-Ch WXZNCv5 k9w.)o~-7ab{)))ox'LL7l% crBA8^cحѓ[_+{rVh;y- fUjOfWyO]a^BΝ|ɲo״uL0>~^@de74''GԜ3/ Iul1/жB~ȡ=qqW1mi} xb^iz6nֶyWZ 7ns:#^_6rޯP>eܹA1i-yp1;^mGmY|5!$.W"}C^B/*>qF__1io5ya۸4}R]|qw}69 !5oqKL0{:VլMy$};)]7BHWt 턐yڦ YanmkNz©B5_jg/~DDDz6;O\j{W9-1cg:dg4WWLIн*[ϻtA',~'sf',}HB…3ի^מKKה;^hy4멪TUzrO>ƵEI2y4y?]^q !dȕFx NH"hy//Md34BȱB 'g Q32zrv 5-'g`W&4Y=׍f7\Y_,waOY=pjrpF9&})q!Ua9&}K>s 3|OIӷM tz->bW~V,Qn%M_)i45]G{hႾX=!%M_)4)bݾs~?!$=-u כ7\/i2QbR0m\E, ]9%M_)8o\IIWtӯxw4/iuN3~;yOmp\\Āۧ=5o7SțE{B};'ʿdW(v  8Є%zǮ wz>QiZ}g_% 2dܻgOI7] t_\5s3GIE|D-X`җ_ݜB}jcs7pƂ]}jcsS( /B@Y.='Bj`K4kPyuuC퉩BsUenjY!SE }xMEղB@~k7f|3&Z(moŊe#;/X.ǥhکSy[[70|x`ذk{zI 9ѹܛcמH|Dݍ>ZzX_#anE@).GODk^`+rѝZVhQ>"J#J:/f9U 9-G(j:pmN!\FPZ"PVe>) GT]rXA.cmm˂E}YUyG?l֠ #GmXRsDŽZ"?*R 4,nbK51Gq )}D)T}DGĜBe#84S,}DGœB˽)T>"S@s 5hB}D/f+j86}D PjVPvV PXsX{ܯBSK0Pm# ()1Pe# N BjA8ќg}fShZ"3>5vq(DΕ徜$'<ϼZCjjbvVZ6L# ߎ;kS~#K ͋nw}ŁM,mzh #2K͋TsRN7a9 ZQe͋zܙиQ-~#2˽)T`z;ޙ3ՊHߗX2 Ai7_#dy-}<>G}yĄZQ=HJhTWV:Ry~&!& \u)?dۗ!4+G5>L|Iмdg4WWLIذI{ct"Zh#ZC+Wz28`@e\kCи4꫁@󆕗;As Sg_7k("$͋TW[ZQ5 |Dxp Y֊B>[\)dY+ QlsR B uqt,ѷ^i6ϕ.9 BP=h@yB@8Byt/N8^ B;SH9/|/N]q C5/PЎs Ý( bKue) "Ϻ6ly_lSkl^` }WO޼3}"Bǐx,ݼ@Q`H3/P/4^ @zL4^(@ d )Cيp Yʈb N!ZQ\)"aZߟ} jQk 8S@ԚSh4P ( J@@R @)h4P ( Jp u#Zsj9)tF:g̘8z4!S4Qp 4 DP ( J@@R @)h4P ( J@@R @)h4P ( J@@R @)h4P (k}~`IENDB`puzzles-r9872/icons/mines-ibase.png0000644000175300017530000000377712161170276016463 0ustar simonsimonPNG  IHDRnnI9 oFFs pHYsHHFk> vpAgE%IDATxyLW `+VjHIM%&œֶ16VŶ ikbŶM(ŕ(r Z","n.{s,_3~oٙl6W~H?ƹV,lۙVf4ϖepPYY%J-tOtY-K [hj߬U'uJEm_]ݰ>?*Pc; }Z .T+.wtLc^ilJYGE`2Q,{%^>zcy克.\_-.8[|^?{&JZp+>oOKү.+]&}Zs:ߵaaC)̟cfإ(%~ fz>{Pebjx1%25WU_3Ojɜ15'=rڒQBcT|}5 ]e%]7||Lޜ 3{?'dX1dYr+=ϩBYqGf{C/[X36)`vN6w;5ִ23O|̃V Typ7CCи_x^|J*Z/2)% t@}]B`kJ-Dc"mJi|r_PPXq >X\e[Yz=dfٹg3}R bqbc ¸`4Gt(8[ZDOFf~V(a;^Jzz7 aAql+%ػVi0#Ⱦ-C/,dN΅$GDzB\38spr!Q*ʅ$G>+`vx(nDžϠ3.ϐl Bg;g,H'A\? J>#l];&(ZXu㑫a,`Q|F3 P$vP@g&aJ>#l7\Yi4 Z6`M6W~kb1!*ATbQ D%6Jl *ATbQ 抵 vpAgEIDATx[r ESޗؙZ+|Rd#^H|Ʃ`3Z\ɷ/|VXוPsuUKt~?ꚙ#'✈yTՋ^zg"?v/S)U.哑Ӹ/%i+"-"K aIs ԩ!k#qۨD tΉh+:xQfΉIe⦔  @V+f/޶#uMVQYS\ԶK'Fi۝ʫ$w*mQsvy-دg^*ӿiVl&T޿6ω&*6~TS<^b3t$u]#gUCs!ej[p+:̫Ӿ&Fq+zϭ(veEU& ܍w:/HbTmi0uJ se+0Z`OjJNc U [()"] E5e03~&n^fZgF$b3d:]b ^v}>3Nsz`>3Г{ыόLl'rgzx6>D1|h76|fl#H>3bzm_ I@b~%ό 65l~T"2cSd3v#Qd#c*iJa*iJa*iJa*ipmƶtdEXCJOaa*iJa*iJa*iJa*iJa*iJa*iJe _%tEXtdate:create2013-06-22T01:33:02+01:00 !>%tEXtdate:modify2013-06-22T01:33:02+01:00||OIENDB`puzzles-r9872/icons/mines-web.png0000644000175300017530000006111412161170212016130 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAgeaIDATx}wxŵmr 7S Lǔ !7! BMnrӨ{dݒU}xwW+iMwsoy4;wfΜ)Ё=g""!kBH'"DD9ȗ iRLЕ_0lkk_^Fm[Ӵq:y"lkk߶mADB䈈@x1H,)@)/~`s^⋧z9^ض-ȄBc(F.qLG9;yݓٙ8 1F9uꅣG:xFQ_em%%%B8_JaX|P=B !$,FR@^0 b{{ȑ#S6`q`f6yC<4se `HB4Mض-v^@RʎA'^KsWRRr'A-.\ SL&)R H?b|ɦi Te % tLa !4M0N:D۶P0t RJ'O>। RBY@L0JI@$ZrժW:thSC$(QPSIGUV]pOheeHN$H 5krڔSNmle'XQP!m|nj4x׺-BD p 0M3saYPGvvciPIwFDƘeYR 0 PdGR$1Fx[BDPbY0 ۶n7NR)%JTRJiJ)1"r(M$56L*5 w[!HXD G]UVkPIuǢ\Q@@DWܬ46Y#cH$P_O)xsHkUǎ1 sΣ&QH)ѣKwأGI" !>8~ }EޏRdv/(.H z{=>zd*!0!{ju뭘  {wC @o?4K{m״KF 0pWQB3J*  VI>_iǞ5x0q c Wͳim#%c1ݒcLQdA lٸɷrgæNI1CQJݭ>ȹg W֯Zf,Ruw{F wgϦ- ?8l#%DkwEvn$Xqtw^9+W}|ӟ(+_#Ue@iŵ8K\uۇ ua+HzBDE]!Ck o]B/*3v+WVvǎ,,DH9 $׋" J|7-[~,Ō>l{UM޵6!c4m^ٴE۳$c23Oŧ_]ڳe^=O PLXZoHV% ! ^ {}E { mL{;*^ Ll[z_?l~"}>kp DB3ϺJٿ?wmȁR+N]1C9x(o_*R{E"dryK)Gj{kU*cv݉ƙg$.P9z_7\UʒS.z)!R8Ķsn!owrye$Dh7Oo}54M벡Ӧ 8{Q׉iOjV,=柍 W?ZGh$:;Æz֙cG;/F<4wDžs HX~Of\>@ȵSLv@̾q5Wܳ 7R:ÇYE!9j6^=όҥ_ZC[tۿsӖMafu^!u;y qBE"m󧊂9z5|~r u?"1bDiAsMHlEB*S cIJ G]G iq`y=](8BB!EE~7lֵРrɷ~RSi1} GS|;w>扆p#9w/O""[9=Gvq'Qڪp%Z&(J{Ǹx]nYYyM$zvGH |\U?YeEEK={M2~ߧy YZ|ŊQ(~ߧ{=KWY" #bAA!Q~]i'Q8_xiu p7'?s2B/?U㉄'??6ce]K.KFƯ{)kXpѢ%] ~͇[3/%Bow'eG׽#R)`@<ΒIʘ{,jF4u KQB !0fBw !Є8U/aU"%cw~]L!ee2vkn:pJ{#,BVZzc̵*[]m2隮Y1v5c< m{=W]5Sኻ׭_oW^A3`8FcsR¹¹ϛpcP+X(s s)%J2#C(B8-D)mt`B; }ѧ8b6 ~a ̚Rx<g k]]]O:eP(dZVCCcO5~MqW]=-|w#'N@g0×BS^0uӏ? C&M(w,ݘ x,d4)%픗2d0%N @G L/`2 ;YBHA@D9"ƢX,(sR"%&(Be!c4lڰᎻ-3d,)kb0Jc5kga} P RmKBx=^4::ZY!kb=[-["WpOTU{raZYڶ]PX(hmi,mA sPU,@$qǎuu_~B]XLlp ȱ )!#LƴLF-mD+"ȵe ! 0Ii0X$BlPSH"{md\!F2䌃p !zbA8s`ގ(h8HGh" uí˓df.Rv<^Z ㄻ\Q(>Tvuu9C#*mu~eC1YWqHzi)"G)1 rAHRztxYA9#4HۻWQH2Fٿ?OgH~&$S;viZq 48䴇N|`ڐMaM6QDz=/ mp0nj;nO(uKJ)go֪w W,aǎZR̸UmߡUo/#HmZ˺"t~(%_WmݶErW4-ʹH.Fh&ֵ+bi$\aI5UPZl~eָq̲XZ%B_ շ>6o9(^*"pU˚u? q7,ܴ;{_N(t x(|w|0Gj aZFD$@;ټųqsQJu%ӹ 66&(1%(?}fɒ"_8S*$_̻zgkK @ C6zD(>$;)q+VV+ilt;!@Y՟).B64~92’NS~{/g z fGt֞Vrj܎w1f^b1n,*⫬RxW9abC@۝^/Fn)!У֊ԧ&$g~{9sk(탖=o=[&mg%ؠ|v,<žtYXX>2SBKNH=ݪ:VPOceuPPew[JuX{dһs'oo]1#r,aB};ߢ%[=[P0HDdo~[̳agkp9)ww}UǗ>ܔgN}):$i{WTn ׅ8$ kߐ17xңPU͖ rF#K{oJ}[@[1F Cձ/llLH⒏ԘM ~m#hido);Bm#磟KU ci2 xo>>һ}G_ =(KC_?t]z6ߎ_ze>"( g^ZwSZX}#GD/r͑nֶo_gu8 (e` ^5*|`'~ćq"Ư"z5}mՉOoؤ2jtDuwmΙ>}[ov_4e!/|"D@ @-89nj nuI 'pm?ضz?$'y:y+ݿ滀0d]];t+Ee@π5>;pwmk!p| 5{^2a\ w{F0O~{j:"dv;o*?a*ԝײvI}3Hbq.a~LzæO pzټ?2M g}S~hRS"B@ |޸';~k+;񮧧<Rjk+'bgj17&U%")EDj1i\0Dʆ kJ^O]"@2s8dRV'/.VO:)*Tf_ݧT"B,V"x}h4CKohI˲#QiZ$ϘL(~Bo~dkk0sf✞zJay8HVNZ0~3@AhN4s_xegF8! N=<669%%3NDOW]眇>8Dwcǎ&fk+H)us>Y[c#&[+N:q{CMym9x?^#G q?u1xɮ]%/r \lŮ cm64N竅'>,ruk0Ҳ! !6VT\Ral굷vkVi|:cD,/>׾\?y2"(wjQmiSaKF\P1 E#..Zwvee]_qIa`Y( h,hO?[] /z֕O>8 I$W3糊ʎ`gWNҧ|<gkBqXk6J BKK|JB`X8Ϊ՛bݺZ[ݯn<~Idi}9KO=oR3c_,Xtfo[}/;:L C$NiDkiIPJyB rλy;RJ[aNyz)% s/< ʕ驤~qqquP~YghJ(!eYK.>R霹xiJ)E!m -W2~!p C4j??7Q[Q2) >7HB)SN>c(-{ϟYv/7{쌵(=YwM4M{ÏC]&M:䓔Tlڶp>SQ+o9'8AkV_"_C>tGHtz=|PJ*R~ʶ![` =k D"ME?$ʃ>ԱXbMg3Ftܻwg)wLa;v"5 sbܞ]",D#$PF9dCa>G)nB5M%-[x?6R뺇1θ?ʘxs`!d ܴd` D"DU6or4bˮoXrőÇ!UUu}D~tK-+UUڱ};q?QEQUܟ2ܹWU5n)ٻw_U 'r0 >veΥ2J)co ((~{P߉ G|>l\3k()pDuMS,[6b˦_1v<N2DHvoHPWжI⾅X[[;nH0=@PSJ#eK N=0 ƨ7e)t@QH&e,yJ@(JH"i]FJy8M%}S)gq;`C @DM#)zTbʔ)I) "r(ĈiZEEŤI~GπB 43URZm-[@d VTTsy}s3b ItIxuG,(2;ѓ2>=:HSI RӴ;}+W{llܔԶ9/B]WS%NKa~3CI$^w njԓO8(,,|?R7v3"h%Da|؛GFr(?ȏ8,[PUH_'Z"$iګ_۪4?mQZO/+;h"xwbYVK1G߹k{orذP1WX۫?-\ٸI߹F"ebUґrEc&s[fc[Ej7'?$Qm=܃9544z<]rV)jt5aBOPtS QJ R5 Fsvg~休zN(t,yc;%2&Mq:۽[?xȳg2Mt$kx0e?{)LsnL )E<y`7yq#R1EQ<k:Zf֨xt]ß}fqР2e=EJi4O+<0r/uu]׵d2xn$kj;Z1""Ӵ#+oy߼aWEaq'(ýC?=<[r;vFjj윳aCy0Xr"z%$)rw66l`-׵nx۹viEQ4M?Rf*mۦx"4J$-s۽mx|[\/RuΝcǮG`=_8mM7>7 łںWϺJ541RUmgVT9R뾘M9kUz"4ÆM׵ zޤ{@Wu]48k(]PJJJ! ړ}ST%PWw&N* J)m~ݿN>]ӾDd{DmƍWRv/Bk۳g'KJJ`yEQܹv WH)eO4[UUU+/RW(:Ĺ➣1E=$M3{DsWj$%O13Mp H- ɼ~Ô"F ĔME MxGQJSGJD L\ElGG "B)%#G\~4@t]|1,\J|)"'[>oϠ4B6B4?w[ׯ>0(i~6u=M$Jw⒁$,z޽TU=nBJI,?|HFK?={AgeFQ7;mK&X$WCh0Y]]}[L#3)R @$"4n޼yͷ&d"dJ7"HnXn&]픲잁Rt{k׮Rw9퐬LjMQPP  wQRps=Qv]QYY>}e}՝Cj ~_J~++=Ľ#Jm :ٔi]X,RD] gͱx:]A Vң4!3ң{mni>FE8 Τ쫯W>ёqbqܞuM?R[{8J&En3`[ @DMGU!I,C@EQ:vzmv)*@QˢXRe۶1PUi۹e8e94M:1Mvܘ/qEQ(KJ{KespEI ض^wT[wFh~ϴ+ +1p3.AJho_8C]QTŸ*" mP(|հp> PM61]];9F,_\t543\QD 66nttbdsF) 9s><T_ea$Н310 ?M~Rͳ;;`Jq蜹>I~+c*fF6a&\$)Olb }/TT,MMg3BJHFKyV-Vj8,TRAD(U=[*K քGYC#(HoR c~#+*=6Vҫ˄egqyWKfYD8SDޗ_uбcj"l^}Pkr"4=غɦڶa>ؙi?AH,>oS4 >${i u, yRI5(>=Z<[RJg,KeSUBvJ3HГNJtq%nY?}4L:rG{LϘyZPO]]e+<[? !Ik`vj3i4[һy tN  N4J-)zJ еmsU C]$Yw|B&L0:G׮ uv2Br%R RB,TxN瑌YA ]~+H ¨iw/f|O[f ` H y-cW ~ ,à v]wM~p ?diy ?gӰ&^o/%i$JL3|o~ʴ&| AlwwHiBmwc*D!q/YHǡFn4EiK"::ʋouUms_`!#XVZ=D_:7لH9KJ!R%)Lήրb1uEEi1PTdb!dxKDKKKW-,CXBDL޳a \@ 5m$}H5MX|굚aX&0L&W aj;-;5-s-6a2dmېzh_Ƙkֵ{OUa/s^L ނ,ջ4(% !7۶UٛL&/7X]ܷoTTVbpŕ\ݵ=5$²/?Bx<^RR2x`-W~/nbqv ]P$UE4$7.;GJsloWz-[.=hCJ1 ݿ?[.|X,~c又b1B bY]_sNEiJȍ~ƒ#~8KDY[[ͅݻkEy#PFxG*$(%J=Jd?B\K#_ 1J74ji97IpX\hHL?ほE#J <8%5Wwvv#QB]]Wnm=[=Su?|P$^jeCCC~0c ^CnP*˥XO_7@ eٶf];wu.]d\Qxqs\JL92刜%;=}e}(JeeՉ'Nv#WpɦiڶmƏF65t]>r5mT_ljkkG#K6麾yh4B]AynG޾hk/XW:ۛ ٤9$R㸳V[[Wg+N& G#A 677ڹMn-?MRqc\}TqI) ϒd2e1d0u=!P4&}䢓La>]܂ hXt߄{z{]{7v(IJN?h2|ћn&GMsN?B#nFW^>c`QmV%J/„Dc@@6ol#4i/uv.ߨ,[sR&/8?q4/Y[X釙 +9Z˟.>cG@M#Fb1';sRBHԎ Xpt8.0V*@Pӂw{&M|(&4;o)ޛ/}]Fz{1 `М!]%K(usCx%{WdLSMa>U qE2 { "tɦ+Z[inj]#GáW466N1G#+W9|;;@)htժjj\/kN( ^Tg R97MSJ!T@趖Q&}ǜ ~C|~ "OƹȦNѨ8\ÚpY4v)D5U|ز-?FUU%[?Ee~?i܃91USl#&d1Ju%Xr3gYlfE+f*))6M#eە[.~EiiL/!*.^ ݥ۷WsycT+pݾc{GGɹݻ˻tT4mƍ|B4=;^\\8N&;眩=(*x O)'+<ܲi^S9Qe pָZ7f6R rR#nŤ(FiZIJRl}[dWEυܫ10EOq̹M+1Sa)ILA&>PEQd ol*|Cfooi*&ܣEQTPh+3׻~cӻo"c $hY[7-l\-]H1`cf_6Ll%h\иXGp2w#0B֭+:] x;no Smy&޷?MiW*r~_³wYX(.L%SV:o_зb1M; XR&XGѧO4;W7]ݲճt 50B76<"<촨]߲q}=NC=LTU66xh҄ zݲHg>`} _Ȧ/=·_9^§Fgf^::_,\n8C !|zi˿xr^YZ%$):CVh^^ڻa 8?u۽{P^R[7 nKRBW/*@ L?kdR9XL2K$D7xmy1c$ɕ#dsGS[qk7Xʩ.]{/;v٣opL6nHq(AaۢMRW4sR\`qgqTj0*9C) 엧 8'ل>V]-z1˔@QS\丗G;>8J?n#MS,*.Fu V{OPYYa${[65(ѣFls!~(~W[{ziN U^xzͨYW: '%hYtbW$1X~k?}9sȹ3R#H4E(%JLVv=*4(aw%}P NRN9_ѦqD[[[(5M,5H`0wL(kFUU8gܭ+?yG+܍IMB 8krbkNifJڶ NYegD~mkn댹õ,!RU3ό\bn9~>5T%յ|榦$rĈHxuXRZEWXq|숄VZkcs.ZYY=^t'PJ*7n9sL&Ǧ"Bk*HF)"r("0!GOד%l۱mqlm1lv M'0y5W%wtthv< ]U5r ¢`G*n<`)kE9w(+&$əW3zAd.Z zgBF3Κ8xO@)㪦q#!g2gƤWYuڥӇ!~'-g Ӧ BM7\pÆ?M7>2@Ηby˔ʪ OGnA` UU3q7-؁:L7 *ROf?~B=SB:SN96ۯFpH<٤UۺB.x4M6cMwshå^ڜ/ i޳i;-.+jCC֭eeeiֆHM q1MNj+ ,fvoQ!i\)]CMEUcDbuifDJ}>Rb$wHjD=?nUzt!%^|etC6q(,٤/XUU!D&WQT臙RUMJB!b$@x @ߗlO_68STTW—NsJy$:G7[q,Ζ Rz<8rENy911~h҂kZ|E"`!W/>iEY8ICw#&̛wP^+IQPP ^URˤ3lԝ] @xGPmlj3af.GB\t^* FՉ;lbK=~5q()#dVkBПj[_~y m_*{rSbkgŮ;X0'sDa}Tx{12t5iiRL&.8?y޹.̡k|ͦf 嗵sWxRK+\JzTVr>V+tՕ+^xBSB#Qbٲ$xϝ%_}EY儀/qD΋/X@ \woa2M.#4'@k:Y3 } PUiMk+W.Yy ,]sbnmn"G+7x;`(ұ))e9QPTkР_ܣ(`Sr}]]ߨ*(;;pҲlzaOYtm޼j[֭y[oyM7eu8ɮ.Wb1k"ݹs֊ʪmիV{۪Ϟ}cqqsM<3Lp!iݿY~ȳ:x?]Hm.-=׎qUQXhLэ,EJEA1iџA(Cw耔BV]zy뽦5!bӺA GM$M!POmܴ93?p^~ug_] }>I8߻MB0J]p8ln0"haey$3eisȄD:XgyR2Ү4G*J3K&`XH+HyI]t]d\s%I43>w|ƈI tK_ᇾ{tt=]XnݓEQ"bذaXtŊ.X\\WZ]gw>0"xڵkU`lt ,+ {Zs-nc̝ J&Gax<ݻw]AzΌvR۶]fʶ,7Zk[+Q7IJL o_KɤɾoRX,&kE6M>ʫnnn5ߠTtIU<`RRRԤ(q&hYYysS# ɛrH˖h6@8)(\m755J!hx%Ji2i'Os ok1Ӳ8D).)-,,L$b#M8 l۪z'L<.XJe˦aÆ?a e˦!Cn"8IuK) !{7n\d 3)#4;3ʎQ2O9T8}r`HIDGݿigduuǎ5tnu<i۪SϿh8W:4M[[w/lkmh?7P[=|v׮w~gXsC~ߙ[*JK~~I9,$YY)1ϗ?= U-ER0*z#8 Y@RB8}h"<'R.^ #(cm/Zh!=9c= A]>? p /rs3ncqHU,+M6Ƙ[NN\ym;(%!Tǥ6-Y56IEFϸ<~֙34nwYѲr_xDs&L5ǷnmqH^8%wGa3TTU@["0w^/8s!l6MqX% ضgæ¿~ i:BXl}W~>dSc3z`w 8xČ:<Ǥ@X$Blӹ ?~ճX]-߯!%$*sjg C̘1fժbI~{ |:׳yk֊c ?~ٸض;2d Jڶ]V~ם1 j@HeAO#% ~3}A#xb gIo=DQOg旾y a^G|.[wQK^ 0FB 084C6xpr~ם"6"QjThT/޷m+83D VBUpO?|d =2RFg-es>㍍,}2$Ϝv1o9[OXC#UdĶ9#~( 6o'nN5KZz*c3 aОDyg5>GدmEU'4} U1،pG _窥Oz!Pq+|WB{&EoDr^Ssɓ"4uiy 5#Fp gC ñl{tNg)Mvv~q-AjcV[ DJPgx&ZC'}ӊ]fb iocEQ6mږM6oڴ_Ws|~vuMMMm۶U;7tKMRL&PB.m% #/.]¹bc"Xrrq,\R(JKWLMM b;I'>C@w 8҉QJ$hIJpʔ.BF"v B ¹bێm;s_`e c p[o|^o H %`%&y,]remm-c';plg!x|͚5)eaaa2Xn;@~idr [Ҽ)e4m "(!t!.AhOBeZ>1a}d<,^@8]pF\l2fL|nY&z FáN}E$Iߗ)5((c CeA.)YBrigs^#KJpN94Qx`ܸ &M>.XUU>|[ .!+*.+*.8w*0>鳮q[P*3jԘΎ++bСκrMv@c۶ʑ#GM9ԁU3) j1'{`~xAc⥳o QݳCἿEK h+lN$BspSϿ`xt1#j"%*Si(ˣ9d:qLv9/׋s.M)/Zi۶Iz<0-4MEU``B9c,1v Ba+.SJI&+l:a7?li R?z1S[+=8rMXt| &sjBF#xCKNF_3sk?|?E)?_@ɦ|JRNz,pQ$ٿW t0=~SB~ynyHsyzth#At "d1 81_Nn_qqqp8!]eE[8E6O(ATUJ!D6q1JjR#Kd\'71`R(Dgt,tÚlJ&k׮=|,ON.˲ L&oذfO>08a7o޾m[b;4[6o:iڹcUy@XiY-Y($xɤc,Xq<4 ǩ]peYyX4Ҽxd"gBpx dSloF,5z H BDZ6r䨼`8NIIٌW]dWC*!6+آ\,TRz)ey.o;;;.vYNlm%ǎ5_x%`jYСC;__z,5zmvpڮ]8t7 .go vpAg\ƭaIDAT(EKOSQ9 / Ŵ8 D 10O0w/sĉhҴ@{{lո+{ V>\K BDnW{&8ff\,2h7ZQD(K융i"r$McRDoqnL/zo"35?~`f}7ZЙ\j5"fsZYJ|IU|. Ü cvDssle2@iIso ﭮ/i@< PJ MSkNKE8vL )7{$Y1;=T*o߻.4D8Q53rEQyR (*\|4RJ>AR-kٴbE!*An%Iukttvw6Z=m d;xXu_@4%tEXtdate:create2013-06-22T01:33:05+01:00Ȇ}%tEXtdate:modify2013-06-22T01:33:05+01:00۸IENDB`puzzles-r9872/icons/net-16d4.png0000644000175300017530000000065712161170302015511 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(}Ry0M`lby3yg;k?$p9'@#cU>?82/" O<>:k""$H, JJƒ]եIw5@'#K{A;V/<+{EYwSyueZj`k$u]9|lT cD? v)$E(sK84>1w^%%tEXtdate:create2013-06-22T01:33:06+01:00n%tEXtdate:modify2013-06-22T01:33:06+01:003\IENDB`puzzles-r9872/icons/net-16d8.png0000644000175300017530000000146312161170302015511 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭnIDAT(=MOUWߵb.Qۊ ʽDlmiҤm8?0/i;80\R> Ow3yi{sD**<Kn\ (76_}뜈XL%3jc!(˼jr?ܻW$xU͜ߺ~Wi|^yfghHD 0 s/N痯Z~2w>t7Οˡ@bYn{6t:n=7?795Y19cP yŗR &Ư^mo\b}ƀ͠;ݹOnL|0Dcc lPUQ$)(f:n,QJ (cr3#^OD%9&,TjӵY{16_~^յ*ec2Src <Џ1@rmaTE eNZ'Wg f s[WQ9˘TU%5YQ*www8Rjܘe(&'_"E"">|IBg}%tEXtdate:create2013-06-22T01:33:05+01:00Ȇ}%tEXtdate:modify2013-06-22T01:33:05+01:00۸IENDB`puzzles-r9872/icons/net-32d24.png0000644000175300017530000000334512161170301015565 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg  IDATHǍVϏWuOYXk"ǎMd8 % ! čc pB\A5DDXBw=;ٙ__^3ItSWzգ; EnD>T?~_gbBUU՜3,437 碨W~p{IDt[U3UR qP(666^&d,/ZsUY>77w ĉCzffI4(ReLȍl҈jvA ̾ ܃_zhtW/]Zנ.#U#"X1p9`aSm87OT wn¥NgP 5f 󍇝?ߌʈ̌RAQ!i7ffbBxíy=*W׿LfS h0|{!@?lB\{7v:?ry(8{߃pR*x~gNvwY_c[/t09+4?}W=5#e8Y]Y!&a N\XY"DjZW0QQj"RusA~"Tw㢺19{b@}BB (|UN*_|QQ$qՕ(q|^W\SSd`v{!b^\,Lhͽ1]T335f0QlN;WEL<6"23ff 5%~@$s^_U eƛoaz"Gt:_8*K'2J/tFv]7ZskbwnujfDjBp%kKY!CPzPV a8fDdDE{ IZ|39y^8>NKhppn!rD vpAg IDATHǥVK$!Wexȓ,PD^ $0(o!#纭sQk-10*.Xlw !W[ A"UlQoR\X$yN^ݼ5}_\I< }&9T– ts /5֚E+[EC[}Y/"sxu3;}Ƽ&WUu OѫFAw4)!.2NtI7 N*=PǠ2JWs-!Z7ά,",Y|QyUvVZ@FQ̨loR\q+*-2"c5VP[r=8œHꏳʷ>A fK\zSEjDQBkWxW2Ig -=%ђV=|S%WUvr5{U?˽83H\80h8j- @I\2g2f=lﵙb/- [uxfrzǟeu;DIx̖rjM}xu}{mC#<ۏqɃ"da?;|"G_Bg? %tEXtdate:create2013-06-22T01:33:05+01:00Ȇ}%tEXtdate:modify2013-06-22T01:33:05+01:00۸IENDB`puzzles-r9872/icons/net-32d8.png0000644000175300017530000000334012161170301015502 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǍVo\G|g޾k;qUQ7MURC #78 gT$8pJHp8R8Ui BJlk޼730a5k>77 "<Ȑ#⡷ۓ$=K8H!$i)VԚdY@)w:?Y{GRXOx!ъOyyId9,bqͲTeh/jO~ ifnfFMphtY~(ISE0WYf- bkz^yoɸ,@WsWnf12I! ̪4Hc._26r9Eh;)큫ԃ t:3iw:>TY+/;!Mjۖ'O )g^76.?oN?s417r;@ U̙ܺ uV|wEh2oe^Y> ïc(@eGf+kk={8߯꺮sJ*漮+c _Ņ{Qv>fe]78)$18*cu{g(BpBH[rpY-L@ڲ Zߧo3f0uzNt$76Gw )DBy-$J1YAkkV[ͫ:KVi#K~}~n(=@UΔ"yڍ(:|oegO֕e9N`Zc-ux""Wi7[SnnnJK5$ !])Z5{}:]QGں;-ǀL7 (a ^cIҥ{KV"2t%Rzok*aYY?uS.[始3!0O>hϷBj4&ۭTJK9f&&KE ((&΃`sk'cHR|Tv7{.I_de̫ v;tDSbQs85kmKKZo?s1c`bi "^}s"& K?/ˋ;q<>DRH&hY'|#9D0G4Ӻ7MgEOީD{ƘXcc_l:d3;<0Mg_x\XZP>l~9Qp_&@QA8lv#cC;'nmlFCVU(J_/4]#}gEUGTF*3۸_Rr `A3`# ,n8#@d֦t8dAU!L3R80 !i)tBD1Ν= @\~%:y0FpgybRzۭ|sدV50cqDao"C%tEXtdate:create2013-06-22T01:33:05+01:00Ȇ}%tEXtdate:modify2013-06-22T01:33:05+01:00۸IENDB`puzzles-r9872/icons/net-48d24.png0000644000175300017530000000506512161170300015574 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W pIDATXYo\Ws{ofx؎!vgJ7ݰ*H݂ XTbɢ*¦  RhJQi6i*'iƞ{=gqWo};w;ƿ^Ӱ+UU0ӝ[QUDb)-6IZMbT Ocs#"ՕNnM/&2sPDdxjO#&wlƥ򩻟}xdG~,]t5'I K3U_$/j$9$I|6NOO1cGsbVUMtzzZD4`(D;HUB{"g!B!q AZ)ˉij5cLZ(EuJ0 (·/sj$(ܓ6; U2$\CDLj-b(dYV*)ZfEBш&6V*P-|TڼӹhpJt\YL0cG}عtگ<}sxo@K/]OOOa֏'9y{$nA&&.u,u!"( N̫ɲjON--UR^^Vz?7;{p@D0=7pX/[֘m?T+r.0y(Jf="U 9'xVx'jn{r7Rݬ -:Y [Cw0ιųgvmllxh[2D0X" K f!k 3gYm,KfY!3֒H$!NPܑ1Zq펀HE[*ABQkkkMd+WZ+R'Il6}MF_c0\ιaLB"*(CTPUQQ!B~S+JVN`6fgf.|jfUXk޺I\|P_\hҗ^T*333TCΜY*Yd/U~~OEL^w3N89sߩ!(4_;Y\RAY>c^tȪHNW8kZX_ǘP5v@&6,DlB 16 (42ydg1D$6l5}~PXf&bk(jmnllMqeWHYh60ݍZ}H[8Fl6#$I^olM^/MQB0q4ڝ?^xq;RQX]Ƙwiq9/˔ 7M.]UWٙ]2P9tP`znP%(GkkkWae_\˲/$6y1&kU$H)$qj~P%@\i@{+ @Tj/n>q%JS|ǏZZ>Ώ W}!+;YCƘ$i׾n*Dp!-}7@T-*T^V%p/*BlC H3Y0Թ ]gOת ܤ4-!IJ@D )c*_<~쑛xrbpv^BCADVj[7{oޣb;?;{baEUnܸy]J%╕ٹyq^@ދ",tmFuKB5},JY^͎.HXl; A@2JޡQG75P]m"vHG-@Dc* h'D4͋j}2qҍn^gꩩw `^E& (TcKF}jS_5[[Dc HeÏ>2|Q}x?ս QK.S Q{\S'ɝx)c_?Aχb<}|8T5 !uhn/ 5%tEXtdate:create2013-06-22T01:33:04+01:00n %tEXtdate:modify2013-06-22T01:33:04+01:00uIENDB`puzzles-r9872/icons/net-48d4.png0000644000175300017530000000174212161170301015511 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXõYk N%7 9\B[ӵ!"̌v0 3;`D;l o-۟~̌DK+Stm"kcxyZ]5N"[&v\ktlك,:>)14X" L?NhM@D*T"" 4H/ \@|NEё2w}48PnXLj2ᙚo&Qg8M$ہ&dT5Nb2ư ȫ[:< &>CLpFm>chvb}d 7Xgv ,g4=8m*]Y<=O "\[rʈ32}wAȹx{9F$3B$1c|z-z(&C3NpB .|OIrU}V~#wlWwl=W=Ip*E4hmx z%HI^.?>IGP4<6ޚ$Ϲ aR|.ۧ/9rl vpAg00W EIDATXYM#G꯻m3%݄d )!pE9K #Q"P"!!"pJ6,D!Rd(;؞]]8c{G(`UWWT]}5W&vLr=,c1vR!.͍Ug` ,@KÚp9OdR&LђK}OPgWqL1>ts֍cu)+_z"{#9'OBL,b17F^2fgRxKf!'RQ") k)~Ka8w}}l VV`sJT[fS =:zS (SZ\.Ft;u,R]00%\4䒱4WrчУ+8D>z_LpSee`,7y #\vsJ v}QQX;H* ,F y)Bha9/!g  )"G"W*1Yd+ ,*BJ)cq R3=طv5SRuѧ?1~edۊBIƊJ*e3>grt@*kp8BT]VvU""dY8{>h,%΅R09q$q"cc#RJ)9 <iű`MҾ6hvq+jq!8ca^#$DN9Yk4tq}<Pwγ ѱ|InQ(zgVo=\7wvy.R.T]$c$3j$ 0DB;S`:bMqj(rƷް+D* xqgRRA(C\eTČt* ˗_0oXcixDx'Μ]̓\Kއ9$QƤT$e1^YXI!"@I/ҥWRrk=".Ð۳qJHۃ;gZrq]tUeIK_L»-5苧NÚ !\VӅۛo4j֫γ}a/נ#G~)QIdRqXx|{c$fdn=;*T)^l+#JĞ4ScZ5p)zpN|}=9-{( cw %nE\8RR{/@o^RwV˵}: VKNJn*ukofyj^PBM͕8+'bs960ٱNjMgvZ [q}B,[[>y"}[{Բ["wu(%{1eT|ǐ19R[΅R8Ay˽\pf MYuo_mmX=Bg@m%tEXtdate:create2013-06-22T01:33:04+01:00n %tEXtdate:modify2013-06-22T01:33:04+01:00uIENDB`puzzles-r9872/icons/net-base.png0000644000175300017530000000253312161170212015740 0ustar simonsimonPNG  IHDR CbKGDIDATxArH%Ma`1L%Ya=xd(Ԗ֦װ爞Zk>~{:߼km޾US) ) /Y-}nۿb5Z .1m}ɵHiHiԼeOcGC8]Oϧ!ReN] V5h!R"!R"5{џfo}i~y ZdõHiHiHiT<LJ~pCws7 ?I[kK_܏ZFJC4DJC4DJCP*\HiHiHiC/@ǞNq"!R"!R"!R?̲̥sCoݎ/ػV^%ՃZcמxSCGsq}Xs[h%ǧo&IiHiH?|nC4DJC4DJC4DJC:LJq?ۿ/N%ŝ)oT=8՗>>Uc1As6{[ks|?24DJC4DJC4Dj3N>r0M}d 4ZE8[BǵHiHiԦ6ov\4ײ=>ܷi*>?|W=!R"!R"Wq3Bg3 @U 2R"!R"!R"!Rkǜ$di.6?uz) ) m^J׺h[_ClHiHiԅjwQqוw+4x],cu/o|KASϥ:^|jڂKyOMJC4DJC|n"YhYkTe!6LC4DJC4DJC:Zu3>@\N_>OWuIFuv-yOMJC4DJC4DJC4DJC4DJC4DJC4DJC:N IENDB`puzzles-r9872/icons/net-ibase.png0000644000175300017530000000152212161170300016104 0ustar simonsimonPNG  IHDRqqmۇ oFFsP` pHYsHHFk> vpAgxIDATx1N@@/ 5+r[IMA4d$ ;8fW!DSLaKivu}+||hT___I'4 CR~w-lʳ)ϦϦlbM^_ꩽt(My6ٔgSMy6ٔgSMy6ٔgSMyy%tEXtdate:create2013-06-22T01:32:10+01:00Dz%tEXtdate:modify2013-06-22T01:32:10+01:00ȋIENDB`puzzles-r9872/icons/net-ibase4.png0000644000175300017530000000137012161170300016171 0ustar simonsimonPNG  IHDRqqmۇ oFFsP` pHYsHHFk> vpAgIDATxQr0@²,-;jǏJy}@=_1wdĘ{/Zo,ֶ?+Uk<;s9ZkC^GMy6ٔgSMyRZuʳ)Ϧ vpAge%{IDATx}yeUuZ{sPU=w'5*$FgQc}*8$A @tWTӭ;{=ǾUTWsﭺ_{w^{׌?Nx;8^!|RN@ q_y?rʕB>#"{ |OrKJn7}Csb3K@@"xzcO-+~t }{6@SAcp.7oVs{B"rg]g#^e"1G8ԧmRI82 !VqDDTJ!"/ g))\6S%L" qā>A!f2[&"D& M냁hdlа^e2@,)MD # C#e_Xh)Q3IJ0ra'΢ " 1cf;&y'EM GW" QvlFh!8' KȍA{vvIQN/BUWt۴A"fO6M~c wO7|]Ri !_^U*|hKuΝD[g?d\>:Q< G;5 \dBDc>hDN7Q2D OSpxջ6dD0w#D3BB\c~Z/,:i*'iۭ?/n$|QMDl-B:^# =d<['|dX?W"8{œf[n55gO a?cFsϹ d) q?Ix5W<uJkZ3p '1S@iٜ""pݺI2@cp1 |?4y#b]zS芅LD?Y, 3_}ZOO$B$W)ӡ_wR6 C߰8uZ}>Ʒ$"80IZZ A*ǻ;՘V2SB $ DdBdx+χc, n4lo{ vrd2Mm֦WrO,B(IukC(vPȗK0QJ9<2,X?n 8na e|>yn0-%HJŢ뺋!X" -3*ӕZfEBLMM#b6~cL>4olt<4],%U#wޥ/\ۻ>uo>ȵ"b];0h}G 䲔m~.uuz4oW|ժ 199/| ?;X_}ӟ;9Farぁqgn8Pi5TW?x1X&"DW?z]_\6XbttqUW#ZK}{_/%.zqy0u333aHlY>;9×MO'[ril߽3N?-Ck]fK9Q)i8OlfppM6aHS*r_}QXqo2JRK cᷨ5Lbcc45ZVaJIhe4lMD$zޯ TZ61l&_uƸ%IlhmE9g5朳"ԍaqᮻ1' /\oրկ(YR ~=x}/8>1XHH,'B~pbaj'zay]76HwAxjjrX`ycSD4Nt:~LXdzl#kM+MY4 ODZ+cͭ@Ƙ9cS`}afI,i4!kv-Sh Df2{3Xc8 Py6ӧ\׵C:_Ϟ:sBkݥD ĉcDT(< uPe>rݵi# cݴﱃB$lfE冥?R+i1V}r2P)eVJnK)ԓjwvfэ]J)cIXS/Wv#%r=7cnÆ@Mvn.J!uGVD!s\>ع0>>je;rU6lk,\0dH)G8Bx;NZ}D @{if! ]׻~0dNCdY<Ю.yamƌ 0J*Ѕ;Lje Ub| u1% JB,\\0Jͳ$0WK<0%olpʕ&S'Q)1&3ԟ_ >)M5Ii0ΦJnW\VؼRHDRƞ)B %& nncr3Al秖c& /+ c{Mo&3D\sξyKTqa+.~ ;wZ~qpI:b-ދ_?p8_g1X#Q$R*1X2U߯5,7. _ʙ_A+"?IkaUB(&0H)?Y؜@0lF1iPFU}l&eW}?"΁Z3$W2UR^TfG2!@I [6'Ss2Df1[;cYsil#g:Ɖ4c9 D+ !1mrg]lOm9鯻`A&.?-@1B8R5ˆ[Ɓ'ޅ=Ώp֟SwDnkrƘR{hVKЕx(hIp ĸ-CZNnrs^Gs3Ʌs<]#"[bج5Y'\pՍ"M&7wSahaEmssKD<}bѨT*t 6hHRArlflDkN!2RSHUq o&:فۄ~E8_@uLxV*Q_B޶m[ZLJ*[~:=&odN\GƘ|'A{TULWr 4Bѱ~}ͫ3J~X_r)1i1d2"0VVGGG)'xoaN*\cNxG?jIhKJ8ܚqygRQ1?)s/`bLuWeotR"Eͽz;08z+^+'fZn>3N?= #X_yAUDD T*߾w_yE4;Wn \+=+1`XkS.sw‹_Zx[nشẀ17W<|@RW뷾> 2M2(c)eZ}%Jb4K7(Lc( }O+r 7y}z˕slDm}ıV]io~3qTiK&ib߱%b~VqScJ䥧tiYg3C'l7יV'fP5mFmZkCCx!2s>sqe ziEv?Bf<@Zb1q@rb1ed%ֱ=P@(<ssBA1 6Qti=,$"y8-Dd.~a QDitsN7|҆R/Gv;x6$ )S;v=][;cVhp`ƍ톋GZ#ͧs 10:\ ׁm|ѿ~,FJf=lׁ'o_Bz꫿=0ȀwrW.oϯU80pYą[c%͜g}+]Do8χ?;/x\f˺P~hԉuuR"̴cEph~GJzjR/b 0 r´yѠ='ZKL8l1#lW_zXRDp]:TuUȲ0sL. =g;N Fo XN 2_?1vL'X ~r}}x  q:;p􁌩0U%o2؃cTp`o;oJ%d2S;w׿^ HY/`!0adKk?F#:~3"HT~*\󎡛Co`8Vު(r=x^J9,dE)69zjyGfzk^~s~ҋC!L?㴡͛eUܣzqoiTkS(u833S-o~וWjzr* c`j^w:.3"W*թn۪ݴ<:7RXP"gVw $6ғMg ydk9H9[2ӄ-D4FƖ[H !cGx+Ab1:{rL&cR\ kc,zass;rع\* .|"q29gQJEH\k8/ˎ4~pP(fr8&=44yn6*&rYpm# mNB>77R邏裢8~smܧqˆ~@''ipФB@>6GU*Jsȏ"<4H"*MyV6uI9V㣔Rjocq68Ғ c8VJI8ci'2"vh<nLSB@jX=LG0mNuI38p/՛^ByV>ߏwEhG839>^'O"ҟm3hD-r`!"*OڰmY~: I)o>u3:3@L^ˁv((dE4f{ar d*I)U+MJ}(Z+g18#SZkT2Rښ)Ǭt`P3زT(͑ 2;l888T* YKE4cmkWj4|sBM\. WsޝGVq}9fBUq_y5.a} rsg5:BEH:WyS7W*yP=[u>hQJ`ъQ1妉- >֗5 l8ah)@†vS  D.>-{.-ET_/vH|wxs,?72F^_H !c8qm-n%tEXtdate:create2013-06-22T01:32:10+01:00Dz%tEXtdate:modify2013-06-22T01:32:10+01:00ȋIENDB`puzzles-r9872/icons/netslide-16d24.png0000644000175300017530000000142312161170304016606 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭNIDAT(]R]OQ3vBښj$PV!D4&d3WI$EFD)-. nJ693gw2EcL2InDw ""}{|""]Qa10ƈ\8"Zk))MJҕFݴm"B!~ >k`@2(7i[6O¥D~:]Olnn;p]Zq! JTvW'~}ߟ0<"r? n{/0Vj0PhdLOe%܄饥E96jo89XkLrrIk ELQRR*Fq Rǯ^oqwlݽD*zݶS"S ǭp s?|dPyqO<#"}~vkZ~-ie2HY%R^\NNT|m]^ڿ}^_$QJRqma'ψ2sJ {n w-c@L1b,N4ʁ$W_fQ%tEXtdate:create2013-06-22T01:33:08+01:00Qa%tEXtdate:modify2013-06-22T01:33:08+01:00 IENDB`puzzles-r9872/icons/netslide-16d4.png0000644000175300017530000000066612161170304016534 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(uR۵!C; Vb)l'+~d>.s &D$ɽyK2091F$$f {p>jj}k-3+DR &I캕Y yAHF)__3=<΀K1hHߵoD @t;`Qkk-)%%췭 93pI"ٲ>ksޯsA"Đ@ҺYΒ֚w1<{;w.XTl}z`#G%tEXtdate:create2013-06-22T01:33:08+01:00Qa%tEXtdate:modify2013-06-22T01:33:08+01:00 IENDB`puzzles-r9872/icons/netslide-16d8.png0000644000175300017530000000143212161170304016530 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭUIDAT(en`؉'[@t Â'PJ"ihRF*: g19 0I0%i^@x c/ %p. Ȳ41B ,-x!5Sb֦iۛST*lZ(J8{_Ry*&F׍w4"j4;KnO++ftyކBzi7:埗QB(RbYV\ ycv,8YD q$$PBB(+铞1Q^[UʹRa a_Ҟh}'m/<Ϗ8 0 *,>4L!z2i͉oo?TJFpUV뵚䗿0"N^g衱XQLkVSo^#]p.B)HӒH Yjq:v"%tEXtdate:create2013-06-22T01:33:08+01:00Qa%tEXtdate:modify2013-06-22T01:33:08+01:00 IENDB`puzzles-r9872/icons/netslide-32d24.png0000644000175300017530000000345712161170303016614 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg jIDATHǕVoT?gf{ND56 GH`IJR)JV!PRKVB>T6ԀQI*$P>~w̝9}&awsΜoSqΝsD;1Dn󲧉Ra!mхi*\kwڵF덍 OJ_m5́fYO"6VjFuCξED"gBIOz $IN@ K0c20$"ڽBӿ+09Ipγ"dgD:b!")eb/ ժx ~SOO0VG~?{~yl4R])Wpv^&ge>O֊A#z(&o<wcűgjR\.T ݺe|~nnx}]pNIm(Օk?Qj s&V:ut[J%qlqX߈:]cLĽhŽ{fsJs.5y#DDD#)}.1W_MDstϝ`vv<"Z[_t:sxу>rr Z 9Z.^٪{6Z,pv*9N:k?Ԥ68 H VzٖGGk/9LRZ2-#?90cZ01LMhn0(" p7q=O΅sT0cWV-=W,shQ3UQq/$"hmmyg?=322$Azݝ3&8s!N'~'g!!4M6\ޢnjm8rR,"z9i8RRYAU Ӊ$Inܸy|%" s.o?[TWrsc&Mn녜sDr([? }?5湩ge"䌩s1fwO:1Qkkc|O9FR1c|g ƘJrxEz×rj@`|j/OkRjz2&&RRJy.}6 kZDc7ZsI4T~a?xvV R/:HӌkounR~d֦P( "$FOwuuAJ`I˥zn` ` o 0܅9׿<" vpAg IDATHǥVё0}:[ɺJtȕ>p;7sL&%<q+˰>(>03Zc p+ɵZ+x[a0sQ'sU9\AU1DsixfAPU$A=Er-]s엚Id)N4{Đ̼B962^x dEC#ǾDV":q5ZlD"j҈Z[Blig#jlȑDD~Α(.D6v }^x7:3k @e6(V Du=Qm%wK~go]WW2[aon~ NzPH@޼Leeoh 7bf=(>eƎMѓ_"吁`0x<̈W-Z#fimaZkjY)EUK)`)HtL%r{.y!aHPќS4s 3sf //ELS| *r ?,,WTfËrQ)f6ҾRaf؀ײY؝nF)k() U \^xfm+a0r70ݺ{0'y p4L9T)lu4! y⊳1<3CU,bwݷixo7B_~wa%tEXtdate:create2013-06-22T01:33:07+01:00_T%tEXtdate:modify2013-06-22T01:33:07+01:00.DIENDB`puzzles-r9872/icons/netslide-32d8.png0000644000175300017530000000341212161170303016525 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg EIDATHǥV[o\W^gΞ3cϙvP' M(<cH*RV<GB)%TqT=sݷRO}Z{Zc1Z)_w'vpZa1_W;h;(յ )!`osc]-ݻ}ɧ1ƺΎr3zR7L}4IwwRzER0F,yha>`Qht<2h4n 0>x'Ag$B2FVrvQLNNh%=.w3BQe(>!B%#TJُ"2n+ƘalVZW;kW7B@Fw]x I|嗿rik_zV1>ȴFnc1FGcɔ2Gx™Etc33͖ùW3J*ĕ{w/^|Q8؂ooK/uT.:(eqb(bI(qAccs[)Z$O? 5/۝vE9f8.r)T3PN+ygL]_xlN*ENx̰P jRT*ryG)dr|Nbڜ\#H~0Ƭ\JjU XHzKiFa2#a1eJ!108McBȭ #U:ȁm[ͦW@ ^}P!wZHx\d<,U$$IW_y$EPF$MHNRZ#lBH/ DcbB#o /$%P8c4p>%$wrgyy3Zdq0$Ixw| UM])֪Uݮa`Op<ܴ[D(qc26w#vSեPQ`S)1a89x}_򧦦(j4ѹyBw^/IB>Ͱ|E .c.Ԫ*u6ܪo =@ "Ni*j Jz͍V=;;(Vq3ƎsJcW{-CYGrLu ,v}J0vGAYqP\_e 0Œaک/qja8!FCP*xٍ\NZ7?1H}ӑP%tEXtdate:create2013-06-22T01:33:07+01:00_T%tEXtdate:modify2013-06-22T01:33:07+01:00.DIENDB`puzzles-r9872/icons/netslide-48d24.png0000644000175300017530000000517412161170302016620 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W IDATXX{\U?w}ܹwvwkjߥJ41@K1ѐ7 kZh( AjS---V}ӝ;ss?ngv]d2{΁No?ZR`bfD?, )e\vL#"۶(2ƴ_۶=yB3\i8aعk)03 _m8ҩSCBa:O8NT6ݵkwJ팘]+70?Cλf_JJk֬}E} }N0#Y"|>8N׉Hk⥖A;v^1I-K8g;QC(yWZxӍ7hm""f:?O_M70ײ˺ D}@N HoYh;vWx7c 9J׭8 C!DV]5f2VaQT16s`A߅ s9ZY=;u+Ο1|>5bf6F`v]W4Jֺ$ ZB0J86)r 1+˲{{'9 bff"h8zZ\, @ zA wYԊEn fV5~_*_2Kg@D$Rj^EXK[]'@g{{7'޳/JE`۲޿8oZO D/A,\w?bN6m k5 XYgif@D4 &Uwܾ_TF/RLT Gym~jAKN/Yq۲Yax/YGR]/xI}< f<w IT^%YT*1ʅZRFc-+5.3SK+#cJM2I8뺇(.ODP D@^fTIR(n~lah9[YS"B˯v/k׮,qIe Q>LZ[|YWW׉'*aɋ P*fSO R,Z4x_ NM%pYzo?LDQ͟??;?+ETꁗ^ƍ>0aX=u TKX1NQT׮]S'̜|H2#S42;bEuww]w}013gϞ;wZij0 QX[n>t6zpp }KEZk{َ!b=ښrr'RODZ'հt (@jׂ`R\`eR0 .]~'IRfCO9~q)݊(mG^ti|eL8?iIJ;7߈ژ |. C'w޽{O.x^E^W0I Q"ĸ1:ݎ!^FF8QJT{g5#Wޞ4$d!Tt Ŝ**P.@) 8;4&&Dw=Z-SA @7m\u0q娝CHU,jqr̾NJlyb fnǑJ%J"B{1eBY=Nχ1T n{F4[911<){\BN[N;l$T{C@BJVgz3 J[lL[l%tEXtdate:create2013-06-22T01:33:06+01:00n%tEXtdate:modify2013-06-22T01:33:06+01:003\IENDB`puzzles-r9872/icons/netslide-48d4.png0000644000175300017530000000150012161170303016524 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W{IDATXXm eޫlތu,$N|>їf~b%? p* ̷ vpAg00W IDATXYYvg׻^0cB@J"0Dq8 >W]eIaNWVRwxГOZYCȩN[7hEp7y;h}')F!PfZ]^w˅I΁7?Ͽ{oj@=6|8ݽ/oUr=Tr|Ri}\ &&3 ˗D)Y*U|>f?}zlI)AD"?3yjImjWB|BE#8gSL?< 0@09@gI2q::Xső ވ2X$y Z鉉+f>Y}βeu'~ n|bl.eC_JQʊG=d:.>Of'wqT ^سX,mܰZ #|R(BV%asKiH*ly =d o[w"IN&㺎2ls]|uSJ-MXW{}Z)$'O8&DiE5 arMlڿͰZfҁ~&>A+TB!̖ k("Riy+[[>?fZH0 i;R* D)ZAdR'/B0ەri0׿8Jz?qG6R@)-H{HIiy}q!$y?+w>RP@h⏁!X%R (e(^s5$8ђ+c^ţGU  ؀}ttGdN,KK=CD)%g \e֞,3cNˮrM?4!dfCB$Ԣ BΫ,T~Vmϛ0 9oHumK)m{_Ӧڧ曤͛~o :=11i,+PlVq-LJm3B A3(!?uۭ7o1wp6T)Cw溻 !cE!?+DtT"3^JQBj5DiLTR)qlIZ~X#ǎr8{{z, dF],~Trү3&3SSHJPaT.NmB")[!2hرs͚#ˇ}otqv [\Bс~!<788QB%N^JisW !Ji-J5m[wF7"ֲ$iC{P=n cǑ9*%JMFv1Iu߻q?nܼۋ8K8WI2BG8b Lk]OI䈹η}%^LԣJ`Bf~".xѶD#%cCcj,Nረ=zrՅ-[6W!\fBn~(֮Yy i8M -Jdi!!Ki?F)a2 BŢDűnJܹ[XS2홃GPJjOMVk=S:HZdڂL0)6HNX)e[ֶ a=8h7񭿊x7 ˋ/dOǗOHw'-%tEXtdate:create2013-06-22T01:33:06+01:00n%tEXtdate:modify2013-06-22T01:33:06+01:003\IENDB`puzzles-r9872/icons/netslide-base.png0000644000175300017530000000617612161170212016770 0ustar simonsimonPNG  IHDR!!LbKGD 3IDATxݿnq}7n76na-]0a@e}8AUrw-f!X|"|k~s}~PXSxdX B]d E]d E]d E]d E]IR=k$k0Wt1@t1@t1@t1@tܱZ钭u{nR9왊:nehsяՄ3ǧʭG|`E]d E]d E]d E]d %SQU3SEbC{؛M'QcA^_cZwqySUfhtV$fQsU}R"p}ufB+*N'mY\pOCc|#:gӬq6/;p`.&fLQIc0.,f#` ˏ2.f^ϱӳe{(n2\ 'g>앱t2?/,i+;|7:ML\g[4s1{ 3d/>`.r?bz72`Hu_hQ_\T":670e4Y\qb/6{u"7X!s_H?ǿQ6,pXNPÛ'!ϱMY>b){t3i8c~@&#|nu11k6WdbOdpa1\P̼r ϻog#T9 JOw3pxY Pd J[=@l:ϟKe>M'>5W >7]e]u"u';`2;p|}<꘍w_z}u^`??gmsE@t1@t1@tv$nPvK*;/%[%%[lH!cY,fMnmbE]d E]d E]d E]\2dg5Z{Z\Еs,]{W"-Akx%k%%Xr+=L s E]d E]d E]FYBs3]f,X=)o kgZ,E0]d E]d E]d E]d E]d %SKUUӳֶkH{؛M'QcUNC_67UNlꀍFMLnEb5W\P$I߫c>fB>h#R&  Y19)`>},aKG>glS}gCӿsY"SD+x c ob>~$9"f^ϱӳ'P{7{el6%s-/l:f`p_>4;FS{sc,/"GJ4`=1CXasTͨBB|nzM13.ls/I# 01C" fȘH\ow4ioDYDDWcY%@$`Nj9Rs5wZ>2caتY>{&c>{{]mqcd[N!6WÚV-U0MiZ+jkL2$7|zXSN0Wt1@t1@t1@t1@t1@WTG}"qcMoUn[._N7k .݆޽?~鏋XSx$11lJf=֔?f Yk5Os k쟴3漇k 3<kJ2^Z{)2ɘ0l{0k'caYk5OuU|laǚ'Ì9jqrz6N Xw1>"c.2"c.2"c.2"c.2"c.2/п.IENDB`puzzles-r9872/icons/netslide-ibase.png0000644000175300017530000000224212161170302017127 0ustar simonsimonPNG  IHDRh$u pHYsHHFk> vpAg!!IDATx흱jAOIAME+:.ˤ0 jɒ 4 !R:RHJ78:MȻ$Łۻ]}bv>kεb@ab L 01&@tt;PX'oߜM/,8 t{E!ɲsyE󃃧V69<%In:{x#dDt@Dʑx ɑxf?GGXXTD'a8gF(pǙ}qYt 2gۧ|NP (XoYz:pV=6geb/)i$9"V;ΦoU "a9̦!܁-1,Mab L 01&0z5 5lb L 01&@ab L04]:L1ULa%?UG/Ky0/QPO7/Vўn[`^Kyb0/Q %VW[.$k׍(p*RKj#)3TD1(`^Kyb0/Q y=Ohs˜ ʂnybЪ-@ab L 01&r`DM%tEXtdate:create2013-06-22T01:32:10+01:00Dz%tEXtdate:modify2013-06-22T01:32:10+01:00ȋIENDB`puzzles-r9872/icons/netslide-ibase4.png0000644000175300017530000000175012161170303017217 0ustar simonsimonPNG  IHDRh$u pHYsHHFk> vpAg!!#IDATxAn0 w>LOۧgaѠ@ܮlQǞ'4c34ܶ!~@0 0 0 0 0 qYa!LZk׫x.(kw8jޗ 㹔;ٌx#_{٤x."l^ vpAge0IDATx}w[ŵ[$Zy\.` ڦ06K/yIx =!MKB BI ` b{Vv̜#iەv|r>9wfN3dKz+hCRTR'?RPJ񘚦/zD|ރMRJM<?' !~'|x<Ōu]wg~E)ik~ɧ~_? !u[~˻ɤa}#ޟݸ୷ &I0/~y׍?eݺ1veJ|=p%SJ|3{<&11E[ȣlllL$i&4Mö,7niC2xaÒd+ ;7 rm܌=O~-*EӴ[o*8{6u+ιa7>3^9~|c"\ !IR,m̘S~1icB #F?z!ÇK&eb,*DD׳lٻw:qkH)ԡҲ'>>OJ:B?ضntm֬hB16tŋ;[lǨiZ8ᆛrԩS09z5>ǚa/ce݈H}kjׯx]wgU(z7m ^r՛isӁ<bʊv_1v/_e^{~qf7RJǹ뮻ׯXSS[oB2H1K!+cnhhpGJY F}a׿~~4M۶ .^/cdzVqcmmmiz x?7}3NkD(ٳ?[mQh4+cZGikr΁D@ƘJbºZd0Xi[4̭ $1V2fv,#کc3cT"FHDӴ3R!PJc2v0Ja1(eWWׅ1F)%+c$XY2#I)BdG諴Q( BeIO1~P!@ +{aUH){EB@Jiz̪RM;Ӷ !ƙ2 `qB+Vw !DW?D4Kn N B BIY+BB)ukܼ%ueuRj=,q)륬BB]iI]9RJ:!e^K'r6{B~!bIՒ9ATpAӗlJ KGL!:g}1UOt!-n-ƊXBo 6KQ!H-eȵuzFgB:<\ph]~hTDY-$7R`kz}5b*:Dz(\#z*9AmM}>L&<( P|ͷ޾+ڢCDBAU("x ݥRz<^S)| !B?-Yx,6:57ճ̘z.C)eee[n;NRC S!$u X2A`\l߮3\'CBHmMM}]QV! RZ Νs~'b햑ijj;|J n{B;|BHs"wLBP{:R\LT;Q؎SJ-˪ n~z-+ 4:|_Be'39h(u0֨dzѫ)1,eU=97Dܿ@mmMz;4͍^&DR>Rm[СCBAQ i~(Tu5W޴`bڴjͭ4M%)UUA{'Y{)5)M9t־HDc.BǼ읙'$ \,K(^ @pΓɤQ/_i\ͮqjOfE0mڔh4խVB,UTT߰~A)%e1F%f+i*9oٲ 9ufL[aË/=SJR;ft0X|͛~r5ivtH)T1 QJ'NodҢD7.KRBPLJ ~!A$bJ,˪z'_͛jǴTOɤ*ǔ.52iϋL}2uG#$ց$B \qBgV_(jR7nzͷs\3Ji^wW_s][[4lJme6`k\`d!R뫽mV]]DƘSY]D [ !pݺ*+ެ뚫 W4zkv`Q$Ccx1w*BY~ HϝC~q ţ1Vȃ81D?QGN2M׉\S檪3N?-+Wm\RV|//8ӧc4zD *gT{OZUqienȒ,*qΣ~J3':>y*ݸ D|+V.[nsbYvUBDpʒR҈FE|!e7?fwYqxm}55}lƿSU ){?ծiuI۶9Obx&Nơ yVsQJõ** l""rΓc4R,\C=#eTq*j)#~vc M(U`c0JKH Js]Ӵ38maD"IqRTſ1mݺ[Z;C\. onM۶}o#lYv#!R]vX\[#dR)K#%kSN-O` @7XXQQaYVƺ?:I5M饿F Cf`Sz`1*ǯp6nܴf-@Mtؾ3)lUjXu-}cǎN$TH)M$c/?^xaTo-oPQJ &TUFuu}FRhFڵkڢ+JDܹb\{}ј3UVOM Y3(ߤR@ 0]٪nfRԒ%^~٥cc;8t ;K,a(|ҲʄQbIfcV^HQclӦ=2qx|Y(p]MMJ@x<-fx=!DFKN:!rlXdE]74gR!d(TU8FM*"QvJ;B2Snmvؗ:yXL}SVm\Es#c*õ7AӼڳv!}wEJIPJӡg1+5JK  E\_ug:ko/<\LV?߼h̓suLp*-Ξ=+JYGHڶ~ݵ'} !hztPiŜJ0bi:lck@E1VpB(e;ٳg_͙`8pO={f[[[g}`Ji\9)Bŏ)uQ!eO8e Rtsձ)?zM8GD !JBX(r_!/Os7 !8oa(pG؟rp^]=Do}伔PϓT.ƥbyRf3RݔYR1) LPݏ#*IW1Jٛ󡛻(Mk>8l ]|>HIri* pц uB:w I  hTX+1D֭@ʶ:h|-@c]n"k)|zYj7?@u^u ^~%(sλ +3m4hnD56~ܸ:4ٲ,0&Mwos M!u4h#8| "JbښI&D, _H ]1/^d23cU#H$"aРVٞX*ߊ ?TT\T?Riv˟λ`os3!%%2e-f-$2J%ʑ#G(ݳ̩A’@ hVXa'sѲ U)6驿v#1xolj>*$X(/7'XuT}^c qrX'@JI0@GP #4wߨ6FD_}x51s6Wvn_x9`mYVsOЄ)(/DzCeH RC&3wP]ZQkzk$bFKdN-,ATAZh{w=˶H7|׮m=AWUbf,F^@?WŎ Úeœ91ʦͳWҎ(ec UB.(^NkL4WH3jLBp.BHEEH扭sΥvGM:r̡Lc+Vs`klqͨsÑ|R5v悏8a|c6T֑g׮/ÇyqkZ>n'\8fJAjUBg"R\ j/QRJ pK="))EVPBDIQ44g [T:BHc|jˀ- \k%5ع'/6YJY4Ӈc1*)oVB@ `Y%ϩB߿gt<0?-^y0s{}_0c+屢$c"K.`؁%_:Iս>N5ԂEtA#RH}ڭ[Yo~Ji?Иʋ`SJB;C~:1ϱu4Kᅿ-Zxsǖx]ȵ4Mò셋n1b^( :dǎqׄdSMͰ.BJi"|@q~{֭};޽{ BB.N81[]s{zlV ,;nĉ~uo1{<㏛Qo"ŹAjqDsB+*f̘B^u?Ag>Ï֗sIIE Q8>\n݇ ,qjT=fԔ)D3OnjB:k ;cRs)9seldDLMiKRMeDLiZ6*19K) D,ҷ*iܹ?.=kfCg̽ҥG~N([4]u] At*ɫbԨQHNGNη/Ν l۶,+MGgekiAti.% JI4m*$2@ƘzGAQm͌1]/B Ѻ+ꀇ}--{#mm~]וx|#lvwc,}dwn {njpӺښjje3RR)% =X̤H] !BHRM"Bo'r^SS]W[Tx>5mӦ[[[xo[G=sVᏳO5f7<AJi4SI8~BLLXI !ck=S[dTy+#y-7eu5tΝZ.AJ;`0<B<;&h$>T 2ӹ@!)f߽$)*ض{Ϟ͛7_~f)gcH$2yQ۔M9@ZvmsbB |\kB F*R |'17o:mO)SSZ"crawرpX<]MIeT yWB&[]u؅ 6v%b>!,^_O#z(@"b;*MH}o Q?x}~ {j,77 Ka:!zqիM#D'DQzI1!Р:aabFJ% 9G c͔T DJQLiS 6SK(!v)x i4(|)LQ1&AH31%*.V@&ضi)c/03s!z;֔jEވ8F P@^F] 4ͷRSSǟ|I)p>z3usSӖ-|_>ӢѨu8VC=7r 7qN+ɵݠpՕWV QL>m۶my(1KV&w;NRNrLMM+O?Qo֩2TUlٻ e;1?`9g)aM.*Q։'ŏ~+EQ4"]y› n%tٲw@VRJ]ׅ?߼쳿ܲr ;c ojnn^v] .괍h4+]xd "ڮTYpGqxC}?=2|İ@ j`p6nګ ð`]n㏛ʫܹqfj_,Yz_={V1Fq Ø>}OŲ#G(ǟK.<"\*;lذ/yta~Xr&U 'koܹs1*кd9|Sf镏\xcZ5|0BHkkO<[B* c|ц7krV_}}Ϟ=C ӟ9k,cY->_8@`ǾKAPoAB5kz۝C+D_jժ5_vI'Penj- o۶mUW^]ZBnjj BUyZp$&Z}ƱDS5>0jԨh4?/V?2>q"Lv4\ ) YX:/]6 H8Z3'cعQɛc!\osJe;j%tEXtdate:create2013-06-22T01:32:10+01:00Dz%tEXtdate:modify2013-06-22T01:32:10+01:00ȋIENDB`puzzles-r9872/icons/pattern-16d24.png0000644000175300017530000000137712161170307016467 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭ:IDAT(]R=OA[s{g#\X "H(E'- ?)E>Zq?m!w۝FLIENDB`puzzles-r9872/icons/pattern-16d4.png0000644000175300017530000000060012161170307016371 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(}RA  ~ge쁎֮5lY}L-"*k7̦}Y&k=Ob_ywM@yCj&%>D$EDDdPړkyy3?@Iq+MN1u$ǬG\?YuaW&σI0f. ?J\%tEXtdate:create2013-06-22T01:33:11+01:00c$%tEXtdate:modify2013-06-22T01:33:11+01:00>LIENDB`puzzles-r9872/icons/pattern-16d8.png0000644000175300017530000000135412161170307016404 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭ'IDAT(]OOQgln & \8qǏ"+Bx"BY -nowb+aI7yu˲`۶LӢ'IyR-/onn4~\ڶh6YHId[  zF\}~q!l4\Gfӽgqʲ[u})b$ e=8lw~ Dc "J +gc7NO?5[O=˲Vyrnn R^ qB@ W*K'76A 0H)@=x q+@kMDZ~y"υ+ xNMmnnjea===#VJYYY]] *Ғ-jYgD433S.8N(Q|}q_Almmw{wg,#0v;cIL!3 D3J DDd~7!/V$%tEXtdate:create2013-06-22T01:33:11+01:00c$%tEXtdate:modify2013-06-22T01:33:11+01:00>LIENDB`puzzles-r9872/icons/pattern-32d24.png0000644000175300017530000000315112161170306016454 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǕVKoTW:1~%b"f:o'YG(i$1dȂ~YNxs=Oթ﫯GC)ED1F@DHJ!D!c{pmnnH)ae7ι_77SJ/ဈ,[_xժ*SJ!V>tBcg+o|[)˗?Z a"!L9Zoll,,OMM)y-"ifgxHRJ"N 3:,#VqlE2y~({7ݞBk}֝I^*%_.RRna])R2<嗝g8=zRJ&P !_"^RBĭ*[Ykر !1@J))uHkRb 2{&0M( xY2F# Lht,`0@ "֝_l!B !~~~og> (Bv*+"ާ<1Eqk׮ !RJZk1ƿG~`mG$ՙg: ,K3NP\(BUUU s|O+;!GD .by5cy<7KZJZSJhXW/F0}s@DRW.WecZ\nZ'1MksFb?Z뵵ofO8a??yLfY&뚍9rkᡵ "Rnnm] GRSJ)&X]Dds|43w$ɼΒNDE91NWUU1KKKlWa}2BOkDDRnChRji&*%1XkcιK.:u9gp¹sXŋgΜ !(~ABp׾5U,w}@ Ú`|^ +sw=cF!yq9FhŸ~ [FT1ɓ93~r^P lPUU#*M 9௒͛7sVkmmMk][?1^^~_<7X5YF:nowɓ'sReY~?Xp}6 -h@Dʲ~ŗY `81Yc,SJӠRk=*P/M.~wa~n8""|r1hjA=@ -ΞX\RI!H+%e$"LM=kYŘJzP+;,7= Dž`i4 vpAg IDATHǥVQ ޽fӹ9AB5ƏlAtSyZk|Ktw.L%71ݍڡũ[F7%OX[J/ vpAg IDATHǕVKoUG=0 (u"~RHG&4MB $Q N%D }{9Y;Ԫt&<! GLsZDm-LXN3,8&N'L"zA?ye7?| !Xg?d ,K;駟z=!ع' ֺٙYr}}}qX|`Blll_XiĞm rZ()15cs.g wE U{c c,@DH<@&kk^v=R^]QJy7EQDD\˨z"_h8u舤޽! $")[? Gyl$E=t:vjֺ(33{O!x8g1"\ 7xf,h=Mh!ȹR13Bsiƀ~@AJ+S*i cRr4*h 0YY`{,\lu]_=N̙3OO8~Tb]yAA&s.b1Itcv~~~bYV1"m燍iDD= ž{<幵FDQ/,,󫈯Vy"d>'Rp]e7\6LLZ&t/Q%tEXtdate:create2013-06-22T01:33:10+01:00V/D%tEXtdate:modify2013-06-22T01:33:10+01:00'IIENDB`puzzles-r9872/icons/pattern-48d24.png0000644000175300017530000000441412161170305016465 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WGIDATXXo?cfvf?HhJLBVR%>7sվTjy?sZ5MڇU%TxIP@ƀ Ėllށٙ{޽; z^s|f|s_GyDDƘC=@aX =r9b ({T@1co/p)o<9Aܺd*)ܕJBFު>;3s|RZ3ƔRN{⌍~?fgQT# !,0 ^6CIfI)R*i'Ji`EѥKw﮼d;m2ƘjD}/o>h6|Eo\P]] ֖?22l`V+C7;4 og)Q~~vZJsYQql'{fN'{}íV0I靛n4vMժמ~;VEBmVV*/R}~|' vmKbn&_!>c!/]k[@H6 lu "]4- qlƑ 8t۴{/#IN qA׎*Kݻv SdbL&5"n{V+?[iڶdG9k #akԶhj:Zu^=ql򺑒DI霁\Q\HVԩ .2G*JA055CZ#_Jա_hJ$q3Kٔi+++v(f$( ucԛ::ާk޿Ӵ$I!)OVB l +W%I-1l'k.tSmn'طoB9m̓zS:x,˔Z`Zkhebά6 pwoTʭ$JiaΘR %:t*D4R(h6BpD1㴁AcRRзIihG1Dx~tQ+ݑ:s(/]MT}*H7 {<%tEXtdate:create2013-06-22T01:33:09+01:00&j %tEXtdate:modify2013-06-22T01:33:09+01:00~{ҵIENDB`puzzles-r9872/icons/pattern-48d4.png0000644000175300017530000000132712161170306016404 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXY] ,z3r}pRgfy褈Ysqz-zA$"Dr5x C7"c%vX@f&ǔ]< + )y@V "s?^JZ*G@ rh8GsβRG.1YLtLXIo8jilj UXh$z$Y-}CgNKẾkMqb%"z Y=^(&eYW 8 %{1gyȉf17D¯ j%["CNM3Fŕ f0C]n@8}z?NY&A4ׄW iVDZF,^!-O"BDGR^_-cST!"W&=#7aes.g*5Ūe*bܷ5e"8.e˜_G(,~ 1.YYJK{(T#rY0%tEXtdate:create2013-06-22T01:33:09+01:00&j %tEXtdate:modify2013-06-22T01:33:09+01:00~{ҵIENDB`puzzles-r9872/icons/pattern-48d8.png0000644000175300017530000000413712161170305016411 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXX[sG靖V-)1e*Uy@(.N<*NW0?/&e*Q..`Gٹz{gf/Rxߔ.782!Bcs1kptMG@]0{7Ji}wu s{k&cT*]] PAQ-˕^וSS7nZq+ϣRJ?^,.եbݴ;cڶ_s _JaR ÀRf{{{=7/ergɲMG $bbݿVN !PI)u]\IU-}_ۋ_Ʒg\8Bs;=7wLSȘɥlvRsmwQR'00X:+L X>i!BH 7::lEQ hZ?qXHr=2/uP,()DoI]*bz^tԥwyI쉟+=Zf%tEXtdate:create2013-06-22T01:33:09+01:00&j %tEXtdate:modify2013-06-22T01:33:09+01:00~{ҵIENDB`puzzles-r9872/icons/pattern-base.png0000644000175300017530000001013312161170212016622 0ustar simonsimonPNG  IHDR+"bKGDIDATxk[rj*)C~1l"&(jb*jюJhBndBu[5MU5u@GIеŨ!a6nzg~L;8<:dO a#@@F0 a#@@F0 a#@@F0 a#@@F0 a#@@F0 a#@@F0 ̐ @ͺvlz{~2fs:1"z_gΝ>7Oo}yӧM]n:Gozgw~J4|7^%z/՝6vlFzz`)  UW׎Κ{~-Ӧ)MiOݰ3te3snv|֮㻖5_ٔ)'Goy F9vH0nBa#@@F0 a#@@F0^HVD/ joo1_=S:گ;u‹fO;Ug@Es^.p棛uX'VU/$+ 'f{<}㛧w)su|ʪPU*~Rj|C#z{+n\dG5,R{@5cv44Yw-g guf#{ȸ~O)5qT\{zvZq[oci|~ֶm߶7}?rjP}?aذawu/YPdρ<>W ]uK/mT*k֦NeDG0.0 a#@@F0 a#@@.:=>RJ;-ɮ7v۷/|\?\|g@U9\ ewfOva=t#*:~^^HVPȻ?LήoaOm}Ɀ7oϾN\??pTDÕKm߱#ٹ'L|J͋?u{@ET9\o%4}d ݽ'Jˣi"΀sr)߼#G.dСC/dVJ<N"zwN8qNySϙWT]l˷dž5ʹWvQ睅,]  N"*\:lΜRJ7=׷q)D<+":RJ[xr}?ӟ먯|e::4j\?\|¸F0 a#@@F0 a#@@}M[~}g@@¸ 6yWx_'xuf̘Quf̘,y˖- .Rjkk˵N{S7:F0 ` Ft #`@g@9I}}=昣?횫 BTkw]VZ/]ASs VKnK)=xpC7/R~wޝR™{P>4dȐo~%hvT-\a#@@F0x`BJ>,):cyq<\a#@@F0W\0Nj#:K0 3ژ `K P=,"\0=j  P|s  a#@@˜ \0J\&eU9Syg,K .0 a#@@??pW#zʵ0g@{zvgEuPX a#@@9?4=:(W&s  a#@@˜ \0J\&e$SPZ\fYot  a#@@Pmf]pfխѻtwܼ?\zGF$ZE7u.Yx裢%!@պk=&N837Uk۶ny,zP*T~礉Sʱ{4Bu`k,8sF0 ac.X s(%sLLřտN9e5>,㼳MKs\a#@@F0W| $@%PmN8qR__9kyC8P??zw讕VKn5vW\լeRJ?o`&@8P 2T~xw.pf^T 2haa+n U' ;`@F01'Uk)?aS:o^'Do UR>}kn㗛o]|G PFRYCY)>l PN8~g2l@iP̹,q}}}7=Ry~[ PN߻b/}'[UtMAs98mƹѻp a#@@L`BJ>,˸g):b,e,,R7F0 akǦھw--cM=}#j]^\W_3櫧zJGucǶ= Q3U;wWW_;?fap)6=~Գ<?}~Jgc]N=eĈGy?]5/gf+ 2Ё^ZJkc6HRٸ)iɳ-h@O,1pu/rzŽ9Y{o3==wvw-ܱ`Q 4(ɧ.3RܻbKjFr)]ox4ZnC{ǢÇ_fwi1k_zi{RX6tI Qe)>ٗ| W_ߵW_q0?@޸vqY (,F0 `1k2uRa#@@s2 +Py^AvPy^AvЀ\\_y^AF (/\~}}}\xɲ r=*\~+Wnmm͵ r Puwܼ?\zGF$ZE7u.Yx裢%!@պk=&N837QܛEm[< (os)~q=FPlM#} P7@E vׄAS0 a#@@XJֱ<΀0q q a#@@F0 a#@@1erIENDB`puzzles-r9872/icons/pattern-ibase.png0000644000175300017530000000360712161170305017006 0ustar simonsimonPNG  IHDR{ pHYsHHFk> vpAgTPIDATx_heG`^8r+=u#^2MJ']2̚k6,[֊ RWis]ĤKiwf 8خ3Ly~=ޯ!9ݙ247uBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBW >}hS2̜9?Ѻ]E^I$DhQќjkf̘oW\ns36 }u?>뮮˙SgyKզ{ݱ:nPӏ}ɳg6?|߻jbALdN>烬f-N*5JMm񞲕gpvJfǾ0Ɣ ԅߌ1L&RX8;f^wꚿ`ؼW,NU1:m,_fuXr?҃D}ݮ˗oN"q瞿vmx7;]~E˧M6x)awc1OnD슶ze˖6ƬXΜ9>(+[{?C/_^٩O>=r7n̝UOԼs֬5-u? ]T] ] ] ] ] ] ] ] ] ] ] ] ] ]XW3}AruNSW3 }^y('w]񷲙Tj~<2sTj~<s\͏G~Nם\͏*`l+r5?~yT:uN:::::::::::::::::"7j\o ] ]\WH&_ĉשkNNNNNgv[a-!أNo(2J\١ӛKKsy>TK;E vpAgTPIDATx[r6@ѤӅqgwƥCU0x󕏘7u]A_W:R']Kt.uҥNI:R']Kt.uҥNI:R']Kt.uҥNI:R']Kt.uҥNI:R']Kt.uҥNI:R']Kt.uҥNI:R']Kt.uҥNI:R']Kt.uҥNI:R']Kt.uҥNI:'>κݨGE벬?n/O=`:=ksx̝u_IeYk_3x^͏=a_a8?vG ui~~XuqxwWi؎Թu>}sgUߧΨKO~;VڸO_qy3ŝ:v:.t.uҥNI:R']Kt.uҥNI:R']Ktٹq [F;R']Vvu'?;R']Kt.>~;_M(r&+;]& 죞sB2wצNԹwpQN1tNQ.糲ӥNI:R']7^>΋w֎ceKt.uҥNI;JgO(r&+;]~gܹs.ua?;G>:GHN:R']Kt.ٯm~NǏ#y{{;kY㱲ӥNI:R'](=?y.Yaϝ뺞Ya:R']Kt?~n uƽ~g5t.uҥNI:鲟qF.uҥNI:rGiǃY~5s6oz un+fR˲8gRnI9k չ,/Q!1<.sf9̝t.uҥNI:鲟:}s%tEXtdate:create2013-06-22T01:33:09+01:00&j %tEXtdate:modify2013-06-22T01:33:09+01:00~{ҵIENDB`puzzles-r9872/icons/pattern-web.png0000644000175300017530000002373612161170213016503 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge'IDATx}yŕgDuuuRlAHIHHB ͇^{ǞoƳ63;3>xlcY, nj[}UuUוWI)3"R7C223"^F{hJ.e&S2^UH)%PJ'"Z$c1UU/i-^*!JgP(tj2U!T7Kc+h2H]iwn= G?(\,;w?oÆsw:=$dk,rPB? ł(o?ir/[מR* LF4I.Q^k!BcL%ĮB/QY8%irdG![cA`|LtXA38!4&1Oʪne2U1F# PJ (,e{RS<4\J(a _  I(bXm{"^((U*IS!8/ٮZNر0{SsSe]˄xsƌH88bK5W/$7ڴ] Qsa[m6M*0ymsnw!]wu]3{֘hB޽gxIS,˩Tj}ZU(G! ]^_}e˖^w5b jK)jnn޷o3g3$s;TUծU ; BRQm{yZX<(ʄ 8p0_}Gǜ?ȟML"'O9{c}pƌZ:}Fkkϙ3˳VxM[p 0;B垞^Y."J͟7vI2=-Vu-,>M>408KT*&4kZ2-˶˭WlV%ֲٜG0ci!BiBm;Bas8 k$c~!!1< a} ; !cq_!Bh^$s '71oc,IU_ z#Ej+B ($lnUU?dEQLMT72 x!Ȳ<wn0^.^vw4yqӝRI T*1]F0t]?qm&49Q(lubϝfqTT,}jk/xP,qw 1ƶmu]k,Ku4ͅ |]]+ TDqӝKQ޹MӜ5kuBzzz=-X/|sG Dv}!LhDųNud*U>~1:c]ח.b+S;(䨐RRՎo'Oe8N}}?y_ﲇBTh4ʜ1a۶=߸M}O/YAMӎ:|XoB 3Cwݵ4|# `Q>mo?PRDY!=AŢ99*:|XBuuqx<ܯןjad΋/nܼEӝ/%̚5ˣEBH4ۿ{[9qd9.BTvB:#B>_x:Nw:qub1L0uuu_Yvҙ3ge]RM8x޽>y2R3_Jg2asΜttFu4UUg dY!lV kMҾ~Yinn㞞^JܹsJj: Ӎ Xq$ mb;|>pR<3}z,G!]/QJcX4񿽷O%˲ޥ*DB/ߦpEEA &seY+2 r"Iזe37 ]@"Uxi<'RM( 03Rh,} ! ,|-0CnZ`T1q>\lr*0M>6%I.]"9*R:᜿K_(BSB m U!R8 Rʾ>lwP}~v1 (`gWLcgԟ6]c)ƨ! cQ,<ڵ$yfmI?:@E#}W٩VCA6@-eYe?mfP($L&(;T3x{===LKJt**HBH,4 ճ,X,2UPR$IRjp?|>ϣnʲF<`D"UUB<➦iD;]D"q]w}3Ь3at6YB"7zmm+]ooDF_R5ٳg]K4%'\ mKtG8^x2Mȥ,K~QMӲ{fH,Rv4~ȼx[W!`Uө en`vM,Rvn%TE)μ ~ʮ}SJE5k3m!Iivy$2/P2ܼy#tСCmGQ(fp'VujAE߻;|#U!hl}0( -r|jpϒ[aB ٫|Kex{.a"~r I-rۉ9HBq[uPk_,MӤ\{0r?C]a̴s+~ek;H$~„zGy)>2Oou擟$/"DZZZDQ,J$I&:*ܶmƄc19d2џSN-X5Bq<1f{; ke& uwwC)a)jnnbҟup QļM>qƀ={lذǞ4Iv緿~Uw:;;Ϝ9 q !1(/BJ%_H$%ϟ|?]3xa4' =yWA 倳ng/ڵ4͛ooќ4;у 8o׸}1o2: 祤vWhm#n_\67l='5[rTCƝEP(dI~w8oTg9W,STǎO&SM)RR {x>/E/_vrnZ_X<$^8)T0{iUYJCc$I?=S$x=zÏDBT7](Y_'wq,6LG$I4: z@ぁ? Th!dhh?`Zu!?QnO\q9s&`1޿?ӭAI<39,˚6YCڶmŋ=%lٲ 6hϞ=M``9DAPU?uc6zQ% ̜5vqΜ9 ̩,uuu566, BTcEQс0٬=|Js-4M13h8 L!f  z{{ᜰBq4fxY1Wrʿ`˵5FPo6@~ YCv ؞eY̝ 4 ~aPke #h |W5*O俗wר-u-W5 clΡCG!uhʡ(Z|8Xt YqMW_de5%Uهͤ3 KSrLP}ssSp&=uLfKy5罝,Ҩ3hH3v嫛KXn-+fB[+%4gtix8RJg $buORf)EcSo3?ɗ8 tP:pid[Lj~!BϵWE4G;pn)ӵRӣT8?M܁?_/}NUJ ȋ <;YV<>k 025 but?YB/ Glƌ= {B-ZL8O?$!:3jR޽{nʄXM\dI,۳gsٶ=cƌd2p4l]]]ww73ղ{ut:]__Ç?8W!oBUU={6`kjjjkklD"젫B~~LtbX*8@@؊b65 P(iZ. =CQU VSЭwiڢE ydNCG W.L%1][^` v^-3W5U,Я?a(ΙV~gG (`+\`[R^[>i?lV(k#C~/pq.my-;ϐ޵xyYc'U@( B2o4zۭi|B4-M(iǏ-\Z5s̕+W2)\.H$IXW.a<#ꉢW*>0Yw NEUh4Sa( HBeJ%WwI<΅7y4g2@I$JJicc#SǀȲ\SS455I&<$3 !`a3,O6-(I>7$:1* ײ#ܧqOH1C >F χOhj H#&+WWY_y5q:F)7o6(Tշ HU16 3 wܾpa:,Kb;j7ƦZ24zHd˖7lZ+|g;_}7888d4%U`%xSp1UUǀ8[|3w##( kVo}e[i饒'{t-֯[kF1@v<`/ ϙc>"UO@zuA|B&0' 80:6/-RqX`?޹x2EqlhhD " >SJE3gΝc&_ B3;H|>Q<=2>[OO)J3ge8LdöiӦzRxdYNz-b۶z{{M$,䬭tY ˲jjj ,g2HgBTUejHpglN$L(ΝLŲ,^`,Ii L6qTU j$ /0p f9f(pD)MAªT@8ӝhQP($SY3gE 1\$%#p%8|M{{{쬪xM *sp~ooOL2s9STݟy:HH^*zn>34488(Jby)T7m>%UQW:MXQ(8c;%SHB0^y޾۶zkkbOS2 t4k^սb--M\N,3Jdud2y2gPs*BGD8^ڎGF+)GBi%rĻ;/t J,{EXcRq/3pp ]:^cZ fzfce&s $mfa!+O$G yNfss_ !;q0Rq A;|] .RJ$I>} </90ƺ33w#q'J1oL>+TŠuގC4S,q%RAmP 5 c4͎!@o3-,JR---<-LJH$xqUfׯ(J>wgL- gП\3!uџ`~_P` 4- 4/fE"EQ TfSy CZx88- s'-KV}phݼ26o^Mѓ70m`ڵĉ-^|UJM죀?H (vQO+LfX, MӜ?ޯ~Gϑ}۶wח;;L/^(;t<{-5+Wx=kjO# @P,襒mA2P._ 1*喕Tj41r^Oy\O_ݻ_r^=o~ӧ)?VӓNm۶t̙MIU@SǾ%P{3\@{PH&< 8N?dFcc#;jMM D"J:T(%T8cD"Q0, ^l۶$I% /:PeʙDx*|X,O-z<<'QE9?~#p9SB◾/rrԅ5xl̫&e@+6p [64[Zo}'${< fjEQEW^e9$:%U H8.E4MX (E=Bҷ}˳fJ5aD*I`7>]yI\)k?y`Fc6g<-h$b!˲< t\4gRAIfϞ=HD˂L@RWp_]좢yƜrzup!8G sP?*bMM  PU5q zA'P$ϩJoXO Lb(ϵ:45DW~zB(%2n_g =w i0O?X[ ~ӫO &s\N׊ И_G𘏳ˆ ujl55ZJ)߳Iƣ":&aanNKgw(d*%/5^:%tEXtdate:create2013-06-22T01:32:11+01:00O%tEXtdate:modify2013-06-22T01:32:11+01:00nrIENDB`puzzles-r9872/icons/pearl-16d24.png0000644000175300017530000000154312161170311016103 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(UOoTU}Ϲ;әeB]` RF\cJ 10/` I %1j M[ "*bzso={^eYVσqh4H1Z˖%"`R0xIq3Dnի(󼺮4t:UUE￾v kk=z{{˯RfgOҘoϋk❥gY&XYY9vif0 z믾ܷO|0Ǝe'㍋riTmjDlp4B$It< [[AP*5J( N; $j8 ^7jey!5Dvlܹ,K=}>?ՌVW;1H@ʲ-r<κߜuFm4 VJe2KGZk jHhQ*$QYyn=1JGR8'''GD J^3G:[IrRJVKTBژ#+Kw`،B!J0 hnn770QLB ʘT^\<|蠵bu]33gf@/0kB`%tEXtdate:create2013-06-22T01:33:13+01:00g5%tEXtdate:modify2013-06-22T01:33:13+01:00eIENDB`puzzles-r9872/icons/pearl-16d4.png0000644000175300017530000000063212161170312016020 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(ϕR[@sF{/|873'~2VQ9! 8@{TDd&[ moimӴ}]W$OIug ǫY*ɲ^]@PK vpAg\ƭIDAT(M=oGgv۽=MLA¢j+!)E7KD B`7@aAݗ}'gu`7K ^wNiZA]EQ&IѨ歟DYW/O>ʁkk돟h*-)3_ [[ۓ$2{k>ٸs7훝5P7~Xpܧ>u2|WJ-,,|Wn\_%""Ƙ?7<h4Z>/_aJaݽQ$E97޽q%IEQvcι`ᣄۯ._:.[RJg 1F01hc!ٓhmZ<TZcsJ,˓^r;oud+9ZJck]i [EYU'6Z0t*C> CY vMe!XZ:gj*BxRy_;4=qbNUd|8b sG,PjRq&bߜn9/H8ck;Q*#_|\c}k- +]\8`XȜ%tEXtdate:create2013-06-22T01:33:13+01:00g5%tEXtdate:modify2013-06-22T01:33:13+01:00eIENDB`puzzles-r9872/icons/pearl-32d24.png0000644000175300017530000000346312161170310016103 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg nIDATHǍV[l\W<ﵝ?\ DQ" 4> !A+ (N(% $NEQg3s==i}^^W\c,!`0D"\(DD1VkEDa !bfR d;SO], Y@ku£3ё(b@D,EHJn*8u NcI) .J !<%I1HjvJIkdP!MJf$j:jޞsRٍq %cZ)R 1\lo6z} yO|v{ "mk7ӹ@"=kW.(| r^;wn'ё(vyONN8fkxx(@HgFf`2eye&3Zk3 W/]⌽|p6M B =ǘn% "Bl/Poߏ~yk׮Ur.qڞ`X,?1655#"tn]!$llYcLy1I??eug>А1ĿGv㯼͛7>eCG"k1Ƙ@v}ic97f37 uu`gNZ۫PWW7{r9ZHtcs3ET1/6ZVrrfg劵VI5==EQX)4s##V\HkiZDžv0] \H+WZiRJ "#"u@5 .Z6Xg;oUU\Pl5Z{V yק^퍥qثLp(_| zmm}rrfnnS v!ę3\#=Y&G$D$H,I;*C!xV|_GpqKt{gwfzZ{0G[~Νq֭7'ޓsNJԄ1EQ`O8 e٧?􋟿:48Xߧc ;CBum8TFkqE8֑vާip?}=HC kmZ۫sgP,"wz{$iW# 4M Ȑys!1=R1eY"j(wp~D zt5-%tEXtdate:create2013-06-22T01:33:12+01:00>m%tEXtdate:modify2013-06-22T01:33:12+01:00ֆIENDB`puzzles-r9872/icons/pearl-32d4.png0000644000175300017530000000142112161170311016012 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg LIDATHDžVٵ ,NH)vb; 0>db@f#M`af8q,ƦD9_"b\#~tZ# e sAs*>P)A"C+~|Y| ^3}vlR2b}7}fFVE.18>q%"qϞޘ,JDC^"V K"F !wj`h7CbિC.0YwB09􄱻:o6:J/|cfAW͠Cb@%ք2R e 6 l`r5Mt[z0sDDDlМd-E?A93v;Bpvd/1jѾ1'0[5roi^ٽB# ]z:q,n4Ll !?efmmϻקH=XNu(Y@}{C\+ܟ#"N~qgK9=ѸIDTkM)) vpAg IDATHuV]lW;sɬm]7*)D B@S^(Rhy(HT)yFHH h ?4Mc (iL޿?Ý9w:/^ "F2Oz†cvǚ҉]QJ6#, Q JLkC)™SRJB0 1(k/ڐ8IB[ߙ?69̄/~+"0&Y]`q,Rc1ZkaJ)8z2 ИR hmhks>sjuι:3bqjH= nm_DlZ-yʏC I|>o~ 8OOE3nw|a]@)Oھon6RB=Te, }$_ɏ̔nΟ 1ڂL̔^x{B䵖S\pw5BеE'S'u_=g:NF%뺟y$c1RD5hPJRH!czsN)뺮 !記QI)c,{뺜s@8 hjv `pn"!v;"@)l~,7i0&/I2?ٕYH)l=&X3fgr9fnnVQ7lJ٣G9Kcz^t,$aI(RJQ)uW=e[|N1R$IIS qv|~yyÏ=˿o~Z[s;vdCؔۉ7*mfl?B`o GV2XE0$I|߿skz76R1A5J/QGOx/t}=}GgJ8AKE7<I!+/_~'N<EX!6IdX(唭3(4(h6/+vRc)sP)Lt{^?փ RAp^XٲIjF.v;٬Vk~/቉ ~R^t87Ëe!ʄu)%3jJR,a $ #J'(I8s!;(Sfzon6hR{^֦[J;hhn60r]{6*AJ oʟ0{r폿{sд/-]]ZLJlrZq{re>wk.BRffJj4bzzZJeZK t",8N߿ܳ{Vl8z:sC!a8??_ߞxrPJ)e$%".X d6Rʏgwg?- ZAPr{ |\2ƘPȻK)m/LpZr.EMLBүֲT{o.#Vm)cVVω0 Tk\uV! 1d8甒`۵ֆRJp0_x&ԉf|֚R3=ʠ%tEXtdate:create2013-06-22T01:33:12+01:00>m%tEXtdate:modify2013-06-22T01:33:12+01:00ֆIENDB`puzzles-r9872/icons/pearl-48d24.png0000644000175300017530000000514112161170310016105 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W IDATXYs\y󎕍b0>HHې H%JT$U$$*pS!(L ʶd^VΙ|ݧ'j"JX>Fx}YD\ -sDD)K1Ԛ` "d hI"+Zlak 8vl' COhbApӍ_!CZ2 D|M] k]x(nI!,"bFTksDzg@)VX$SW\Z|ɆZJ2d4(TWM1Dz^>}q_xicO>=ޞoQD^|?oo~ۭJ%{"q;>tqygFioK)1ET%}۵Eܽsd1,KJ3rM@)Ғ~_D}5XZbMM,TeFd`qX3m`YpΛ@T#[\b%"NUg};wp; zuD(Z(36==9rVs6;Ac%BX<Ɓ ?O?9WJ]}{Xr7ч ssjiɭYsa\/(52@eRywuuI)ϻANLLLNNJRi2U*JRT*O-k9'08cI$ TT4qX<V^%,DZR9gmCD -˲m8gcQ9wqlƸBv&DZm[m۶,5Us"ưX8ujF)%k76vֶ( ''# *aT*'O.JxθG빣cg jxxskm* ͌(8+?Q!NY?@IRM?I"g}8cR&Q?q0ݼsMdp" !Rz K)nXvÆ A0A_ň(h@x3zYsA\ζڄz{/65Ϳ$= /qI6RBQA!2IaIDQhEQAqXVa X( , i2A 2XAq 7@0DZ cy rd8!s/ywuϳϽmyǏ> 7^g׮]]\ԓ !F֭[}gFl[d;Put###I"qI@}xa!⭷чARbd}21 "假\G<w+ #q_kϞ=/+jU\(zRiƋX*5 6S7/KHCՆ"k.QӘSUGs՞s~0$a)LCCT*3/Ό( \eƘ칂-xq"IqΜ5lI =9OKGnjU۬*Z{њ^-Iv0\ a&M]{/^yXdy{\ VaV6!M]eG;339.)3γ'|kk5g ړ)`Fi:ndžߥfX6{2ȜV4FqN,yH7LC|Wg_:::Qʤ+5 pBzj=w~kS,@{UJe{0فZyj ^}ͯ~[lR8m5/8 e=KM9cu0 MdYD4۷o{Ǔ$nmAY\@mn7\ nz.3Cw}Rpne!عnĹ1UW^ R|>o ~࡭[2|}W/HS]s@ղ~1V7߹|˦y'@Ƃ7 `;_3{3}ۭAP U[RT,D,b^nqllon9. .ݳK)e@2ǗO9FXheTh9ہ%tEXtdate:create2013-06-22T01:33:12+01:00>m%tEXtdate:modify2013-06-22T01:33:12+01:00ֆIENDB`puzzles-r9872/icons/pearl-48d4.png0000644000175300017530000000160012161170310016017 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXýXљ  Mdp7g9P=" HsMW4($пE,unY#LM Eՙc{Tlbk%tEXtdate:create2013-06-22T01:33:12+01:00>m%tEXtdate:modify2013-06-22T01:33:12+01:00ֆIENDB`puzzles-r9872/icons/pearl-48d8.png0000644000175300017530000000531212161170310016027 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W IDATXõUy3l{wm!P^AP MZl5&`A1 Ҕ4DmPҟ`Inongw̼Yf߾+/7{{~3;wg N@\ !7N$aâ/MAE&08=A 8wq)A q'܂pJq,誨-+yoz\wefco[~l4;c ǹwWt]P!|<~ᡮSϜ,kM UzBt<̳4%2haĎD* טj/Ǡխ$w{Bї !>TB!4ͥK ⼉F >![!n$HTsxݫffa?sqvw}w0t C !͛<'&'L1vQXxV:}ܺ(9S5.*͔իS3Gw?`|?v SSSjծ]\5rVVmVQ3Z02y]|> eD 1=^'@mEkZL .]-L$D'D0YSDZiQd2*uF30rn2,FǺ%!WcPT.^<3 C?qvblRHH=56 bffT5OUm50)T*q^H;qR溼^w)j4sFmflaˤhuY!07#BihZFyt: q ?j劦Duz$o^zۭ eX,[>Z@@g.wsVNЍnQ[o>fC՝jߗx\Bp-иq=rS#rIJXֶ @Ob`UdJyxxr0le4!uBP #IS+s!B4#;}C۷? ]Ta@-H/!-v i~cMxÍ7nrNmr_8pY ݢ+B*iڱK' AB<)˲K,Vn]6X*]Z IDj °&^PBq|:uXPD]Ϧ2MӤɵÐL db$qC+ΟQ]p#jx4  My\&KDM:N~[Ǜ+0#KbC/SZ$ 0Hd{˺K&/}ŗ^ӟ }yY&/iR) ej5RB  A;db^-)⇬˺Di&dB% Hf:ٞR6~eu]@0"t)&J'";&&_.;rԶk'''u,3JRM(825֗87̟rЊKܤV /Wp`5,MLe|ǔJ3FϟZ7V a dt6݉j6Y$]b o~b/*䋔JJ %[liBߏ%;,aY+BvTazTzU7Fͽb!_,sVbTV9E#I˕Ƴwk?n{`+2=GNS޹j|u` !j:3 ?\NX6r䕃:~]j٥@%&WF5mlaQBj~*AX OehCRYXI|(!g `ӳMqFs[;i52=jdRst%WԔxJMi~aTm۫׬EeѢlI_R*F]9>zlswE(`buxk>zddD'EUYl[}mJU)N RbH}nY=6mK`oW, PPZ<÷k~ |kAO8͍$iZC>Jc?E2&ۍ![U ȢI2 9B*R ]t!e+0hC֑ |d [@غ  %^>i:u:N|5yΆL6o'p%tEXtdate:create2013-06-22T01:33:12+01:00>m%tEXtdate:modify2013-06-22T01:33:12+01:00ֆIENDB`puzzles-r9872/icons/pearl-base.png0000644000175300017530000000567112161170213016264 0ustar simonsimonPNG  IHDRBbKGD nIDATx_LX(]폀9l U&w?v 7KXCQf80eAͶDDwc[(m^"͹-xs7y=~y^}S#7TNT*@H@H@H@H@H@U=v"u:f1p" K8cD|;e68H@H@HXEW~{_YFSܶmZc[bD:19)BWBO?rk䶵fr1쮩|=Ok" <hOh8~VE$6޾qx펉D"wVTi1'Od&D"~~e_}]]Vμ͉xl(f2B3]gӽ4cۣLQo LQQ6AԬ^dZF% 1GJӽ4cwhZ&S)VM*Ҍʐ}!$ҋ/]]p*rɫwՌ}m)|`sߙJ1۲gC1{}z4;;;7jZ>՗. lĤݮ3 ~BK׳/-"|`x.G/33G-b0~i{ ے6H@H@H@HxJ;"˝z~%,JR-'Y}lich~HpzZӕ+۶m>;x6n_ݺ{.766 Ã|%eg[n|1$qnͲ*wcHmQzE\}Ul۳~ۛRoo2+wcEmQzeVƊۢ ʬ܍EAY+o҃(r7VQfnH-߈9t:ݛh reoKJ-ODm*y1$(֬Kh|ܭr7$ÉRk{g[[[0teͻu?wcj׷t/: @$ @$ @$neD1KZɅ[3  "  "  G}{_YFSޤ_e^_Ss듡P觟5rau|]P``x9]l0Yk>;eI0EQ۷uRР^lU v\zbI:bt\`= ޼9\YYth`= sss GDڵSSSIMLL+,҃X^^xx<Gĝ;v%Xb)=&SԤ\dAN@[޵ҲSz&ODJO9lǭFv9r***l'QKj8U*`y5- tҍ]j ?@$ @$ @$ @j PK?5 "  "  "'B=n qɦPA䘜 pk昜 p"Jfz"dV Jfz"dV Jfz"dV Jfz"dVSNDɩP'"dS rOzĀ!@$ @$ @$RKdND a '"@DD AD AD AD AD AD :-P)ΙIENDB`puzzles-r9872/icons/pearl-ibase.png0000644000175300017530000000256612161170310016433 0ustar simonsimonPNG  IHDR^^%49 oFFsl"e pHYsHHFk> vpAg^l\IDATx_L[Um6_ %SPƆoIM^L;hi3ƲoK5cV4K(msm| zo{cg·{˥r1:EJONG",y)!G}t:= 5|X,X,ccA-t>UsPHYk;[uGڻ޷W4<濯x|{'i>P@́(TA5GbBjU~QrAμշBj^}M\_<ΠQs3-4^4{'TRc4#mmet-7CP sl^qׯM&UD4\aBa.H !5 Ԁclrj=ev^WayY"M~&!5 Ԁd5_ AZ &H?DQ\]]Vd2I#./--vT__o6v{,06v9PNe}211100PvnllOg^ q۷pPss˝Ż8b,ܼ)t.ܼR!RrwwdXlBfɓf(2,5f2fr Nk4lF;;TȰԜ>u2H6 ODaz3P(TM0eU#"[/x)ޭʕ߆,@[(٠wjF"T*JpGG;3&TЎƘln>~᝝NfГjIl6c)g?ϐRBj@H !5 " 9M(RBj@H !5 <1­*|Q$T%ITF WP ÕߡR*¡*Q*¡*Q*¡*.2߻U_ a~\(Iy˧HB Jy0%*ܪa5'TP@j@H !5 Ԁl>~9,[u%tEXtdate:create2013-06-22T01:32:11+01:00O%tEXtdate:modify2013-06-22T01:32:11+01:00nrIENDB`puzzles-r9872/icons/pearl-ibase4.png0000644000175300017530000000142212161170310016505 0ustar simonsimonPNG  IHDR^^%49 oFFsl"e pHYsHHFk> vpAg^l\8IDATxQn DʽfKnfN~vk֝Y%P-I UӟPƍHkvz*fP j T@r?DȂ7 4GҰmk%tEXtdate:create2013-06-22T01:33:12+01:00>m%tEXtdate:modify2013-06-22T01:33:12+01:00ֆIENDB`puzzles-r9872/icons/pearl-web.png0000644000175300017530000002242712161170213016125 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge$RIDATx}yTŽo-Y{`fEApTTmE%1#&*Q7M`5Y@n^(1%<fuT{zc}D>tկ_U}}/}Yй.@*R?81!\Y8eBι"o+Q!&|z,IdE g@]z?~\$bŭ5!cӣ|soyyBaBd ۡ@ v5R}P8,K2BH! !7x𠫯Xn(_%;W3IM͛[*c(n]e: [jf:Fu}Y3\N oڼm ,aaS{GLFLȣjSJ1&d1(J! q raiR-$cmdXTpn ~. x̡*dHЁTop S)ןgzwr99;L3.'2dY$(3 {gPG$YNVP(nBƘ(>oݍMTU+V\;r@ 9R!sκ;DQ+52ń~|#|?Z\ƭ9l{"î҈O!mmm߹ş䓿V߻_(s)[\ 8'L9$}0qwBCee5s=/8ZG DdBL` >cL!D$BdBX@)kwR B.ZCUdYZK9kr^(r]!*C-ZZVla0x??"q! O? O=4D0Etu0Ɣҗ7l_bn5s<^FQT?q{Es{!qcc~=EeYjfWTlo#M[nY01TE}}cFWhWg~!; oe |>Dj |?|pᤔnM.*fiN*cy9NI)O}WDz ZVfpqǎlSIƘi^j֌O) x≧q{Yz!B(,[45w!b\t=oii^?iwIUUmpYV$93Ѝs")e5sBH)[RL @{{; qU!|>].WtFhтH$"IN.DI(Ib%Izs>rb~޼;,hS0Bg}F 㡱A\L '>~-AyބWz=˂Qr-18XhIB3EΥ.Rdpg;<,H7o\46Y˜i GMctT7$Y]YD16xR$K Fәq "ϛ[MK88?8fikkO~#l^_}LUU͍(Ju]/dv[SMӠ'14?|ss3AH{ŋ浴J8skZiS'9dt:_755nJcF-Y4%麶m{IWPbYV2Zrqѣ*/bƫk}^_jM?ł=嗄M:;;MyTUQU4SU#$24ME)*cI&4 Ձ@dY4MUS*. *Qˊ&RMS!J\dMS5-D4*&nPJ5MK,뺖B)5 #~]"݄<}:Ǔ>hbB;#4coMdEEdI &0/J1c<}KfXA~TQW.*z|`^W"1@ H%Ec,(% }mH 툘}>ӧh,G= Y&E޳iƸ(N5oi߾}Glg?3&[$ uϭx#F@u]_dɦ͛ܧmrv~oD޼pn! ;eY?裇v ! EMp!\|Wu$1|tfq~wƆw!Cw*a:F⸦Ўв,]qMM@ʊV ѕgT0?XU[Zss,ˌH$bYBBqV8teyܐ*DM@aæNxmEɲ,I /׆n Q%L!|.0 zÇC|'i mqoB;"8pK,~gO?ӯ~ ,0&)Fa;@ 8,1&T Dwgy1v- 1aBws<5QA2'3΢$a|Hwc<4` $ΔL- %r$!z9IDVUUU $Dgg.!bfa(+a#˪*ʝQUQUU$"T5(˒uBTU$F4$f@B|uKmjs%vݻ Xv{FfhSBM؅q ;vK qUM%!?x NVSuu{$IJ4#x=B4כT7RGIm {6z= $54FB] eB~"~h_6fv[{2 qBfNWUq{,^(d]fNtWUeێ]3M6MRc$KnjZ8'}ΘivO2pPJ@(s: JBѣ+.iiiIu}ێ.)qYV"u\ NBhS7o!Lip8u %cmf1tͰ#LCka CO!!* 1M# Q@u #BL) c5M3 =动nt)p$D#&H3Tx"%I 4͉`$BJ)cڴigG?ԳGJ =ͷ|g?٘1cL4輛oW^{. INkz쟱/tHs&seu.^g+++%I2 cٲe[l9nݖ+o bUg#GV\" r4*eY%%%?яwW_kzAyG{(0m%>3<J!6w^ꨪ_7ut]Ǿ3zT ȪNNP4iggh%Knjg/rm3 qFKx<ެEvvvnNB8D!=BvA85eum DeUWWůjj攗 u b_׶e@[0pTJln[9R޾q;~`0="k8KmnJU%%.pȐ"A % _tS'vm?#:@ /˱z ڢ3+Q1DLVд5k4 s٧Z4ޤ Y}Y |O[" 1:Lƅ ^]}͂ GA)ݵkW0  r0aQV{\-]2yҤw{ISեK{i(wHEƠ$a%Iz ܵ˖>ns=?yC8A,*j;ys c`gr{ <^4 sw2 cˢqwondD, AltKqִnHvD !vmb>AxO1bt b QSUf/xZ,|B({f0KUSE01caUU45aUE4v"XQUԤljzZ/"뚮iZ2wꚆ1FB1-`SSs:T@Cpێ"!bv-+I=s1ƍM^e9h{/o獈Ȓtr:%!rN\]5S S\ !;v9c( qeG&vI"MlF:U)GMSw=elBڎ'NeQ(e.׿u"!V\2b$Dܼuߟ! bE6MCUUΘ B} c4І"j_$0DFm{ a te 20,+B)s:(yfNCpHjBMmw Rygo.9va 3F $DGII%RHⱔKb%EE !γ 8U0 HL_dL(Nm!RD‘H1ϛ)I1/F6)3!$YW_锪W]yŵ˖rYI|i)vvEe˗/:th1Kҫ${4BӧZsϾ⟿W7m?X`n],AJy ̒V$^w[.[R|*i. .N_#*K:fq${?J̣WVVzͬ^Œ) Q42qaY!hYoM. x<}`Yf?97 =lq)IUUӌz(BBH]}sƝZag(?o֗48q*zxzID;{ _!`EBo~1qdBR\r0:~8ȟ阷l&䓿jJ&?_&OX(ǎ}~սdOp$6sWl'1aOGFQ!\\32 GrHecB(JI(X:2)c 99Fc̈́81CěQDt&\#㷂@qGg9S&/K~(}#9&2"  Q>V̾[A1%C'˖E.W]][%R 8KG&s./3JQށ1?~?$[3KFvtIٞ CxA\ZZfYئŅPZhXT3UU D#@TUoi%a㯸lm[<n ƘwsF3M!kfhY֙8@3&c\c`7t˚W_3[XjllC B>oKkf? WD.Tŗ6yQAU͚oߺr*Bt#`0y[[[9cuq^ۖX|)?z^Y.ʊN˲=8k $! -Z0qℿ##IW $ܘL$%, 9ms#;s"M(x<Ÿ.f!::: C1c*ƒ(-~Izz$q"v~9)q$s^^"y=% O@"gHwJBdQbAb$@h$XU{A( FX״rO4]m3PT]䆮@tJ-[ (`꺞# BnI?<sኑ#BBnwyeBҌ#nl,+-R eIy'pݍMlwHtqq7nI JB%yi ;_6u*n:f(uu{N;L<vYc3yrZ 3a•%.=4ßv7u E\BL"It?eLUUIa Q$tܺ.Kig3 ]%,)q&FB 0%9Is#!R˲1fQA4,Y?J-+6s(RfYA,`b.@ ](9"!E&\EQ?rU Ep8Znt]S5EB)3&knn~'O;FpGYY3\q i>cBI3/v-,>,--歷.Zp̞]]##}͊cC/v-7$A.//G~?n;m{wo;p a > :i_4sv_tДNrvmGBgz!@U`0n(1EQ Ϙ!tyg_$p=r`HI{fظ/5"([lVdرEK{oh+"!3bĪU=A|>6o/MqgD n~իuܸ].'{Μ"I1!Bn崩SgBh>}Z"OH3!'!&!!X{=CBd]$:,z0=\B^ 4wm X5]pgRU놡3jD]YDmDa݄W24i^ 4 1υXyH LuYK\.B3 Ȓ8p@7WDq:OC3Eq,޹c̲$ιe(QR`IJ"qPdv29C}4 11謐s 1ǘ0hoӐ{AB(nr΁eYj71c~dvN&[<9;V6,u>m׭(M 8ght~'BJ5U37oz鐢 ܹ1rH a(dQV.:dsӦP{-yu+kWWr--g͗<8="&#":ʕ˧L')2cڤs^L~ږ8reY0z}gо:dӜs!:Gt~$$D@B{DSRAd,$ =@B $Dj; !v4HLS @MӰMB5M$4M"e uv $Ki8$D!\HNg7yKYB +#ax^lpIJH.--|$KG~t:n_ $D!/E&$%tEXtdate:create2013-06-22T01:32:11+01:00O%tEXtdate:modify2013-06-22T01:32:11+01:00nrIENDB`puzzles-r9872/icons/pegs-16d24.png0000644000175300017530000000142512161170313015737 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭPIDAT(mOQgf=h@9!xEᬉDЃQ4jJ hRCA9}37I>?lFf`tAZ+5̈ͩ~.B%D[ (|ݙBARƘmreI1\rug'Nm!DvlJct:H[J1Qvzy.@JGQڲzӉ͖t)"J1JRܥNmTBtZdjaK0Fz` )6#GvP!Gy)J'}s( aZCgW`D~\dkcEǢ&&- Zkre.0CCJa& >ϋ4!"Qӑ8fཙ_1>}69d8i+(T.`lmeN2jGw{;SƒC_vڿᮯgNNN7nVVÐε(BC8=]5r(n( )i$+`3BEv1 %tEXtdate:create2013-06-22T01:33:15+01:00,%tEXtdate:modify2013-06-22T01:33:15+01:00uq_IENDB`puzzles-r9872/icons/pegs-16d4.png0000644000175300017530000000056312161170314015660 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(ύRKC!N7Yr2Δ"`l$$ծΔd$I FՒư1nIKɚIurǂWbTg1,îM|CRs2(X.?5(I\7sw(Q;4%tEXtdate:create2013-06-22T01:33:16+01:005~%tEXtdate:modify2013-06-22T01:33:16+01:00DIENDB`puzzles-r9872/icons/pegs-16d8.png0000644000175300017530000000142712161170314015664 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭRIDAT(mOQ=δRBĸa_+D !&7i\F7 nbXQQHk0u`KOBLRHDDNaY[3PJ1B b۶GϜrW}?|`aD$8dҜ:,5}PfI)RŮ+E0~`)ZD\- m"2wb{c>cZKJR$Ή_J%XJ#j$2X*!5rN+DOOT.X)M=/57jTJW-Σ8aoutUZHӍ4uj@hAgG= LS !ih(?֜__3MhBy"m:q٬Ljሀ>l_rb{÷gF"~ln6;#3#RJAj(K9MrApjTF?…1way8=}?H$ ^dlrF${u0GS=*4c8<vl;~/XK%tEXtdate:create2013-06-22T01:33:15+01:00,%tEXtdate:modify2013-06-22T01:33:15+01:00uq_IENDB`puzzles-r9872/icons/pegs-32d24.png0000644000175300017530000000347212161170313015741 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg uIDATHǝV[o\W^ks?3㹤qmjQk]'LJƈӡC'On@l|vK)?Y(EQe\? DVkxc90КdN^Fqg ̦i[)t4Xa=V3e" HR?)շA0ƦVac}j.+"Y2#f~"R)Q$rd>pJuZ)EER KPD$k-b9 TdIf,kP0 gvؗa@I"o8bQV("\M3Al)e:L@JaFcHclYe&e]WWgf~t|Ųqީ_|ďg?4EtlG/\D/0\$ C C/C6 qvm hainu!f8ە7SڋKsk"?xxp#wai^7qx?[\k6 O-`Y1;wUDz4>80o}[[l-Ə.Ko7raltJTJ^-4*KZ@$q8 I)0pl,RJ2{0hr"(""( '0M{bVgN>1)|7 q`dE%gg|ND|gfz2͔:3&/td8y9f\\jRq.kd%D}u@@ QD33}ϲ G}IFХ8 D Z͈c]"Duߌ}_?{fPtN#aU0a01"BAf4MF8&3oHFvƀH2_R/IHy%tEXtdate:create2013-06-22T01:33:15+01:00,%tEXtdate:modify2013-06-22T01:33:15+01:00uq_IENDB`puzzles-r9872/icons/pegs-32d4.png0000644000175300017530000000134412161170313015653 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǕV!F}Agaefrdq4ADs"Ⲏ;&".ئiFM2/ڌO&Q*%,r3-DΪ$OsNDڸnDDx\q`GE"[4(qQkG:Sg:-1ɳLv9@=b7&ZT7$I=%ҟ&ozD;OFS)#Cfg"_U][B]:AwL`E+^xb]-y:0"TٺAcfUMώ2s<23;Vմ+ۦK?`q_DLN 1']=󮾛<-[߷<%߬1f3z6U[]^Xt=Xt͈IDHmAklqm+fgcG$xB -n©zc(N DZe|d:vry;A1,Qqyrgh#uIV?n@93&9ꟴFG1zmq4//hˠ٘߉%tEXtdate:create2013-06-22T01:33:15+01:00,%tEXtdate:modify2013-06-22T01:33:15+01:00uq_IENDB`puzzles-r9872/icons/pegs-32d8.png0000644000175300017530000000321012161170313015651 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǕoEƿtwsK%@/o v !@.BjHkʻ/dMrdό֗jjҞI0Su"!DLJ}:qD:ΞґI fDZҌQA) 1Z*0͖-8Yggmcv͟裭^f^I<h}}=CR8aϝ޸!bI?HJ4qΝva}sS)"^^޸!Bhߧ_~YtD>qedeZ[e2J6Q*k}VJX, !RtƇv챞i1^@ +2} $Ӊ[;#.>EMZ|>5?VWWˇe49^Ok]4cmn lw)Bt xp9vMVJr ˇBXvL$+gQ|R2GXk x^R"DZ =WA, ~. df2JjÔRݎ8gZu5c1">ڲ,sFM!!+so<™/ |%}/ ð=[(k&eь@\Xjw=c2N'N'wtǛ7o:=XU1ܱF񓏮>HKܳU9t`Yʊ N ʊT}ywF'#o }Kr+p)Uȼ3=!)oX4I }e }oa@8?5&ʡ嬔'9J%d߭Rg^9{L@VJIVOO|9-%y[OuͮwzcCd/ÐLMF3rF@N<9@ `f7f2kg|jOK;/ONV*g':QE)I4V;Y.. bӧkO4WWkH~;Aw;kk7LNLrTı?v:tJP)4!`:6lB$,v `{!P$v|0k;q-ˊ"55wf0$7=A8)ePpfB(ٛo0U++.{VJ%*r̙K/փB2n:JyW,8#7{fْ[m`h~V*v\WmmYâP3w T28d:05 $lW:@JwOSo%tEXtdate:create2013-06-22T01:33:15+01:00,%tEXtdate:modify2013-06-22T01:33:15+01:00uq_IENDB`puzzles-r9872/icons/pegs-48d24.png0000644000175300017530000000516012161170312015743 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W IDATX͘KpGǿg5,ɒ\GC 1p!&ےlTCp /ۡ !UpHBTƅ,GvgWݙGO?8j8Bv'm` Æ1_c7!5BHxb_ !ŢpAD`LKJmkBEbQ^BD6z'Lk :i(s9nSˢ+++kګ^0fJk$ c2kw>g~>328}+ gҝ0Ő8^XahYRfn{trYUK[;SѰu#@m¸UyfT), jx=6cTÎ,؀!DRacs9"Twzb@A E{m2wO!54 !QaۖmZ+)1&c`L#ey*! mKRsމ L!px@Jkm,3{sV zg_ʌ ''N4ef犎sݬvQ ǵuV*|^5;f貇COyP Z-S|5C׈?7dj(ex[76p1 aRa8~ia1"۲'& k ?CZ|DݶXa...J)w A[ !2"s2ƒ$᜛֠2Q$AɅ$5c,ϙYBضd́o\nSڙm돽{` iƔA^6: B01>F,˚?ٛzU۶LOU)YP+[fva~Hk`ZojbJ%P07qo7~oGy)A1$%R-K BBݻ* b$A)P4Ԁ,˚BACJA$V,Ut@thnW*RGl6&>]Yaժ(^CŢl*۵KQˢVR)od<]zrla!S<|&L](W_Sc=4<`o7 !q_^4Ʊo.!|^giᳳ7njq3nqԻgZRbC8N7}.qߡx#NZLlomO*C?dM{dtgRږep!<nI~ᅑ%˶u΍.Èe n6l½T=r$u- xsO+ 3LZԃODh{W<9|h,K{UR:Z(HBUGI O$<6nXۃ<ʘv s0Rݦi1*PЌ HHjQҎ#$ 2]lJJE(ss j_:U!%epP5, (!}nҾ\6IZZos PPuOjv,Ԭk*Ѩ/8|=nîVŃm8ވӚ"} ~h^R,NCŸ4lZ9'/tiu/qdudZPJ-QB8jRJyߧ^ R0Ҿ}cR)c Wuf* [J!,/[MSJMI}rҡf_hڝ2\NtSԄtϬhGnT7X-M,&q'IlZ 8tK rY\Z)>sԺ;ON0\ yj*z>\'&+ \.;ajOOrz^۫kRp6Y9Nw魬2Ob{[GiZVvUڙW,ӷt~w])A@YE~R*>rON A@y5>m꺍bIIsvM)&@}?eۄ|B}+}ote'74%tEXtdate:create2013-06-22T01:33:14+01:00[ W%tEXtdate:modify2013-06-22T01:33:14+01:00IENDB`puzzles-r9872/icons/pegs-48d4.png0000644000175300017530000000120012161170313015651 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXYQ L}{/xpJB3/D=܄iU`Y/{pE#Ԃ8iufY3Ԁ6B&q|3}CP~lt9t 瘙5<-;.ssqAG#n\!H՝~кZ^xޏuˣ+}x"d,^ƳDtnN[(İ6MuGm2MD:Q&c@$Xv DDu6P9"$"%-Fe~3cY2T"ewXѱYNF4Ja~P'e1E>9)Rj,i7Z/O1#k!2Ϭ6Alm!v_6bMȨcOl3F+CfL _/$`%tEXtdate:create2013-06-22T01:33:14+01:00[ W%tEXtdate:modify2013-06-22T01:33:14+01:00IENDB`puzzles-r9872/icons/pegs-48d8.png0000644000175300017530000000474112161170312015671 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W IDATXõXMl~3\,IIdҲ؍ ZҠ AОZ @Q;bIAEZRP]iڵq H6" ZIw3Hpc!yo͛7}͠1>!DAv)mb Zl` Aޏ`q+BlF"4uzPv{Bcm:J%9iXA c\,ٶH$c;7{dhZ^.:r]ne Jq., &ՄaBrUAι۵e+"0ax7,,Dݗ_xL)ZnWAmL:Ta|iyѶm0F0 onΚ<ٳ\q8sfP=dLh)NazVkiyEz#7J7oh65Ǖ`qXt6cҶii8mcuCS17J8t& 9CgFhb~Y~b<ӛs(%2x^93 w(lqfCB]J?5!& X(^`AT%zl8dwГR+ ߭Lf(mfdg e I.#0Uv]NVp SwM\8.\Ҳ3_kj,ӯ~pWZժH?OYg˭Z%-~^u⺨Ri+Fi&RI˸o?wctu u9143x~|!gL}|"%qFM>3W/+s)L(ۆ1gbQZ==jҷ޲ !h{bjp7^ְ#CG'kkꗏ{ڲ !Bǎ/}U*kR_/S7x<u>4!ƸloP,Vx,뺴y7/F~f&u;K"p'DȝEw ipVʆ9D)4R3nyIae;C 9^m:$8k5b^yVmlh6M1v&Y/l1nݼ6RURW^8R%&2Gj; 50HqGQ]ߤԩR;|2)$_h =N4M,Ș2H.*Lu_MAF|r-\=|F‚˵_>W|j9W/WJdlNO矚h8/&""]&Q,P# Ac)xhEvk"/GoOXeJ{{.(+^5t5?[`-",%tEXtdate:create2013-06-22T01:33:14+01:00[ W%tEXtdate:modify2013-06-22T01:33:14+01:00IENDB`puzzles-r9872/icons/pegs-base.png0000644000175300017530000000750312161170213016113 0ustar simonsimonPNG  IHDR,bKGDIDATxo[q8+J5`])1&?X.&TJlE"*]`D+4Z5T aS;nR=po9~.'] la C+TP @E+TP @E+TP @52+۞=*I>?scq_N* A+_bW!jp_hV>ێ)Ņ~]KwFU{uzN~ɣ&Z^TsȍD^V3w'qܥˮ*P/N|~8#CB!NJټ6IGJ)\P`ի^]ZϙkǽƙVV %)eH)r~\p*رcpF#ъrη)e6uORqD6;4ݼre糛V(Ku׆R)8|2U?tu׿z>IhEb踾J)D[|_u\/!VkN?Ј~_Q@+Z8<=x}TZX?H ."\Qx`Zb,t4bf~ f-Rm}jvj*'}|)s=%5#ъTҶܾ T2[!P"!rg7 haYVfnmg2J"2?4ᵽ{W{>Ihjlz۲ggӓ=:#D+e?4'O~u~)/tmϏLF+ ?3yBa=^obؘLkouúE+'~||:⮥;#B DYаBFto+4A] {(DoDWB DUаBFt5A] {(DoDWB DUаBFto+4A] {(DoDg>B Dљ?{  Dс?߯,/Z=޻DsW !mCOݻzDfIi9-q7e4D>BJ1="1_3k+4@E+TP @E+TP @Ū AǪKwD'BV] "8PH)Rqem}qGSd*{PV]\!"8[JoQT߿8qZZY]sgtXeՅ0El/樂THGJ)\kj"- KRvj2_X*+.U@|2q\.׹ Rl6xp.A eDSXDC]b\"\F4Eu+NDӽ5O"\F49s U!1Aˈ&vqv..D4Ž7u{zV]2AˈT*e=@۶SS)'f%(3DhJmI'f%(3Dh,+3ͳ۶35Yu ^PvNNΦg:?-{v6=9fV]2AD{(t:;ݰl;e33^/v.|'~BaGשDB_>ηDl56#pb1;16Ne2ccc\G1|OV"Jt:=5J$KLV]!"8 ЀUV*ZhV*Zhbehq2 8 _ 8 ym?: j>uz͸籒G(.Z# JW: #Xu nVf (4GoX {(^/apG3b;hh2cepG3bKf"s:#ê&3Pw:#/ŒPs:#_faX {(9 bEkVCj@Go~.Ov_Z:z/G#cEwfXu MfEXuTV*Z<|{+/.{@ApĞ~P6 k|q}ř^ !OJ|1 [AF%z`zx^h f ǰ04"~V 1>FD܃nj졸1~OeFhŦ'.]v5P/N|\"G+61yTb1:W] F+0e8F窋h \u1”.fB2slBSct \u1”.fB2slBSctVapUъ f \u1`pUSъMo&Hȅ#ߎZh&'O~ wܤ{('7#xV]6{(f{1D4M+I4[ @E+TP @E+TP @/g .merECD4?A+ZkrECD4[=&3&WXud .̘\ab+Va .~B2ª_hLZ!)+Va .~ )+Va .~B2ª_hLZ!)+Vl0crU_Њ fL ZɌV]G+61ªKXui D.tE+I4[ @E+TPEW|~q|"kř}tkE^dDx^hV*ZhV*ZhV*Z 'IENDB`puzzles-r9872/icons/pegs-ibase.png0000644000175300017530000000451712161170312016266 0ustar simonsimonPNG  IHDRB oFFstJ pHYsHHFk> vpAg#zuIDATxoT}ۦNI\FB0$F%I<$L< IhE0M<6M=1u[բF%[q_ZҤv\yl{nASCp< 8+s 8+s 8+s,z/qm=a3)@_AϡU* pW9^ z!K.QRw.}8:ZՈ?zlP6 Dp$.;?)NtwvX z:3MgSm뢮};o>|GV`y8;GY(eHHr{=+f=agR#? GN4!e aܿ?wߚ vЕTU1 <9h#X0E,Br:*>%== z:˷TR!dBjT]IRI0W] h:Y6 !$ZÕlto߉\ b:Ia[Qn|\R׽fߒlCi\ѢvR\(Rn|q߾niPعWn_(zW @3ܞƫա$$wF[!4^H(J3n}p9Y1=Ir];q Eڽ<64s.ŲHPnwB,p'I7.Q}ei޽0ƙL!ty}xhtx|edBuML1~d6V-s 8+s 8+s 8+$K4MYeG,4 cey\fp0h)d#,BG,"XB6[[[Uų<@Da|d)nX'YJGuDa|d)nX'YJGuDa|d)nX'YJ,%BFwE˪=1Fhhpya%#cPLDh^xpW9^ >'ކK|ݲ,=p\}. +ðel6,Zmse 8+sp $.7]B*BFK92HVu9i s]2z:'~Gu?:~| JjuرDX|^ᑁU2D[+2D[+kcP8Ǡ$(%gP8Ǡ$5Bs AR‘3(d J GjcP9 A)Hs AR‘2#g8 A-͕1(d4W68x1~N Au|>[ TT uHs #aőkJ,hd2kߟ*Dӡ:D "]U 8+s 8+s) c%6K0헨ÏZIH>"0okkk64˲*JY/{{z{X@5+=BH7| @,BrHW+ KW+ G_$l/6JB j%!苄|EƿZIH>"a_$$}VHW+ G_$l_6JBQ%3E0Eqx(n_H?u[H$!AqT3[ G_$)"IIYQEQIYV*!  yfpW?ʖd-%tEXtdate:create2013-06-22T01:32:11+01:00O%tEXtdate:modify2013-06-22T01:32:11+01:00nrIENDB`puzzles-r9872/icons/pegs-ibase4.png0000644000175300017530000000173712161170312016353 0ustar simonsimonPNG  IHDRB oFFstJ pHYsHHFk> vpAg#zIDATxQ0qKnxx2- )vUBf@0R @~e_lDsT4GEsT4GEsT4GEsT4GEsT4GEsT4Gtݲ} sT~QQɾR/t1)}8wѲR0 xNx{oNNmysrrnC1$fmv9*gg;.ÝhEsT4GEsTM% }mhI%\j̕܃6W91mr6ϕr}?8}(T~ŸSsNzhhhhhhhhhhhhhhhhhJ~.7s@zȹl[C}'9*7rS^+Ex?Gˀa=2 hhzVr.!!8gs  K+7sT4d4^gx2/q9*9*O2b3< hhhIei.!!<B2s  Q<Аb[5G|zhhhhU>p_]>m;C`cãacX+8]tXђhtXpT| .| ǶGV!m|l"zhUDqU%tEXtdate:create2013-06-22T01:33:14+01:00[ W%tEXtdate:modify2013-06-22T01:33:14+01:00IENDB`puzzles-r9872/icons/pegs-web.png0000644000175300017530000003405012161170213015753 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge7cIDATx}y\U>[ޚ<8Fm! Rl,P:H9gRJS0m4*D8@i-DeBeN E(V4jITX8'4G9J q[CxN_4*J"?̺uw^>[0ŦX\ :\DEi*TuӴ@$YvxPp{ﻯ.s|>E ә[>WɉHDtι(^]QZSh<0 \.W ookfqnckA΁ (/9+=7ױI3б㺦BA`Ecc^cCCcci jPʼny֭_&m"\fB4Ԍ*f2qJ)(VcĽs2(] #(d" Znlۄx}EispM)p9 L&-}J-ˊbzBFc1˲ y9Y惃??*o\T lPYMBMנɅbYaƘQe%^28Jy6Kq:6& Bsx[Tc#VਭR<RBgIKQԊv\=nwƘ[uX8} "5Og55SLMQ3s \.<BOᵴqux_[ZZJIB@iwzuՊ"aYFRnL&Pړ~p0 T&5c>wّh4J1\.W0 uVEU#e@Qp8xX%FB@輹[otl$h7H~`,f6 1&rwWd88>jL ]zٝwZVUvcbD8ڎMEͫBN^ao /Jի}ַm{ %/Zd]SN1v\&E$N }ew+ mat>N#"[1M9)cSAK-R~5rY]3!(͹M/5 8d4[,&U!cv38xPmx7tv^UY,Ǡ(~Ax4D"i&xnhsT{W:0bXl9!|nn?JI& f8(nUVUZFQ0Pg &ɠ t P, L rÂ6E̳:Du L{4O  !y  nNAS1lNnBb B?c@i PZ!i㓓5Th8cRىbdLyj"Yf~ԈpN8zFF$'?4-[8TicŔt:HD/bD*J v"DI%]ܪM }xYD}[5SeЦ["bA0QXkhTcPs/hulLN/5 ) Kjl;‚v4舝y% O2[aqeY挗@ 2 -@(gt9Pkå--VZJ:lv\m9%Q#X@sMB,؟qJs@xCgYTJ(?0UU+˲l˲,˲eoF(5BVuwuJl۶, A7&5i/Jэo}>T$c9 ܹ׿~뱳ʇ|Ƙ;wvqW'bv:;WdlZA !ӑZztn[*gaŲo/Y3[7 ԇqέP-:S[LJPB\|JXD k1a,@H072uyca^{2RXQPGS*d#@Npd x8i!fp⾨*ʲeK{9;&?lvn) {ld= '8SYݪLI+̒d9rtf (JRn[cCf3N$ B%%`tY{O*B y۰P| O*g$I*tbd@Q0A$& d + eF 0A4 <@g8IjK' N52VuW]\QmQ`$A L)Ls0MSM#M 9\\Q$a|,tZȏRXȝEa_~zG`l\(?_[*קV&;vyiAQؒ%&a xa^r& +KJ*fZK_~Yݶ=00"zgkVO9%k\DUmà;{Y4*J?̪]]f&CK dxX$"n~<׿z4z=;7t8 Pphm[!1f(hQ=x$NIYƘ5kK/t؇+wջia rFwIx<-o[K/͞}MX gSo:en:kq:0(HD7C.{GdӦUwxq/8cG8Kjx$๎sp멧qmP)n{w9UUmamfi7|u},L UUp%7w.{6r4*pcVC979H.F;,!erRtʗn^74@{Iak=U#i~_s萢倛%G@rqgJv/;Ѩ 0ͪՐ 0Kb`-0*i] Ý.FlVz"&Y&0zR(A}qMוhBV#µXjmzlJ9Y" pl,Zڲ JBKUCN} @E][(R8lvεS?dPa)^mEi [u-<5B|B!kD5pkĝR,8̜:Tb4D(NWL ڲsR=OQ[38px- 眓r.^A-.Z4JХKR--f ̝-8#8%I֬V?+W$Bce,UP;^pFfŊs&Lvt_=mFgϞ20ػtI x{wO*+ܲ.jJsj^1jP $۾]+ sX~a;>!`tmcFmq_ioNWHqzrPwF\*C p\ZmYfy _Jy*%}cIaWm"~#F Ռ>8 Yw5yh"1uHY:{in% sЦ=[ O&-'(4:4O ϟY"G<\msB.u~}̓.LJ-lF>KR+R;M?/J /qX׭;Z~a~m3Lsљ3tvxMn_,&:Ndyy/ P%)ànyڲ ,8tU㙌Pq._v<雡uױK%m`:=##.G`剻< %Yj}}}.|X>VQs!B`NDD:Κ/i`ŊdE_e~7/ki13YabBB|Wz`O y 2_xM\x2%D"x'?u'nZ}/0tiʲ!R):;ub2#+gm"|X_kdrRB)~w˷ yfeؤƆ 13trBb.@J2Y"A 1 VH|ơk?8fY0PZn6 :8B;4{uäl U^;GF$$f[aW$"2{z v5x=]ɔ>/ Z>CC.p2Yj*ؘdxg ZɤP;@fDU$XLT.K&B {Pe9YuJ(oY&`qs1 RFZY&XP1=iT^KE'Y#s bausT/EnF!W5΋*0v cJxZ~ߴm~XfĉK7{8]ME ܐ u Ԅ(8 DITƋyu <śVJ(0@CX*m(%1"NqcQLvţ𠈐+\Bmo&3dkIpCbb[&zJ@,[Saq eH.hnsmYAO ! I0Ѻ˩X~}옼-1Ipiٍ룫V%n%?;UUlYjع&MeMTm' 嗕͏oOFD-l]")IFB5FN&#4?e86g'S `Ty5XYf_cOځgtq{z7ax8p ><f¶m]pARӊ<]aWO.I͚nl|㎯ݰ>UsCD,5]e 7 EeށrIC}}YX"vjIUwѶ cCkX|==P|`x#x`N!#"Wku`"k^D]{x (x\dR@fp3q0Q&w{. Q޽]򤢔&S#GE"Q )Y519yرU9_(0cwkj I1I'ȅ:{,g遃LF/!)8J_OLHbW^UVR8"tZxWmh0')~OUƳ~ՖN Ts"o[^9emeYǏ[UWEM׏1P{wPWFQZRۣ QѨԖ@w͏%)~㜨*{E7֎p=SmdjIp{YMBe6 >9dY@g5N-[| x\uA`.PddQs"I|AR9!Ԗ@4*VYBJ@\Hp,I15$8GF$I]8GGdq" cp\ 9=*;:@IIalL B\bY!$r%1?b6Jsn\9 ;+M4P㌢: YtXV ߪ$w4Lk*m٤oPg&::L_qmJa,t,0jͺk(&ι[U+>-v{S11 ̙:Tn}3t;vGF%ANn]*TD7ټ9dUlYrRg?9o4IKzU|ÆTJmrR|O`L}%ubpU77jۯn~޼`,(6`^uS?ԭJ* :i$5R)0i׍a[2)XDY>lV!_f|)  na=";_ 4 |j(A_-}؆~#F>:QGu<ˉ{|jH Dà ";Fu'o:wwIY.8Qq`P1M"NAe:1Fd)=<26.Xz/V:-;&B5bh7mxO 0e#>_9S_=Lr??je⪫g+-Q4gqn׻i 9OPUyW/~ٞԮ[_u.[Z㮛??s}弋eNd¢aor^/_epBH& dv׀+nߴ׸cL0+!,(2ApU:is(y< vso|?#*"O-[g7 &sv=CWgAB(qz޷qC +O$Lܱ1L%cc j)حbpX4DR4[Q^-|ppc XLt`DWaUDONFbX>>l&(x"8?Fmj¶F`ŗG~YfA\ȨC }kG6odJ29IRa)lړL&'#%#D&#-D 1W".M!N @FFG*mJxQT$+XӴ+cgDGbm8 ;np3yW=@gTRwFَ~QT)~}CC.[MeYXbJi,+L\|hxz9;ve84YUr_D䲷pΣhyv')-מUMEQ6 D CSxѣQQHrxcf{d UI:-=QHD 0+)$1fUCιQ^EyXeW!kX9Q*^ yjuykxg R[Bm9"IRZKU^8s%\</Nnd =8Ji$L,X̟-9= G*VZZyQ %Q_\}^T TWQJs sUUdYCA4cu/n7۰>fNfie;7U %Kd5zu18nm(#JZ[Z(4֭sTX*Q@g>_YLD *<ɲ75֖MXw^>ytI= q(vt<r|aFj#ȯHy+wa~:^-̙UWWVz==ݔRlfmRӃfW#:ee9}l,Zdv(խ{ι,˽.T(0 kOA d~:~’ X>k>ڶ=q*6}{,xڙgf&&}< `кc T|nKJ q\@Vt "նl bNy-~@ 2vmu]s%F|>_wwg(('@_הt[0h]suQa[o(3G':Iؔ{BR7}npѢLjyj'Avvȿ\sxuβK9^8so:0>.QO^ӳنʫ?˻k/T-9;|y2_^fM֊剳NIQ^XLg(I|E++W?Ԑ g+`,sEaiaZ>Eak乺i*BVI$fx߼i'+ e|A9ɝP`g @,H&r5Dr#+iooѽOjxJrH7 MȻ+8o(V*7_(1UQQ=7D)КO4-JQʏp(D`MPD"q*0-[V!'ش?M{ZORC> 9ioqv1drJ(yh7L˲f:B/c2CoC6C6`D|$r0-=oW2P4]>wY~Oqƣ҅1fs'в2o5xv6KaŶez0HMT

ĵʆ,&d ݮ-[qentㆨSI`@ GP齦3BxPnmm4-J ؋{FG]hmwn(q M7퉄?tH]:.IPbM_~>'?6zFȈeQף.w(!JkRfSs%j6xBv7T%g(^Sn ,o'ýe̛̎~aH&#J7Xq⣬$qnMBMBJ@%1_kaY85@pD#EKVH\3 i.^vJqT$pIDMht P ,.{G4(*\QUڡv-8pMjm ??i'oܦ["¾70(pj\ݓ])lVX0}GFBxyB8cpu# d ` \1by)ս5FQ'ɭ5 v|yT,*Fcb&# `u\횫o@(\:֭R$"ZÙ27l:FPc~کYLȖ$idd4v)U ,:Ё Kd- v&Xf") sNʉfX ٫.0H:CBMBY6K3)M8 ,ΛN U+^ 1BL#5 J4N\NwZ0;Z-;N%fXMB\e\v~6ݫߴ$)bF0XMFb`{5"Qx2t' {m%lZX|Xm%n \ysWulz8я}Uy^4?Y]L/5D[e?iOq7Bǰiۿ6a0z7 ;z^ e;&~ l\ȫ*!厯fNo)߾vOջaH.+w7ggu jޙj"l bыjÄ.ɭCî), <FPjBmDmju7»D"▭gH8 _}U;&ǢSi1A6e@ßW^U$hrTd3%n(O(Z[ɂT*$ m&k9.f#ؙ΂⩪,j'Hm7qD*h "6G?/9$K-sXI̩8F ?/p|o\.fuL(ioEK0^;'ł=ҍmr^l63cqCT׋jK _ ߯$pav;ߓO$Kޯ} C=b4NmkVBT4]?PfqC,,mVB4zæ(M1?Eza&f7uN Rho77}ơwʮA>+jUS)!$ϙ[oX2J jp7k:+-<M*;?0vMC}}\} ;S8v97w4-Έ"xX<.`]y(d Ru6QU\QX(d-mD"^%TGx r}MjgI.h( wml 'R)AN0^Ԁti]u1wlᏼ;U7A^1h14M\ 8fO7ӬBAȑ͕xYpwyAi9g=o!II{2$*}PssR3,_`یOwYL&GEonD<%%tEXtdate:create2013-06-22T01:32:11+01:00O%tEXtdate:modify2013-06-22T01:32:11+01:00nrIENDB`puzzles-r9872/icons/range-16d24.png0000644000175300017530000000144212161170316016077 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭ]IDAT(UOQ̔i hMBFč-iЕ@HPLΛvJ89srҚR ;"aD,lq||zW_"b04Y}0suEۻo޾%rDD -ywn8Ċ8==5&s8DDD"QewI(۽PliYX)J;;I/9" beibn\.,:Dnsonna8LH _~KDk!: CkReF)V+!"bDZmV/ +"q1^uI;D-8>{RR~zzjdd̩I+ *%/.D.vǏTkt*ʃՕ½_gZkp?}Q$Q{䤈4 DDԘ s(j65s?d45q|q\QV!Rs@)'V9Ȳ78n%tEXtdate:create2013-06-22T01:33:18+01:00ea#%tEXtdate:modify2013-06-22T01:33:18+01:00ٟIENDB`puzzles-r9872/icons/range-16d4.png0000644000175300017530000000065112161170316016016 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(mR0n&I2J&MR܅<% pwy@ 0媪c fPԌ*Nӧ uXrQ!<±ˮ}ضluz],Oɘٓ<D5-_ %zc{$xPISttpUH§03Eb 4Y ,u\m"߱^IGX6"8 Y4ܨ7$״_iRO(/O^.O23%tEXtdate:create2013-06-22T01:33:18+01:00ea#%tEXtdate:modify2013-06-22T01:33:18+01:00ٟIENDB`puzzles-r9872/icons/range-16d8.png0000644000175300017530000000141512161170316016021 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭHIDAT(MQOq}nPDH @)ƫQcM &^4^ $[(][v6 0I潼yÔj\k$c;B)ŨszYV^,32!DEQ ez^Z$h{Aܜ?AbmDdjJJ۶$hqt("?[^:< 10J Jn(mQԑ%tEXtdate:create2013-06-22T01:33:18+01:00ea#%tEXtdate:modify2013-06-22T01:33:18+01:00ٟIENDB`puzzles-r9872/icons/range-32d24.png0000644000175300017530000000325312161170315016076 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǵVMdU>VwuWMմA! !fb"pd #?n4b$'FнLhpFM@V8MB\&3 rz^[=xCfgU/9s>>"0+(k5k-1J;wV9<_Bg2}'^1r+fuCZq) zRebD~௯{7#.VBO^ Aic"y !4HJSB !4A!, A=9sw>x_yB )Bk=W*uE v%g-Ƙ?H'Wkm.,̬ttheR}f>._O~|e 7|K'Mgv]g_Aqt>zy`0SJ)\s'^y|nQ$3wc`g݇^hz JJތL \)M={gO>T*-.,T+, :3~Љ(Z]=>f '⹹k;#8B"RZ?yE8 3{ٜZl -9Mӝn$Izr 5lu:81ֺLiEsG5=jRSY*Fq߽ҨECB}?x١V)33)^jvcGQ,ˊK)(,RJk'B)%BQLP)%RZ0 $ADRjq~~yǝL^aQOq5"M7|Z,|Ie!lm ,˖h]י`Ƒ{i'R,JDD%D̃AvBS)5\ϻ\x'Aj277ƥRhXv022?8w޽ffp]_~?];9uj-ItӔRn;;;'N*'~x7nޞO䞻kynH!Fd!Į)1G+D\XXX^^ vpAg pIDATHǍVu0 )[IJᨔtubUhtx2!7Ĕo -fz_uw>Ir,,{dTX.d<|um&VD!۔=Z b=-- -35{SbgŀDDW])JFuww7@xnU4AWJueVx,m1E9 땟R$JsfuF'jGBVmxb  B~iTՖ۶5+gK"[ɘ *rU$MC+zG,aTy̎03_,(|qa2SڈideX l( vpAg >IDATHǵV\U?s߽o^gfwmn_61P_h(j*ߊ$'"hj4PbBL P(Mw̛7{;f/&;r|R 9`+#_4M眳2E;0tB6#G֭”Zy_?bV5DN"k 1TD9#C+?SkmQΙZѷyd J3"ժQ%d0;;w'q1末D֪Zl#Cd@Nd֭NG1i:vxgsE+#>|̲\#oufggXQ٘@di4Y=&('q77Iu.4M[65Ɣwv'휟Y69"}Ӈ4wscssPJ|۷YܝO9'~"cLN0sJ"Jwl=ޱE4v:mUY3ԪnM:8gRs.**z>O1q)8g1)!a&A($O|PV?|m~(y[ȸQxk߸) 7'8g).d9*c хqސ IOyH^KR~:r!NΝ;YW Jzsz}ZsKtwtC?靌o맔ҭV9XZZ '*O >WsV5")_}?1)m7EE DTJFqLaֈB+!J|9gA JkfٳN,pVCݵw4gZ]Y,"]"PV'WU=e%tEXtdate:create2013-06-22T01:33:17+01:00%tEXtdate:modify2013-06-22T01:33:17+01:00vIENDB`puzzles-r9872/icons/range-48d24.png0000644000175300017530000000366612161170314016114 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXYo[?خ;_Ԣ:PeQiHRm? N,*lۄ6OZ JU4Bhpm PG m9Nl~Kl7y$FT;?Dy{ι977a!d/()n5>os˔R#/%s+cSk\m[?2 H)7o?8X7Τç~rjj19omG~/rP2~K1 F9׀.\H)s!"*+BιRRJ\P'<5|12ԹVJQJ#MB,*W V1F ?@)/_K&H|M>۶lb&Vw}Hq^"r׭[{ٖ3g/ .t 4iλ|y0)e }w>?;uV˲.jjjr<n Z=sGG>x G4 s?~ S }}gtؑ/]oBr[[rwZZԙVHJ }ԝd |9JŭVm^{vMmULi"YfRq0!$BJB!Ѳ͛7R "O͙swP Ji,\ q!xs8's"HX-o80i*d#3R򬔲9$=Ɍz0 )D4dJbE( |F-BtzX)Wi9fg/~vµg fg@Q.=ϗjSm鋜sp \x.~֛}>nos֖|ɧLv=e1}׋=|jjz0 ˲&&&gsN.D˲ Ø(ue`JiBDƘgl1B)"c!1RAEW:n^(MMG7d&Og@Bh4;w]zգޠI)~W;wzh4V7]ʜRjyleB\t߸a×_Gn/X Te7pXn\6R͹f?G\؜+[')n(M$ @"777d[G FmVMPt'X([ _}5zy|kU*y7ב'[oݺls bQèɏ|>wwu۵}݅B[~\I޻)S 1˲oo?zL+e u=rp6;(UZl)BP'1z陙l-B8{4ԛeܜGgB(! R4r1ZV"u,)%ՈR͋RQF)%1f wX4-"Bj2m`J)=~{uk&[[pԌC:|C!bR=;v>p8 HJy&Q<}{dh38j떿Z !0np:=tyH{*E)w\ssp`d`۟?֣>K,|!hYVgW&$iv#S "JVu DLwK=5"!2F:3&.e} C\tƩ>Nϗo0<ʅp%tEXtdate:create2013-06-22T01:33:16+01:005~%tEXtdate:modify2013-06-22T01:33:16+01:00DIENDB`puzzles-r9872/icons/range-48d4.png0000644000175300017530000000145712161170315016027 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WjIDATXXQ L%7 O`a 6~tRD}CSJ)snQšSBCy`Z۔R.gSEVR2q79$1ߵ"4 3J)<& D@`Fs2,Ci.Cą+?!P !,[qLsA-1-"cI΋]Ic Z=jf B6!FC],9mm&RJY./&)I)%xNF! gG:AY z0 q-SB& |< mfp]Wu'./޾{$˚5-/PR}X%4T8RJ̊8)%ȴĽUkU:15(Z9u؇Z4,fL9 tӏ<[ vpAg00W}IDATXYo\W?,xx#% RUcS*D-%$HPyBB HHhmBR)B$P4=YYxg{ghK{{9߷3O1cƘ7sx9>E^To1%D*M ).)e|KK- eyt] @'ؾw}|Zc@k޺5KW&&^ɹ<.HBί|zxxR$t.w`k!BN)B*%VRJLV`RJJBض"H) !0Va.3MLL&ښRkFRaDɚHSop׉D<= &<]ٱcG FBH)]3ƂL9";dO?T{W~h<Z`")%c,NkJ(~]c!JcxHJ9}gwiYVb'<}uDkAC OjnPѱܾ֑#_JtR~6`BƘsN(Zs@NJ 1Bȁ1s93>5ki7((oϥ&I)aH8xL4ղ%%HCb,@a(ԗV!RZIz 4A#KG:C33x<4ԍD"^)F{?tҩ@_#k<1 6E rq> >_`ƸT*3|Js#UV(4p+Z[$PuE!΄Xu&-BTM wQA)5Ad yB,!CPZ2&HLac !*)e2 BjJJۮB1%lvuu:̦ztkkk,D"u̶펎X,9q߈5k!H$9.F#o[EZi:&BXlzOlf׮_nZ.˗Ё?:ҤΝ@R԰QQ165="fƮr΄w KvU(ZWgt_12|'O9c`tޘ~49 !N Ge K+#亂s8NT_Y}LI$cyT2vdy3fNJ) 1lֺzse9s۫Bih#}}l7ԛ\xhRTLjV{xc<CTj۶ݻo6HFi2 ޽{ns1,듟W%FiT`|Flq:<cO:~T$iԌ#V{`p ^liiK&3!jN޸$>Cg"!ӓ7;z 6 /#FƣB{qA !nޜW_߮ɩ龾>L2y 6ʟOx-[nNL^Μ~b'@oBQ۶|p?TJG۶ 5{m4\CyV:gT*J)ixo!cL5&FW5 cwpd>foJ d2ٱx [b 4 Q7/񈈔ǁ%tEXtdate:create2013-06-22T01:33:16+01:005~%tEXtdate:modify2013-06-22T01:33:16+01:00DIENDB`puzzles-r9872/icons/range-base.png0000644000175300017530000000722212161170213016247 0ustar simonsimonPNG  IHDR?1bKGDGIDATx{PTNaIFG4fZ&ADL(Rem41D LD=l G؉) C.# Ev?1{^so|suϙvP)!@!@!@!@!@!@!@S:<%S3 ;4 @!@!@!@T±(4Jg|1@fle|{TiqC}}\oƘV]|?=n<%|#%9QV س2~ZGgSƘZN^(u9P2b2V+m1 Pm,#5|;]Bo81- Pe1(Ç+.MbLN \/@KLMIw(Е1ooRdDG)c4HC4HC4HCiN0p>d|HC4HC4HC4Ha::N644}|u.|Y@yZZ*֖;wV 11)ɉŽ%|l.d:Q۰ELʘwddqbf\svOU+NG]0V/7^jiNLLo}~Á>sgO4~s4ƘnSa>ũkBC}===veXfppodem8wGa'/PF^^ݻ}v^^^Bk[Q,g[Y)I" !]@Prsv>WUe,*.+*.s\hlj ^Z,rx^^i*{0꺕T*g|0pj[=h֬7n:/pG;^Xq\tTZ\_c4DswxQAQĝ|5%; PUe{dzzzٓ(pqy|cPG 9>7?^4{]b |# y 7nEG_@zCWjVaK~ʳd";g͛l6ۭ[]ٹ*p??y=**"3cÁdmll|l9k,_ šƞ7onVz'5StCyqq fӃf.IO_;[WٔaSiiii8LV@!@!@!@+Eu7z aG(4H#cZ۸WM_|;w3L]n{X&eݏ?$uENCizx7_4חaϤ.i2~8QQǟw+jd8?7,oܿ/@:::@9kҎwtt?x𠾾a;[+!mmS G DGG#q11zW՝lllF37,tŊ7x _0?|| @!@!@!@Vc   &~{Ƚ~ aȂ?:1c,<|9Gfle|{Ti/6)>{cLծXLr&&G _RZ7Rj=˽~|]ljSk.ir˗iZiO:÷|{!5S7pÇ+.Mb@ 0Sc.{Mr&][qɣ)Ir[ 0+-Wc^I.irߕdOLtդw%@!@!@!@пw f    ݿvg:u7oW||dCC{xh'PX:!~—]^2ȉ`e7KZm?L.D;oæ 0w )cDDzwZRKs}]mubb#\R&ȕ5FSLEy?x{PX,bUpqo?_)m=D kws8ƘַRPѿ} 0eEpk նmM_pYf2Ʈ߸qPu[SW]2cƌԵc/\"pk"@ϟ7HѿK[ 9% u drV+W_߰}NҥK[9w_<-OQ@=QVZXSS[c {cYܪUol`2+Hʣ5!@!@!@|P0Y?r)4HC4HC4HC4ѿ%+䢥Bohmisjzyy!X>zf:(Fe1;gȈb1&Dnx,#P;522LlugΝ=cv=ONܙ}_ye~;Fql旕9PPt͏ ;X(.)ߧRa h/wv{_߽Qw"?}{oN )7g sUUƢⲢ2u_&!!~˖L <<PTk|~oo/;ū2{BC===CByXYiv8Qu}JSo/r\ =P~܋0ma~=ƴd"59{swٹ*p;4P%힞+g͜%kP܋N`0pl6 O>=8hvlù(xh6elؔca !@!@!@|P0Y@)4HC4HC4HC4|%)pa|   cD cDx,8߀| HAF |RdЁH o@ f 4)xQ*@!@!@!@ 04HC4HC4HC4HC4HC4HC4H|OrGIENDB`puzzles-r9872/icons/range-ibase.png0000644000175300017530000000177112161170314016425 0ustar simonsimonPNG  IHDRbb$ǑY oFFso pHYsHHFk> vpAgg܊IDATxKq!ol^tK7;UD]c`DбP] VD'liizIgܸ[76{V=?UUYa;[G栴y#2Bm]ݽH&t:pu[~_2Z'Et=>yu:."38"@ohhx|㷙 )8 ~#2yj=5ۙצc&3A Lf0Q!/I%tEXtdate:create2013-06-22T01:32:11+01:00O%tEXtdate:modify2013-06-22T01:32:11+01:00nrIENDB`puzzles-r9872/icons/range-ibase4.png0000644000175300017530000000122612161170315016505 0ustar simonsimonPNG  IHDRbb$ǑY oFFso pHYsHHFk> vpAgg܊IDATxQR0Eะ.-K|`(s \^4Rg9`Y78Ǡ3!fB̄ 1b&/d˸=4!t;/Z_׷iBb%fD\t ӴSV\KY2:9T֦;ɻ5Mi.(S> ;J%*S6J%j OKi d&L3!fB$]3!fB̄ 1b&L3!fB̄xr 1omS^ӄ 1b&L3!fB̄ 1b&`cNMS0b&L3!fBޝ9ciBfӄ ɺx~?`i2ӺZktCCkmǒ6>?54=hL˲9BE7VxbV?|Gen]Ǚ 1b&L3I,I!表%tEXtdate:create2013-06-22T01:33:16+01:005~%tEXtdate:modify2013-06-22T01:33:16+01:00DIENDB`puzzles-r9872/icons/range-web.png0000644000175300017530000002577312161170214016126 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge+6IDATx]w`TU?7dR& AEzqUTaPm+veŲ*ȪiJG%$I&Sޛ޽&c7̽w9;hpL~"ߔңݥc҂ ԅ,4BJiMM d7.PJ!RJ)LP 22B2%!n&RJiܮ6,555Ct $IjdGWBH6:}A^^ȃBx<]taAq\7n^vzb}>Ξys{mb\'Jws55(rbroiJoĭK)U]} /8/ qK)$)]uu?t^=uMbdM[^SiB=7| xTB0rI>_aZ|qn8 A r>싺aC;vZIv:t5H&[rRQ"[oGK.0& d{klR: RH$E>c!x?~9hЀ={$I0(H䪫_hV,ºpZ(ل/(u0ku]4H[K f8AcfY~Ma%%m|$yo,\ؼYf\Ho1!cB(4#Q}[ZPLR 1_cܠK&R>'B`,+G%cn#)͸rz<*B \kXx.'dQ!xTQ]hɲ( ^GuK&#MO4 QJ].'B(I$M,t9U*3riЦ !IB.M`گUĘ,k_ZZMbŪD"01ޙ_Zbڲ/քDwØb1*XrY{P;6qii+,d$Qd' f.]ӭٱeVP1(p;se 8&YI}A3 WN9XցUU] 'K^a 3g= V|i_n۾ŋQH16~s/ҹ?VQQe)kt:Ôz|0l%)5t^>>hw"v[᪫kfVn/G$~L ^'''Z8PNNfBg$QrɤˑDf|xw=\Bt00m䜜QEBT͌Di4M0 u0LhGVxӊb,9gig8."RJMHkiq&Fnnkƅ!1Ε*-ڔt7V&?ĭPzF+?}rj`ȠǭMg'qi-lOR ./N+b,,\.g"rxUE}m;HF^^ްg<\1/PJEQ O%KkjjBݺursƌn1!${/O9_筷V s1w@gxBm?:CGZ('dDQvO>z`X?k=*Zi$!N:# BfͩmT}k^]:vcO 4p„+q' 1z /d!t@q:6o6b7mt:0M3''g /1z $uM7^7lؐ|uuwwݴ.BAt]}[|s6ūV˕xcٴisy={J1V-+/W^OŲeH={c!4![nܸc=po^?"HR =oyGvΝOpI|Ų1cFEQc͵N},AH%%%cJJJRl`vDQYgQU˩Eq\_QQ񯗧w9~ܹt'7\O:4e  nw~~>Tb%yAH&Q #D"پ}'"H"Yh,v9c.bBH]]X&X#bXӢ( [B4 |ܩS$esf mG&,y9 ¼D3&G`N}pٗ+֭?'tӇK[ޑF+Ǐu7naM7w9:t?y!tTڵ J&?Y-Xh۶}wvP2w!{;th=wʲ @YƒIJ$ݻu;塓dF)I& qmgR @c;J& V܌ 1&Ʀi6;i!>LG0&64Mɲ4MM 4)K;*![xX@al;p0#hgBqm5B(b宽@~~ŻfPUUP߯ii*"IbAAibK‚IE),*4L]{J].BHUB!H)_Yf)C-YeFn"MygAXI$(REQJKK`YcgQU/R_x)JOИwm55kHAz<55[m{aNSlT/@WE|U# dk vfEZ !&qz+BZJn =[vfY f2Хjt8DI&Th;3~G^!bŪ/tՊsrrLnE /=6o4kE:CQȲH$?qMxcU K$uuus^8$bȪ^չ_2v&H躦DQL$qf;5MKRVIK4M3MRjֶ$igi% *vB*4MK$˲ Hbd!H_\CKcj$|? ,#6lY|@C:<I迉 !dy xњN p p& d2I(Ph߾nY%!N:Zf.3O|KiiYŋmPYrf`%E; cɳ Xb чss}{=DBReߝeɅUUB8<!quu'PnZƿ~Bm )9B0R,p6%ޙ:|_r_, Z[9꜡GǓ /K=r9!K`xӌR /|LJBIX"-TBv쾩1R nXPY Β n 08/LK)v.5M A$Cx<E1=ϦasrmÇ Q{2ia]N=Yrᚵ_R)AAAưߊ+t 'PWWd #k^;uzy3d2 [.@mxg%z`}޸qs$UUw.]8;%M?{^~Յo\|TAp8m6nq'l۶ |wk~ݳg}Oոf,}?v\n 쩾X|ҳ<{nGX͟ޞG) nwkfvciJQļw#.ݖHpf Եkٳf@Nvӱ}}Ya!A^ N6Ƹ}Nci; 6\sN f9sa6PK?_,7k>գngbCE2K.xp2 Ē]q 뿪$E'ܷWf^Q*=w[&61ԥG.>} +V 1z/| u`|_FAB4aJiy>3뺑J- X?aLTJVk6B#hEeUuuMl] -/M%Q%R-YY?IΤ(0dH$&2]K,Jzl;dYו95$̢ kr0eIeYfdIAnnH$=(t:%Ib99M0G'G܅Lh~^xue_.s8cp='KfBv=E>_cӞ[H$H{ '4kh$Y #555Oּ /d@Ne2ݘ+ApMR[PzaqQ4M4pw:1$)Bi}Mtm̘qNg\OeG(=s4޳FÐ V/vaaSxf=vE!yNJ CeK%D m6ƅ-lP~C6X`!^BT*r~~>woɲQ\Ph4 Ðe[4=A(6]`'EYmzD*:]tZly<?X%}rs}K>xxE"ͅ( t3̆Q#8}#mHTWtӍ\}%79:W4X8q0Q$UVV&IABPeen{:M|ŪmKE尸S1؎z/ Wl0rH!^I6n~//uO?(77wu3_͢'8wbό3c4icigc&blIfY3O$ȣh~J7ˍ IJǵmKcw8ᄎB*X&EZrl?eB50w)Ҍ )YhYh҆Ʉ,_1MwRKZj:e eyf\(˜A\.t8NKF\.bmA rN._\n˟p+dfs\NMTjObwc+\_O0kkuJT4]UVi B (ʯ[^qy3(ʫ3Lh^՚ ڵے-ҹ \s}ysEEEW\~iexÆ <([N{+.СCe^}&^pyx<˗xՙW_=j񻂂+ l6*i'W-agg1[.:erZg^ֻw ?ۦR-x$I1MVOmFRMfb4*S[nw }—@_z PQY O{QUp$ ??nwּN~[BT|)6o{}ɒ%}x TZta)˙`Ѽw,fc(+KE_yp8ܧ3& aX(bsqcԱck ߻-^wb4ʱ[o{~d6|3n]vaC!xlmێ{zמ3ft\Yv~sޘg_8N23f///9o-//Y0ϾW_aA|Ů޽l׮XL+*,t8-^onc˯r.-[믱o_?LϞ=JJ׶(hV.MIIWcX丶mKJكS cQY|fu~{z򩧷"~gGJSԞ={P:AzGXBHTWW7i &(An]1ƹv➿kXW_ok !Uj#w7r4zڵ4d2y}z{c?1.-ڶ-aCf .d3CQQQII ؛ vq$(:{D0|sWM^OtcaAA<`8<,D}*CǤE^w/8Wn _e]G9W/,(KK3/I2@5%FZ=chh }f ܋_C3A„@y/ր$1{|eIb eY>@YvS˲ae*dĆ.jˍnZ6rs 5Q4 i h,p LblSdPgLV7+ Ö3v8JB%A\[j%(YSALdFn"`g[Hth쌀1~kq[P˹i:㵗U`7ߚƬD"g61.lړeeӟ&y0&yIܾ}`P)E_1+ B D1M憇ڳg]ӭٱeֻڤvg01V=N퓓cw(WDe%%m Y"1Mv-\`<ءCAܼ3%uгgG#`UUi[H$O Hޚ%*H$(h4jw&1#hԒ N'$eH$YZ 1ƔB"D,\FH$?MAB Ҫo 1݃*1$40( Q% (6Z|X9 Bti}MqXg 0%vHJRB MG8fgOyֈ#|w.n߈dɅctYs4Lu]&dV#_dɅ9^s:$8Νewy͓'hAl $| 5$K&Mƾ,`Э{v)p1!etK/{ecotZ"%I3K,5 #/?O !۸.C Ç~($kjVZ I-˒%>49W/FYW^ѣ-7o~y>گ~Q@iyyyy3=MӟVP*e#pƸh?]fSpdwUA]t7@t/Q4 uٖ.SS ʋC%If(T-s}ɒ 3cIۖw^/B43:_ C"jjj'O)RT a=i.L B0 =OI3.$`+n aV)/~CqnC9u!B%]XK({t~#)͸tzTt9v]{񨊢 TUES xT,񨺮mY(dQѨ@NBHQGM$lt9TUԂ=t:^͛]EB.[` qeKK XD"a!_qYٮ@ \]X4VXb֞*bFiiʕ-d)$_6o*]t97oޒvSZbg ز*-n&]KGP(>4Ի 4 L@)/a4G*RQ 2+͖V2iPBXMyg21ɦ4MpD$uбS/N~ߔ?#*%tEXtdate:create2013-06-22T01:32:11+01:00O%tEXtdate:modify2013-06-22T01:32:11+01:00nrIENDB`puzzles-r9872/icons/rect-16d24.png0000644000175300017530000000143012161170320015730 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭSIDAT(Mn@ϙ;ShYP-EuAPA<R6m33EI?3?CDDd栵B'D!$Ғr}̈4.V+^s"R*tt;~[OEADeYy^9::^_SUrlR)fBc<5ij$0kM!cAk]wIU]ZXJn=Vi!"fRF4Moߺr]!μH)vv*< ~J)(n$ ucRlnkzuzu8sQQ ȣQyȲ 0dY9yO,˲,MH_D*8R*%X_$""R\H9B UeR/^:7-EsnSu<PCn@!'GDd2S]$YBJ)&qǓ4M(j8uC,̬ښpX!,~NSfFk\AJID!zq_z3(H1%tEXtdate:create2013-06-22T01:33:20+01:00؛(%tEXtdate:modify2013-06-22T01:33:20+01:00ƐIENDB`puzzles-r9872/icons/rect-16d4.png0000644000175300017530000000063012161170321015650 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(}0vlm*fdl>$"<{/~PA>^$UTuwU-RZ+Zk9#733e D'IF]{V3˭f5EEZT@vp vpAg\ƭ\IDAT(=OoEī_?=;޵}!p$ %p!R>HA| A8ƖCfwvgzv_sX:իKO=vzubJQJLkBPJR^GG}u]eUU>YVwBˋa"u]988L?ݻ9})Ddg0)l@k|\^g3?_uXkG ba+{rolΞ}=R F!ZƘ£ſl+[.2H˥sn}G杷ߪ}۶1RH CКHO)%1FQJqJͦ]cQYYf1y33 DEĽaf= 1|7N> r%)RR 'h齓i>SE*i>kmˢL)QJBDb֚uqx2fȋ`PY,4kLDε"BDl,˜sͪYL&Yf_>>%(ԭB(?z[sa!]%tEXtdate:create2013-06-22T01:33:20+01:00؛(%tEXtdate:modify2013-06-22T01:33:20+01:00ƐIENDB`puzzles-r9872/icons/rect-32d24.png0000644000175300017530000000342712161170320015736 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg RIDATHǥo$Wǫ~==Lx®׎B!B""ZP 8$$Dq8AXw`]{*ogJSSo}^U|Q!"|bf@O <d8CfJ)/J9DR?,)"Kf!bt:(x $םN'مeMp<}1..~[/A ,HJA$1ڷeC "!9<M6!" B!dI)>.FRZsYkol\߿- 39KgqDJ;DT"3@8MS544R]RJ"c0hd 0lZ늢`ww<'Ye$1V*$: , Ak-3{w{o*f@D)RsiE"ΞJ!Y0Efho/^o;jFW/ʷNNNqƛ/gXCDBDܛ?1jLwD` ~ŋ/|ιFڵ߸q'?b+r8̋by"o:VWW>f$ {GWwww޽+̈5U7}Yu.,tuȞ$ry?ceUDJSnGH !"nWvYRJ"F_sL$ kC08kp0cCi"yzr 1p Ue-Q4Z[^W,GeYNr$QY湳XJY sc-x瓤ajk{o* IfŇW/\fR znlfI#i[3sR,REm@11jc3c@k]d{ >2siIt=dkXw3hZkڼN&4LQ+:e&It]ctݥ4MfD${n6003,48:RJYk{Ndc{χ}eWJCgfE7nܴ֞`<3˳o4WQ1""=">cD3LIc~^9v8~ZYyqi:b׷`pERQ YLƘ@sp{{]\\__ ,`20ngϗ^|}=""-,t|7a K}sw)rYQȲ駿_H0iDuUQvZk"43")7?p2$FFg0]#-&qEq A$I6ιMȲ՚`~~~;t4xv?ui8ru|2#%tEXtdate:create2013-06-22T01:33:20+01:00؛(%tEXtdate:modify2013-06-22T01:33:20+01:00ƐIENDB`puzzles-r9872/icons/rect-32d4.png0000644000175300017530000000143312161170320015647 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg VIDATHǵV[0 +^32A(=̔~Hv-38!hY8_!2s }WKS}bD̈n%ԣw}Y.՝}^Q/^fsS>)~FQ7ed[LFKVܬͺcXhSA<'2\ RWC\&C9 S&Zb Ўeɴ3t; 5nymo>+VE"ǪG>X|ϠN=Q@^&0#"IN;Sgac \3rΠs cɇp,Dv!nBڋD):y1LW\%Kp*Sb!Vv ⮹V+ vpAg IDATHǕVKoU>k;NR7&HʣJCy6 D 5HxbXBTh R)m$1qx&31,3v@.{9;I]|\׵Bk$Z?C) _om(`ιRsnc-Ow]79Voƛ~8UZ;o&"R@PZh,ېT*2'0<<0F)+J]FR_zyG>]ljqZmMWA*(O0lAD%0FCjm(\L!B]ν{ۑh^t ikeBZc /ɓCCZHo݅ccK"WlF)+9RJ!4pKTlh? Z+)1T,LO_X)!J(QBبT*kl+!>IyZE.cR* óg'++?+r΁\OEI~7k7& w, 'fr|>ǎ{^9c;'L{0KXd (agRٹwG& Cِ.6?gccQ5 `m:iNdҸFFWU<*G 4q=B"JD+"-NF1T*N11]ŢyאQ !9k=I V6'\_#jHL�I}(RESDC6d@ 2^RJaYAHʐT)Jah&}#ÉCBΑZkXr9PJB6>[*Q=Yk[4Zc6 MqVQʈ( c|@ ٞE sΎva;6&7`N[hIarB>0ž\OO.[*4,jDipf3 Μ97s?<C vpAg00WIDATXY]l\G>3sݽuC6'h mh%T!E [!D6'3Y_sX{sƘ1F`JJ1FQ1ƈȘJbHBV6"(R"*f"f$ʖks_1/Sg@9||w2"Kb V*{}3lw 8O%OK'&ڨnE1iADc ho1h I鸮J)82_etV5 ː!b$8"qΚͦ8poVkE8R*J"r]Z+q19H鬬 !~ϝSrL+%\쭪T*]|ܼO p}}]JbRuK4d"Y[876Yb,MVk$h㈕z|"~li6D*JZ{8g\mZZG};fn8gY}|~ǏML4Tz=Ϝ:or$xuR*I(QEqqc$u:^/k}E1n躮]Lf]dsyt̙xcao|` cpvZZ)έvE*ٶ53ӷΞvB<2|w=z/$q2o/;@DXD{tC]ײ'Ra{HL`hƘ#G>?iL)}(Ole6]7)wFrc8ze Z6ImGC|nԞJ%"rQT*˵Z5ctH+RB |epF# 5_yumzرW^MOs@a8BZ-Hz]@1z4&B_h:t0.pNp$ ""Gr̟_,ʾ_VJe9n!`2k4M DWښRJT1ٛo^,5D s_836揍)\Wr.$1X+mu PJRJÐaiN'KKߵ}@vuͰ8ms5 ]v NM1TD)mιzŷN<tVW^n͕؇+i>7n2FFj1&pk?$gH06is.8gL1V.q!G0J)=ׅA`Qu:0z= zT^ey~h"$ u]UD9x4emLgٳaj9i)? !ۗ,[=+1FS-O9>l!Dd[i5+ vpAg00WIDATXXQ 5 n&LOAB)!mvf5{/zXAlӳBD_ b _O@D31p`؈*K <"{S4iES:eȐI4}MvHD~##(5W? {'Ly2B"xfdB-d /x@ϘOM͹LB~:۶!"c "nclICjO2$UD&5>s9j4{7jhjf[r8䔝V~i\ vpAg00W LIDATXíYKGlFx`c9'\)gGXH ! H@(r Bƛ JxuD ٝTwږU~_5}J=/IBeYwyBH!sJEʲ]nE,Cw<4Cu{SJR8a6Ph %{jzK%B˥/{8ct9U1A0;wfgf2!D!>^ &GC6~Y YlMOOlFgV*_^}& aM)ؔڶ r9H: !"92n !DA*Z1(@[AqҹR*R Rq.~Y"aP'!Bl !SǍ0mQRqp) ehR8nmR(DRJ r̃ZT+c#⮮6K)8Ng}=l !RJ))뺸hwGGJ1B#t^d^w)2 (vGG)hZW>z8v{rH)HHݮVjӻvn߾- DJE)*JN9snthercuDZzkw!!0Dc(LZFE/64#j%d ) e;a&(:jdkjS5i &R5uBC}Je <#_ˌנM54o3a3VLa`!%GkhtRI2i6iü LhPq O$cZ"!0|>_.L MljHP4$o.-^R"Xq/GcccXrh⋿y}_,L!SVNlb||bb\)CKKW'&vƼWhvovl-QUBbJT\.cUNlȺ0vG>1-23Pm_JW-Ams|9qWlXSk `E۝vy+|2=38SwĂ`mm,//{ui83%_RL)ńky|tp*w˗O|`@`h6e2w+_޿E¹P*호,(=хQa?dH#Pc{~bKGD IDATxmPT*DM%d,Фш/Q*bM VDk(/ʔ Bc;#,4Mbe1*h2Cj ;3ۮ8ps}o3~jZ⠠5n޴e)(za$%8,lXFDW.ʀnMLĐ'̛LDgEޜUj׻y&|eyM{F=ҷcYRJD׳Ύg[]َ̮nG*1yŋD __t ]'Mͮ-x+җO!ٲ7қ[n;wRVND׻(oh,.)8kV<9V{w?޽{#GbɒV]UU'~/7gqqS[#ɻo g9p@΀ϛ০䁢Q*HAϒo6B] 2*gtiޱoo]Q( 8 g9tNW}juքxfXGT_*Oߥ(ƿ$%Z7}r2њPr:K=uDKs_=P\nS΁jg줘7*oh}XNY՞ k´!TzD]b=sDtDޅ KծRߟut\yrؗiߥ(f\_[rr./UKQLԴen`֔E]Ke9+]iw (/1=Ir]b6o%)I1Q}<}?g:FJG2 2@΀r3 8z_D}B1:DrHrEQ~I i(zo 8 g9p@΀V9z3^=zL_՝aOyq&Ƽawww[ iţFh~t46vdmmjl:wQIZ[VWnzm-/_wdYKfxA|z{ݫVݸqRM!Z]%^ҵ"3-[崩/ _YMyo7r7qc6]鈿?~"w/Xm6^8;y/\wQ#>gׯ;!*'M}9qTKzI|:;._~7v 9k>&qX9}mքilnBVk|.tߔw#]z2@΀r3_?+ojпTH;UMRUx.Wpȱ98 g9p@΀767 MG::x2=jz M5΃]]{"ormn檕ّ80$$dDtID3Q7u#L ß?oq=Ӈls3V|Xk޼9}Gt L2=)]Z7f\xz{{?v3n͌{@k቉ ɳg ? rMQQ&h}9p@΀r!1  RQg̴EAfTLכIw)jGshMdTYm9p@΀r?^wvm7y g+˙2oBֿ9+-Jտ)䬯Sk?OZ[[nI7oï쑳ٵV/rWG e!ohܷoKp#kqrMMșj>xgH=7r3 8yIfK7M72LQ0S.]Z7,3 8 gAu.W˭[G&;6"@gޥl$%Z7}r2њPr|.wص7g}Y՞ k´!TQϼKsDtDG PλT Povt\yr;JzeMk Rn~C:5^.]V&9oHM[v mMY0M]*`7wW+fLO~w)[=ۼzzz_T 3Rz z vpAg`IDATx_LSg_ +֒Ѡ23Ercؒ-3Cd:W,ӌD/G9)f 6 n-[)vc$&e .\239$ Lo8}y sz6+C 5z͢`(, 6͢`(, 6͢`(, 6͢`(,,@޳(,zoC}V@{[K/z{4Fb%%N!o>oyϢ`(, 6͢t yܜn/qsrrĮbFCee{Zs=]8]Ōfϝm^~qWÑW1{LN|ɦ6JA":$<܌`x|KLuaXxIF`'yO 0@umqmUh*=|>75?LiVbs(lEfQY=7rkvl"jdYdqbjd~Q`(, 6͢qnp/]_:r;Nl]waӦgV#O@2vS[{(_vu$&C߮4GN(dT+KwYk~1"J&T;,.lY=J"B Bjd2=}DT^^1 ' y`[xjvm rHX4IyOWfFÙLfvv/E9H[ h vpAg`yIDATxQ Sq4"Ԯj㆙:S8 RhB,f)4KY RhB,f)4K:sBmcg\l<pRhB,.`{߿>H3+ Z,kk3R,mvOsrb]W[,i6Ak[6س^@b6A,f)\ƳU!ny  }'f?o~hB,EFm잛'EJnss"6])'Ev}!V 6֓"oΎ1v#'sByAl3w/ҪYa/9}QT]re%n)4KY RhBE 7G5f)4KY RhB,f)4KY RhB,f)4KY R"/o'CAM.! `,f)4Kq[<ٺaڹ޿dj>oN=cԹo[O=ߠNW<_jZ_V]EnۖXEJvU~g E,f)k jxnop!?!*'%%tEXtdate:create2013-06-22T01:33:19+01:00Ìj%tEXtdate:modify2013-06-22T01:33:19+01:00+IENDB`puzzles-r9872/icons/rect-web.png0000644000175300017530000002240412161170214015753 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge$?IDATx}y|EU_s! $"x" " ^?袮 n!3=]UTLc25t?OU=Ϸٽttw‰Bh7!Tԅv!lgsJt%B;0 Ǩc N]h>㎾*$lҡCe7VP#AC6'\Bņ59N%?@ !K OaU B!Somy njh֬ՄB(L{qQQQ 5wg0&쬬ܺsӧE""~tRL_׍ܼ%KfOn ]X 2DTt &RJM(T[[+  #ᚚUU- 0EIZiDcx(TF-P׍X<C! n4ÑH4c B,WD$Iӌ"dգk9Q(X /US0c:a8!<'U }Ξ:ui,3x\4i߾v_λ_r%X >[=EE?uK$Xڵs珽JIx-U2tH*rl!6m%+V{WTW 9)Bte.\$L}Q<.[z ꆝ_ xcścsouTHAty n>iD"i_{ុB!G"N1%Ũ4p| V?Ve򊋊06beY{]Pi`ɐP[[;p`!'$ہC NX @K㊒wYgui=we<%9_(3Щņ][ji'!$//hPMM !7W.((*JYړ򊍅-Rt:1Ɗg@RPPPT4(J1G\8sHYy>H{Bӧ|ݵWG"LÏ̝h}뺡Rf!t=MVUH0VUt:B8JjI6 !:mˮB5xݦi2KOmvcIyn5!`(ؕ0>'!m-uZ2J[!g!i6;A<!zc̾ AѹwG;{Е8b=' Ƹ~ 1w1v8{yO&Sys^zYhɂ~1eh4VT<{ ClL:[|,7ZH$ٳGqW~52Cs|ЈAAAop=n"8.L$ ۷9+t]϶[{^ϷrG>_i !LRv^x[Z:rѨT*Eq\_~()ҳg]׳z:! #z}śӦ8~>p*'Ǐ{G۶m{n Xn{^3nn۳Mo!‡Ӟ|@ PUj b]5J4gk!}/~1^670={~MfͺwAKM"x,}զÆu |^xs޼W^q'2F'U!󪪕 3 !ϲ$ @3yyyw}gyFMF*;dh^^^*ND B.?7wD -g :5(++$ @:x0qa$v8N }-ԹP0T*p( yT\+Bj2y;qY^)Je+qҶ%LG szR<%N^m>gLf˨_[oXz?X]Amk2[*si '[-ΝP^ P#y\4-H0E-^bMv iETX,v8#<!@"شRQ6I8WU>m`-jh/Joyt >wɒWUj~%njI۷z)'YM.[ 20 W^Y޽ߺ\K tI}%%C"!GhKC vJ4V ibRDX f`7M8wG;hBszۏ^f Lj 7N)iĮ D' ']IZ‚&0ƒ$u D,1v8.y=+UFK`Њ7 7vV3XfW_mǕQ:NG0u]ʫzEK?lr2B׮pߟx<4~ aT$Yv.]$&O("cVv|޽>Ͽ/Xi7á IΝ_p]wR0=&l'T*D( 1膑H&#դ%p4t:L& #Xg3iJJ3LRM6 :gNxz7u9yF4>xáx<>1TԬ,XsuW1h׮nW=?ӨW^Yлw#_8R1[9v~PW ,-+0~cT!Pą}Vq'N]*< w&77GkÀ1ׯoQ A1&o>PΑ#G~uH 04UTB]MAdW}s2[Y?өO;y36\.g5q:@!OT;0MurWF@}_!_Y\4̀_ʕf򭪪Ф!K-[ 1M@o1p`$^дi7]shi1z}ܗBt!NGG,Y.X6Â?P:96l6iFIM%A4 L;ei2M י.qpEtv++ Bg*7(L@*;BEQhu5ڰ#7YB^h3|.iJ&޽FcT*uaY (+s?3UHeyÆW`?q*l>o_K^G8A!_UU}La|IP11n)wf%Q2McB@$9*,uGc1XH#hTD"ϸ\#[*b0Ȋ7޼暫7y<|g{d C~'?ٯ_1̱>O? /}w@ٳ;v]{=$IxAh3+O։Bi_z7+.T7s^0Mn?BMB}?o~,Zw,wuřBkjk‰dS\4*9v!mv?)R<7Lp8Yr8L@s4/,+]bFB`暫 y JOqD"3Ʈ44*$Lday Y4i>N'/MKO) 6ħ,g[B*l!{ ף{#yy1@mf9^!~0qgB1JD)X] !$?GzzuK!_H kmPɠOMm͆ ,\B Ǖ͛+wc4!cfh V7mڜ͂?#gθcC]l[/^zԛ,RQ{gBr,@I$DQ,w2eR:ٳ_R2$fJ(IY,Dv jhNaĉׇagvǖ-{t+0 K զ%%C,Rr5E5:!E2 GS`v5($Jd)e;3E #t_9I`]T4Xwuq ̉rYr0͈3DZ(<[zqDĈB LYyl6Q2bw:BDq2MU:CEq:4>t:z; ⢢ tꔀPfǎ]~f0=hpզ#G{~2ni_4cO*tnݺmO^{؎ɩakP(lnЍ?3oXXiZ ˖$$U:pjuhܵ:`ʐoر?,PXP(/(}'nae7DN5W-.]6oظÆeo~A>gTU;~+~<0vUV$G9Ri$',ˁ@*U6W8΁ լPe˖mѣGhtw?3̊RPf[n `u~};lz: ܺpDґmF<7{r_짞agd+BfL0O=vǎ+?qgfs?ʲ¿_bP9Q׭;~!|sɓo<#+5`{ui-5b<Ͽ{ƺ. 1 &C*Ÿpr{RT^ny/uXvfC{t:q^qe*zKLuAN$IX3~r` pi"B:,vӸ Ƙ}ц I6(E? F$sҴ+KUV[BuiRh:cbUH+0A_AcKTۈ(qt7nn1q\fn&eO2 #d2#j…ZVj7ϚqI!0TJۓ0MBTUUM> sxr =&O"2޽Gxe ފ6~*vD'/SN{Օ'U6iyo+S%/%"3YBIԄjNBkWm]_[$qW\~`+oPqS:rdalZosnx}˗/Α$ Sa>#G6m1&&>Uk\.Wԁ L ԛ&\1W~knfhHZsmӻu ^9~(f7dx"`:tHӴgvFFAkvy\:&nD`!GiM!VOf DH4T L ow TH-L޽}/v} 8S5K/瞻;k׭wNt ۫W}R#wIZfܧw]YV6Rl~}vft2rȏ??v8&MK;s6@vֽ~=UU~]wj%dӎLq:yBJ!<]rɘc!1ҴtHOޙr4)2}@UT*u\lBZRߘ&BBSK{B9iOcf,0v *?N[B=.{Ȳ,1&dYi`LȲ,]#q8!%: NKTnp6T=n7EYSTu69Kb*rIm۷ޔX `씝T)- w=og:T5͸3\àZ٥N$B I4j!tޅS41c!S-aA3M dԅΌm =i %tEXtdate:create2013-06-22T01:32:12+01:00.IUS%tEXtdate:modify2013-06-22T01:32:12+01:00_IENDB`puzzles-r9872/icons/samegame-16d24.png0000644000175300017530000000152312161170324016561 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(mIHaǟo5MCҡDNRfB.B#()J#"4J<ĈC *Ybn8 3V!]}-X;q/ D $ I2t !J*HhygKJ;ss?A%qXcFG2[M?gGp_WDsC_)Z\=]3dGiVVNf]={nS3u.ܛ6@bawnee-e·Pp4BcC@D ;V/efYj1 if ޸`/>-f\QazhgN1ooHhMiu\)X Ywb-(P7AeZuK}FVs2LSܡ^P$ີΰI +Q;`"emY#$y%ھŮda <+EY9N eNZ[js ~OXi+SҐƹő,kg~#3dol+Qpsʧ*=4;*ͫG0x8ۀÞ~I HsL2$"3" ba9Y%tEXtdate:create2013-06-22T01:33:23+01:00s2:%tEXtdate:modify2013-06-22T01:33:23+01:00.IENDB`puzzles-r9872/icons/samegame-16d4.png0000644000175300017530000000067312161170324016504 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(eQ C nVsdޏPJM &I@V\Uǯm>FhW}'l]L$oa lUSR|4thh"3?tqޛJGYeĕ)Bchc nT5FE7̩Rid6ۭOXZ^R1xmڑBl%"<0T ѣrX=1Ef,6{^3rIōf17%tEXtdate:create2013-06-22T01:33:24+01:00, %tEXtdate:modify2013-06-22T01:33:24+01:00]IENDB`puzzles-r9872/icons/samegame-16d8.png0000644000175300017530000000155212161170324016505 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(]RIHQ:XcZYfP ! R D 2Z$QR vpAg IDATHǝVml\G=μv߮^{m'ӸI) D --TT%-"HT"5?@mA *Ѡ**_ YPE(; ^k׻U8\==wtιg. ""}[tV6 ]GH8&h &Z-T\395Or_W.-6w_'cw22&"@de&'^bMOxQuj9[fDeums(%ME &"0yH982Z]vuE_3"BE"vA%h"ʄ& XO+ NdB^/HEgtpks1jh?Xzs|Kouu\.Vt:휻Jj&m|牻G;'~r@9H)qH?}`}#'uC(H D`X`iDu3̖svvle7Zn,)QE@_@_~c8;ٿ뱘c[>1| Gɾ6|t>! ',5xC m M`@"+ϻv &CTR"Z<9d6v给q7gnd fD+IѠg,c&W7. X\ ,:`QSẅb#2 ;r{)b"PJ@ǿn=#3VN\ F^z4}g# {8=O DOeB[?-),Jmӭj8 r\{`\f3A$C@wTJ c-=s.ͺiR]˂/:a~74"Z[v$ݶS]-]"oמWuQ0"M(L""LQdefU0C+3n?!C z%tEXtdate:create2013-06-22T01:33:22+01:00O9%tEXtdate:modify2013-06-22T01:33:22+01:00>Y2IENDB`puzzles-r9872/icons/samegame-32d4.png0000644000175300017530000000152512161170323016476 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATH}VA l_?#/c A͌e *HӐUmH f2.o : F0zq<9Htm^P AUu6"U+.@Yq0lgոP/Vf43UUU$w2oMc˗fPUאFӴkPMyݧA}{ ̊ug^j=]Z!zPXx%I*3V 3Ynxk]rYP8{QҘ1 ,+ټZ[rP<df;ʴ?,:sP9ck)R=lONל̞:`BtmґCJUk ^OL$^Z z.N$n}9;"a?y/W4BYK/6ڹq.0 Zu/G[gz8m׮̷>V %tEXtdate:create2013-06-22T01:33:23+01:00s2:%tEXtdate:modify2013-06-22T01:33:23+01:00.IENDB`puzzles-r9872/icons/samegame-32d8.png0000644000175300017530000000360012161170323016476 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǕVklG{}GQƨڔJiSG j!@TAhAUAD*Qx?HhP[ B-RZGCGyõﵽٝ^nggϙC.`İBJܰmJef aۭ{(L3HorFп}Q=vr43(ג51@\iH 7zṿs> 5mg*{4WgulU6v+ $ew@|Ҏz%gs%%us ԅ22rɌ lصr.L9FcH0G+t_94v/NlSˍ:oY)c 0H9#tCpN1dvd!=> ~?K˶. ]~Aѩ?V4h,/A2˛f ~4{l\?UE|}p5Pc!g\2AW\Jg6xC/0咹e^CexCF;iȐS];s0y^,6PcB)[S^.F1hƛ镲X_%*kY2IENDB`puzzles-r9872/icons/samegame-48d24.png0000644000175300017530000000500312161170321016560 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W >IDATXY[ocW}̄$2鴝hA- !QE/<"ODKTBBB@%(B"2ű$ĉ/gñ'Lm ,Y>{k}k'5Ö8N A0ҀqoT7̌DIH$%|DLvƙ$D^oT*\.3zvidg~JkDdgتCă{;f-ƝrȰ ͅzĀK.4X fmQ޹]FBUO潇!4B n@" yfx'moK5v3t-xo뽣 ̾Dpٙ P̷WV=!].C2t cgq@7hf $_k0Hf16 B=O854@ZÑQ=N@ϼ%Цs/鴪gy78h0qK_VEJݼb{;6@s7oh ,,&H3#iyڐr UoM:rMcA=3ȑ 6Pm`EL&ڑѪ=Tǡ}6<^@ɗ_KΧ2-+Dfcz|yg.egRDJ~w#DZ[ZH@9gޓKCv1kYzU;EDp uRGuwMGd4)X/h0N :vfb1 |xzങ\uy2- `M-DQy|Gu//\ˆ+cslyw$ Q":&73%x*LUyhy#?#ZȈvtByݛ@ S뚵;(unefB2(%{0R͍!,9>~ >;:u셥lVQH~_vlZ#M^}sqy}竿n}NV,䂢8I}@$d @^nyvVEIxzB r!`8L5 \)y BU@#w Dm'H@BGaq5žEZ xM>B{vTf$ 5Ƛߛ @)~H5bb emm'>?cQdjg|Lr~Ƭs+<_vr,M}囵/~X ^"ܮ˩tT#ˎi"Ţ+L?qqmegU`Џ6 bNae&<#Zvd3/^(PAalږ+~dGvwv>??ی |nG ©)LSV~禃٪3TUˬ⯂k}}}:DYpwE5>ܽ~,yiݷzVܢ&wAWG[ەb.Fl(L14@_Y+mW `FjJUnжg0Cڙ%DG'/!HrrDLҍz]b8Iz%N]PM6>@M,̻ZNh}3we'5Moiǯ# {"s Ɖo>6o%[^{%PS}G%czcZM =S`$vʵm6sޣ\BKE*&ּ @\1m5PZv ukʣRQA! L=]~Рw-#s}k꺂thTL~`j8ޞIY05}9st67mO9۳ɠ:gʵMzBS㙱'>{2}fv4 'B5| 5 ڹиp#T_Bx4<qsbX6dsi$4ZF lT(0-Ee Ͳm 溸tu*#~|ti;6M gY?*^U5"WPy!u^ӓ!kr6o䰦3q`€ӳW;jra=53ucTݹ]#vӇ.r3f]"!vgf o  X Q&>-aNτI;eDq S~ա:ģ>8qw _%tEXtdate:create2013-06-22T01:33:21+01:00~#%tEXtdate:modify2013-06-22T01:33:21+01:00IENDB`puzzles-r9872/icons/samegame-48d4.png0000644000175300017530000000221012161170322016474 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXýYە* ,|7g:E(ILFl N/C!8Uř[^j(S;W*ScbNv"^ċjzJn4K>@Ҵao\)ѩ[$M˰/K(UbI#k~@E+};  4\P aѐA.G 2hC eQ}@0SneFU dcց?h0]X@.dTFUNYJW' K@n]v२-޻= pUovmn_ dH@'˼ߙ*A/d]` [c# @pSᔩ4j#h\2ۈq;2hLfGɞhu`e}0\bZ 7ߣA2jVdF2n  U"*vJ. "+3+aK-n0E eUHT0L1GǏS G%MAh5Ys971b}Ʀ>Z8V`6Ԩ!l5c>D%Gʼ`1*ƺ~wyQ.yk@ݑZ6Ϧn*YlFclET|}[k?Iq="Q!T'@O(Ut tAx> (2ϳSUUOkGEC6+F 1pk5G2*D!ƫdhr]*J*pT݆%Qd2/];xnݔ1~.dWH4;g%@jq8CW@N@tBA%\ל)D(6ֵdُ7~${}$mC{m3rZOBd{gUu`d5W)o "NbBy+nQyGf`|Nzc[KuEYr%tEXtdate:create2013-06-22T01:33:22+01:00O9%tEXtdate:modify2013-06-22T01:33:22+01:00>Y2IENDB`puzzles-r9872/icons/samegame-48d8.png0000644000175300017530000000477012161170322016515 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W 3IDATXíYo$GGM{fx7(JdY QHH'p〔DBq@ D!a{vmLViG:;;_z{{UM^~Ӓ%tMO|K^iSK_Gh-i1M0aAv nIx ظnq۶'!C,c1?LCto\=[ ܽ@?B&8۶=?;'1QApMѐN)`gSCiVsZq]24sH.L aM{]rD4WҦfhJu ѵ v9(s3I^F>#T%̻ Dgrs@3S4&:bd!LBK?+ @`ԩ?sJ`*u>`D YD2{H']IhD5yuq1WA~ BzL3Gr561i$$!qҙ=:TCq/bopzTc-ZqU ^<>9\ofK/+Miᵭ-b,.ѵy}j>y/^j-rz Z#[fb;S$ʹEci|,yfAKf},vk4/Sﵘ͊In@t8dI&r(\ǙbrdNB wYi-Tg<YHHŁ0X\^=bDv;fySI`.:@H{e{7קzZ1+</]w?z.}?%+ Cj4.ㅟ=վ,V ȍ\ޓsC0zb!f5jA/`Qqh w~GnU%NM3<3OuJ(/+K$dXMC&Bl!|0s)N@Ų!Phi-N@\u]]ٰ3n+^XO) {o| jj|?Pw+aX B8׻_$o_w5u n# 1Jd~Ә`#6S|?&N'<4^L%tEXtdate:create2013-06-22T01:33:21+01:00~#%tEXtdate:modify2013-06-22T01:33:21+01:00IENDB`puzzles-r9872/icons/samegame-base.png0000644000175300017530000000326412161170214016735 0ustar simonsimonPNG  IHDR^^BIMbKGDiIDATx1n#W@Ѳ!\ptһYT_Ząqw>'׾= Hi4A @ Hx?+5Ͽ?N>?ị9_~y[|W=/ @ Hi4A @ Hi4A @ Hi4A @ Hoʆ{g׳57e;|>=:> @ Hi4A @ Hi4A @ Hi4A @ Hnʆ^Nǡ矟ƞ=[l =|nݠG={`kWGky7e_q[i4A @ Hi4A @ Hi4A @ Hi4Mټ# ?[Vo y7b#kl[UF~6~?i4A @ Hi4A @ Hi4A @ Hi4A @X}S6ߪ# ^No1f ְwC?mA'0je)]>|zi4A @ Hi4A @ Hi4A @ Hi4A @r_8}_翞wf;n=?|nQ?ṃΟ.U @ Hi4A @ Hi4A @ Hi4A @ Ho^ ލSc[~zk#>r'׾= Hi4A @ Hx?+5Ͽ?N>?ị9_~y[|W=/ @ Hi4A @ Hi4A @ Hi4A @ Hoʆ{g׳57e;|>=:> @ Hi4A @ Hi4A @ Hi4A @ Hnʆ^Nǡ矟ƞ=[l =|nݠG={`kWGky7e_q[i4A @ Hi4A @ Hi4A @ Hi4Mټ# ?[Vo y7b#kl[UF~6~?i4A @ Hi4A @ Hi4A @ Hi4A @X}S6ߪ# ^No1f ְwC?mA'0je)]>|zi4A @ Hi4A @ Hi4A @ Hi4A @r_8}_翞wf;n=?|nQ?ṃΟ.U @ Hi4A @ Hi4A @ Hi4A @ Ho^ ލSc[~zk#>r vpAg^^IDATxQF@QOȾT;+S>f,sηPlqޯ9 ૿>i4A @ Hi4/4Ӎ1OL 7Xt9vۖ9燐75A @ Hi4A @ Hi4A @ Hi4A @ HNoʆwvϙ`jGc[t9W?K @ Hi4A @ Hi4A @ Hi4A @ H6e 9s,{8v#'qݶ1V}?ui4A @ Hi4A @ Hi4A @ Hi4A`S6_?ѱi=\t{M @05g]˘cѝyj οoM @ Hi4A @ Hi4A @ Hi4A @ HbGxk>u}S_9>[c[tc^Xu}?ui4A @ Hi4A @ Hi4A @ Hi4A`S6/2Ǽq^Sۢz'WοƸSi4A @ Hi4A @ Hi4A @ Hi4l~9GbM @xa];gՏ15c:1U{Ksm?ui4A @ Hi4A @ Hi4A @ Hi4AFyƱyW+_|gjÌ]}tގ9V5ƪ Hi4A @ Hi4A @ Hi4A @ HiMǾu>w[WX}aja̱({lyw_fj{i4A @ Hi4A @ Hi4A @ Hi4ApzS}lR^?|n6501>o:~׽?TY=\u}O]oj4A @ Hi4A @ Hi4A @ Hi4A @ ޔ[7n +y^p߯>0 Wx;i4A @ Hi4An=?̆ %tEXtdate:create2013-06-22T01:33:21+01:00~#%tEXtdate:modify2013-06-22T01:33:21+01:00IENDB`puzzles-r9872/icons/samegame-web.png0000644000175300017530000001236012161170214016575 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge+IDATx]]U}|wd7 gt:(VNNcNNP*TFV-5 * F+ $Y6}sηܷ?νo7,ws? I޻{s؅ e*-ϪCP ?EǬZTX ߺa(lIW_!I8~iv80Bq_'66]iFaKm^BkӦM @v80~]?غuAЫ}7g\PK\r/o<Cdp o3\ WD97b#UzgϞݻnj 543n]4>=NޏSLjsd)|WE6@B i83{>n۝ꍸm^ % EzoD:PPvc{u+$+ {"3נ<!Ԑ Q KiGEaQQXzTGEaQQXzأaĔ*Ahd]Q4Q>@?>eWⅇ҃Vc 6 'C!QU.T2Ӵb"@P$Bc8(\T! ۘ!k˄ * Wcڨ^r(\ 2sٰWq*ң(,=* Kң(,=*D:GcHR],M+97TKG\+9!zAuʹ\W`pC=9 KFT6PІTENKcÀ,.v9COz.bTm6r*EJLBRخO<4H"I̶GuI=opӅR>4GU|kժ8eu#BU+ =#u t'4m ?ٱ{Pi+zG{ UÛgO:AuUw>sǐK Yt24oUb5g d}o?`i /zdLZ^'{'HpXϼSjҘc[?^zcSKg Ua-x}DH٘;+!8gؾd}?SO[_E$g 3?ƪhlB Ack1$hL;` lXs/`O'U?pp~*+SAc0jp.D=RQQXzTGEaQQXzT_.4VX[T.3 JHzj.tsT3A|YyG_FLE//D'qkՎ(}agv{N{ "xI6m/h^4-Û 9b*@5!7d0ˉ~k?xs8?6^Q1"Cc9wJ)M[Z[{F7y1@6v~M*Û]:[Wvc*3-:)}`ffm茶R,64n.   1Z+tS#_UL13֬P#})TraQQXzTGEaQQXzT_.UD?5$k1%bwN=*Ң !& SjE_}ǣOKv{y@ D{p4Pj 9f1ޱ^B q< =}h"[pI q5;:l0<ޣ;h\s/ 4C=1:9i.zԻ޹wrTVa>[{RCyVxɎo<x^׏7oLj玤ڱt_g7dl NL]?`Bc{n?cLd?Ψyp$Ƈ㏳"gf&߾4/PL<;c[@ڷ -9X.>sBD=ּb:I%&C#;/OfŜ a(yiEvo3BhݰV!`B [!ǩE.z5U8jTGEaQQXzT:TP(~ g`= ,RQdO@'}DoUX~E5}儸R c jna4w]qɎK&Iig85*kC9iL5 zJ17 h+-\}tlx_6Y|g >dNLW의W& 9۞{uo]F6t*©'O:x! '+6}h`b֮:P{Ww :n?7? -zY᥋p6'mUSd7[x~pP1 ^Ӆ{l@$t&USҍ?}ҐLN~Y)5wٙw .H V0&*ޏ+RO2c$AQ0I )T-^XC;^0 N?K vKFnq: n)\%GEaQQXzTGy),UV_a繯1jnvDv)+g[fG J7qZQT5#gCDR9Hk9@3XLQּ! YC\Q(i7 gJyt;S!&xt U}V֛ ұѱ>OE (VڼKq~dxOH/֑Շȴ$ާu[":9ś?{1ANݗ_^Mj5/>XM%}ذu/ Tn:hxёQ@8d[} a tе}͉'T;Ǿ 2u1 p:NIoB,%cX4tMD '2@g4KgFXDO(}r#QZ s-ivEcW; "'4G86I+KzDEaQQXzTGEaћµ)syBnXp<1!e>aq/F?BauٱAuYfZnU  ;/29bUt=c9K}-R }][{8evR5SLLBVX4T(L &6ĂDfajR"{S(S_ܢ(SO6>teglz3Mٻ6>nLVv8׼_'5Sm>>#YR cۯNr:BuЍ{9?" A_') fw'nyPC9ly? '|ummXu4%rKgwm)]]xp?ޕCXNq?O][x^KT^ë&v碃Sj. ML[qD' i Q 15bog1 i*S8:!NahyE{:hM+1f:3Dm .eA]So$ H! Fݛ Qd.^#1s~+G/KE~vOJ<܎wCΝkcAyT}QQXzTGEaQQXz*+f[ b#Hu=806S Ntþ#ckE8D磑/Pe\. k)i|>82dwGN z wQ9jaLɠm.)؄2^ܴ1,sz!̀84l؄@#WG:il  iV7lj5D|3)b0:iaҕRW{y[cmqcfϟ}򡖭U18/UX#δڦӶۯׯ[9A$dmS\鄦m[u9IKwP=#Νc7}FBdgKq?piCAZ1*=q'_?zG*&^_} R\e"?7 2J%5^vk3fJH'?Q~ ]Άlp"Y5/ލ:g}b8e 9'אBy}p`ɛ*7It]s$\*r1km[XƬWOV;geOyKң(,=* KcIB`X\d٬sGՃf^~YØ]SܬD\G@^ 5E6*U9l:#`۠U Ban=sʐ (!he Ep=tMGTxZu+Z}4jTM#=K7,F09Y߰yÑ_Ah$8}}#Z74 3v}bc::'oWob 6g\Mw ;jVۿ9SQ7PWHо"YV('AfQUPHĞ.xYu0G$}V8Of*n"];ݖG^|Q]{uqk-aqw>yEG2 O-<\BT' M%tEXtdate:create2013-06-22T01:32:12+01:00.IUS%tEXtdate:modify2013-06-22T01:32:12+01:00_IENDB`puzzles-r9872/icons/signpost-16d24.png0000644000175300017530000000136512161170326016656 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭ0IDAT(=NQ {̄@IH@oоAVϺ !I静kw^XΧsL߿~bg Pz5Rղ,p'rGo8*qc۴CfIFl~^`[$",[,uCTm8 0x< Tt|,fhyq2n+r$8lo -OR777; 5MBZUiex_Ul`f.Ƹ\.]zON4mB˺ ]׵=ߛO4Q۶NXd<t2u==\~f흋͢p=0}=y3dypTUZMMeZ9|HDY- D"ud2C UgC2˦zZn=^D(  WC vpAg\ƭIDAT(}R! v6QQn0J+*$ iflKH{cbu$n) Rs. joYX[jc|>0$ӷE<zkKH+2=DDD̦ Z0rFࡪ"r(@RU27Hق%tEXtdate:create2013-06-22T01:33:26+01:00K%tEXtdate:modify2013-06-22T01:33:26+01:00!IENDB`puzzles-r9872/icons/signpost-16d8.png0000644000175300017530000000135712161170326016601 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭ*IDAT(MRn@=^;%Q[Z$(/<7-M8΃+`Gsx0 11&[ AM݉_|Zѓ<oKcRjoooH牓o6ݗϟ RNc(k "P 4M !(~4ϞAx<(b\Yכ$1]l@J9?n/.#) R:,Q2YMkL!a[.RJr>q6NqW[U69qc"jwTU%za6J@$D䘑~6;>b\Ege1uם¦ @ï< vpAg LIDATHDžVoGݽ8 RYME ^oJUxG-jSBرsaN|Yk ̀,"fc-@FD)%HfVJJ D h ֎LDvKIXk E@cL^.V=gf E_*ZV(qHFV=<kyduB7Z5j k*t\\ӔGW?!yQp7zMrVBё VMqc⬱Z*\zƆc%I@fBݻwxe5=()iDJ~pulv>z'zh6/^H:v{{hqÕLD(r^jfy֖B(a VmD ˍEckDI\8v{kk+ C! qvvX,#1T*$;'1'jLv-ޞRrvr]p766$?zkkkw<";w r7of2r8]J حLID~kk-B@D%qZ+[-Q7Z1 x5 a1RbEQYbRO*6?" w&siǝ+}&Zծ''":8Ԍ65%m?ƌKKKJe?4a`F^_WQ*: 9::3ƀ1TRʃ뺻 Ԅb3NQԚ_4U2i =/_$d| cB:*>eff.,sDb׉LRJ]~]ky^: - i~f_DJ_!fq "2 t٭3T*^03 HrBg\$e1F%=FQDZ"s.kÞ2~Ջ9w~ 鄙(={ArzH{"QC 4. 3Uc̀Rǥ>ցɲuhI"خ1 ]fNldDI$;// =2w{{bΝ;j{& ؋ŕ EmBZ⳯lӁ3D׮]sg}}])!dffbKĜU`ˈ ;-o]9ctX"5&| tA"  n3Vi+ĮCF7D&8}ԯ@B@D@˶3 0ht2ȀybV&A %tEXtdate:create2013-06-22T01:33:25+01:00%tEXtdate:modify2013-06-22T01:33:25+01:00IENDB`puzzles-r9872/icons/signpost-32d4.png0000644000175300017530000000146512161170326016573 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg pIDATHǽV۹ ƙSd҉XJ:Nqud_|D\ff`PTT&ज3|ݚZس|XwrfbczM_u Ye42-#PF@Ip#DkZ[޲'W:Rsy8DOD|^D4n1s؋]C?z4b H^Ly5pf~};ܶ}"bjDTkW;ÔRJ ZJ9R^+TJ !*{Ι~Ĕ$@hHTa)^Bl5KD"3g,y:|g2 b|1*ȝE" @8tEDU4{Xjw*!E33WENT3>d;lgfGw ` ǁٛ3,WLmf,j]J$!ZVmM;sTsr>"a 3(1t1TV5K1tm-Swk6Ez5"GϬhGf}y(]$n"nm >m=pmVwD[- "̈hVTq6‚%tEXtdate:create2013-06-22T01:33:26+01:00K%tEXtdate:modify2013-06-22T01:33:26+01:00!IENDB`puzzles-r9872/icons/signpost-32d8.png0000644000175300017530000000256612161170326016602 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǝVO\E'{*Dz],T[ƶD_I?MX6EԲ-˲w̝.'ٙs9;g~3`ydLB a8ùFI<¹ck @)j,d>v>@&62n}a0J浶TY4 ßmꝝ޵֕Jvcj$H gPdO&P )1㗯Zo盅qi *yE&Tȝ;R)r#yۗ.w(>H)TږR ;7?]ⵠ@geoo_f/i)I;߽уZcv;ðR)j,]?#R0mŅιmJ%am6{l6/r. ֢94MM<77W(6嚟{bqZ3~iX,@ԙoݞ[ ]+ߘl䔐Zצ!EOR>(ۥGGry?h`muR5'EFZkI%]|Q+X54mlCքB{&u2ÝAF WU&1W C/`INY dFGGFg&Ԣÿ_A/A5(MC+]+ۉ6%z7J+o a)I*?~蝉ǎ3)E`F0dʡ>b(v9缉9E nݙalo0+__~,qvk)ErBY&J!* N_PC6666ċ{dsq~Enݞǟ(fN:A)܌}U2:w؄b{]xZNEˣJĕic *l%\I@2ct<;2}}Wb`q?hU' %tEXtdate:create2013-06-22T01:33:25+01:00%tEXtdate:modify2013-06-22T01:33:25+01:00IENDB`puzzles-r9872/icons/signpost-48d24.png0000644000175300017530000000433012161170325016655 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXYSgZ h-lNj&deVR0T9r%9pY; .6,# ehB}{IEI3ݿ~u S_Po'DDJ)hc`bD & 6 "0dZ Ѩ1  GJXkm9 +V?/ma={ٯX* !5& up`{\! z=TJRW !}5@:9Rv;#K.///g2՝8=|mO9it]F40L~8LOO0B'&8?R3E!0z,N1@M!8狋nކL:LBA@tI9 !ׯ=+ ccc#/-RV,TLM1)ZE7́u &tjX\t:avòf!"ETtݓ r RJALZ&p.vrgu5Th gݱ""d>y`(gŽ=Gb^L }m-zysn +~sb2H|ce߿0Dx: 㗔huhdL$"-APVH,m7f?. A΅1ک=z%eHlmmmnnz355ww9}/0EG^Xy.- "c*89!8k2kT*Nx|uuڵkdzn//oY+ߐvxamq=5yyr|F|k!l-\T3@dAXe?6F3䜉ꇽ/;訜NLiܵϟz*J ]gSȾe˅o*Kݟ38kjX__˗o޼9H蘌6j:qQgS z3'aYaK&wyuR8Xi !9"stbfrbffj!gBK\xZ8P,뙙~RZL`DDLrA#+"Jg,N1ڐ\SZ)2Xl"fs&lϮh]~m\V(vdUSscQֿ ^b1l!N&¦jSPllB])Hш߹s!k5 c\p:F DDl61Ƭ'qas 3![oF>uj5{og)sEʕ]?:\a͎- ϡ tY裙˗hA8w_B8"]t5":(ZƆ2 ׯr95ӜB x{Q'2h 73nrOYG:F"y7nfL&!@tB@Q][]c2t<3ad.ʕ+lnn CrD:E__fɱs/2gEi}r3U=;@'=tZGD6sOpFgÖqg%F1 @ ڧ#FI 9i.}@sF!vK:ianQ$sj3پ%9ى ǩцe&U[޵u%tEXtdate:create2013-06-22T01:33:25+01:00%tEXtdate:modify2013-06-22T01:33:25+01:00IENDB`puzzles-r9872/icons/signpost-48d4.png0000644000175300017530000000132512161170325016574 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXYq ]M(Y%dyx;4wG1x֓.Dd $q^8k*π2. bt-FIUn^ǖTlʙVBl%"段{i%Qs@PJ3趭D%˅Z~1"nS.!&8D6e<<ң,p2}mSJ@&Kİl5R-;1M9lrE &X n`0YjvIe"bmuQDGuį%Svt,p 2BZ/YIZkFH13yGud?ɚ.|UAU̥J4Ɍl0nd~8fYJ*fـ9J|ڿ~Q\c׳%tEXtdate:create2013-06-22T01:33:25+01:00%tEXtdate:modify2013-06-22T01:33:25+01:00IENDB`puzzles-r9872/icons/signpost-48d8.png0000644000175300017530000000374412161170325016607 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXYS-2k,{0&c';0laˤfSm.=!s\@Uv gCX36X`aY$*~zjx~;UJIO2HJ)̇2FRןi#Bzт BHPs,3/|7 !p}}| >nxuy92C䋪Ts8?:<;cYK=0jY͍A8%\(=bX_}crRxQBr4 ;?jxii);%=Dp|qh4aPMK(vvvsvu)8ͦyIRRMw8}cz3L oNOETROOH4s `PFhN6M)/L}HI.v:8.WgRvM203tbC$...ث2XBzr||4C(siW'Pj^0뜙?9zΨRP> }>!ejh0@H^=dv{^i/_mRT=7:ܓ!fXp8Gvl6[T@T*yF[Yp~B4kkO2ath9v+O!|ɶR*5: ŋIJ);Qt>`X^.e!W];JX!BFY2㜇}ERBy :rlB[۽ܲTT(c0Zg۽ڔ W)3ˊ bkP齁$.:,%NO?gggjPxv^(~$j ^}W#GnUE 4L'd`ϿjsFv$jṾs~˽ 8R}p3fvh&]Ϲ?ѽ:oQ]JPMU2%%t=>>ee == ?*>vuwJAUM ~6`㣓eY"B V:hh^wfviO sesp=w=v=`CZ䤊ۆ/|FF+xZ<^mM\y*,}0xT O ̤,*:5tjX?s,f@k "1Ӕ0 S?p U .*c]BZJb!pj%X ?caʒKV1MspP@vα-&foAX7veWbQH (&DN ǥ/ˌ"2Kz+1t!pzRxdkѲ^G9B۝s9>{(Fv{"JHO%~QW B[?o? Cᜯomm5ð]9{k]D- ?M< )3L%0(h+ )J?usl8N/-HU$GY+qIcoo>:| 2>Rmh컽K*ѥStI5[%tEXtdate:create2013-06-22T01:33:25+01:00%tEXtdate:modify2013-06-22T01:33:25+01:00IENDB`puzzles-r9872/icons/signpost-base.png0000644000175300017530000001730512161170214017025 0ustar simonsimonPNG  IHDR7~bKGDzIDATxyt׹bْ¶ll06 ?\3`Q:P yiFe_r_b2ۭH3gZfkn^By>LOO7oa˯ANHP37/UT!sssy iA1{6Aba9b b8`zeR$VkUUU?I---QQQ999\q`ɶ{RoߞpG?Z{Nsݑ^lPL/ rv8FA1턦oȆ+pJЙA! ^_<"|4V+_ Qxn?Tu&)X*QiO>M)ti;m;`qP :;D>9zEJn`Q2'd6fח4 /,q:$zժU&ihhtbLwt&aQh^fz%>>>>>("*3< ,4FP`1"2F9&̒@lŊO:ǟq=/ܹs&(0c`ra itB3vЌF,4`y'2x(x˛ <8O|\xdOR\g2O7!jrzŊB,XxBxNP(l 9juQQQX,F8xJrOvZG~ %,ܧ 9!hLL\ MA}FΝs\[m"or]ͯrzN%6S})OI y)f /ߟ—L36{2il?#ld\Dq҂_මل6ă\>. nkkeXz\VĨӴ}˽{KW,L-li |RI`"Cwuu]t$IY|gc~E UiL~W͝otNlz|6`dih2=_nNEb_*=Bwvvt:ORSS/|tʆ?r{Zq6Y`;]#N׈O?[XGoSOi$ؾl T[[K?$Br\:+ғ:g*TJMԴkkHKCA-]NY~ ^ăѵ6Ur{>$IҥK.]j3UFx轈Sl0X<onZT!8=+量}ONgSoMQIŢkJ A~tݶt\D34FP`1 Xh[AhfU6;SL38rsj:׷9 B&t}`Xuݒ#E'EYi\ăضd/Q6]y^- )O{;6SPN:14;=6-Y];6:fGIOl x`@|EiBYeSP)y~XCӨl/k֞~nnyӯ:ŽBL&+UutFU:`ψm3_rL&~L a7FZC7IVc rg;KC 8м Yi""R!J+ -SD"(N +df@YhlE*3uINqfE*wxnmF%fj<~wPXDp|e[1ْÏb8;;[s:tMl3N 6~]`N(Ig̘emEA=lxӠXr={ֿ9@jmDl`7 B{1y tԜ6O N(dS'ْ|9mfqPXCÿe3ݒ㹣il3 s34}yی ׳/j6SPNR6t-9"E c=6" 1`!j*E 4pR!34FP0YCoo'wsE1߁Ҟx?FBc ghBcC'wy:azd,34FPL zJLSeotN(!"$1ujX$;4̿F|Q0M<?f-ؙH:od\:vWuVCb̛-*h [B4<.=4aw[]Q=J*OV\W):tTI6i/5|Bmw[?>nskȽ1KZZZwwwllE6mڴyf(bX b <1o΅7<\&gK~4=VdYBp:]}L ʦ6g$|˩o9a 9*f1 .ӏ<Ⱥux>pXz'rmj(I@&fK$2Db5-k[_8;!5!Nm;[]zs JMRSzs7 ?OM[llϞ=8'=E^K n4nzD?P{,κt fpކY)B3gj'lɓ'núu먇|O?S\J6MU[ZlT aSh]Vzuɢ ~;c$AJNT@elO)Y'љ8bdCGbd 7s5X+oLЧwFC_2m1Tv;HǨjH-Y)աG,YH3U0A;7GaKlN{} Rl sk)w^$IWFr^JBfhǢ{ƲBZkN744d>OЯ><+6S ܹ*Vx^X$Y}oxI,Y$ e?D)M &A ,4FP`1lQ^^)F,4c 5-LJfAkr_,43 mjh3Tc)yXk ˂^v6aRr<+s)IW{"(p{906z<#i34"y:?Á_eJ(ӓMl3ݲ嚗 ( ;fsH큒:f #Q_NssQ:MlgKrx\5iDRR/d4uf#)f/(Gg4Bm Ejq֭|͑~H hJJJ_! D]M0@C.И Np & *)@/F2#PQ7ޏRTW8Cc#(И1._v`^^AWT>@NNLݕӳeٳ#\DO}c'/'/d}A!fgsI5Q~rwΓC\H jZu+…ەS\C;v|}1uuI##Fcʁ 7E~A=CW6v&Uu !9hwG2R8Y2ꦾիllMջ޷/NN_~} 5/{˷w `kS(ʛ]+W f?qxttlg^!leWzz-)!jF!^Sa}bz1[ͭN{T嫙=gk,VC"?+/yi˕+Nϗ<@̏'W?֜3@ټ97/_pWXz\VĨӴ}9ޜp(]Nz{v+ӍtG:>$Q*#Rr|~OnOIqu/Ñ~gR%D"KUgi05@ gzhL1SVQ;صk`%CSk͘O=7h&xnX$rg56ޠ0A?P{=^"I5" p:dž^=69YDm;栞 /9f?M\9<t`DxVʢXHBc˓LU`EVZoìpaBK_s12}>2#?t&uPSi D51E,_4ydm8tzu:{N683gOt8]#>}=Zz: fL_{: XCBΟctn?,ʽN")=i^~FBCw@wUQg|'ǔݶvo6><NUC-nz>,*"YQj<zrÀ NE&ipǎ9RRo-oqh}A!Y3:xt8@ZSex7 |\N9_ = ,4FP`1 &s;1暖㈬ .\[p"߁aXX,\4„=ԺcE{pcO{'8]VVF'ƻӞ| enݎ{vz:i3 N[,3g3g:"Rh6Sd)xw,** :"Uhi_6Sp/)uz޽VV+UGd~l;9El˿rIf zseE2::X[[[WWj4[:~F3gN^^ޢE͛)eC~4888MWHFh4~2l``›v!W;;φlY6S\{jZ\j[[[}5h46-fh4$_BNk3EhN?Fۼ fМ^~}UUfZ~f˖-S(ݯP(-[V[[T0"`i{1m5]6SgCc$M ^<`mnyTWWnp~Z'Onٲſ b˖-'OTٺ:d$1{n^jY`~mM$E`{?Px]'oG_=… n[$LX,޽{Vݴi6o<fTHl`U~ scbs8@-EEEJJ/HZ6̴(̼pBPoH$z~|y9_;yPO06 &?'zXOPdfԨΛgOv{mm-DfU(%%%_~eII = MΟ?/JSSS vpAgE8IDATx_oV1%$vMJZU{L{ۤam.UHJ#U@HG4)8ރSIn_ÿƵ_k|Jq>p~~HN_MXx#JZ)>(s ZVVnz=@8[ZZbNMO yqUUUYeYn6R)DQa5MOSo\pwiT՝UU?ɦ\.$IJ7n8U}KR!sj7i0GLs&I`隞?^TƆi<'zM6("򣅅278Nj*B9>>iaH$牅d3$,˲,kw O&,,ӝkr98l6qcM 9֚E45֚Ըki+<M"yvYFX,SSS$yٔ ϥ+2zP0v-..A6'NmZ:[jKd2 vpAgE~IDATx[n0 "RݬJSubRS Ezz| gv1)ٕ9;i&w%oof5ES)V12,+\MS_9U.)l~ 4)E1SjcOS̒u+lh{#],0fhzÑ/]SLY'R/fxOӚvS&?k:'xO B};{w|^:MLҚ6uuM&528ҝ%0MKrCkFԼ4}s>ZDo{҄ S_`l!#ў7 nM4A=˶pf^ё-VJ5A.#tLt~2x vpAge=>IDATxyeEy?yꜻ=MwOL,"(-QԠ1bb~I\EDMAdeqa`z_o9U{:y|~`:UooK{v??e#TfC@DǠP)%.y{,@}KfAB)94R͜Va6ZkqFqf@ڏ 3#"?f^Ar]1ST( TVV-4GsP,[uMw`1uH,t|DD5M>D214ojjGjѕo|ӂNweٖs^|G]Pt:{O@g968o%vNn&cݻ#ʕ+}CXb9<3#I,r EeXTGgt"z700Oh֎hkk|_]It(R~tj {W\\l5N"2DB $ě;Ǖ{1cҊr,F{g65>O58o5Lzd4q 8q0^HctGыǎ^SY)-S9Ҝ;0[nVJݭ.--]l $~K of @?6?ߏ=`@J `s .[!dҧ7 ط]bŊ+앞{ [[[HNnRy5HD&PVVnݺxjj~ |߳gOPhʕ3NოRh4h4juۅP(H$m622@r8 !GA !H f=dƘºr=VJ oݺuxxx!k<= )Z=r BXq{n p֋0Ij":yd2̉mbOOŢRbs-**όsȺǾfFC0AўY;T i@aȵvN)eNMD4 t]7>RʜCbq֭sNqq,魀o-B @i?Q06'_hEOs v R):rXB$ɞ눰RYY9Bm|6J$>*&}Ѹ D]KX]X AI$<r7q81F1swwwyO* Y MP\:ySk}gVVVΙ΄'hֳx=Wv~b]acy饗$!Qo=ϫ_xa%MQ__ZJ|BP(8ι; tf,AyWb.q+ xf\D]8rBlZ_:gXB]tŋHynD"B+V\pB)8S]]y۷[^c6l`#}/^$ǥ- Xcu-[XB@D`x>~̨2f@zq4Հ33O\7u:m۶M@ƍ7N3ˆ3\SSsYgw^Dc%09߸u Bs87o\gLaϖRZ8a~pTGe˖ot]FCJ|/qy4\Xۀn²„h1E!D,?~FsT<$KC`}^ MSXZnVUU%ɦ&?1oY8vh١17nd2yߩG֚3s`梢s9jW1Avqa ߹Q5h^ :%'g_攵ens B7o>x`kk(̋3( s[S*$1kca>V%K؏9I87A Ap|@K @!`#y@4]~} 4EKqvjJa{mw`@a;a3>. fGs**ߎh;'iHgu놚4|jO8wI:&ITUS $0)ㄦqz!3"p8F...>U,Kl"7@(d#Pk]ý<>>~ނ%T+h4 oƋ3:rRhmRV[dcѬh 䦅fgrۜt(m cǎY>poQْL&sDU!U!'*`XE\NeQWCo?6v %>SuW*gHksݯׯ[[q$rSSOvtˤ}eE9)g5?xcǤAT\xKq2F;1A"'{2'z2^2.)Tjht$3;牽GI/PRhÿ;qyL"U!J#;HyCc]\uOz~G?j+oX\SJem /K)}ollGȰ6(ڳ IHOzI o3Jc5~23J\~ɲiWɀM&ИSDІQ={7/Ј=ٟ>no/P7j21lyM`4S_y7mTX4X[G\'O* i ZEVɚW[RoX @R8Tȑ.` {v{`A ? \0u~Xeu}vlKv}Z"REEDGgQ! Sz߾}5k0KcHd6\=)!9VڷG#,7tb`HvH˖Z Ng8R,*<ϑ@ !^=xgYva/ "("҆ ${ h_~_  u!j+{F,+:(<^,EkRy&9èA6dj(XL E̬swpO @{uˊj3*AJ9U_VT^cm Ȇqƺ˜-%%O[AbZ)p"'{S}#ɾx9,404IQS@nݜBR\s=o&)͂&Y8tФq7VOqq ‘!@ 'X(IٕNl=trkS(D<~sa@?ye񍻿® ?FD"qAu'OIRP6Fc!30+)F2sOtqq4sqZ W:cXWෟxQI2IEúЙMf B\pҥKmF>H$_iZrJ#IQuMf!竿?$] A/ RP 3KA}wrY6mёkcPEg|V mt+$1G!^ʌ 4L+!XfiZύAl z3'^aA Gw}g.o@*9ugY|yOOO.3( ,$6nb¹(cTIzX_|?;o-y >90ikʅCe#}YIiie#yŭ1aD_{x;9AdK";-lZ3WIp%E"?kV-VO5gv4̕E~-lZi)&~`.'!DW!3j5J8"+ F:O ZPQEqT{ȮSSeTY1ghq7nO|a.fYbdk]Y%WdF 1͢0($d296t1B!AUiBG;_2CƏ[6ȘESR ۫.0' HHr)/2Q tcER0Rn[L|n\3iA;y^]]իQsKbՍ5kuFg=Mh/*i]愈ց3]0?_%!AMDgy ;%W'l[A}Hy~ Ok/ -^~^ۖ*6q͛7 , ʊjPchhA$TR!D k:m sO0 G $D"ƊuB1rN*(G:^ !54WLy⊒3oEaH!_{i $ 0{OV,;3LQb`h]spU8$?f#lΔ,TAѫk+*"B#d' K}jx*d39G7Fxsf^Ey93j c#ByNJ;4ʊAbvI@@,fi~{,#!O)U3A$$%! A;LzJ)cڵk/%j5ԽY}ymSg ş jH͸dky%I "clF5!YKhح9tyGmU2%U*a8~Ժ r_a2fںk:v+TqLY+Z/Ow5˧xf *HAl˩߲ty(!3xۑ6xq65`$矅4槮@Lm$x. ;mhp+KAfN"1@:v\? _3~x &Q*4- ;I(YŰVK&jS>qhhzC$b6md"SJ5OǷhT[3b-b hôÇ|3wƍ-[f}8s[\8RE[`4Lf.7g${zM6Ged*g6R8PBϫ:qK\''ˤtH<=~c! []]}ȑܢ[.D”ӄl3?R~=Y ̵T L$IfٴU6 dfw#]RpgiXtF,\nxj({QbP!GGg[Pm"'d1Bap7|6>:cI}3 LwPˆ3Qm[הƪ񙌣µbrI2zCd_qJG ð:_>s/6B}zMR@`9qDKKK8^nݍ7x%{)켄[]r?S=w(TfL$zIn*Dm'LQ!aH{#;:wEˋk  k׉u|x7Ԭ<7 t.P} ͛7?~?^{w2P(O @"B` ?~oz tro I6V *O5lPdDwUU. tR(0ۤ. 9;8@]ZVTWZZmO|AYkmmp_wu###_җYH zaơ[!XttGm!h3^RJehmlBT6i_tE@OCQ6y>8B9*4WlvDg7Db޽ ` FM)~ (|@ Og3XƳCݯa{#8yw7| ~`6׾qњTf̰"$F6R!f J$]t;xleE5LNBsKcwu:w9*$*Pxgg1k),LTh| >|.6dGˋ"gkHj[g3xt/xqp}Jm|V90$ SW n_8]~WUwW| !Ğ={~R){o~-P?Bֈ7v$2вg--Jhx A"%_ǻ8*itw򥞟 :?D,[rYg•S760Vaڬu&2 <4ɘ7lF?RiU&jmhò2xJn;]Qa LK):4~<B$~2B2䨳I6W䷅))nA2A 0x*9 8TK ʬPOg?_lə@4M:]yx3 `q=m'z6V*m4&ytaRjeŠ^ L$'<xUeMl\'G?9co8# 1Y9hy~Km>uE&+rݏ{28=w3 a1ܻ%8w$3珜WfꣵEg,*[bL:=A%1Lg:wAJs5ִ]}t r|u>Wae)ǰ~ 06wӐ8%!^Td&>K.hMEJ8Sln?&J:wAZ!6.dbyX?_¹p =z| 墆QŐu.Yh& ɴ7r?/T:㍶̤$P:3,baqgs W- R7#>g .'N뵞f$=6H/+jqFcn u*->mY |k"%hsW"58;j[]*G5曉o2zڵwq833;Ji=IpLB" tПeHfv`k?Vb]#@hmv]XImѮKh]it$'eSYomYq-37׮ES`8V5w&~>:bk֬'r٘S9>Ѽ^A,&`0V7`e U~ ok趎L\5M%'ˉ,jYS^\gL`X;*\!): aTZYҠg13-b;j~ׅJMp c]r^f1&@uk[&dHbdLRIIG`Q m;SM5J7߬?mSJ9C$(d!$ EXRu} P^AL4[y6c֢SW0TL`\'TۚHV4֣ ʶ>swifM7cBɎpC7JrG$?Wp,6"0[ Q=~Sͺ<'$fvH$Tltߌ]GRJ!'2Z(A@)mic:/Ps֭թ: կh30`?]2ɏYIxz <ξj ~䗊YJ''z +^'g\G:pr{gߡCΕluxW prEijUȍ (U@0sk` xi/0Dz]UUk~y/%@Q/h<(+U>w4rCYJ23x$-!3d]]8K=`!܅~ټG1&;'@!\iam3+3pTd!D҆%MØZ฿n7V[[۬Sxc}ǟʝݎ 0sv &ַ^};-nFwWGR-#F¥=msC""%Ɍ8b&Hw3XZ?o>O&[_H E}:tG3aAPc U :_7 *Z(,ZD[TfX )ꋟ~Ȉen_ڿoD"႔r f}kz֭L4[sf!WvUQa+8*ֹ{n菟(-'4J8yX)GZA0)&EEEO|3###R!ozmp3E[$xҁrgBq߿uV>8yM ED?Hw&$w}Uߪ,iIk)RfN_$V"aiVRqsD2""MaR~|LR8Ul@[pѣGݵkמ={ve{=ڵ .Xfڵk׮]аlٲ2I(@려'n9 60B ukW}!=.x,xRވ8YJv5Դq( ]Klw2cGMMJ|fېR~N>WuG'ES&#W_tZ)uַgD{ݻwۋeeeo6m4i.j4.0IH{?@ w>"L:c$=@koܖdZ{ ֗^z# m搾]z[[[l2u0D2ކe]}MP! ȁەr tbmuyh "C"HmoٶwDu]Er4ui󹝝]V0|[7lXI)Nԍ>я~ӟv]C6RAlٲ;cÆ Ǐ`Zoٲ{lmy-fiMȉDC%B:Ffig Ma!'sa8^f"5YJSqT^0LeB)5.6L$d.M7eyM7]veqO)%3BS#=Ȉ=;/J '8_O-E?p=B1m^+uܛ*$y62W낦}yzBd29NLȭ23_0QH ":{ۓlB)- $8zh{{ʕ+PL$BH[Diج[5׉i퟾ (L,tv#GqcLSS{Rcɓ'q:s3Y-a͖^{V^rDӤZ {g=yꩧn'|򪫮<SB5$:hW]3X& 9.K.뮳[{׾fL>\ҚO`!)h7~U ')BDLdf`@V A W~4sΟ?TJJ)s)ئMq0rٹ1|;33> [*̬3L IW/~7P'Z`#1\lS}\O82:VbTJZq _ܛo FZI)eE#ѹuʽ֤U3dž>OE .y4_?mąc&hɟ&Siw()MD==Dr>QRRI Bi)]wZPJ,棐pB1v+BFNmw)Vg9 W2ns{D}{,|GmFǿ͵ ^#\"0]26Bg,Do™o?BQk/J`%tEXtdate:create2013-06-22T01:32:12+01:00.IUS%tEXtdate:modify2013-06-22T01:32:12+01:00_IENDB`puzzles-r9872/icons/singles-16d24.png0000644000175300017530000000165412161170331016451 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(5nU ;+AjH6q&!EQPwEEG*t(ʥ K7v:ND8Wc cc9!ß~>:jwݽ?2/I?>l4JR0c4mRhnwh4; iʥ)$0_ZYls|\.Aou%"1',IH˸{Kϵ8Qð\.B(B3 11tNq 10BzkH9%tEXtdate:create2013-06-22T01:33:29+01:00Mmt%tEXtdate:modify2013-06-22T01:33:29+01:00<^IENDB`puzzles-r9872/icons/singles-16d4.png0000644000175300017530000000062312161170331016362 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(uQˑ0:1ɝ)I@<l0$ݿ%$IVuGuWSޘ9yVq'13wHWf.w ҹ} 39: ; $}D7b³9Zk[64IG }/vTc85  ;4dL~sF9%tEXtdate:create2013-06-22T01:33:29+01:00Mmt%tEXtdate:modify2013-06-22T01:33:29+01:00<^IENDB`puzzles-r9872/icons/singles-16d8.png0000644000175300017530000000164112161170331016367 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(MofM;1Ԯ4+P:-m i7`\ qA@4q M 8ZEalD2:qvc?ġsz>CI*c<߯Mq0x@EQUL6u[-"Iiip޻/"缻uS0ֶm;N#}[@862ra06sWk._[],oeۈY:-RfUew-qݫ8|ߔd444dTsyr6'O~rr4wv|PJy4s~g]d,ˊdDV<2^k8 lZ/~i:yQq*QJti3_y4^e?Q*镕WSӿH{$I0ƌ\qSJ9ڌR[Y]-OAiZʲ\oniGQLLeYbV T*FE~> .}E0P%AZ+gmW*q|mUDQcii_'")B@E0 xgGl뺪yD)M@' ?rHg2z\,pg[5Uϝ?{tuu>iF`u''rniZEwnA驇$Akul/8( ]]*wz%tEXtdate:create2013-06-22T01:33:29+01:00Mmt%tEXtdate:modify2013-06-22T01:33:29+01:00<^IENDB`puzzles-r9872/icons/singles-32d24.png0000644000175300017530000000513612161170330016445 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATH}ViXe~,99p"KPn ZM2)-Qr$PTL6mkJ r&JEЬK4Co|;?N!Lݿs?]QB)Aq8Xq(ɲL)Ev>B$P1@(c8.ϟ|l\  ,DQq 64ƛ:bY)VWפ,q֭㞘sUi xKOOO8cpppꕬW, 1 srǏ]L`0v>SWõϮlOR:R}7EQUZ0?M WX,J)!Pu՟*NMIlvQJ*Vk:ËbZ6n*ؽPGyH ,|睉wFVf4])S&l61IIn@~^.BAdfNSdy` @AcA9`Be!+aA]g&b`n7aݮ i6::eǎh( BAEQA> Ō0D5>R뺢( \.]7,&yaFt k,8|h\\`14m~RdddTTx^ʪrA(m*Zp~A`o_qV_ ?w`/ ^Vλ^h`I}IggWaᲶ0c,'1qY*|R^RSS0^,IB$)iZDDD~^niYEYn|bbKA@`EV%..cT]]w bcgq8  Ap\|\'Vq6banU%zEQyBp'&$$%=|mm;wČəK09y'^*'Og ].I)ep BPV͛=Ow:eU+W^t ypuaU66V(Zptt|$eۼe=c4MWd%!IJ+W ͞xo_߶W xa˥_~yp4^ g[&MLMM>u tRJMӜ=g挬u˲BdZ=66vٳ/>>.kzix<cA~1111Q%%%(JzZڃbS3˲: baBzOՇ1]nQjZ& vYeY꺎 0aWU57oʪjB!3"Yedtu]_j+^9GiaLN|l}By!8߾_600pw=L7.í7n0tw|g5/,Y2 !D)ʲv`Wume[Z.YzSEI0m-2#5OZY3ǏRu[} .ɓeBpEgZU1 0̰Ȇ"S`0C^gVd,uuv|kѣRSRLjBA#"0 90#'\i]]r<7ayrׯwA D/aE#B$I&nKU՛dԨ(0dY&F"lP% >B@EШ(Ji߇G_~?g9%tEXtdate:create2013-06-22T01:33:28+01:00tf%tEXtdate:modify2013-06-22T01:33:28+01:00)|IENDB`puzzles-r9872/icons/singles-32d4.png0000644000175300017530000000202012161170331016351 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg KIDATHDžVq \:a]ɹJ2Dt+K$IZI$w$~5 >o0!33Yh>nqQJ[ t tiN MkMGo$#D!6 f9> :k,Eh*Qa j_Ki"o'}^ -Rjf!d⋬TeJ\Ƴ$jso^<[蜶y H_<#xWLgOQXJ}?S{:d9|۶r・-罔r|j3d$32 5xH>3ywy~F2L]gms.lf`vPTJʖY P鑼8'6DZGNj [kܨuwH>[ DyM)h .2@{}s.d{Wl|!h"bbSR$<x5ۦLG5njBɏ7S|uv@c9NW"}?/8(ObN&औR c# SSrRX]%tEXtdate:create2013-06-22T01:33:28+01:00tf%tEXtdate:modify2013-06-22T01:33:28+01:00)|IENDB`puzzles-r9872/icons/singles-32d8.png0000644000175300017530000000475212161170330016372 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg %IDATHDžV}TTe{g #% + jns* тlmnSmun%~e*Za| wf{aH~y{^Rdihh8SNSdYF#z@R!=;.V􌍱N.Ku3mşd.O?|X7gbc Cww˲Z 0BhyFZP``SSKU5 DGEef mw&ǟ9W{֭{dlէy Q֐g2 vʒ$#ĹUէ7m|'+f͊8;Pvx o^CQ|RRZv%Kw{itlnmiƌrr(5ICC Rdcv8Set 4_1x<c, IgΞknn/#DxefST͛Hst7 V<$ù9adI"8KHH`YG}CӢ NwHtuuMxta.bRbАJ[{PPe&l6UVV P5wa,Umw?*sʀ@D~~ 9+[4;>Yrr\O8q$IZP:ё` s1iy1_{aIiᣊ R5a奤Zm`Μ={eٰ'\ohoh9p80aa˖u:hyd fFX"Iq}Adg~*<<yyF&ɺK_{!.vps =c4EQ βG{z&&˽IIk X-,z-2j_Qʭncٍ.E˞/&'%h)u8X~x||Z>a<~$#cuu?N#r[w ~ntWk\\@V!E+t: P;Zq\ww0W[[r{<Ѹxbcc%Iڹsu$a;NL.gAnpP:YqMi *䤒}{\ɧf9~l'7Z!yͦR|~DX1I~#YZڵk<h/Ty'I"թRUzv\ln϶nۼa6_78x*1EQ-Ֆ,,ZpLAY8x|GGZ}RR|r.@-c"$ 4Zꂗ:tCz` >X{6ZB +eID$ȑc.^(~̬l+1"DBSBy`Ѣ RiҶ}g R57>{`lRS-2Bh& e%Kvom**\ %]߻oz25'I$ơӢf(Wܜ@7 E j1qq=de-|whYҖ-;7o?~vd'@&7`~ZܼJBw>~[,8#DQ(J=Vc%FO*i2Y*?%tEXtdate:create2013-06-22T01:33:28+01:00tf%tEXtdate:modify2013-06-22T01:33:28+01:00)|IENDB`puzzles-r9872/icons/singles-48d24.png0000644000175300017530000001044612161170327016462 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WaIDATXYwxTնg6̙I&!)DB!"*:0 ((M^B1T%&!m&d2)}8a\}g͙oo onixi0 Z~BwknxW@BH48S%UUq\ߎi À!A4Ͳ,,+!Zl-@BeUUih4VV^/,*Df$LP5Z0+& 6)`0b` ,: eIr8))F|ɲ$Jǐi%%&,[SSa㖲qY!~̗_zAQ^Ϻp"P迹C9?01!^aUUf b(I >?;^VkNC~[RUe M\1 7>sv/c(@UU0(˯/=zT)t_s֮mfaZ_43Ͽ^UmEtΚMDn΂1㞻|ʽ=d^4u2\VQPP(bjjJBB<`jV'N|)C0Fθe̡=;Omٺ=?Ĝy OxtUo8z'tC1fmǍ>ƍ}ZnY TE|k]UXTti3 ׫7wm_;+^ h9bw@}hjFx< Ny})'p$l6g Nh0KfsVѢ(ͦkiz}', iSn4bXBaUU $Iy%Np86{b$J8)D! EGG/_߽皳`0hƲ("a(N$B0UUu3!IJ,-Ɗl(kkA_S:a $q+SnBP(!DQEQ$")$5M#p:CP(ħ& pkIɅ3 yᬩ?ұ7q\~ P(%9$IL5[:$._evQ4Sv^ Ϫ*g9|#cpzjJr MϿ~{E!fH߸iˇx<6dH:g46r3gΟ/Xtǽ>sMijƬڧ&;v@ttKϘ=՗۴eY7$^}^HtrrrrN1iiv̙ߘ5%:*RpPߊ ÐmXvq޲^5]XuGyƍ-+/n:nni[7o]/~rԣ)P[[w_~==oQ#?_L"Ih4bdS'~q\ƶICbccGs!CfҐAJBF8$cB,w?=zب* òII<oeڷd4l6MJz%2M q1 64xƍc6 ꛓN ImRҰaC{ `UulQ5 4晧ƍ B$I2 QZo4p; v5;vNfc{c!&$Kgv8$3cǎ/Z 0,X;?{3O&)А!&(onիVdYу BhZEQB0 &ʹ׬_|tV`I`S ̚6o;֭h5 7ҲO>0}Su#Ю]Yڸ 9K^|QǾv(7vs3!&Ϟ=C4UUeY^kNg5'M.zuwʏ:>{5K(RϞ=F]v8kj,Zpa[mt>ɤ;B,8oVY۾}Ąx5M#" +GOO ˲ivg~5 Ƿnݪ0YVף{C28V3 z1pDz?z|`$IՂe%&&WWnWFC'F1f'fa uuK:vl(]$iH$PTT\U]=g`0xj{8!$INEJ_Gtt4MQEEFFB*Bd(Ji~=yj^|5 ooxx1$o W[[=5 << Eq\M(8m$.\u`bbEQ^ӻ R$6a㑣:o-W$Q9sZ5z}1Q4g}p3 a ,++u.Aeee(FDD$jj&.>>^SUx5eÇ~ I]n7A(10Az۷wދ-^.rmMq/.?W9C"VUՌ93{55Mԋ+9rzpVThhC+H亽:A57oޭdB\={Ϛ=/g֩~HYA!Iav[?MC eIrUQ?Ecc#=<-kJ׮is.vMt+ʮ8ޣ{ACb_Fܹ~?_f[,%Gp{uM ii ^ݮ(J8pW7o;;wN[WǏ$IDaQy MxvbXuuMuMuJJ2 $7nBl[b厊cVSW 1rİ|f I",N4(d:I$c{ :Sf4 ](0tHl4MU$q=g~ 4ժ[DNlB lX^ k{!B|}~͕oo22ccb$I*,*mm $CԹ[$A?FonA^pwa$K;tHI(l6z?Ǘ/^GFFoРPH%QMw! CAQnSރa :뭈lVEQeEѕ^A=n<\k[^z>A V Sfmw-~m[$7Y˧?g ׿pO%tEXtdate:create2013-06-22T01:33:27+01:00<)%tEXtdate:modify2013-06-22T01:33:27+01:00laIENDB`puzzles-r9872/icons/singles-48d4.png0000644000175300017530000000231512161170330016366 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXíYK!T%7ofgh̸J ?Zru{1q,dրipny.L4qH6 i`!bj "$Қ K)YJk,IgL!RR oCBD"*ZSJZClK)|-wDqJvД\%@fj2k|-֚ $ęKcUjF4<` *sɌyG#"9~8DX(ih(B$Zx`{ X p^>!$FªR9Z5_w}(0LHH<5&$s_}k-Ι'c|]!;9秖qn4Z+`kXKSM5q0L2rCgq{̄Pַ؝ie4bL}e'>lfʾva([aBJ&&%DPa1ەt& M x ֪A _0WuK 8u .'dZ 3hsMq =QIuqJb}]/vg{X}˴;@ E`rRc "A[@+觕^IzGLfz!f{TsΩְхM ?M0j9uGyB5T4(J~ @X\eaq+1Wq5wiz{i3|JZzطőu-J3Y4"vF| >}j~^m3`J)yh=u](l/) :2N h-](LZzJd.^O{J?D·Ls:Lغ0EInHu '@cNtbmg)TaWǘ?tʙTɈEj &иO`-c Mҟo>EK@\[kݷVS~SӮ-1 iWk%+%tEXtdate:create2013-06-22T01:33:27+01:00<)%tEXtdate:modify2013-06-22T01:33:27+01:00laIENDB`puzzles-r9872/icons/singles-48d8.png0000644000175300017530000001011412161170327016374 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXY}\TU?s_f̝3 6(cJš/)i*ei* ծ%YYV[n[ 2 8ޙ3sqpU9w}<3/~?uEQTAx@@RI~* pI HQ$Iz<IhƼ!I(~yVQ>/FiiIAĸBw׳,[ok덊JJgY'뭢 ju niXE2PdĤ6YV7b[wS4%K S< e+/_rwSf3ffۍMMHBh?=644:n"MBŸ.\\_P=5uf3IWsZi?xs$I²g72/X4jMqbEi;wBF_$??/+3ȈGF.22fMEsxo_Te/Y^TT0={v&=qRv)Ke\FL(B^wzʈ}_I S9<=;YZeBÛ9NnM^ia}cb6o.7n޸q$II oD!Iĉ#"+XB,k B^I‡EEEy˥V\.WڄGX,nɷ4MtAh HhIEm Qj) imYoALHzk^0^RzͺI=lْŋh0,;=b J.<[6aXݺd۶@WvfY3hծ8dpƌi{}oG&&Mݞcdd(UUeeg^ja{ 33ڽwYS,+aXXQQYYU\\ &$OϣiǛ!C'=< G>u+V-^:7[~~Op8(ӧOVZs7nlf攄!(K*&Ȳ~4Mt:\QV Fvi4$(~^n?|ٳN)**<80ڋ?q㚼sAr' A2d(S׬5j$a(BƔ_~Y{N޶}$I8`E P%K*q0trT%FSt!AG=2wcRG**~txݛ:S(QE-]ekƦm$@ bߘ9+XpcP!/_\. |iru a $_l-]8hk7/V\YY}I֮Res3w)/.+ݵ[eimm]:…}/\E s'B^/IIiW|i#xOO( bM5lԒ$~W[gW[n0LtK*s]eWk%%%IDQWFH9x !qqk }X^~۞Ɋ"B>߿1=455l3C_mNaCw(4L\37ZZZ9Nӑ$$/g'OQѣF4LTUU[8W7S\.,$V0|C=ᄃlo1 !:qTƔӲ&N,2719222x$G$y<(,̀g#6FIwQ[$^_P_X1e2Pe~$a?ɓ<tJͷ}߯_];w`n*=UG:sZv:.U($ A#V[4EEFEMAHeNۍ9Z~HDAtN!/v<0\$l $ieP |zy|;/:ovlEoz|`'_K!-[Eo\rOnڢ%tEXtdate:create2013-06-22T01:33:27+01:00<)%tEXtdate:modify2013-06-22T01:33:27+01:00laIENDB`puzzles-r9872/icons/singles-base.png0000644000175300017530000001646412161170214016630 0ustar simonsimonPNG  IHDRObKGDIDATx{\gvESPBWTQSR󖚥ecY+T^ 'zRP,h {' ;=͙^fyfgaf_tُ:@|%` iP֐%` iP֐%` iP֐%` iP֐%` iP֐%` iP֐%` 4zLp퓧9VuѣGFQzKAQֺN=*/h !L:+k]cFѨTO CYBR0o0 Z^G"M.\M4M6t萀?6mZ߿.,,vlg:u(~F?{n{4ʹJ5uʤq6ϯ]P1 YY7n.//OH\~} وYcKxߝ86]y{Oy{}56ZfΚs!5A{<hy4?nڔU>>>/|6׮F"M-O.֕)oПeyE=Y3g4-4M;{oR5k{Z] J &^:e2;ʣN몮V?x@VFk&dڴgc@#UV=vUUWٙp="4]~W;th2/"Y b͗/[̝)֕dfHېsD%X2ppl\_W 2lD:x l|N}>|tY}TgL0̒ኁYV~ӧ~}`/H~`-_gX'nh̡9J Na!@ 땀4}.?6Ν;/GNkڒsL~/F2 ukWyʦ~WbXJO]]ݠ8LQԀԔʷf zTb7ewRR>u4MXXb)**G(w{(0 Ӡh41CWc͛wǧjϟlؘhFUr\=|hxDq&;`XDq|!;]XX0whxHXD|˜m;1ϷfrJEw$:ܝ#MbOsaK@JE߸q/o߮(ѣG:]NWzT^ʚjZvz4vZ+Naú5bGL3 M]y~~~f QPCzJNZwx|Ʀ4?vN}cn1e<|w:sffm95Fuk.e?.^(ڟ;88̬28[ FQP@23bckVф E2Z-;Yϙ4Vj<_MR}E윽9|}ooFG( BLMkĔҵ6E]. u=0 J]NߺujojٲD=BT? bzՆgWpx|{`駞X,@Vt9Z]XNq{S3?=wS+Wl&( vaӠ11&)EcΩ1pKbe~{d6G(g;w=zhl׮mhHqo  0z'Nߊ<. j6lN0 喧:֯.ʇh3w ÇCLp? |FL ^ڢOjΞ;:dCZ"VC7C)(ڴy5k'@`f-[(ZvAPzMQͅo2e]3y޽/wi3wca, 1e@b;~uİjUAaش6a0~CE:J~mq"kx$3_xߜ] .*/oؼqSFhhؘ.ͽ{oݺu&7pQ) Z䳎*&^g#ͭ{:uܖQPP"˚ڂRJm6 Kh6iùaa$(~<(?/*.>{͛7&IѴl٢cǎnӦGyP@GP%NQ;, gSw?(w6q'_?(w(6]!-+_S.ի'O}+)rvM=<$A| TU; *rI<GDn<>>>b6,\brהnsṷX)w'Omޒ"c>QH`N*2jg}JӐŏX,.UZm.ygTBP>?#v+47WrHu>RږЭkf&0䤥+#?hEť4M/Y)H_S^%h!obP|5ٝѡ!NYl5wZJj5@/ +4AӾt{3~M|Vd(>˗?" ~ЊK7m=ok3[d?t*DB#tEf9_kd9y299ڨV(E;w+\&&o}yF3a2@PH" 1O+XWWƍ}^tB끋óqV?n>|>9Μ<-AEB=WhA5ewn߱E`GR}v|$f ]Rz!{W𻰸~M|h3֤cTI+AΏO34vH>aU*Gǎ1|X'x .cy\P47́47"]}8}-i?:{;|o-Z/X|{`vVsx=V3=[?l3mАm۪joomN۱}+w7{[ojղJz:wa}JjJG?(w|LI; ~P$PA;8 gSF~Pl(JJA XoYG~P/>_RAQt?e4J_FРJ*~y T(eUT t?|AyzArMu>m^lּeEF%X2ppl\_~Q6s|w?lD MB:UT)~VcA7󳂃:?$A7󳂃:.KwAeBYAo  tTF)ؠB KwAesncP~P;Mη-P?(ҝ~P[P~?(ҝ~P~V@PۃcFE|fsz}UiaOL"MBC\sy|49. SNnvag:aซB/%~S y Ou*q J)Uzo*1Uz8uR*~< J*~yQ@R*~Q~Q~P/%` r?K??__pd2[HXHTe@7>$/5/-\EgKuswc]å˒wDv?H` ״_ٵ-11[׮ͼ5M`3IK٥W " QӠKR_STQwӠB ~M0a7oV;\g+/G" ?AK& =NizX\iSxpK1Qg&KЯ_ k2 M]yQdZBӠKp& _Ⅲ9(jllھ)cpQ8dZBӠKA~M0_Rѝ{`o~g+C/!ucPP&)ȯ% u|8Pgf  /5/]dyˠqA4hLLd=|W|Ue.O;~OvNuu`49򣸸XFx.G?p0fk6`0~w]<¯iɿxrXՀȩS&9\G!bق_֛ッZlR{챀ΝlXRՊ̗~[?U w3!_*:SΆP/U)KFߠJGDu}KF KFD7K__zF7G* t3Ka4/g.]/=#_.7K5J`QoT`(+ח^HJO)o>j^,?;Pz2 5J_v5J5JR)]~W;th2/<`>jPMAp7p#Oɟ.}f7ٿ VN M~4\A)y7o*=::qCc̹PRp:p)%~SlAG<" 72L^d&5eU>>EݹsѵZmIM [ νSׯ&~ 7Hߙwut(0 |ԠD; ;ϝ/d   Om' C'ߔ8) ; ;kkkiM6[pSߔ8)  r?( ·N8w""uN~̬.Qo@'Yϙї4P;My= =~,Zߔt;w+ *?PN)?p~S@$~Pb\(ʺG j@?4AwFEg/\zݻw\ʮ֛\ߔh) ~PJ'_RZN H`N*2jIx-~btRZ[3x߳k[bbB]y{k4gK++8 d|Ҿtr%iN񩫫[:= *r%O?A[wfL EЁ5.;NԠFM͓pNq5hoS#T+=y*/eJ_';~'jACzJNZwx|Ʀ4?vN}cn/$'O>p|D ?hVfFl̐vj4}>x?=ɓ;Q?(vx' 8>w~PcgW<ɟ;Q?3?=ws';QSyp5AlܦW444 NbP5v_IN|h'߉3izcbyӥ?U(Pطga!m۶UN:cٳqaNT~D ~Pw?TAQCJ_?(jTE *!~S7$~P|eAQCآ vpAg]Ug IDATxyTs3Ax`жPm,]d=\SmE-VXm] EZ+X[->Բ,ZUfK3/ƙL&3%̽|&~ =q z&m m8U`OѨܹ mc'xzRoCcA/^(sMWT&'}a0/XT_5)k&jFww7[_cLonojnܧ ٸ==?VW޹Ǻp; JyEĆMr p˗/ebАX|ьrܽ{pǿ5I7I.O [PO. ALLVZYt`47 ?sP 1UW$&}bԔ=|>[ɵ:dĐb{#"!ZqX:NC> (,lßc9#?u  HzkF`|7_X[+jmkڼ ̷0Wٚ5W|7،ԿBm/;_چol.R/B%1(x /5 &[|>?=m}ppp?Ta%\ ..>ðе*prr>[[S^/}⫇&LRߍ麵!'$24WhIֆN)u4r@ŕR.W=j+HR^O3nD?qXiMEQww7A/@A77Ekjj (ӑeLKðήxPxQQ55AJyļhIDo0yq [f5V(aBLt*hJe,-i0?(@ dBL#eLD"Gup]tj!减~x3X"JY]ExR)vq-'NkjobChIDʘܻGkza+⟻{45rcƌf!&ѿ2ɓ&(z:[n?S\;0bNi_T*-(!MZzFeU ۺ32bS1M8a2ݗx"E_ٛs{w@GW]2pqf C;~Y(DM&1d_KݿÁ}ҿ)v0$H0@jJ``A2b?д10 suuXZZoQPPXS+0!!~Ջ:b?Fw%q00G&rLFo^ptʮGJ3̌E?%ɰh] T8 (&H SAԇt`|qS,M,Fo2F~mH)V&姴M*J&=-=Q(D2Q]ݭ[=2l}``Ϝt*h盈jjj^d\.>u>iNh͚$DK85AL&FHH&F#,6mj]C8H[qIvݕ1޿yEijm7i޶,Z8EіXϞd]/Oy*5/𽂟WX6q.L\>{g1i僚[2f9 >iZ G~jԭ]1QAtbWgE"ALGpWDrO7o1yguY]S```a҇-3N_ >hhHL]_o뤲Ksr? 6tcH$0 ndK,vsm[)81zXK?z-Z0_k/|BD>i~)<!R"[>\Q9b޽{'jggwn >HqROtt:m ^1M4E䃘UG|xF0 ؾfF>|}}Qi>RhD>ޢOeڗ #Iڕ0Ǜs,}/:1PZSL s:ꣀTtLALgZ,M,g( 'LMFO7/?*dgѢY ˪/<@`QVv3{ҺGD]Э[>zG]1gKQ|1S,M.70D->?o%߯d0p qMI+$X~8ظ%*|;2+FĴFĴFĴ(&*O` 7SCCc}Q;f]Պ\~٥e.RM wij.R`|ӢO$MLkML>57\=àS$|Kp|Dz4enL&h~%I EʬtBرH~(=&b~#|ZX#>1Wg"~H=&+Dܶwaob)&]߯D\4Pނs a7}J/K17]WH۰7bo%;#!Zߤtw}PS]n߯D6;^DJ~("A\.)B7)hOXbnnpLLxVǎZ iA&nZ?5m^`;  R=~8--phuh{OFĴFĴob{He(c ភ{\7efe̢77 p7UUk⛄B|;G;=Vk%tEXtdate:create2013-06-22T01:32:12+01:00.IUS%tEXtdate:modify2013-06-22T01:32:12+01:00_IENDB`puzzles-r9872/icons/singles-ibase4.png0000644000175300017530000000251312161170327017060 0ustar simonsimonPNG  IHDRbb$ǑY oFFs pHYsHHFk> vpAg]UgqIDATx\۱ $9B))(~8q0`<$qf!X9ケQSہ9lwΥ;笵H6oCz#i4[j(uȚ MGe0Mдqؚ}p$INù1ZyjZY?=XhYl51H':oA&#<`g&0xox)"psJFr0~÷ tb'+E:*'9M{by}@EN7*lw!^ܟn/# )9<*hJwae5Jv4}G> 6i3}cq~t+.L)&[ :zQvV3!#E$ )gv:=%yTfs}{P[cj,fdaX1jݬ m %M5pMVB&uMZGAhSNp}p{ 8A9 oT?%o/A2(C[B菊ާ%M8G"GH0?֡[e}ӆu.a`G,ש*x#Y?Z8j Hj{ 0y{CTH9E\#djhYhn4sdMv[8,sz7}и Pn(8@ԆT>M2oQ?%oW?z˲EeYeidm B&IP7ͨo}i2551n}ݙc8 71t(4iu!t~Ak^i< 4&7dG^ciT$Mӗxt/ EC8Q%ZI. &9|vg:]a4*E7q"&n&^)j&n>Cq s:} z$U`7ɠ3V ĨoSͤYsqR80L$S$6QHla&nMqjY;tt8#oY뛸1IƔ?JMT&nLoŖoҒZ\Jč[tiRuj%tEXtdate:create2013-06-22T01:33:27+01:00<)%tEXtdate:modify2013-06-22T01:33:27+01:00laIENDB`puzzles-r9872/icons/singles-web.png0000644000175300017530000007242512161170215016473 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAgetPIDATxuxI>WLdFI$HweEYww b=]?* !@~޺k0RS]}>܈_FB{<׾ 9)W%B(!DQQ~sPYo!ArBߠH2-#jBsmFA P4g !.B$I0 0WPEc?_3 "EZBK$ AYy$I;xA"p! @ES9_wBZQPgmyA-Y4_VlvZ-lĺR;zoْCy҄~o3c֜E ~9kh(*Ο8qj5kTA2dջ{+V`XB&C?^_|e:Aէŋs.I>oN+|B5k>|4M@"4jX )b5 ;wٻ5O0&0 J3 VsƎ +RSjׂwCT޹}wێ]g]{Ë8N8xP=v"**$`0._JJ-l6Sȱjָ{gB e^=]teɲ=w4EH&A7nFhRl>Gg޳w?ӟbcJLI9rf<9WWW^;wB@k֬aݟ1Hh(˲?&B+~mn4R`1m20 ÈO:c4߾{e;(2M͛59s~F~x(JL|ӥHgھuc6t|}0AvPppݺWYߦu<!T&6s&C jgusĩ={S5j1Ft:!/^.\B6K,`Ź~oYOTINNvF&hZr+hLO D9XIlبs?J$( IAEF'eYB@؊ڽebKcGə3gm.^)Ur% sBy?|P"xxhGta֧iڕ-("H$GyPH9sg6jX_9!ESW^wI,aq^/۱cuWZe ;Μ?SBmvz .@$Iܸy+4$$((0ו "~yGd"+/^t^vȧϞ߸y ڶm=bؐ>PW_s{6 uĩ9Tu< yq`Ϟ  )3!CId_0ٯ:88HPܽwUA?"ȧ*i2 èT?S?NXy&A8&XK'*n;֯ Bo"DQӻǛO;'ܹ[nJ!BhguҸqOL8'O.Y::p̞3ٳ/7|H 7EbiZ5kPD;wdR)B!PTryV-)Esɵ7(J] ӴI#B!qV~ %Bgf+!T* lNHH,Vhdd$redf4IP̌LEQ VaYbg3"dsӽ (r Bp8\.@H&x^~6ibE!A$q.b~ ŊE EQ"|&B!ϻt:"Eܹ+zo<<+<t]p7oY,޷w|xr/Z*w:rXvm322M&Ә#=q^n j :nVZ*pN["E+gzugBr,fKtTkem6(BQe2YRdzgתYC#'FhjҸ\&x|+VPNڵjbU(?!$1#Ǖ;zxRRr͚kת٬ica?;Dؿ11*W-L խS1*T(o0HL.0K#IRQٳ՚u{(^;w"fZ9?TG?lFa:W[~'Ngtty.^OҼ|:Q#:w$6(OT` $ MP?KAOt"<]$$Z~XB#W\{oD+""Rւ(N@$ ͞o߁\&!II'@  -ll׸qCBH$BBCG^| I$bŊFEEn[hHj2QaOi2LPN@$Afdf"<wb27 ?۷2WOD|OR]<h7"< @ jK xAdt`8~h\!aZ@Abߡ2 ! "Q.򌌌OR BѨf q9/$l:?x!_QɤŋaXEer=~r&*Y"n:KVe@O)>y{yf{FOyٳ.]j/-Ѭi2eb@v$$-V^,^\&40*8(P"!$˲ O-?y,4$D(着ɳ:1lx{zXhDL@d/ܹkbiKsZ !4͏?0qL&kݻ/T9j-[w$''WES5>ͼ͚6ճ[TTji(m|` S@apc@ԿO_N ܁g"+}9z\T$YR. G5&.X'jIK3R7nX[jɄ/ 7 ,˚L6oZЯ*0j˗-nۦj #!^.,Ǚ搐BkW_θn߾#P^7?BH"0 C4!RȨMӸq'D" mk(T/[4Mz~T^+UAYiFjΞ[f&S21jի+V͛͛կWoF$iNOO8yZ$#H>>#Ёʕ+Kyw͚uǎ]fjU<}zͧϞ?xp9 #Hl>re&%Ӊ޼I|꺵+btB۷kk.qßDQ./_㸆ȤܹW[Bg$|% sQ?F[~r˖tfY$+MCv+V9zD\\[n'$&.?KyJuŊլ]4Mgk@Ӕh[vdw*_>dre֭[ij*##O\p76il4}HPP#۷oh\.n)[npБaCgddo !@S4t˖3DQ 3^#Il̞9C p::vwӧ$$V\ {Ͽw!f>}tZ5kT, B>uLRѧw zRR*5jT{w.]ǐ,w?aa8Yk6IիW5/_JIfmV͛58$O.::/y TV:RRRNIΧ|yӺ6={d2_U DrHE$"ZǛs+53D"IMM{ c!L\v݆w_ !TV'Nth4Bp`u NgjUX&>a/o&Q@Hg?3 N'~dAn޺_eOAXEݽw_V߿/ nkmwݼy+)TاO|Bs]. >>qbɓSͬSVFMf['JzK,w#)!__X<7e2 ^u9oIJuw  DqBzqء];DGGefWZsБaߜ;ѣǢ({A2dQ@XƏ!JZ|e45\. Ar!3xWTd ~:ebcl|!ABT:tdq(YxK!Id25mxȠaU*W:xN<"Q#={`~~IC|!I01r>" !d0p' `Fzޠw }Ţ$,=!Q7S(VˣyWVxAz mGQIQEK.[}xk;fIB>pcK*{M8ݣ"̚yӇh31dxl&NZJHMMKKKZHa'~qs*??gϟ!$ 8.66&"""'b;Zs8єEsZ"^Z9!<B~~n/eRϗ*5P.](L֧wm[7FF9yQlfM߻Z*D In'&d 38˼XQb"I;88͛7IIB -Q*/@AAUV𰡃pd25\"Ey{{mݺ[4_v'OM 9Q4h [h4a@"95zIi!&L*H:#8(8 =yR,Y`&ǖ彼<_nFOVۄSNg }7;PV{ hje6l_~if6mLJuڳF>>>zB;Aj5;u8dMߺy6~w8 m B)X!nW^]l(jl' EѨTko|_@O{xM[pJ.%bF ;r>*[&Vd!'r IJO$I l޲$IA""«UjX-B|΃i>8CQ`0CC$Lt:wٷw` TRJ.ٷw0)"ESv=)99$#XJ odNп*obN߾}T*}|y/$x$]Pb*1;(2HQR`9gp:Y#eKJe6!FqLW<(BjD"[ÉWddqZ\.=D4ǹBK$ϋ9A J$l;z|O t@"Cz ( AR)U"E$%%/ZBcKRDQ~"B.p:DWj֬ѡ}[@ T*hLkozJeDx.t)c(IVaEF"?,E!^j4Aq\.G|sޡG߾}k٥R&߿QժW go<<<| &E|}! ,X'RӌFWA}I[&efdR%tҕk_~CDs={W~Sv\ ~Hv7#hɤ܎7 ۷ҥK+Vi,V˂Eg̚/:wS_ XIdE';<,!1cƚ>a7l 6uRѡC4j MQ55-իGΚ=sgό,qy1S@uD| *Sό=^wڹfa t7 oYlʼnF֡!F!lEQo= _jB?.:nd*עE.;eff? B-332oTt>{M&{r ( $Re2##:h܄.^ B_}|Ǎg/Ow?z۵;fd^E0#Sp\ZmXBZg<}4_Xv\Ǝv[۶my>,0o<~$~%3SOy Ѩwؽy6JllL>0D!\~ 20g(HHع}+O?|֭Z &7-Z4[f]H!nǧw%%51$5հt4l`BCRDDX* |kٹuq*w;!NVrQNEQ^r(bicJ_Dh=J@8}lPYD!1^$A8SOWZEbVu\ W [i7yL^HsQNm/9}Brf,0{,/G8 j:..fcXvk)YիdVgcaf>$M4ۻsR(:1J  F_w֝WvK/_`ܽ{Ys]8"Ǚ͖<pvRՇ?w6m$ɴ4>>nP[HdV<bZJKKgld2+WoԦ+7gc3^^$IBS+Wen :uA(en=]BRIbx:νٽXlRD|MFJ:xqVLllL=.{B!Z?D*UiDQ` Sŕ>uVS(-hn̝@&.](0b[!tރ7o>sǔ:u.m o.^,?x՟gPLެY㋗.L&d>}:bv6bWEQl򷐐fAP'OVg wo `R\6mCR.6lPh2]qmrup֝;v=<|cccFE\k֪!cC>zp{9}<3S݃g,^8kOKaƴxû مEVP~媵mެ [޽VpDDx;vhD"r0?_bQ׮tz cF ׯojjۣA(__WwGYٝ޹s=;vرq6HNNƥ^.訨ko/^zxxxxh18ΕִW@Sj"Or=~='$H66m:$ݾskǎ(nի" 4? ^<<q._=8b0(ۧg[xgĪ&}ifg[ƳQ`!LCaHL024qvy/?$&bY^!_-},!$bMd1yetP vj*VSHT(phEQ(Z"ApTSW\ttD"J X,Vl94C( N'p8\a‘asutTE$Jh:(0ob`T*fdd>sR*K,ѠA]__s$Id4BCBP_l$Ib)T* p'"D@ LIM i!Ͼa(t[wΟqL& _nh"9wpb?psoBR$)\ ݻoBfWBjZ($IH!v 8uC[RիTJQN $ɔOBP@RS$3͖+9-Q&6ryjZ=֮\A@~}5jP(Ev#-=cq*Px21~~>. ^ruů^j5YFʹ\[ҹS¡XH/^Y'+j,-!<#9V\gU*Wyx@ /^q_[ҹӠHħVmRtSDڢyccc< m0^~.]{6_o1:]PW*g_;vǔ&MU\|8_Ba6]ya#FoݾsƴŊE!Q!cvj9di&L9%ۻJC {h5˕qރoڽgƌlwG VQ}~yMfq'>rӟupNpv\.ZJu:uak7|HJ9}FqG^he+ի3h@HQl6;6FST*~ӦzEŠX,VP(7vܤb"͝Y|06rF th?&Mھ_K/\bFFF b)AQ.}_A^Ǐ7_&BhѢynţNH}r9_qlWWɾ}zfEQ//ONu)QUjFTP$efV]FP` RRR4gXVJذiy w2uD__0Z0XRrqڵ ԃedxtZZvK-aJ///BJ.Uv.>rFj:t>䳶dq>Zh4 4CR҆u[h.b%(jJ0 ,7lPt\VVXcJ 3N'JT*JRTj&I$ !.ARmڴe]G gфa _|` ֫[n,ۦM+;~bLLM\I*ڍӦjݪ娑ÜNx{/VZUTr C*/[_fϙ?r0$2#F+`lDb2)@I<ݻ@. `0 .ft3v~ۍYs?ydƵ+U $I$yK.㜯ؘҿllE5d4O6t:ْO̤;p:!ȂPw9,H~Resik7TVG&m'%' u۴y3JDG9N\f21 8k7;f{ wSʕqvgy?B,v+Ͽ<'%`&))-Z49CK>#%InL"9!w \nF!Թ;A)\t͖?g6Q^rm7MBFѽKHHXSA pg4>$%yxh==uEAZG#G w*'}5hF(JKp8!*5gq |Ɩ\JKKD1@А.;77?S_ٿ#BqF) !˖ڸi+׮YC.g[A5Z @E)_86&RҞۧOAC?yTM AV nP1DTK55Buh BZGDQd$\$icbJmذfq͚1}20 6%Y6 }ɺ /]d9~Fj=ztͅ0YLrDt9YDhZoojU|JM]dI.]) A'ֳ%CR˲Q=,4>|hł|o6BHK04H`ZU*eN)b[?:)ľ'F !VL&8Ç,mLWT϶+1s.Xd9iKs0C#)#v9T(hƷ#ER9}Ɗ(RkX nF40kzo`쁴} <{Z+RSSEA n-e ʹd) Yy$iw8RS4j5W0$T*ZmRr2h:#B!r~˗;v"Iryk֮JAԮU[?<2g_$I?qp"EBk֬~5ٌ=<~Lq<[ gϝ?{<֭j4S{ԩPR[zrJQT*Ww^}t Äf[A6ʕs3>/LVY&l_ts322 v͊dIEQP<Ϙ+ZjV>\ؘ9sW(ԼYq>|HmA"E {{{YY[y//͛ǿ7wbk+W7o3~5sx^h4Ϟ==w~Uk֨cDן84yʌŋalAzX dR4qT?__m?H8Κ5WRie˔ /bX9  n(#xx^j*&'Ϟ;_jevt:s̊pvdFTnڼe̹uح[w$e15K}-RWӓ*+]zmj:::JѸlRy~Ϟ \h`l>>(VT+W1KtZ MIGT*!.]ٻdZp^ɒFh4NbcJ j7oʔJu V*U$I>~߸5cFF.]"e3*:r]+\8TT,"HQR!fes-jִcp Oln_|? Boo/˥R)խ6\~ajVg.Wݘ>JtTVҥ+o|ERFVZ-MQ3=Aرj! r^>cG*U(߸q¡Zj%%'>ϕ+z_bdža @EL_Ydr~!Ӽճ?,Y|e+WwU̚\Fյko.]b ޿߯/3F2N ^ta (r|V(8,H֭>}jeWIH2iV+(3[30,]&}naO=oZVƒj0{_xi-?;\9 9r'U0v'6F^ QDH.^~e]t2xB.E)ʬU)F!Tlի-Zt #:x>FT>}fu֞:yB@@hz$)HKK/2w̲eˌ=ysfw>233Ç֪Uf;BA8wlXֵsq g#ˆS|iZhec  p8O[ ̙3gg1G8hd&LRWC#-^,:*266yTիa#FW\qY(F&!ŋVM*eÊtoזG/ZC?I'Iؼi]F|UZZǹRS+]Rt8ŋ]zE?:>j 9پl~ !,W-)U ~Iw,˪UH_HdYv⥑G ²RTϞ=0hhBB"Vmڤ1d'4߭w/\b]AY3giZrΝ{ݏf!vla2Znye+VUQmL"}S&OPj5 ]N|H$J7ntbE].b ;wf:R|׎AT*c'_?oVg$yɏl?ub訨dHyAh:2,$E/jRTJӔp*(- y¹Sђx,MV-]BP7̙W-3֬^~™gN qwޛ=g> ƈT(׺U˕֤D"II4c\\.ia/Ij9<0y]mqcG]<ؑj-#$t:o޺YrE\`٣֮^#N|*{-w!~:,iÆ67w&I9jsW*E#"J.up`}!hJ|KMM-]<oAas^AR>az싏V$F2eLeK.#b>SBRʕ%bFXHccKLlA 0LRRҕ+~pʧdE*DQyׯ_zU*H ō Ev޻JZg\p,k4aw85oX=y"~F8D|)FSJE͆zEQ4vd2LIT*UǎPvY \:""Xg]r1 V=q!+WI$3geddԭ߸n} r:oH2-RSS_o#i6[zp Դ4LJqq:}oڼ têAbX֙F ^K$thhhNe*PmR>JG\.?߄|f&)Ys"#w7|>g ?$%{dMHHܷ`n_Q "xz?x! $t:M(T*lvDQlؠcRv݆w>dQB(NCpS@"jsL4~AaaEvN<& T*%IyI3wb9cFq@"s!b~}sbR)cXXO4Q!yNʷb:qCprJX A! 1OBQY/ CD0]:w6d0Ԫk t?7wDQ$\X;u柦M{y>}feR[\DC%ժg)+ ^DHD4M$ !$sTeT DL!tlbEJ%&cpܠi:'T B)$ B^QAdgCAxxx`{d2 ,2(Dl:Pιľ_UZq9 IrIn6/0bcc8H$Bʕ+*i[1ҕJ2<,'X%Rf!άÐ3fǿ0M/F%B2FݫׯǕXzЊe.YxѼ-լYS"cf2=7 Eq:|d2#6i|Z̳}B8??VXmXd2^njʕk;vvo^QTx[֋TRzՃ'L)g%ƎinݺW*LÇr7I .]zx+"fjg̾~-# ({`MnzRLVb}z}IQϟȬTڨa}H@s8ttNHHJ !tx?_ӧ?i=;wHa]:Y"˲ ۺu ƌNQ$ǹz.y^ Icz 6kd2-[H˖\fFI,v.y!,\8FjJu\./4(T' C?8ԥKАg׶nQEVVB Fl^nC5]yy, F#@DAxWN@`@@Jʧ^djժl߶)>IJ*&?gin^RACٻ׎-qqMӴV ;C>9j5?u&3gkX~ӄS͙٦M+ 3J)0V+>޸dg:_xv;wdr\R) 35-} kذ\.v?%6\.kܸZd,VTAVY~ϾZS0JRR4 hqv~O:g?ȔIa,!zt!ƎQ;o]~Ν8SZL'O޾sWVuڹ{.>>6a A7-Ym-[nӾH•*Vt4MY,o.\hѼiϞ##[VJ!0-?Zf2ҔHh< }z,zd:uj`.ƔZh^M8lדYs ڝ?щD!5JB̎4<|w~EV+WxdؘN'3qVD"Ԩo?~z֭{AJEhhhբ#v>dY5Ϟ=ϕ!"EDH v:Yq1 KH$nwX,[>ΝiNi//ؘQeL& t\4@!p|Y:VVb!}k4 &9/^&%MQQ H1F@zAA11VInp\W"SSӞx5i򴂭5/h0+$> ŽRM62d'033֭ۇ;r[njQbl^8ss pxY*fws*VkI EFի[g]xy͜1dDQ(9V >m޻wϠaTV{.OxCHB!a>zq&N;l L ж\V1?% T\n:SRfϝs߾!&_vY#^Fc^}{;k7>CI_LN&D* IС!!!EF7F W!IN'[byZu /)ʕ+:QAVwG r8Q4<$+jKB@{۫weW ߒ9Wf/\+M!!!w$IH{:Ǿh= ̉Q ƕ-q@h~$qν!n }Uv;jİM X#xb2ݻ$)^.]<_1sN ;oy" )TgAEQ|!p81?^8yzт9*4s7-1 ڥB8?_iS&?zɣ;S/^Ss݃<_jh21 RrUjJʕ*,YaORwޓl :mtZhh4c!gٿ.Rbl/tXxC~WolܼU-D}E9Y600} ø\|Ν<<< ws89@Pik5;m ͍`u&U `X=7bHϻqJXp_cL&kެ6i_}뎛9, 1)>p:1EčBQDğ~$"V˲,jVDݻw/!QABHhlVB].OO]Ѣ7nW;1%ERiv /,7nph;vjP.FV\i¹_LNN&ȠubksaJB4lXvKeYNŔOQVlV>%Itn߹T\y04㰡Ǖ;qǔOKQZ/~QD) H "S9%I-ujWVu%5WÜE+Z4t)!$gp`2r/*Wl&?|Dd.& \.O߻wǎq~ige9wG$Ɋ>.]氋دoMZ\fq,a$!&O \("%tM\~s%Z%bYV7 I ǺGݛcLlc#0&*233p7tVmڏ3~Ժ AVP(v 3#|A}ԩդqլY`0XV<<7%ֿ-6o6hP,WhD߾=-^ޱC;ł?xAt_0ij&j׮#R;VZ,˥|l'ΫG6""|¹ջ߻w<==qVkBǔpsJv?(ᙝ4qlTtd;JLˍ%ˤtecME=;Nb.)g^=Zl KJr d&r j߫w%'Me'…0ssyNg"##oݶ}(FGE2 # #ŀ"7o6dȈķή_.˲NSo08qdjx*|}ԩ+2LZ5<}f/_/^˓(ap?IwbhLVa$$E"9~νR0 kV-_r7lA d4%$&>|,!1Pp5#Hp}a8S(fL-]UkѱH͛5-R8Tج'N=xӳ>zvJp/ 0As)Ơ"ˉ`KHHX^nHTJܸqϷT5jXp8H$a;zɓQn*|7 DooZ5k(rZB#(R=}vgϞ=}\VRiR%JDG7o$ l1 F"j*G=y'no$""5ji1gߗgȤR\˷`]t:\E IfeȜjο"(CR)|ZLf|$V} 8Eժ `8c&UEA&)rqdRoooRa6[ܕplsVV[Zz KlvA$#<~Av@Y"\`a@&ft22E؃_dFFaS5's).%ʫ>I$g'^#[hOH|6c___SuwD?~${@AOW#]Uo/?W'b%tEXtdate:create2013-06-22T01:32:12+01:00.IUS%tEXtdate:modify2013-06-22T01:32:12+01:00_IENDB`puzzles-r9872/icons/sixteen-16d24.png0000644000175300017530000000132212161170333016456 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭ IDAT(eRMOSQ;B 2,-A [6$~Ν6qvgrN2s@J jfV/щr!""@"9PhfRL&!fΛMqn\eQ̲,/v?}>~,"˟_6 gg!GD!!D\_,,,,/b7_N<=2sQc"fDjhd=03Ḧϙ""HBuDDP{"J,H-1FcJ)!MۻF#"gՕ*ˁj vpAg\ƭIDAT(υR KunR6uL־I?8ȕ4If{W,ncF,ߵPEҹbmD$kUcP}U's&Uy+)_D@zd33^'i}JQAq{xM;2VMG%~4֊3A%tEXtdate:create2013-06-22T01:33:31+01:00F#%tEXtdate:modify2013-06-22T01:33:31+01:001IENDB`puzzles-r9872/icons/sixteen-16d8.png0000644000175300017530000000132212161170333016400 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭ IDAT(MRMo1ό׻Mi!VN@ Nд7*4Tj>*E]=98:'yƣc5R""k{Di:KDD:@DךjeK_&Zדɴ,NgcƘOg?51|}u%" v["BD~j">{d2(("(""繈cs9c "fQJ%yQ4Dg(kf!7cZwv)}]׈nj:] !Ds`x 1 _y^Ognn../k>,^|Qy^1F&zjUլ,Y|,B)/'jS̬J[IVJc9O.c)rDDp>_0="N&SV$fֹ R*0 փ~z"˲Dz58 IhA)ED&˒mvBb%tEXtdate:create2013-06-22T01:33:31+01:00F#%tEXtdate:modify2013-06-22T01:33:31+01:001IENDB`puzzles-r9872/icons/sixteen-32d24.png0000644000175300017530000000330212161170332016453 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǝVks۷{vV3;(%aWDFP%IqH]@$c-3=;=}W06s{cZ>o wZqf<ϋxkkmZף$QSxP1x28.3W$˗Xs vPJ9ddd}tV,PsDDF. "ι(s^G0"w^ "Ra !NT0?bbϞuZkș_~;Ƿwr1;;-ťecZ":n+֞>[{xmmRQmmmowC!cbonnt:EQ\reMD\z*c{ $[Fa)'ғRJD.PJn\)_laի<]"sRkm}!sk(bt\i}Gt'D~c)(JH'^d\ipn_O{=)k8p NKt;1r]7o1˲Y'*ADtJ1&IHA0ACUܹ1>i`yys(ˎ @)c@N7W}عe$fOLw Y_8tG"? vpAg )IDATHǕVە Dw I'JRJr+VH'JN(cLbRyD$ "S\<{gfu{}B#/`(YW"A(2CD94;sgfO*s(H2^VPqМZ7fk)O4g&T!8eZ߄GFdǬ'Bǯ6DǾU+%vlI  ":$46BX MY(ef^3)uUE˲DQJm)mʼm?eYd")-LZ 2y'9;rM>2S1U'|gƍtW`'hՇlʃLs˔LZ0f.Ö@[n*jtx!Sg0 vpAg +IDATHǕVi{c HթJ\cUr|M}'?I]mb9b׉,SJiY6u ث$I@wvwv郯B1RoPBssc !ֹVBc;B(aۃ.4ֺ1fs`T*j}).:(YEQ>R~Dk? ^/B(ʴB^s8;= (j58I!sN)yZBDQ$ ~{E8/yrڍ~\ܜ9bRʌ17nMO =:Gh>][#,..cοNkmKR*rsyW_-..xg'֚+?~fB8묵 jm~&F%]X!PO?kZJVj !Ijld1ss,FEEQa6,;yyJɉ\JJ~j;򥇏&'>WVK1Zɉ4M99Z{nn\eِ<ɉ<ϭ5#`B)n;RBrsR_^*0ȚQKI1R XT)@PcXkAѨ*T% <6(;a5h"g ns_'0dc?э1iDJF^Oԭs9QA##kC&PKHrsP-Jt5w,eàsg΁,^x| !dձɩBJ<17c ^;|8pdAl}}wru ?QA?[߫-Aqwρ"2cLqTR)jRG}?:Apg(Tm6gAhs0|蟟ޚObcc㓫1Zĭy.}): v^zp% f~dxrnvZ;K33RJI _9(sNú,)R]k# AO%tEXtdate:create2013-06-22T01:33:30+01:001(9%tEXtdate:modify2013-06-22T01:33:30+01:00elIENDB`puzzles-r9872/icons/sixteen-48d24.png0000644000175300017530000000532212161170332016466 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W IDATXYkTW?{;w./5BAibJՄT?hOX@&jM4&Mjj*` }P `eYvw9~8pwDχɝs3gN s?= OHe B;-:A> ι,˝YӬ! !hY6TeudYV<1^ !L699IY`&xvΤ%JŒrKJߚWs !H/!PcL0^%w5j&͟7 `scAPU}<Mܶ?qy^xMUU=622zZJx?ڵVbB+J{|O:@P;qddskZᷢ(c\!X*W\rpߨV:֭\WrI al6[ ! oPggvvX,w޵0'5MTU"sqN΄Wnh4_yb[r7dvݽ46sJt~K7:2Ba&N{Ww"бd"y^GGFOܷgw*tGC\u]|2!T,<,_P(D<#d*%SZ5ME,Jňј _+W*W 3_PjT)7680>g ,JB 'DYS H$oBzz{!x}5pks! vpAg00WoIDATXX[v 59WܙfgʦP4δ|%bRz<) r΍4a@O@l7nEM"F  Ρe7gZN P4>r9ZKSBe>2)`c4XƜ}/j3QB6u]Q4H${c4[`ҫw{byq4m^h]a,CLXX^jkׅvhDZ#B*㞜ڦԸs%NꡇF۱}6)"j1 >1SZfx_gٞCQ[Q^%tEXtdate:create2013-06-22T01:33:30+01:001(9%tEXtdate:modify2013-06-22T01:33:30+01:00elIENDB`puzzles-r9872/icons/sixteen-48d8.png0000644000175300017530000000545612161170332016420 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W iIDATXíYoݝ;>7858Nޒ!@PxDK+QB+U /Q-R~rT"!'@1`MǗۻ۝<7޻s<;o8:A ;ۚᏸ_7c,v¡q4o#A@6ׅ.6"b$I0[M]w$4BU*iIs{Inm约сߗCE $42Lqq+SiYiXԲ*N{!TUWJ娮'q`²i X,z\.X,tllѴw"0",ǩS)N5B'P'!$2n&z!^B;8\.??x󶭠F¸Edž%Irj5MDaA!$~nw K`H !mU)0өMr%vuaa@$ψDBTUMMLslY i Ղ5vHĵM)d2k"c|ߗ$^kS.xTE"q!}-D lIm;FBחqH\:W6Ƹ+# cdY%Ro+i ^]7!j!۶BP)9PCDִ+E 0Bd „Y[K+1]ȚCIhOːE/ëR xFa Ţ+yP(PPTsO>=OLT* b a^ bIjӇLE륗Ns  HP@D&BzSI(F[Q@S c8,L&&)mÆhą)]c{#΢eC'GˆN34w^$F*i l[@&*\ 4kJ&l|u<' JG~SHWc̲*jVtj5۶ Mժ]s}?e VJ:MX,znRu,Iөsza$$B*{#fPDjs{ G}ݫ(y3gΎm" ǩ nޯgӾ_sZY::D!5Ƙa$ysS=mۆ< cLU}}}t+: Fg?]_ԶX,6lGWIU\,ژ,A{Ӹ;}%t>0M|r?TKxG/C@~q:I>ż D%tEXtdate:create2013-06-22T01:33:30+01:001(9%tEXtdate:modify2013-06-22T01:33:30+01:00elIENDB`puzzles-r9872/icons/sixteen-base.png0000644000175300017530000001545312161170215016641 0ustar simonsimonPNG  IHDR  9bKGDIDATx{\Tu/36+[JDMAm&yGKqM}F@ɖ+_K SxRUIu)AD6òUzy!? 9;?08pΜs*K P0P0P0P0P0P0P0P0P0P0|`&SIdT_w_!{",T:n오T yǻgMHXXEKzEl>y *s"#‰AA| y)Xr8fȣ07 s7v aV5((j;ȯsfzw=G A~C5Yc?X/ZVs+k0D-qO<(C(C(C(C(C(C(C(C)T"d*Q8^(@(I);`v%''*|$''*9%mc vS1C6crs.);{SEƂBcUթ[;)>wn ҵAƷlib W1UW3 =wKH4xP vK/RXdt|$vv,;siLLGq\kMׯX3n~?qrotKJKʎgg-nf-=vX_G?1RC<W?+,2jڵk,Q˵!lQ-ðY3a?32/ؾh'/J*-2[jݯo7*5mToBC&(.5 ')wZ"Z 0[6{OIbb3V[şܚcgr%)Oot]"Od0[rӱ &L|nm~뵵9}z`^yrU.O]nzcv9u;q'j+[y9YxΝ;w弝fOH#>rl A[֯{[qڽ{7vRc^%?u߯h?!Rrm8J^Hd_W$)/Ԇ>ddmEٔpS.Rxt |v8;qD]Ӷ9:q]KDc"o5pv` ` ` ` ` ` ` yfl?X/ZVA~<G("<$h!{8SSǍ;hY;D\ fzw=8 }J<%F y-        w f24TEFrfy`&SIRJJRJ)-]ɉ @4ɉ;`v rEY/W1C)̂]pP19ǃj:UXd,(4VURaqR~&\WW+**aG$q3@\m4}w;SsCI<`0[{SͮѮpOe^.L%GM%GE}CzC2=Eo^jT-_}ͥ3]kC;Y2 }CY Zeoe]>.1.y/z!._w?^[qhũ."߱w6oj2j-8?TNW^ÏhJs\oԩozNtz:v%OdtLj4.є-%'Fz֭ow4"mm|JLt./xyΣ;thh}}}O?w &;l ')Sm6nh%DWTAI ȸ/omnT̥YY9GzCK4rl~b1~~ۯ֪#fKVV7߃:&cas6\ "ᇕ^.="?ͺ0"Z(̙6ٳ /q.&EwtL>w~ݑ#SPԀ֭^P?SUթ;?™3޽opiӹXyDdΜ1c-Zh.p͛7vj]{7nmڴ]>жm[NצM|`Uۃ=7XddLrSaBo3^=w/\w_=0p\)/׆ө䵋Mu1^MBm^CvH*Vۄv.1y E OS.R~g>hr6Gǔ?Pu ٘hB_3N        w>h.jWhy"CB'Z31%1晅9C5 l6[x y-H@ W ϭD#\m=8` ` ` ` ` ` ` ` y`&SI shZdT_)8o f2$*$*҂9ڕqDc lW-2"\y c2 vmBa]Rv$2=UGmww+!!OҫN UU_~yջ?c^ k3f˖\?QcG#sGn z Ox;;5]]B&?wǷOb;+Wd`Scƍ/t/VW[zmE9ݕ?q뺺ǎk'Fz=kC;&& +K$+;+Ç w8^b,=|Xܧ((4 ,,t;O)<ŗ]:wpem}K·rOA<={FOɮe3DQOV`hP)!isCB:]HHDǻ cY7ɟd$'F;{$'@D?%ϖ6v\Ν㎻{D3gAFǤLC;:fC3nBΊg^I_f 3XRID}vr}b'L \^$T0 ֋vڵk|Oy̙\ADK2sK2ҥisҮ] ]t[r꼴D1:&i'GiƎ]O5p@;*YQar~SMp=)/ӄȈCNk5X" N'Ə 5 1r#&.yicސڴi;v0ud" .yq~uv\{fKI_&4{ykƕ ȸ/omnT̥YY9w??qcy/>/>Qf*$ްP"-đ=ر2-ʈ(<;Hş=7yb1o č %++Gz;94ѱ`TW-sR_%~"͘xW|듌חuШÉ(k l^AD#G g^yrU =E}{;,JǞۦIᒇޒ:'{n,є緎wÇ?;g'owڙy7.Q_icvb0 CΝ&Ə֮]>}zlihwǼ2;a$^a[秄h"$$OOY:L4GFHT)W1AջW1xP,t5?%91EQsM%p:vɾ:&Ϊ )/Ԇ[EW|[[pS.Rxt |v8;qD]Ӷ9:q]KDc"oqlP0P0P0P0P0P0P0D:lV+ BD#`j=O)N)Ǝ4,"eي?=;Mhy${0){n yS3O`;L-~qO[v{ڼWFegJzLj4.-*;~b˖mz~t:!fԩc?7ib۶m$)9ZJD'OVٳխ[.:Y3w/E{1I#vnt>ud^4Yvk*+λ3g$p^{O0+Ѷ%L7tڥk}MUNgΘ; Qdd_9ǶcGb'0-OBB%XQRdܗ6{J*XLLtvҬtLD4Q}7/??ݸq{CK^adex=c`TW-sR_%~\8v/ɨ>g۫ϥ/ ~ ,G"v;:ѽ;u~sBw`ѓ<KRWۦIga)(r'uu<֏;k.`0tibmڵ <:.''si;vhѢEvÇm޼!4+hɘ*j=qWycջW4o3^=CKy6N%]$os%$7Pnn>\ۊpP.Rr%:>OaH)1aEOzvWNv.!]Kz@#_0000000ȣi6E.G=Dv`CO)NfK ; hY;Z]cZ`>````````HHjV=#^lI*_` y-#D``````````HS$d^<>_DMd*IJIMNN{ +אG-Q~3x}^Q=.Z{OTBmQ& F ƌh!F#YctG[yԡ႑ch!@#ch!1SyG[y򅂑12Zȣ vpAg  OIDATxP?Ynr]#kk -kg `m멵p(R։7wzskDѻXRj'r(B/$GVH?y<>ȏ+OIf; D0v'L7zDt=vaM<>8F!!!!!!!1”0vj-  c)3ƔН0քL+j遁/[N4 |}7-pwU;vFEݝe)(M =yN髫r]tߗįeaJ~Pum[)Wy,pX+kNκ\vWWکhdS<9KGFF{{6a㼧eORAQKv&%1!!~/=mmllj;~õbcbN7h}@BLfZ_xC]owG'33@EˆEk5pk7@0ɘFchuMɱY %s<[?=y ֟hj9%HCz&F3g/yp|d݃9I ?@z&^ CCCju_>YnF36Ɲ dKyK>b+jǹ $yΓ/|ӕ+7'i^/&ep.ǽS'*n܉~êASј==Kzz{h_A~dr{0--Ё*!Z$;nw)lnʶ;pmYUq໅Ek7w([ v] K_=ޱpQGm4+hǷ|g7o1[vШX[ hvo}q2!Z /._V/ [oX`><%}%}I$nR%z=Ƙm cI趀$t[@X-Dfi0d0d0d0d0d0d0d0dhrի[OtxK߀[s oyXPcpj% hJtwڪ6.]7%tΖcL2FQ/Q85rs'$tԔY۶@ǧ6Uzeח5 3חXa /Qgh@7:tJ5GD,XSp{<~Ւ7 5e*y|7mv.xGG] 7m["/QC {(=w0yC[yAl/[tx###A_ !!!!!!!!!?-`%tEXtdate:create2013-06-22T01:32:13+01:00>^%tEXtdate:modify2013-06-22T01:32:13+01:00c[IENDB`puzzles-r9872/icons/sixteen-ibase4.png0000644000175300017530000000245412161170332017073 0ustar simonsimonPNG  IHDRh$u oFFs * pHYsHHFk> vpAg  ORIDATxQz0p(EG3'N;ZI)?a)1:_g.15fhyk#PAa`PAa`P48GJRjwFa~L\iwFa4:j<)RJ4>|vy:ȣv;,겜snF%hc3!1/QdzYo]X7cN<ϙ8n>8WP#uAesH+w)}=N:;G[%.ZXDcm2|@u0W%NMCkzx߽NؽllOfdG֖t?$OřN4*lez%wٿN|q.JQd+ږ|Ȳ>-œi%I-0O|Ga~t씫D'zAa`PAa`PƗDp!ZP\ؗXBm'qCPgQVct˃їxlQa$g#j`/q K,K ?ʠjNu_Q|it/ ~mz%Z;꿗XrA300( 00( 00NMU9.gRKA(rFӉ0)tF-L f vpAge8dIDATx}w|)3;}7 RCUfAE)J.zU+傈4i"M4BH}gvg',!ewv}_!)>AOg$*ՅRJiTBe!$%KUr\H8Bm!!d4EBhD%PAHjP. 5MXǏ|P )<)o6,BlWDHE?~b1kx-x6whf~WUU!񪢔bDoV7oִsN$!g,jp8v9w $0b0dY7/C6mȲϰ]in?tymk_0Ƴ̷m'ܴlJAv iNc󖭫׬{{KϞݼe/EP(k329㏗6oެ}vWҸ.d;xТ>XڪeP8_#=gn>l]d}z`0tӋۼu L1x%glݺ8k3t)OoqF˖l֬Eb#0Əq:P(wh/ɡ?6rpKVg EM[a AQp8YT\ױS}>iV}Fr!!t>O;pB(Ip/8({RK`,\o&vQUnk&[pƘ.ۿx'FQp DQ0_~QUS_9"EyssrSSSvܕsgj)bBVSOݻWf銢Bvw}&M|aE B NF(ƭ[}iM7s\c,(*?)6Us?̨kEi5W>lPY$IV~A0j]bU« x+)9{WGFHP |>6uPJ!&p8W\<6Hdir(1 gJ8Uhs=!4L~8ABM8aBR<qi,Bc| (Da1el2`Q1Fǣ CUac.4 0BF=T4 E!D4BF4J(f)"KI J QRi0MV@.R]jl6EQZ(E}MB v1ɲ ԱQ V(M#v]%FnS BH !X,F!PA%Pc! [e60D#`Pe}k$ q=t: !1!iӦÆ];n[޽jbM?j[ųϽ #aeZZc6O-߽_7rpIW_}x222yb۶mx ۹d۷0w~k.YH)5 矯9}(55妛e=5ǟxF|h4bX7kCvƺE[`Z~}<(&)ԴGBHK.ȿཷX9 П!bnެْ?l4rwٷlzzz8{9@bl$e6IKdj$ !|}W_ ԏQ'1sfCnb2srr9k=g͛撏?%kd9ԯoqL~}?]RU]JMMշڭ[ץKo߾CޜouoB$_rI%/E\jVC0DH)q8;w٬?__A<0% ^9!ti۶mq$C[oIOO;ұLNغMt";sPu! ͚}ZB(FB*sՕ#nk,˺ObrE |>_=0VoY4(%T4}[/xi V̓AċO>>mKMy|@QI@~$kUyyE[U:u@ﯾI$: c vT^TsnnΝ=W@љwG~y***t.SIJ+ۤSqȏQ~wxVb̋(>%7$.C`„=w7\EK[pدjYXWPv~-|d 7';ԋۤV7) 63J)Cf* n2( 7,cyyf*n=R X6,xc zRƜF1slݺ`T<%jaa@=p!!Ǔ_yH׫iZqQqBQZjY^^q$'AC JRxxqt1z;U}$-sٽ1 1Hf1q˖͛7OtR oeń7:t!#1 %DEEQeK j"y/))C!(H*a.ʏt EER (H0@iI|Ijr$!=>zE.D.KJI c;u*/Hm0N|_߿ P+?&8.DeSr蓢!Ytc"h|gKg-kas7ݺuDfExp%ލ 2W]WZ^FnDh&Bn]$94gG~}xack0p?~ JpFcVV&Njm~ֲeD9YHYY'ZlѤIL=Zz]AiA\EU[hޢys)ht$ARјWܵgsMH^ɺ͟ۥ]߼HƟ{/nݺVz 5F ={N߁ܹk /B!Q;mڴf3U ?h rovCFF$qjuG+>s>شk7.2ZSN͙F~}%IviWUU b-uy;8k<6rpƟ<|P;0[ }R 4M{p} Ezx#YsxLx ͪb/8CӴԔ7z/nӦM(W{]ཌ羽{Xz)Vu2axS`@#$xޑ#oڼeyM{!(O5!$ˡop:"rnv=.[*V!Lom2}?~7n1roAQur-_9yDŢc@zݳλG\y4qÑƨ"(ɲv=|L1|۲>iT`0>]tNetaP0ۣi=$$s Ysvw3ij)7.Ɨ֙L܂˖.޶ucVf _~_^&BQ^>E vI9{?d(!b` Q!(<0uMiӺIZ!EQ,]~,B70\Zi BXZZqDA!toeoWYBe>dʌι@ 0d`EUX5yWc,2 `Lc@L=]z}}DvFEivSУL7_.htͷEEœ&Hu=:dԵ7rg1y DJ P)f0ppxUc0d`Fy&v1 |Ūɓ'r/XiӦDD016 6Bvqnѣ{ٳ:^;-B ɝ:utG6Mwnk`;ꚫ[ldPHEAlہ$IUW0(edܹ$ɉba$mڴauF 80w8:g5MۭsTuAӈb~\.7~7Z3̋#G ӴTEQCPY"B)e+& 2'"$9$IrkP6.UT=@i @pnoÆ˦wqPϿkV&]wR`}t6K;1骢B18nܘG Z^^QײZgc\QꪑÇ_/Ig~qEXO_T@ˆe2~cJbʢɹUinBNm8s Mc Zin%oeY#__z.-yyoɓ&LpS\Νd?2u}/$Ir x<_:bD;v8 !:p`JJʠAr 8ۍ7nL2 s'MXb`auŸ@}׮ C8  ! Ъ#l LEQo w~kFB{W5{q.1Ѿx߾|lQlA؊S! U0y҄eW7h@bg2}V aWXO۶m;v!s箉ns:%P(ԪUn]?i?]2*M**\Pp8lXNKz,R y{,['chQޮ,o}rrrFcdEݪU˞={(560vN.i߾}& iAZl!nOoDA+Znu̙g\5Mob07k&l^_~坷^ɾ&dPiݻ(ԳgVZFSV;BDQxg͙yHZjcء|9[.G:Ν,U?\. pyRUBF1''wG]f[9g۱s'K=Sj*0s翾u'N>\s՞={}E?;`jEӁö 6ozM{S2P%̞3o-#G |[n|i~oLBaC8ԲUy;=I=-{/X@8gΫݺu c]Æ] ڻoℛO{xExi_7i1ڷ Wܽ~0TMݴyKl^2(GVZfZfyMëf]ZSDκRD9s{<.+Ge f cN4_+%7 4ATRRRe;xY,DQwV#/;r$'zCm2?W@m6` Ϛ#9#Gy}.G$% !"D8r nHB*ءl=Eu(995RD~ݮj\oӧ=2o~}b0Q;P($INׁ̐@{CЙ3%1ƄJfxu;Wjc9;8avvDm㎝~=F.Z3/N{/Tx0$2}zX F9/[r̢(\6h q^~N7ˆbt~i#l.Q^=T$`+))Q5M=#=w]{5/oֳ0٭~ۍGY-^T1O?3QW?pP= V~\t&i:jMmիG;RTN;u.+5yUMʼ+!PUM$׿_6f?tW 5{^V}"ezY kӧbQ q\߾}0ƚƞfeul'bٖ,]V\|Ac̏"cyUU)F a/F8c_yuV1&f%222څBaJ)g-_9fO=3Zʒv{<z-#7OVYn~}\SNwhϒ}"c$|qd9D,↭Bƒ;Dh($.1YǨPCELݗvlཷ?\qnn) F݀EUUY| B ҼY7z X tSC"UCFSN\MhnF#-_DKY <'Ȥ$I$6@ $TM|ZC 5M%)AIӴw`eC=ϓO>{R`0(˲G)-;W^rA6E>C(!$ݞf͛h2+! @͢#E,_Ghy>GKrǢ>7~B,GpFdY./+} X,nϿhvŰz?2xsee>}͙ʕWl0{M4ñyg{! Ϛ3V_)"7{ ٢Pp7q'8E)?`𼹳Jϖ;pS0ت zp8ȴǺunͪwƌwFFSJΞ=Y(yQ]nrGzG>a=^o޽>dVv1LKKKMM1 $I3个6->Zj)` 8ೕ8ӈXSBjRw~Pfbgݷovb$WGEqX7s!dAD"D*Zxa%E͑UMs5E3#6R+b@!`wyJI6\;+˛+osGʛ'T/8yb1!:vcJI ""D>e ? <3aaW˙e颸6j>3%<6s,K0)p(bYK2(e)&!\΂ JyEcfM}>fMDg%m8go@ۊmZhvc2]&S$F`9qEQ0Ɖw:PTB('%(q(d**4W0"^ܹsI(4 A]Hueߒ U})ȥDmID|OI@IǠZb$ZLq5N,o UYa51 `0$˰Z_!|n;rB5|ZZ,JӧC*n[-t=^2m ԤIE-XP$(H BpFQ7 ky>bhJ)ba᱉nQGbX?02;K!("93 "晳f<3bNn='i㫯Y2dIfoX^zqF=H 4M6Upؿ;I !޽{Nxf5-dY^f]{cXƍ#"DӈXX 7L\;jÂ5?c, JO|>hd./Y!9x Jul R!BxR5 %9b>$Ԑ\ :+P 3V"hC̍Xϫ$V`\* c X:IFʜ f Jb搯$4eŞIt<mumt !V廍6lCܼ.hZ-f<GW EV^l=lĈaQڿSO{Ʋb]@tM7krBݻ+Gtر)` tj٢E>sry=:6֥;vٓ2~j OC~s- .dѹsm۬Y>}{wM#VСçO~'Vk=#a{)={.sj0p_a7uUD̰^zج/:KTTi#6m;T%ԓct/hZ |Mk.^o{Xo!}m۶izeVhgXXPnݷ??epMq ܲekQqqVV,<VXu ׏>4#a 矯钕e0}gڣ@Ew r(ԡC{Y1b3 AUV5kʼh1F} #ᱮb+_u붢,к|Ū7N]R.Y]2y7͛lyj /Pv nO}J#O?]HNn^nޓOLΊxq˖m%[ZcnÂAK,kz㎝V xq Y-UV5压?+˯sѺuǦ?"R7>|;d`(m;~гgzU_`¬N:#u~i 6FBYVt8*0a٬:}(++3-99ZE!U(nձ%>"\4FZi ڲ XRb1h4Hi~'2F㕋#Up@UbRlTUf\Ls!H* !)=Fil+YRݺ%tEXtdate:create2013-06-22T01:32:13+01:00>^%tEXtdate:modify2013-06-22T01:32:13+01:00c[IENDB`puzzles-r9872/icons/slant-16d24.png0000644000175300017530000000155012161170336016126 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(=YOQ2gXFVvYpؤiڤ D ԄmfN/Hzy/~2cBH)i`1FRRJ1cl8)%!D)Be'׿nBvrE}BCյ Y8%Bzd2BMӈRj4[}IL6{JRoXwJBQ)EBR*x||d%r٬Uk;$%TVvM\szj%fRJ9r"mO>{]YJ>c8 Y^ZVk^/x1I:.BL$jl~t`J׭ϧ}ϯT\`Ą&D蘉8<B9ghdm5h4?t2'q⮯RJB#²=u1ƖeH ⻭M~B< }:`0;Ξ}zH$Rx)B\0 JsbSE,0 LJ(Y[J^oiiqD \~eelR)Ma,nV}?(ca]NOOjmYzmYyaB ŹRsRӴxBӤ3%tEXtdate:create2013-06-22T01:33:34+01:00~ *%tEXtdate:modify2013-06-22T01:33:34+01:00#IENDB`puzzles-r9872/icons/slant-16d4.png0000644000175300017530000000056712161170336016053 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(}RD!C 9P{ˢ HUOE#)R2q׀ #(ʝF^op# P&1)cof<ϭWJZ" .mGt^~(uoq(?ғ6f%c##6} ~\?ЎC[%tEXtdate:create2013-06-22T01:33:34+01:00~ *%tEXtdate:modify2013-06-22T01:33:34+01:00#IENDB`puzzles-r9872/icons/slant-16d8.png0000644000175300017530000000153612161170336016054 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(MRoP j]2&- ft5h|B3;R`f\(`a%]K;@b^w#DD";;[Dr<zz7A!!">~Dl4eD|8[__ZsNƾ{ӥɤݴ{v{{"'(ܿs\ ,VK˫^/N5I(5t= V,'SNO7|sc !yBb8OMM?777y8a`U"f@4Y*'ch9$ijvٌ뺲,zM( zrww۶07Ð JywJay8}ӼEbNST3ƊR,TdIcLRXV{1PU`@T&5CSD\\8i1pBu-e2sAT4]nee9Q*sEQ'&&!3y9F =Gj$ D$i8"(asB"-(n$,%tEXtdate:create2013-06-22T01:33:34+01:00~ *%tEXtdate:modify2013-06-22T01:33:34+01:00#IENDB`puzzles-r9872/icons/slant-32d24.png0000644000175300017530000000510212161170335016120 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg }IDATHǍV~oy3h+HTF .QLMAL7(3 !eEq$ Ef`p7ɔU]][}|߁B"B4-bY4- !Ye!u"  !4R0AvJrNtT3 M,S4 !BUU^oP,cY0!TheRcYB`Y\A P ͉+KDQ!Rh 1V,ϼuq%q"`L|# Phu]ݳ14Ͳ,M<ٳ_|yb/C;h[d-@!!u]x<.];ƾOE_|OnyxBJE|Ź~cA$0OoɲLQqe!TU*k{^wexGQ__Wϝ߼NQƍsss4M755>]7=uHQM[Skj''r\:d.˴p8T__wȱT*}}x;Fg ]70.i(r$d2eYqIsWM[[B;|#h `$u]Gߏ4q_|կBz~qñnS@ L<%|uuM/xsff& yH! !6EQ@K}}g 4ӛg>>}Ճ\Zra466C.jmm(cYpz>/ltطh4Z(',Y3V0$ òy4|PnttF[-Ypld`.vw/l|4 c !ܸx"ɲ ˲iKáȢ毿򕫢(Vn4ML6'D"Nex`e.r㵵533jE-JRe&Բe<P(;wwi A!IJe9c'N8n}MdлUUtvtl=|EQ|AY岢(,˲be\VK%٦o:~'/]*Svnw7'V?!H$'[[[Be9qNaemJg[o#TUuA\.aPv} ˥뺽|ߙ16Ms7n_u}?ee*[aXܲe($JRWW{Coݺ%ip샄X BϫW?T*8(P{RU-L!Wxxҥ+DBUp8iފ4moA˲ 4zu{{d2U.+S@d~Ŋ##3T*ͰL656 5h󕢨buЮNܺ548$>t]rF;c)rss 8_ظddY)KRP(]tٲh4ɰ L6i1ƩTeY{N''FFƜq{}q47ozb vpAg IDATHǍV MttNrq`ۯ>S"T{>$Y[]:8ool񀷎iU-C8TYUyo]'w1FT}~/9s99!VVfN鮪9g"͇uW7UefMr܈/ZZYtX KTm"bNW֔2kĹz@l1Jw<`]BDxFz^H 5L@ȟ[{H2f&j23R[UHg^&콋Aj7rh  1"d}AJ)΂T'[y?asaaMGc$1]13QuXșYkFKX",}EDywFUEʝ~K[u{v[JK#TTwێbXy=:Rh{;G9D^x$KypXe}[.E#C&>=XBbd%tEXtdate:create2013-06-22T01:33:33+01:00%2%tEXtdate:modify2013-06-22T01:33:33+01:00TIENDB`puzzles-r9872/icons/slant-32d8.png0000644000175300017530000000466312161170335016055 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATH}Vs9wfvgV]iJ q `8*OHU$Td,R)lW%.c l$q !EH\U2?tutsgNp<$iYc8Z8ͻKӴihxci8~d@Q-"˲ӡP0 aЂjU8VpXuq`.amۖeEU*|^eΰ(j5tKq0 =v֋h4=qia((a@ P\?x00z!(EB! P̡G7nXo ;y>&!t]Wo}/tmmnҩ O=ۻ[ee @ ?:fIZͶd0:!e :r EQ`&vumٵW`0io}玗EQ$I"}`΍hFMӮB 8{<@Sѝ;^iūWEQHRӝL6~3t YgYv|RpfIJ$iȏm$I"]]j֑uvzj,rW4M C)L 0n\_mmm אb`$Ic)t0UU+Kի;Y6EmaR?Qi(b3,Ǟyz~YU$YC4մ!"s B|$Er,D|0̩e[8(TJSSS},C! pX4MX8sɥdc4Iٷ/,xc1E׏ @Qx~g{{wrW$)--QRMKe|+, BXWWб#Gd"fI._qƧu'=};W۶ҥKƾL&BQd2dYvQ]v뚦={/zWi׫Jf ttCE ,g%0[ZZa z-+O]h ce$xf˕ 㪪"6`_Yj$IJ`(ׇaYu!A)㏕Jeyf\~}}Dj>뺪aj]7VX,_(Zy,hhhJ:q?H  V􅱋@ ! r,jUͪUEQfGFuD{qWfg>֒N-611\ly (r$Ia <{W,/ĉoeY\$EQ$AĢx<861~NQ0dYYEihh Y"2 X,өUU|0aE{=ϣ(2ommH&%I2MO~iB\!²Lfi&S$I&!yRUvᄉkޛ秧9{zUq\><FQ% B{?{ZjeGP( ӛ<\.Wt]Gӕ+;V,P(Ke˲TqiYA Cs8u]_$k;j4Aj7SpH 4-UUEQeom\_BX0yG ._2tD8u]y/ u= ;`P*PH :6I<ϛCKԹ1&PDlF\"G%tEXtdate:create2013-06-22T01:33:33+01:00%2%tEXtdate:modify2013-06-22T01:33:33+01:00TIENDB`puzzles-r9872/icons/slant-48d24.png0000644000175300017530000001036712161170334016137 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W2IDATXX{tU[Iw'H^ "B(*3;:gttfvYBI8;uF^>XWA (@ /~$JwuQI {vN>w57`q!?\㸱}3W u=wѨ:(I(u î,haR$IR6HMDAiJcafA${N#δg-l % m.$\D)(('qi6&pQBEAH~(BHEic$I[Z;vb;}_ mV+Kx)UU BH$`Yv0Do=iZ[x O[aܑ,˲, !riqw:n7/g$I ?z_>Z^>v8NӴ`1tb Xڽ}[6v%Inh !lBLҥ3nyG -?w(y$ lʲсAɱ0 I$$$ʲdAȲ<붙wV,p,,/I:rs/g$,(˒$IHE $YXO&H8 R8iI%<$l6IӴA~7CivW]ݗnynw: PuڴM>c90-\xƳ_}}-c Csϟo M/\x{Gg紩SMýHD#^{uI/Y4iҤqq@D;Nud*2 3{~ȑ}~ﲻ9cY Beqg6:cf3u{CEQᥲ9O>OY.Dg:cI |W bk1@`>ݶmGߧɨTnj@+Vy<$L[^ dҙ%z͸1SI Ð$iO?H$B,[VQ}>o}X߸q0 +X|W,C4e0ŰQĘ===M-]]AOOυֶ"H==yyegL集Q_IkFѯ >WQO}fkhhL$\:uĉ$]a# E4|/oY&h4NKJ&L2HHF(,;:IĂbAC!y;wNg,Lbᰪn ˲<1Tj,KQTժNV eE<'c6󄮮-d:%3Ѩ0xeYA 2d( HxdYyA$Ao^果 Fd8 ܺmGe($r. qc:J cZUq PEI#}]EEE$ !555k$kT$9A~kG~/EFب(4x'wzTjZU.a#̸#X뚮]j8޸a E0A@͛ٻc>OUUST}jnqQ#[vC8ٸaesk{A,-_5.$~2?xrd80b1 t+W&Ir.Vn{w|jvuuwt^yaD2Y_[9o !{d (vvuuw_2ޕa Ay<IҥoP8t:z3}-/>lݩԐb;~ɓ'5ZpKO}obkk;nOV[ooo2,/rZ[ۖ.L&Pqә%?}-Ԑ,K@Qp׮pfU vpAg00WcIDATXõXە Ş)Npgt:\"【.z6H0%/N2FK/O-0٢D6AUASkEwBsf{G&HNߔR)%@ݣ.m@K/A=zyR<BPnb=%Of2 nB/ 7Y,~ iu3L=8FN< !ISk.}7c&&NQ4ݣ@QD1f& {-d Ph9/hPkm^2)3MRGޯB!}z5t/`n k4w;G !DcfB梫;9'ۄ2x֫4v5W9%z7Ǟ#bOO!4W9nK?i΍gR/&Da2d(aV,u&Dipz)80xǶmfYڏ5-i)fj?27|-w#k kO%&.#Dukq/JQOy<~=?X:n}qsMNqL,hpq(."Y)-ub%tEXtdate:create2013-06-22T01:33:32+01:009%tEXtdate:modify2013-06-22T01:33:32+01:00IENDB`puzzles-r9872/icons/slant-48d8.png0000644000175300017530000001001412161170334016046 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WGIDATXõYitǵ饦G3ҌF!lm`6هp$;Nl;K/6`Hlbc`v$fYzy? ZI~U_}u[Mu5R0 4h}`LW0 B8x_г 0g @V?$yYQY3]g}(ʬY3ojƧRwǍ/x<\VWw W:t77% "nݪ6l OPݍ(eCLF!%bZEQ$Pe }bOI$QǙjjj{BF$w|ˢ" <O$DQx"AQ0D 4UE$i/_`2ie-vBHQMSN#<IMSS3f6YC#K4f@ XR2rA(*̜9_͢lv87kj˧?I3fT4655JwE=DQ4LðL陏T>nss3`@ 9V}ͅ)S&G"?yʔǡg2H4gΞ;~I d̍ xƍ1 &{I/O<_y?aR>*p6o ֯[m2;o>`˕oXlg;$VO B(؜‚`ifB h*+ t$IB.$//o-`h˦ )A0LdeY%IZӟϟ7?X IDV$/fӒ@d ?^R0fF$ao؄$y:b1DӴމ"ȴSv~?>_'II̞=fQǰN2q\$D1KH4PK8%%%pb$II>ODVU5@-'7@C J MizUSLJ>9u+Wy>5x eNw:N'keN˕[P*..N~JSTle,C>=} $cԢ(6޸^ںi`(laֶ[$Wk䇍6eԺi>}q+_Uտ}vdO,{x".,e}CcxH$`h:+~ \9K$E ʊz9.i:MP/H*y];޽$'y>Hξ q77})aRݻv|q˃?r85Uk۸Ĭ$A$a>‚t0D͖s׬w:zu;~yZYtH5qP3ŬV-H/; -ML& C_TldEɲkU㸪(z$ə3gȲnf$s//^O8˲4M߻@^n.AłQ/MӪYVeQ/AEa̩L&d2xVb'N<!4CaX8^  vzيqtA?Q/IN-ۺ_}E1jeYZoo=|DQ{ĆkE]jme۶wt~m(YF H$anPT%oڸ %mxxzloId2Y,rɟ9snСuBMŋ{k.]~{446&I c ML!kP({vIM^﨑#O~u+Æ5n=A:twZl,X6M2)/7ΝQ:v[,rܼO=zmX\t:NSJA?[7_ 87kJ|sB nGyƊiHn0+w67xA$I!p8fhT$$E =^xs *bb]Xxx< WiaKKk-;0a88v_"&j(>*R9l6%KrrrTU%IL|fͨQ#CҒ⢮#g`%b}.|dqkXrrmcǎ),(Ja)A˗ ).UT|c:YkuWwwZL݅Y N{'^52  E1j/h~WGdicl$(f8Y4kߑfK#_7d<ڄ%tEXtdate:create2013-06-22T01:33:32+01:009%tEXtdate:modify2013-06-22T01:33:32+01:00IENDB`puzzles-r9872/icons/slant-base.png0000644000175300017530000002662112161170215016302 0ustar simonsimonPNG  IHDRAAflfbKGD IDATx{\TO*%.^-˼_ߓ]ӎ\G^uԂRCjh^4 At|!b3R;\ 83ugkzCև=/q\%p8ftGps8l= 06ps8l= 06ps8l= 06ps8l= 06ps8l= 06^C]݅ү.+;uƍ_n=zČ|v>֦R_yv)Oۭڵk|{EH9oX:EgzAXoӵtO ]:jHs88`[F (eu+_}ÇS0k K~:rHK?.QbokkhEo߿ɓ_I^}lxxȈ +Eo`ط~8FF}ץq'No32zM92s8`Wp~ƌ/+v쳷sL}U,;oﮪ{w,;U\rTF}+ xqώ:?.vihho{{yw߄KȨbXMwXڤx(:\,><|Iy'G2@DD ><˵kA2v ~MM??_hjjQ O>}BUhnnA;666;F_?X/ 9Y*Ǥ?jkٹ[een:t}}{MM=7f̓mhdt~~~;Jo[u|k]y7q75#/nx<}BbrEivMƎ׮] !ܺ~XpL3UUOF}k**N'$& zGWСCxGK /%OGK4q&M13gG{~W=b2c1\*Vb??1ϝz|SF}쬴% d̍oص!z 6 N'͙p@0*8:WS[yߞB}||kk߼%wa _òƏ<;o&$%>p疼ۗ/_T?1)S*vm=m<|7$&%oަܦMߺ-.^~bRrk !7kC\iɶqLLtmS̩jhp*=#{괿TWtWA^N;H> Р?d7 HpKKac7z/00pd>fXKصDbk)9'4Ohh첥`> У?g^7&^_n`׆* ?6vmhӟ.fcn`׆B{\ Sap s6Oqs64OYs6OAs6O)s6LOs6Oo%7njjV2FJ4חE%:m}߼ySK7]6^M}dH~5G"o4O%'m:8;e׷ Bd> L*3.v)Z6r>$$XIA%%z4<|P``EE%ܬ yzz3""|Ȑ%G z}Nv./XfjVL\z}՚~}9p/GIL쉋͆/QulWq Fx {1kkb{?)/Xw߸a05O<=f;uȨ/*}_IQ?#d8 ;#n݇3{GWz ̗'wϯfÀ{vx烃{z{{?#))k_~1KI;f͚'4ԧS'0 F#>np[(G<1toۭ׷`'">3vw'>np[q.^Qe- ,~i:`P|_,6D*7֥gUU-ر!;-ǭ88j YD; w'$%߽{ӧnٔӵkW;75g/_ӵ?_ ,?n1W[}Z֗]]y೽9kg<73;3a|]x`M1H/rq[qp_cNo62zM9[T[a7.؞?}ڔG}ǧw^s:^dcw)/~9sfel4SS[]@b|͆_!~\vߞB@u4n}#a⪖olwY2Lɸv3/AA{Zk0//Wt;׽~[)ÝF^WF#t:8տ=P=sOuۃ=l]_0ظg8p0=#Eh4W ݟh0lXeh|6|ԓdoTD}3RZ[^^T`F>GߍxfC4h >;Έ>l cxb`v Aʑ8 J[rJHu7{ܥ'0ChKU.H7FO<_WF~2^LRk>84"A)|ZYԟuHxx~,Bl|Z՟u"bpZ)U}S v͏Ł0ȧ5w1|l Occ{k⭭wsb7FU!q׿xG!0 pn2+s{q?`a~A[YϏ |cgAacq~3j ĉ㏖~7LC2͚0~-<#"GEjM}$-*EJ׼lQ>ڏ6uLKF?9vނ>Vӵ|]i8*ҥr6M_+~2?aҢ ݶ7~=YvҥKb2u?&TP؊Z!|{ߟ.>{GWz ̿mJsYлweK??^ πmΚ5OhON`40Ǵ|]@yYpإŐ x^M扈όuzio /_bgw +3a|]?tܹ51_L|_VG F{f5uiYUUg v4qi?L|&Z[jq/*q1 ty਴ <=vŋ<_a|HP/{w0Yfg?BIkU^_ ם5g/_ӵ?_ 2"|3Ju];w~K [VWN\ԐG|qn>|]AQk?5L[C3a|݂ӧMyG<==}||z5wά5p|]w.純c;8*rpT8򍕀;bUR" [3paP{4$ %88l~l^NƧ LSYYpVeeUzFi>鮂 JfP{4.ܱgr=-ݡ%q+4{XW^wGYϏe$PìDz?BXSacY9ی~0U6\<5zCa`n3*d`WmFTxX+LzԟUNC> 2یB {H$a`"n3'a"@2 OmF"K.,:|TUewuZ(ϧA^ߝ|rrƵ%z,<_Z!*m̱ϗVzC)Χ 7o7?ǮUyGcMXgvĤwNw*Y~ۘcp++x@{{{\66m:]mqvJS\3lPT Xۘc^te_c[ IJKQ=H@>*ܹ1b%XI!?dXϏuGU?*HM?T-HGp|znB_ = nr<h0p Rh`m`5 KK/< .j44h0p3Sn`mBLNKz>3L](벲So #&fc6[1H_%a_:R_yv)Oۭڵk|{EHĂTx?*ay<P2dyR@oX:Egzf|N׾=[?)xwm |/C%Ҧyxd60,b͛Y&is//ψQQZӯoߠ I sKP[8ԗ>C ..9jsӥ#wsZ7^_Fnv mmm&L>n”lS\X|-yt>^Ҹ8h}8}TShKo1 oߩ<ùӦNcUo&[|.]:K_b͗Va8lZ\|d٩}{wUU߻+$8d٩⒣mɏU>V-Sjh՟ Oݺ=hF_?mll:vy 8[+S}onn9;vN8޴W߻\hS4w*% An_mhd\mll???VTߺs[[y~/*0(Ja~u?-B̙3UWTU@XX?d篚<L6Wy~_:ygfݻw.=#LdUz8w>0^*kx:uҤ #_}~aϜ}#GY`|` /(9mYiK/ cEYTdrw>0^Pfc="øm!+;|`A/?|c@hC3aˮoF#tZx|cLawX9˧'X^}{ʗ.**h4fa7W!~&B>m^NulWq Fx {1B>mvŮۭFGR?? :IDATK^l|`S}t#GKK^'Av} ٹKecߢ>8wd`&øueW [Q)@b9[_T4 Е?,=|]@޽b->{{!U룆?_Wpإ0_W!.v+weUB\^V:uUB\^5K+wu(_`}q`Wq(v|Wqw.Q ,Q:uԗ|]>=<ߕup__"uqmw[ U=lcǍ"[<]8OYں[w૟yߞByƵQu=Χudu`H>|ZU_= m̊>{몯?[RQw>-^5uIϊ>@6|Wτ>@vmZ=E \qXчpaBY-Lpš_drmPeryЬ-erPEerЩ]ZerPu*er+Цdr7P]Λ=gg'-_9,t?J&7/= $FWuzd^\6I՟%VG&/%O_$22""{{{wOL+`dd/I_C84)ks.g3@:u,\0/;3-孵+ ;>h<,fg 4Px-=#{܄)QMݦ ?WWroCV4ʍ]jo5 [>zolxkBr(l__^'-4" _ ujqeBUUYﮐeKʧYW<\ ˵  {\EM_ӈ,\qunz%22jk/o&/B&MsG}}x:UHd{dUsr(/ X/"jxEqoooW:::*<==Wݻ9Lќi!b2{ߋLMX1>`4AY7&q/; [-W|n5662]|G%nֻ,>>}Ο0(Eq7yq |_RgΜ2r o&;|PqVS WN5uݫK)'o&/b_-]>k [N4!zϜ9^ydIǛjsn.xJp>n}7&V[8iDX!e`@av71&>{8|7$&%onzDɂ6.Ĥ7 6TbepQb`nc50] &&pǶ)SgddTVV54hhUYY=u_#t FTX! HQ^׆/̚;dX nLhr337I&,[ J6F Lk ]pHF΂˖:P ӈZ p+k"4Ʋ@ Dd1a`ncPe`Ry1d`nc@1 4b@ 4b@U4b"Pk`ê imB,i26rq4r1 p@B2\ɺ[sL@uuJ7|zاMy1RiևQAJ}}ZZӦ<9zT``ok׮}C_ "Y۳c :H7oޤSi!O)l=~bꔗ͟=:]Olݵ"~INc4O%-Ox.EU&Wԇ)[5ofel0~iy <#"GEjM}%>>ToЗ !Ktz)**h4f9OzNK@#ҥr6M_E eO ߒ@׮!!!էgp]|IbKS;?Q8$uN>uF16.C_ܾ}`0ܾ}O:YX:߿e<%- p[Q)bwYzYT+SJ!mлweK??^ǭM0jjxXu6C. mo/w&=| /7ѿD$[[`u 5=N8q냻kǝAvCZ[d{cؽ[mh4~~MֽV@wq냻{]A"v`{H|}o,%"_7 !y % g!źH>go%"uOK++Iڕ>k:}o4_kl٩rFFؼ)g<73;3ͩxTX60 q냻!>?ߧ?_ߚڦÄW<<<Ə`0zݻm> 3c߼M{g===-L uƦ?n}p:'a0g¬7 %iW.,q&g>Շ0<ХK}Wzx>g-O~ݵ}8^uÆ ((w^+_}`0eXN`ׯ}+%:zMA},m_}~kO:zTLPPp_}/dTOf"wg"0a?XZ'L>nXQI볎0ac{<8ps8l= 06ps8l= 06ps8l= 06ps8l= 06ps8l= 06 ПEfIENDB`puzzles-r9872/icons/slant-ibase.png0000644000175300017530000001120612161170334016446 0ustar simonsimonPNG  IHDRz oFFsm1 pHYsHHFk> vpAgAA{IDATx]iXWzH;ewPqWL4 N11zAsdDgLtaGG%.F57ǨH4 44WDa jw):/yϧd ﮽%{[[[tIiEEGy@&k܄)I_::VGԬg-odzye?1 ꌴ_@ / [߾7}Ф>1sNEm殆>}4(W/-)/2]yTt\]Áߵ3D]*D<WUeJ3 :}?aٛ+N0>>"/¦\xeeVV,--[0&d4󰋗¦N:}3?p(6> +XWK|;wvvo(.arG^kh?zsߧ&IlFr'NG`_=ѣ[2Y ~*G |}o Jm1!NL|+`Yx#1cCp_+߱sN|̞j)ݟa> z0zזV_/.zⒷVnin:q'|׍l zZl\(3:F}flmgg'( 6.kRosq >0ax,W'M7UJUZj}piaS|5φ{.x#9Y]]M8~8~~؞$tCTqֹs}R>}4S&O 9d`s?.+0O8cX`,ڌ?.-5QTQj{~D1#  ;kL@"c7ISXTv19ہQ5&S] D`#Ī $lgvkL Qci,.˻r9=uY3gQ*(<<}ݤ2/L TUUuNj';5"(pMF;<πVu2;V,X][NxibkK^uuuey_|U{GB3_e~ySXab ,X]*2bn@?z>ڔٜ*1j*$dqcsrd?64u 󭪯}nѣ}}}we#X]\S[7wfP!׋K;OWW yx<Ҍ-Y :]yT6tjWWÆ._K3tq5,+5Sszy?&8KӃnݶpiӦRΜ5LԤ C[{{uyWn߾=o3'O1E񛚚 CWWWu3=GշgϞ~L`oU&ƨ'knn1 -7JJ̏P( IwJn.Z2r~Xv_;_^sAݻ aO+*s`=̳ǧ섢.;3:XT4`+9gQ7 9>ka9|%K^2xp770 F BR*=#,W?ť_~7nwyLy966(pK`pm| :?xٯ|#CG ̹?:p۷ma0-lʏO{:lOaCmg|h\Qq q4NW! ͘1!DWU#/%%->>>Gh`Sl05NNMGb>z "lhh?j8JƦ޽Ngw3?olf^R|x_6d/ufڦyUX(_ԌZaP]-?xDp{=_܃k4u t h'ۿ+VN?P*,,@`0cXl!C#wb??b$?~\+ONIȈy0UyNvmYPaz=JzjU$ j$ 9h\vTޕߘ2y]@1di<=t AAԖI&~kkkwss1w c*?ܠFǫm_P̞=`05i)"<3:<߳~M cESF0g7!((P63Wc_۾/'O(R?C[faPwk}n ̼ke'X,;XrJZccJruu׿oެ8utF92HI} a>/040Y3cHb_Iė?Xl+`i]a-VPwOuuu4r¤i\75r;{yxRS[wܾˎ.ʡ.7Ď/;̦rJNkJ[Sc;@XTHSNEkۛt85 #YN1BZuPSc,\]vji1!`ԘuiL@?a4&M] .d e1`iL@?XF. QZ(v]&t'v}e\0_?VyD`,n`ri˳IK=7kpZL V}eCB'ωLNIo6X\eWnooONI CssKAAፒ# YR?[FVeR_h4FE?]ss 9,*?u0rޕܳJ؏Qesrޕ#Y%0p6>NΡwYWw<jcύ'W3&t}dǵhBBF[P*G&b_8V*飡ύ'{F#{nL^miɏ E~[C`G}}h4>u 6?gmm]}}]S? 0ps,6?T"z P>~LWdQ|1StsL{W K>UoG]g&n(j[+W&A>qB>>sCa~pv͡<:.}}yZJ"]Ɨ;$|`[ʽ~ ~RǕ Ȫ,M vqqh<֯eǥū+Gؓ,F9p p p p p p p p p p p p p p p p p p p ?.zXR%tEXtdate:create2013-06-22T01:32:13+01:00>^%tEXtdate:modify2013-06-22T01:32:13+01:00c[IENDB`puzzles-r9872/icons/slant-ibase4.png0000644000175300017530000000514512161170334016537 0ustar simonsimonPNG  IHDRz oFFsm1 pHYsHHFk> vpAgAA{ IDATx]۵* U2)$RR R AX$qW#K{ ybi!.0Ƹ+hΖm8_ؓx!c}]ZnVw!4z#.L!0pmd~׭{2_-PMUh%'bp?G RlC'^):y@`JkUtJgV cc5t3w_K˔{g(;E3-JhmJ羚h!Q NgU bUAc;#NuAXAkY].v5V.x ;,Wrw>$'ј&^FXl{ ;9Mi9|2Ƽ^/|>_|33({{à VW$&ٰ3kEٯ0n㡄edKӸF)(sKL, l <keJ9q˷GؙhcvGAkue}7K u+DgTj`h9uTQIs.?A%Qab7 hrFkc7f36lf^ĺhjx7L{5=9]9DCl?%s"__+-C|1fڧf:\f?69j3>-A D!U>rsc A%r9Ԋg`~G/M(S\t_A.IViΥ4Waf.G: zw|k}} :zxF;{̿cy¿zE 1r~6)a84}Ts  jcP3=PO!, cPm-mPOAhbP=ԟ6'POqJbP !ÁcP51cmig[cCn\\ z*̏)4z{ʎ-3 u.rٜk٬}CĈnϫ1[c=w؁e|͢_]Ƣ.[c!Rpck̎B45fXu8ak܎7B0v+Q^Rc=ꂆiJ]P?x z/BuA5֩.hPvlBZ`jb3F3ϧ$pjټYta9s 4~䧚DsO\,×4_"X:yIo>Dc"Wn"'+])U۟?1}ЙL5G?/oOcʗ /-/-/-/-/-/-/-/-/-/߳y%tEXtdate:create2013-06-22T01:33:32+01:009%tEXtdate:modify2013-06-22T01:33:32+01:00IENDB`puzzles-r9872/icons/slant-web.png0000644000175300017530000006506512161170215016152 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAgeipIDATxutTG{qW AJMHp-b -E -åB[x d}!&lB䜦dܙiOk OڿڰB4M3 A 0 MӠ?h[4M(*p `$) fsS a8 ah4_EH4tׯ(,*(ݽm6NNrVۘ M, G rG??Vk/4t4M̬T.ױC{oo.Qh40 akk8RڷoH/^MixxEQ0 y*M?d̜#G?oΓ'n߹'  Pt)}zϛ7~+/5g,[@hjscAS ahhȗW9鳑#8ޮm_=00f1,9$v;w8tWxf/V60eRfft:4r޼)]ł?Z D"}"=&FaPRUS]чCKJ^/70v?եsv<{Ml~#`<-&>=4McU*n~Y;wj M"REQ$d2E0LQBC*%l6F`HTѸUx{{ffeS4zB !y<^Eee^^^2afkזii]aѢ)sB;d ;i6_P/p AP*F bfx pgXITD"a(dY|>A`F,!IJ N<=$ùLqa>[EqΞqMS-DJEd$I6pQhZ͍$aȲ //jr<0 QrJYd>jX7AN׹S%nڲM,MJP(a/\2_vCnn{ǎ:w~"PaAwVvZz탇_h),*vrrTk49$)Kf͞r2fGM6zPmBq  Cz׻w( C2he2YS Qs?Qn*hM3UUUumٗ$bqΝt:0 4c2ryZZhоC< {Ϯ]'Oh4BDӴL ܗ/;thZXXtݙ3}JЏ(q_y3zL{j l1(4=uܓSM3,(ðZ $l0Hh6 l$IJ-mLb)q'|d2s kRjwaXT$$đb'*у d2}$亯V|gd' Uj5,P}9ppbR>ܾm3j^姤ܩE!@Q@ (--7~{17L்J4f9/_?㝖. loa-%[Կ;aT qP KI:(^~%RU( 0-_ +lDӦN9ygϜb&!>^x7|Eah<9?m~ cZw;w?3L@tj܀0̌srr_?jQ}Q?,b&(tP(Ѭޱ0 !P(F1MB`ů3 CLL/OOD:itA^FKhc'++~c~\.Wv`^ǍFcSIc~>~g0X,Vm!X(*,aJxG&Il6{zzH$b@nݺ$>0V!R52k.>^jr|o;~-[ssr_;P k^0xYS(*&I X"a0B%sRP}$i04N ?wL=v$ D"KEi@1>>NP( ^H]{{i S((Eo 򫓄i`۲M~k!~J;!du(&4*iHzK(.aBN5$v@=~@rBP(4Fh_~r"PјRПDBax}v]__Xl6{V]]x9s߻w_"TWWmfXT*@>P˹_ORVV6u,Lj2IM>4f̨~>7}d,Y>̏=zt8TQQ9a5Ϙ15%7nJ($O(ZOH_Ӓg+J6~3v{>4wN-ͬB.oh֭l<./*#X8A/]2hP }`aݧWmmmW۵50f|Ϟ݅BLݧאU*UQQ>0:gl 7*JPpDm`/jMttoV%sLL+WEEu"ݕhE9veeՓ'φPVonK:uqӖa}VVV^SSL5F /((B EM& qA@"##Zj7Y,͙?`[B[ aR(4PKa!o;ɇ~0NnbEEIbN׾][@pORD.w~O<4@\]]SS[jt{{n4HbLVUUkE$0b"#l(zy7o޶u3AZV&~UpZ-l܄˖,_ۍ߾d2[{4\\]ʽztv̏={wP6AXDZgfdj>IT*zz|Ը|2(Xd 3ih@FX=_vuϞ}:ԉTwwGS79 CCCC32:v`07GQTeK8z̄soۺ(d{Qƍ]XX7qRhHȗKy{yE"l3,lt%<O&\w'&L7o^WTT""?rukWYi 1{ߧO=&q3MIɩBQ#,&`3LO0erA$IraN>k)bM X|mF?}+stwsj8sÉÇ}hqz׬>t8GFF<64BBZmiEڸ{mfH,)G1 0~,5 la /?ytP}/"7~kWQSSCF77WbeC "qҺ˭~;|;s9EEA3thHL&U(YY09Xxhn|j ByWx4_PPPA hrE'Oquu qViYYzzFkaIkoo/>5f\l=mG O:SZV(wh44MNz8 w⧌7櫵4Z- p'Av811EfX,Aʪoo'O z"-+; ǻUȈc&8;;'֫k!B˃T*_HFQ4$UDx8Pqtp8$I>h[ suq4 1cc{} E`yyyY/_rwwi*44l67^N/.~Ochb xdR&(.++j5&; 7mƜS'x{{k4p ||fNULpv'>ЀbBu=bEQGGʞ(*cRRI i(H5zllN,_AӴ`li!'ԭ?aEZŤ_]|嚤T "BJJRTf3Ydw[bf4AJN zI~đ&b5 4Mk4ZRT4-ckR0t RTTjvNjMhhS߸9Ӆ, NL&3RwZ؉'_ӾuD+`@beiggfe:yԻ [D;(띜'OMaƪA`<<0nQ#ׯߜ7EPA7M&<1 eYl6A`IJŷoߝ7)>?CUL[Wbc~S>}5?aap6 0IEu+*+'Oa6F۠(O~Ƽ ,[ bC0L$՚¢b 2&k!w>}>?q6"f[^oN?h4D">_[[_PXR P_늁J m}oZSlI(H=M[9zLѱ9l^ҊM:3vk@T*ӈ~ZZWYlss_$ٳGvڂGG~ԩeeeげ?PQo#a3cFH$ ݣW$LjB؜k 7!HBn+$&&K$b:mfVv?5uq˖~˹ عg psu]`~FFfnKO AbxQRz]5z >anܸzk?ٿg0TRod GQ~XֻS}.:oTfΘv/A۶$_ `{C5oGP0LRM_XzfO63'}}u: bX܍ Yl<}{pI&FG}.b  Px=oooooO3i^DF 1 a "ZآnSX,F =s[w n4MXxnJչs \

by󦼼D6}ՂV\ITamp8?^;g&~*.&Y7p (''7.~rTNzPT<>h4~F SVUԥKgRz/ZꄈF[!ctޟ~LxxMM-Pܾ{7)ȖMAaO<=x(z< ܘGhNi?Xlk߼uVG0мO0<\R 73xðd iy]gggggϿ޽{;}ɤF pE `6<~}+*+׭бc@}#SkZLX6 @iw $iWvҕښ$) yVZZ3~ə? Ӻu+X|>Ym4%I㳖5n;P(zn&gŪ(ݓ ȦA X,ވ۷r9ϞzzZz 11&%LŔ#H)00f@EE庯7t`H(\~Á۝MG^IչӺ+ `6(T;wDR'Ϯq-+;bGv/w>ƭǎ$jAjLյm>Ŗ !nhڰЎ<'''`MJ<0iKqvvl^_j⧤$6h~o8x80e/ZsN)ɇ&&L k,֩cݻ}$ u:}~c/?ǎ$T*-G&`4lG}2!Au;{klL Ah 0 j$I`8|hq+**| 11~ʽ{1I6#t`޾} ZV8T*:''vcG***y_ѐ$%[ ի'8ZS hbQQA˽6͏֞>sZ'<:XbA(Z}uT*e |>kkjjuzݍ\\+ ,8|Gͼ9 %S+0 Ib}0dЍ;6h!Co׶ //: T5:N*|I۷  U5?XHXⲲ1A0@j0p `nYC-=z|.]F ho/_#uSh۶ʕkիhT*Uq.l!B6L,c nN=rL̜>gڵ*Z Ł1˿fc}}}}׮Z9r)^~b՚}@ 3nh??g~ :nˆ XUUrY,? $I:9_X3y~}^ӯoX$sm"#ݭ)ڧOrU*ׯdܗO<<9C[~OzTn1h&ɘe|)2"k;<|`k~+|_ ꔚz,<[d298:tYѴ l*ؒP4 ׯe fg DF>xp=U(rC CiYYݫ/Z8CI:`0>zXVV-U*U׮]NjİÉɅEbŋ:e2 #W\]??O5xЀRblFv̹BO^ R,^jlAAӆoazzFUwϟ~9|6m4<4Ͱ٬7o]B.?y:ب*++M&jATV@$H55/ "#×/[|S3n Ba`QQ1*9sfBtżWl[Ty{{x+U*}A+M6SV;::|DBJH$ bKD"c)!_r EQCi&$k;M<W v|]l[6S:vسw_###45uE%ρ:8pwppDATjToӨKQ) }6 jp EQ ij܄=#t(z(u+ل֙, A:3-A[ABgܗ ߹{vࠡׯtuq˗db<¶Fhݖ4AIrpI=\]]=eL 5քN_ CQ\]\/^ܹKSf,^-+;_a^o3v,˫oܼ5|m۶qɺw`VO>j?ڳgtt Ef3 A)|?`pdDXppPyE֭;~?!<,uz)W egg=\U]#@34 о9TUU?l2|;P%JO>E˖|CwJe0\H$<(_._U "-ShypZݔ2d,r츉NNÇ5_<*(hfΚqhFSϡիkoj0x\LAI)Waӗ/"|o쬜R||iAP7n EQ0yZuu1[ dg0 l6KfgH$bqRW\8"_C:99!+**ǎsrrIтǛ >)񚚚|>h4q8oݼ~ӿV t/++ooFF$IiZ`ى888k2[h93U*lh 5lV(Z/J"aNwvv:~,r>]{  ;../@(!UVnh25͒%_̜1mڌ9/^$͚T%eg< 7-$H { OJ<Ь} ~JE޺ Ȝ Sg@ıIޝaa )6@WK|1ki3\x/PlB.ȲYuk8% EGF~'Y#VZ|GxJEEe¤wR r?:f\5E7⢙3N>-E:v^- g}[af̜u ~nMa8+M/Mk.(VV H$ _,q8KbxX蘱o)Q\d̙ӦNuDBQ"H 4emPb \i3dee:q&RʂpK.'40F"YV+JfL&k JR"Íܼz}.QRdʵ -yCmK8jjZ*Zg~} XjmHH;w^x;9ٹ*ݍf7!TtMo>.^:xPL&z^$Ig~lv5>E銕kd.Q QRxa=;~ٟZ1ݻwe˗/(һ5Ňܽ_hޠj Oa ٰkիGLL윋xKyg!0˝2ufvvv֣/]IJN2x@ 8q7AhE3(JwZZZ{!.wuu/XhaI#W]H >{_Pa1ZQ|c:9++_~[hܫW>kȐAvr颹sg+J6m.75 `>m2 Ǘ,[1ޜsfY"G,-;·0 D"{É)f9(80aa/^nRLE>o̹NؽsM ٣{YY٢D"//]z4Maa!_U(ud~ϿG8B̋ivlo3_z=.v< 65W^n=sf4(g-? %6׍),(ѣysRH{%Z_@(Mi)A$I8;?z$?@R70b8t_,^a??ZvYQQl2AТE^[J)iip& ko̟7A6l6h4=z`1I;kfaW~~ADDBtr4["a r>ksfA )hxFQqpph\ah>윗04p:z$ ŹuazT"Y,u>RP9fU ybl~? r0h=e[S%(a Z ׋|%,nM|qvvJN:`~ C[2sxF` B)::x<^K:7&HY`|!( zLVW,Y:ߏ b'#I2U^V)', 0py.LI=:0fĔ>ec!Mj:î?zxmN/NsDӧϧN0ιC I? "I@O;$ pppxrhhc?6<oǎC JKPԝ:vh4h-Mt{|R 0~D".*.p᜿pc T/*f@vO> ,<uVOxL쌙s~ػ 0!)_KZ޹K?RWv0p}&yGΚ1F` j:Ab)f4Mxe5~~OxX@d;7oٶeE|M(0l4\K-^<6nRJ!E TM-((5z\'ƍOLJ0iڷݻQ,Ř~-aF=y豱P~Ǯ[mߺy&W99 222ёf$;~b|”ăEoo"J3}}}|}}x (JQ@ p0L+q)7vBxx=; mipM&`ZM"i}{p`E;ҍc6(ץih *gAL34ütgL?\][,SHI=zCOF@ Jp/((3..<<?aBaYYٝ;n޺SZVc  ipi6ծ][/]`EQu۷(D(yKxz?iEڑcǹ\NdDKvN;~C[6}3rpk~\DNU\RreaFSC)EjuBgg?9l~㖫sm`yi!>+pƭsgaqI^o9cG=rXyy9pP ðZELL}{ww`÷bͲvG=9s\pLJ7`H8BĸܢNѓ ;֪T ^S[3WEJQ#>;7o vcGkj cmi<}V|lq6YَrǥK:t8 j@dY(j_~}挩gbkn?pҤڷo|  ׇnj:ԥKgVV5Dst޻{Ѡ1:N,}{u&LB)=N3gVvvk4'Ϟ}cO0.3+;0ߢi?q_hw؉^|uɬa~ЦMĹ~@_*vA8VUU5v\\XXCdꀐO}Ą)/_JN9*Jb KBC텈rvL A?\.hϜ:=glmf[~0 E,WWWH-Æ 2xD"uNE" p ef9$5Mx)7oݩE>}r-S6MOU̞3/3#+vpPu c#~EΝP KLNޭ듇w?_0?%HuMM] OG0 ߼y AZ#Jx! 5Ve *-- ."(0p*ںbXZZ& }iYjjjnF)--kɫa Cj AKD"NK 0/_ڸqBrttpuq(4WWWJD/҄B!,]RC Xd0^\XVVUlڰh?ӻU_~MQ .hYYA1$I4pB 2E*J..A 01 p\   Ml6ia]j Cz=AlDTglpWL&3LBy[fvUAm5`EQ eAP^߯oȑkk;u{14Ec(a(?ˣB~&~f]wE٠pޑԤU*48AfAb 8\V(w4QnN.LzyzFc]'&oڲ5//_*hZbPRbGQL*=y_͙5#OoHq[a,A֒h0HV$oµZ͔ Cx!Aׯ߸t])rww3ݺvJ%  ;}l2 &O_ǏDFF8:: U oY|E63Nnn_0߮]{|<ԭkPPRATbk C CvmKڵmbҳg***^v'Oqj|AN̛pɢ3gNUPݒE&plIO|-MSM]$)Zݺu+TRRҪU+Rk{]sNO=7 IZI._YCCB?c^.+۶mVm楥iZչ8MM*+8I.A-IDm@79|̘Q[l t\u֝OȦvZ￟*8fA ܽwҕiS'I$bczo2@fദVaAAW W&+VvA0#M>駟'NuuqL$I36nٽk{!BQ颢-c}U*d4ו +-- wtl3?\^Q11n aM)?\YQ9m$c[/8  ?oׯM&Әѣr^g=~?3kFU(a@hm۹kysݝa7fW3$e2酋~q]]]zaJR&m߾s߬_arx!Epx/|}b`((,G`3 IXXh`Ni|ot63 z_ر{˖mm8~W^c(ڣ{tYL2iF1//?=#*2"< %8(pjŷ7ΦqhҸ'MnMa0x:ddfe{{{= oF>čCDF,x\@K:`{6nn#G EQT$UVVa(*Ije~gΝ>uҥ_Tja JVfL&wĄFoLnTUTT?Q.7Q1En$IY46o.A:NRTjU~{믎߈jRNzJR77S50 (b4-tQ(T]tNNBuU1틕(#!7.~@~/H vGb|'OjH$J%;vn̏iׯ<| >//^1?}ud2W?|nذ{/^k>_ft{>F+3Kחd%G'd>$-=bH f39~-6h4Q޳?0cy_P_fσM G'I2*SffFpX@9<Mdě7Ç}O z5aj&?P#V릀(f 84ݺ*N][ЉKy[DFl6WXY1 & \nZڭ5_}O'[TT v)Gg4Je`pa1LA(J0 TZ//ON׫gwCVuwwh4%%% Kh^ 4@TvhߎaNa</'''88` h (-=˓(bb Js!0ពR,Sj)g6N&)Oo@p 6(wh>>wܭvV5..QQ=00 L&'$ɚZǷsM# ðj@Zq sf3TZ3*¨Tl`UIY{g̚;[(JrfRPw:bC7oʦL9)a[)V(*`00 @ZO&H"(Æaa êkwP*UE6~q S(4Maf0X,O?Ţe+/ jul6 rCDQhD`0ka(@4EY?-M"Z{.{7c)jNIIǪj W $Rtj+I+**|~9g M0aZ@PSS#BBZEb͛GL>+,4te*j:h wrr2 JF 1LE4ET]]!64W889;=xHPH$[aѽkk"8)((Tk"PR Z͛R T*5B:C=q-֭𑣟={rzf&I3Ew?lha(002,5M\֥[޿6$IjuOĢ_~M>S<== bV9JRRZTJ3|mGuuM}J^hUUA* Tj0d6Eڜx...O=Iɩ\.7%hK23CCC,WL&}M SRǎ3WWWPve\XTT, \\g͞wS'V4\s!c{8تUЀ dB7=zu P?~oգc&A͕Lfrܗ(悂_~?^}]zfKMi:""ޟo߾aEQeee\̚7A(6SiF;@-/5oo:yZG3YY%ZmIRM<^˗iiRiP`@i?!ԉNr]]vvʟ>}%Aڵqttlde fx __V;upcGk-Kgddfd8 j׮-A&+W 6ؾP֭EXx^^EzjZc~~~\.r8jq?ɣNNrEEBaiH)+oI"ꂿx<1%At:DO3z5Er<==anʆܒBB7Low% ɿ/᠝AATjR۹e`SdY䤃NNnjޠ:J j 0,S H)*`4 )G$8uzٲ /Bz_AMkMwF0cl6WVVԠ",zX@q-{=6Pl\N濶YStvv=fBEeẽbSV[HP8EQOÁ[o_xi.]p8|_b1o))&>4zllEe%#m)=B153?֧t,V۹k/Ù9c/t^\¢Éɖ` q"/dwO5+ƌCR/ ipS5!>̙3嫼/^xhmۦ޽zT&&_):9;Yw}Y($:}e_"b5'##ð\teYVUPP8jԈWyy`-iܾms=njKKKkY{g?dZ+˲(']ƌ[2k7PKgtr.c;6O}44!>h4z'Hsr1 :bh4MޭyT* Aޢm($飨2e С{ܾx<G0t:.AIi)B1bzBDa'޼u{BlukZ nڸu:f_4MK⍛degom@us~۴ekȥK@~[7|u 1z u>Bhdjo@@;cT*x6üͪxHyy7mKXr͑~~~:PUT*ǧ%:E-% FNOdRg 1_1"<,+^߳gbfΞJ ؚ-4IF1vDq֮ճ豱/^|`FnuX۶}am/Zd w?”i3NrŲ=gdN>iAL&s! A߷[.wx̄RR3_NO3S0"CI_Xv!C׶FL&=qL&ݦ];%'OFӑc':ul$R( 'arX$ {>>6>33S,ۗn +vˆT*ݱs-ۀͫQ-I/_2}ӧ.[Q8~4%##sy8fSZH6s>g' 񕕕 n㇡WͿ4 &'._f'ǃ;K[PaTJ-3#===n޼iV?ΖSTqPMI>$ F#&y={t7!!##=Oo%oǎ]6oݾmjkk<$Id0cKP5h4cG3fn6?APb'߫ۿ@{Rr+VfI ufBo:d 5hJAPH"rdڶuS%6n޺}Ç MMƚ:~0\P5(:|J`p\TT3?vRBIZdD"PRߞ۶naE.NVwf6եh4~ǎ&e̚=>E˩b?M?EB HV `06(nml#͝>!?Kkkq܄5uܼI%%\$k,v[QEW;mrS,TZN(Qt(67|odARY?o9!k==wk=}0$U/~>wRڠX͏a ]Q?P&cc[Z?nw ZBmܽ[W+ -SVlo(* 96 l6[$ :~ૅBЄB%t!9q|,!##E/d+~uV[Afwޛ`8Tj|<*//7IN:  M{j o߹{45144Dђ$Y^^c2RM~ S_v-f@M>kƴ [VVV<7WݻzyyZ 'ٽ=##񓧕k&""LLIUU;ɲȺt &Yߊ )kY,3I^~3DѡS=%%%l6ݭ66~-33{`L0 ^|u%>7l&+=VYYUUU- v?fKK˒SDF]*yy̏S&/^FSl>x(Q, K?z}zuh+ ^K4]" 6GAAAddf]~kĈa(T ńIӪkj&ZD%S|! Éɾ}z*{? i݀=W`B)LFA$nO ߾s7' )^|ձ'ڷo;/_YcCgffmݶg.UUU~ۿi}~ P%"1)e:m;ڴ`W PcIGLIq%_gd=[.Ϝutppvvާ/^^^/gkZ_TT;a\ܽR,S Ib :ҥ+)Gq 4M+Jߤ/W^zIYCѽGv̏W)/ :y괿PX_^|!U*U&N (5@(ȟ@`S5?jucxȈO-x"mb܄򊊂Bcmnݺ}Mnnӈ OQùvFdddǎXR7?X,\>}fF\:&QaтGzc%p8W^ 9gT\~`07(jy3`XI Z ww|P&X, [k`0@b qZ V*UZu~܄}pzn 68#b0&h45a bIR蘙':&/?3futpϗJ%2 8AA9/)pEE}cp]]+**L&E 33EQa !fٳ߰qs^~~99@& iM)` m7I$ owyyz| c>kzqc>%%9IԲ5'$e2YyyyFfVNtp<3_.Ԃ.bm/lv>,,唕W788S - 0L׮]x۷Q`pXSREFFX˛fgg̬ʪzx]*DGkuon |iWW_pk׮m۶:>.vôZ'`e\cCXaak7^ʛ>m2%[G?q:wqd2&ճ{.:Tw-+/ONNܵKNܯamoe$?>r0#I*UUӦN7WBifVٳ?߷Cv..J*==smf@~֩vU@q?f4 ^%tEXtdate:modify2013-06-22T01:32:13+01:00c[IENDB`puzzles-r9872/icons/solo-16d24.png0000644000175300017530000000143312161170340015754 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭVIDAT(u=kQ9c2d6ˆR;;%)҉gB0(b!بVmbF3;+Kc 6>V @"V 9D ̎hQɱE$p<-K ϟi*a*V(L&jxrkgg(MӴ|~uZk֕ Jb;l6J"vݗ/EQi6ЁkmS&MӪfbw DcBDDLڲ,ˆ vpAg\ƭIDAT(ϕR0S|ڬx31YAW>| )Iz~l W@R* ^w(($Dp vpAg\ƭcIDAT(=oUϻY;_덉Kt 2B.A EBY"HIH4I(1 Y0%Ɩw>ll̼wNutiO)&Ec"BDD{O^RM&/^I($A\]]WugY trr)ǣ*,U4I[ߒ" b9^P=6Vsݢ(2G=8|Ȟ;E`o_Dမ>MLޘ0hƘGrj:x4M~"Ic۹( ز|4QUՎRJM&c"!vx [%v%tEXtdate:create2013-06-22T01:33:36+01:00w%tEXtdate:modify2013-06-22T01:33:36+01:00IENDB`puzzles-r9872/icons/solo-32d24.png0000644000175300017530000000340312161170337015757 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg >IDATHǵV]]W^k}~{~δwI&46"5 Q J>[ИVPև MdLf&sg='g^>{gL[({>"aKD[ZZk;̲a3a)4VWWKJD@HDϟvQja@ec_JT H)er(!EGQ%%": | 80#T8} jF)3d:tc"RJ)F Eoߜ\ڵj&Dmdw{j+!gH@^?L#i/?uWt:Dƶ RiC//KM [`n9el ?O9 ,{CΉZ?sJ9Pl0`xa/eF1t:6hl9B"}x0eأ'dYs9 @'=lu.[ KbfDB2D !BUUy& X 1EQfy彵rTvuZZXK1f9?p(ͲB$I҉3Oni'SEp8gAqϑLOM]zm߾j;2gu$I̲`2766sγ, vpAg wIDATHǥV gt;v2tTІ`~" =Fx~9 rVu^Zo>-Hp?sF;'<qRf̩5qKC{sx,f#RIG_JTGуu͌#)duJ}. ܠ{ul0c˙3xwԋV湱 #ס"b>lT! uϿ)k&}ߗeF#>ؙ Dx_;L}<2c<8 5 dI$OO0@x MCMW=Q IoAI_)\Nrwm5ٍox6*@ {b~gkbZSJZkeYscO_^ZrUR)z燨5VҺkJi񒦟)}}Ei9Gu]׷$g=m"'EFЎ}>C/J>ɸaJ7Ҿ!!)VFy$;\h z?kopoG(껠b%tEXtdate:create2013-06-22T01:33:36+01:00w%tEXtdate:modify2013-06-22T01:33:36+01:00IENDB`puzzles-r9872/icons/solo-32d8.png0000644000175300017530000000356012161170340015677 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǽVK\GU}ɸ" bl$`'lP/X `%b ABH$c C`'23mO3vtt{eQ==3mo[U9[A_;a&b6˲+/LVΡ ,ϳCB`-!477l6ְd#z=6Tr'B(IiZ%<R*0L$I4J@D?kRJbXR*֫KHM%zRF)r!1jPkaڟ:}oF,"R/ugO~ w@c 4xNԫAIEM)9f:c ?x.62X)jS@mh/U7{11F>'OuU#%@o~+W/whk6v(u=BmPpaY=zV{S()v_|O}rȑ"c @C3G>:{_!K EDιKݧO?I,Ks)ՔDQt꾓`P%x̉"3& Pyeٰ`k3VEE! eGV,rGf)$Xk] b͵Nh:څ& Mb"BȡCi*m4Rڍ#GWUt6)GrΥH6se; !RR4T[ku%!@)5PB5!pX9y )6h!,>ҹ?JAJnNU#2ƔR0uY^0;@ǠO ι{plFc}O~̙\Nh{G81qaшvc…i3B1Z밺=;T8ab+Ј캮Y8`9/u+ 6X .6J)nFJ ZV5??/RFAXyA8M]A9 C6 | OQŋKfGq'ޠjƄv [N7}J)B,,,;P/\BѻQpNhVVV˗0diڟ]!Q͛D,BK+Z8.ﹹi!Rl1>Rv?}vnvnwn׵~QYLMBEE!Tk "=,-JnᎯpDZj< ^;J%tEXtdate:create2013-06-22T01:33:35+01:00F %tEXtdate:modify2013-06-22T01:33:35+01:007T"IENDB`puzzles-r9872/icons/solo-48d24.png0000644000175300017530000000466612161170337016002 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXY{\e?{ܹ{gvgv;R `GKȫ塵@*5$V!`D6I0SGnwv̝{qξ3 zEDY b>gM"5X+h+DPJsgM$JDSO>ڪIu8>vj5`,R6ݻP(*N#!xR[~!iS2\yYskfY/r|5F ɣ?"z. D Yr?cʹXcMN@,CFD+hmT̅8 #Mh&.GY!D"r]u]}c촆eF-IYM̑UuӚ-ɴgx'ֿ[z宅iu]7k.|_3"pqdr*.{nk2!Hv&ۥwv{44Q -[1VjÆgq^wܼ}|||qƟ>qkkR8^y`)p] & hXoj"\.K֦hc|*z/&R-Ȓ/SYU#d4)XJ!"Ǒ{<~Å[6_[LM%gKku?~y8,:Y\S\ʪ^UW Tĵx,WVIP8I[joo7Ƽ:k $5vWz}'3 -sSV  =^&5gb]NB \ho{ZZZ"EU#vRZ,8$*Sqzzs%ɉohX( +<~9JsV(VMz+R=U)VtqHT0bGQ\|1D@Xo$Xi(Ȑ!4ZIT8$D1$ڈ!tӓe8ZTR!VH2ys,:j03`d2mmJr \I7Chַꮲ\ԻiձUiB  =ȣ뮻6#%:BĮ'ꩧy=-.6RXα҇np[eOUiA%tEXtdate:create2013-06-22T01:33:35+01:00F %tEXtdate:modify2013-06-22T01:33:35+01:007T"IENDB`puzzles-r9872/icons/solo-48d4.png0000644000175300017530000000153712161170337015712 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXX[ %]/BvFV jJ+ᡠD0BD"bJ){ap|_+ѨGߢRJ +ŝUOjL)@-ٜỦ@h01ĈEc !BL,;ִNK+3&'$NzG* /YYȱWm"k@8ȶm4!}EgkZyQfI\::4[ TtB?2\Կ-jj4"p k^56ܤN͡;SZ7]@cր aի&F9֗R =SAoR!nC7\ϖ 뺚x xhmحb>7Ҹ@+NZQ`<*,rwwx1lQL6.63C#fVޡtӟc<&q#%\-q<|.T̼1Sp f"k4h^$" E uI符HFq%}"Gsc7!>_| Γ^lCC([P(*J=[s"S4՞[JguLBjlanpd?sgR0GBC%tEXtdate:create2013-06-22T01:33:35+01:00F %tEXtdate:modify2013-06-22T01:33:35+01:007T"IENDB`puzzles-r9872/icons/solo-48d8.png0000644000175300017530000000530012161170337015706 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W IDATXX[lwfwv9رTRK .iH(҂+@6V<*3.xt^0_)bR"7h%P"+ރ~SxKm5+#pcT2UJ)LӔRbgiܜBB IK-xj!D+򔈺)IlS7i 'ӆ3IL!68^~vz{lO)άxpOtTcBQqεT֜B0 W^pwv*ܷ TJK i̇Qh`|x$N{YK5ldZ_x&)\Rp0Юn OM+QBP$M--!&n_C02 %03[y+%S$C+CQN8G4]BuiӉBםdm1t\PښR&24  DACh㜇AR穊eV_3й _Z6a^eGe.Gt| YC%tEXtdate:create2013-06-22T01:33:35+01:00F %tEXtdate:modify2013-06-22T01:33:35+01:007T"IENDB`puzzles-r9872/icons/solo-base.png0000644000175300017530000005635612161170215016145 0ustar simonsimonPNG  IHDR(YbKGD IDATxy\SWHEPAEAMۊhqT뎊۸ZEhG[q(u***!, a5bHHqKD=8}{!ONN=eE]P\P\P\P\P\P\P\P\P\P\P\P\P\P\P\P\P\P\P\P\P\w (h h ׻렽;܏0<Q Wn~a              z{?^]tknx]KJJoݾ [$ʗH$/^k43NNN~>&lf3-&Ta]\TxSN|1݃\:a$hSL^%LHJdet?dbi4˗$%_έew3t/llqbI߅~6>VƖO.IޒIW/Rcj]ǏigiiJK_YUUV׆oR># IJJPVjH-">*SIVZHg H S!<(:kqV!vϭK}f Rʘ_@-㉘?9wBPqSG`&"a.wwa}BE3doNM=zݮ{'̹'M&o]xt& tO^7|F8:(>z K%y$6s;itہg>3fŝJE=pS;0VCC\؄K>}YfmMX<֤+Sa!hVNA|ɽ&0mN۽z:๩E!M]pdTQT̩ݞYxdeE0C(={XQY/ӫ ^4*Z!~Q J LAT13DDmHOn})s_in4/V\Ӛ/+J~>y"[S?RbQP3a0B^Syoàx"͟z>‹9am^u9H/KONx*Euғ东QI?ApF1?ސ%o#͜滄25V56nUԪ;ʦ ؞qLv ڇ6ⲍ}bQk)xaccg 趹yyX l`ҷ1s/ nVƙG5:BH b~\K&-\kwn+m, Vܖ;j!5|.xQX߾.l6[7S&e.ѱ )=F2y9S1H&O\^^RRyBF~6_F{.2.C sseK,[D3!!n=ԛ0#h2 Ǎ[|#`.-+3;ΡLk|ap8c0!tVg{^wb@n#ǦϜSWWG;{Ďj #R*n(M )7…-?q?#\V%C~6~rmm-u_.}|qt?^-zYhjPkե9;&c3?nga[uE8RYA䬔yB^9'N! xKu_59rs>l@r^'%w=3pFPx}mgi??p8Li1\0AER+nJys}ǂZ8wD򅟄'Od29ñx|+A^}OBQhRs,,]Gwm>(h oٚt]uh5A@oյkzzf;36f0rArArArArArArArArArArArA$x'{GΎq C0LcLIIwdDŋzFcfg„̘ȰR$J^^-~$bq_],.x&T*.kccݷKPY,T4Rj4x( Ν;Y{ݻ'~_35`;4#--#~m[ 60r_-[|2?_/NJJܳ;F!R=g~ RmmEb56$C8a][,Yʲ8--|Ef2bx55.nkllD)wo[^|1KAAG:ujhhf붬/_ٸaSHрdQm>V!vsonl2/oNNto1{ TOZz:C鈐2#H>FsqCP=5Ljss.)j/ZFh@S@FEcLkjn`#G}i Ͷ3llm2 4s'n;8دy֝JCCC{O $6c0 hFt炰y F"F"T*X\ 8yzG\0sй T*VT9999G߽sǀ HS[Sbth^_{+=(({0!}yDtBRnZWu9sTUUa HOn})`0 x,ɩWfcR{;;/_3=?V.p8Y\AfVV~؝u7;vsuuu{1D.4;tR\'2 tH^ W0}E <u63׷?w}7of0LMvLtkDGѧ]Oa"qZظeWQ(6X3 N,r?̺~JT.]PRi3ݦOGrUBO+_u+**e"L& [dx5WX"7cvSLKw޸`$k.f4W׾oskg'!\IIi)z100زŠAo0][9tq0~?ܻEt`"}h_wwggGk+kNyywC_H&,eJKKB#b) 0|}}٘1AEd#6+ZM۽s`7KJJjycF~|t1࿠ٳ&.kew<>CgLg]YO:#̼+HJԡ} >8d&'{vYL՚rL~h1pp)))uNփl(_"xQh:999L0<QIx)UKdo+=þ3a ۽hSE*Yd0.ݣRDH#x94#--#~m[ 6O׽7sLGhVA)Hy֪jS)tnn֥P(X%f⌤T*gϙߪ@3% t+?އ'Ojkk-3qt{S!cq;{ыɠS![M,%W6Z-BֶK@ΘZ(<~~5Z9~dvNsKK˘Ho/OPZzFȪ*Z6|ϗ1џ|Œ |}, '_ē!t4 Xҹz\'WַUgÖbx55.nkllD)woEDnӣG5c['<{h6)n[Jʵ_0߱CJ2|}VE}GW~t""ֻ?9wBPqSG`![=}ڊ:dn\ ! \VAs-vs8oo*cz ''GϷ=ktmF'-=[VȝH8H vvvb0 BH힝LB]XTxL!HK)ë'Ϲ䦦&ha]bmn-Ks_V-дѣGB8Z*p}8 VS/wsA_|Ȕ'm1\j%ytӗ%l֤ЙlMb{CC\iBΝ4`~C[w*++ { >}*u {M64ktܛX20qHt1}1RI{?C˹y=V={ !l“G\PHC.PTT[R=v|Ǔg^FiZΨer Y3':2٫'c_IPTGgL½M,rW0,YYB11Q:5^^*๝_KQ_zChP(RRoO#J6]uUW?3wAU =^5m27X;pGZIOn})`* 5:OϞ5axC"W?/Wt|m۩{:15yےc#~w;o8zꬼI? $vrNhwsB***0u)V~؝u7;vsuuu{1uSk+ӿ\+بn|lqpup١\. Cb>pG2ؘII'f]s%}C.Tf{D"Qr!4^(q] OIbllfu:e{[;;:ʪe/캇c?ɂoj5627L:s.LpɮkVЈ{%hD4bat +h4!wހD(7͗-]l΄WI7O$2.CГ%tO޺͜t.{'l6.H~}8k&cK^wYbԙ$--nyٞם$Rdhg,fu]X .Ϋ{C<26a.7~n-:xFjj"xx)x냸erF֪G N,Ƌ?hGsբŊV]*+ݞ{[k{~ x\.WI١G??ӿНS 𧿌aɘ1AxuiY;U FuczpEښ:|sW~gO8p o@\\.?yeA"SϭPjw=j#}%`LP۷Xh.Њ^'%w=3}K;αYi4jws41O޳nUޡEӮx ""٪'''wVLM4>u,hOq}Wl~0Ww?p88o:to|md}o.l? N/|Gf(<#ejCg8acl?vObl#ğ\]x+aX* 33IIted*<#+Kjc>{K>b֔JJJoݾ [$ʗH$/^k43NNN~>&lf' E/ 5HU͵1%np[b̃ʮM*N%&V5v3bXOaΣ-mY|{.ݣҨmm$R__t  ^P+6dn:W|^N޳h G*J32߶u˰aC ܪesqsqRqaߙMQk27) IDAT(<ժNYWˮM?0.F:MaZBXvg:s*bm޲dQjS*Є344d:ByaLiVA?znTyٵcKe !V.RT?Jfs!PyDOxvxyyzy So}GK.2lT7Urg&1|13#CH`LX,op;oQSS|ظFR~oOν x :5do˒>.\reuxœ(# 6~1ȹ BO `&"JeHV㏮r0)~a*E6kqV!vϭK}f3٣G5c[^ɑ-fϚ35]IKOǖ'yCNqC7kk)Uc\ 5Q[V=o={x7| B</2"88$ID 4i$!zoNM=zݮ{-OZ }eiu.c`qtm.˩alaO,aض&]}tfks<(~oU+,--1 ֝4_yNPОct穝 [xTZs%p&AdrɠjuAw.oVar>Fc̀.{s[&:NkתTVJ3 }SZV(p'!R__(B@QQmFSS{;|П!t1RI{`ک-szR\jAŘgQ(:֪3}Li}fa s6-E9\RqCԠhjH.O+HYl2 ;&x8+B &&½zd)ݦ jų9 WTGgLW0=]]0b9wr+Λ0ocaGځ/٦1?zzPrNJX鱰~6s>ӱS/ǴJQ$9.{ ț鹬K:e/YqO r9BiP<7}8t;FgeqF~>1(Ե&!Ё?퐐qL%Aiڭq˖VQ6EmxRxRY\rŰ .mٞ= D@;!{ݜzuI-N|FyX <*iq]\lT?|sԋvZ!==lƊ<5nP'f>|j͘6LpɮkVLEbv&S'KwdAm ey[yzoO9?)ysCeK.LYd=ԛ`h4!w 2j7﹨˸l# a^peшvvv@z c@Huc=yYBt{I粧"B8M2G=XjYyLhO6.ju~æT"kᬙ-KO?I@G @#=ˢ]:c1K^=oE !F2a_ށMrV-ɵyh_il_sϥW^+Zut{ίm F֦BYûۙ4N߰.hL jݷ@?BcgΩ{\]=}b^ .f\Qryтo/{b֥eɊ;T7ՍUikZ.3ǺDsskwVXi{g E/ugZZ{0M_n7b1Y)4ZM{N!.\~Td[ga[uy v=j#}%`HB =V]P_4Zq-R+D`{8zC \}ƾVZ k#g|>,ÑS7/귰1 ;6 ~ |lF Cq" ">>'auf'2DZ3q?_1]^{= EIaϱ0p3wmGL3AER+nJys}eVM0}8A ] LZ[f Ϛ4M\6ʈyxWnm>K4%³iUEOUZgsY7S~EhW~o'ϡ'a3ٌ?%. 5j źz'S4#r2st2sD.Lxr%-RH2@.@.@.@.@.@.@.@.@.@.@.;o݄/Tw9;l9:#' ",C}ayQy:\P\P\P\P\P\P\P\P\P\P\P\P\P\P\x?T"IOgg%2=arWٲks=eIjM9Ll?8nؒϥWZ#x#!d:uFyW"(*COOA..}p&h4/_IJ[[[fwg_XL:Rj'LG ZސDVuʺ:e]vmnbq1(}># ԫF38 bCĦsv=~{߇s$,^LyQTy"QHtȱ-5j$0?ύ{@rl}x欭U L R=g~͔йaZBXbP9.RѶ]<^^^^}ObfFjDi-໯ҥsNo3,JV d}}fRStGoIQ #H+L'"BEECTϷ8{uf\:[σ7T_Lty BO_O1"[*]0$!تSZV2|!"ÙCsIMMMT{0@3E/^^tL"eM8KL\h^}j/Mǚ+]:f!碯C_6qHCyE!X=`%ג}jg33ly沜&FG)JݣG?lgQ(:֪3}Li}faLլF4iB^N!R__(\kEv=O=;_?e``@ͻ: 3>|DfL9ɂoj5627L:s.!=W(6=F29VV- 9tԀAJZ(o_ccc6ݭ)ػѱ<7D-F ݧKoSfN:=ŗ tDՄ\Yz 'a4wu'uxk! |%˖.LHxe-<`&ɯ]gķfWE[.cs-Oc㹩 'Z:;az=T\FIbaZMu+[wvA}Ѵkm?&;y&L}M_yeqQ^qB^b{z=O69k VM0}8 NӜt=Yx6*I^ xޖ^>fLtzy/ +&>utQy|lA޸YRRVw 3:0̋ϝ9|'0Lpl=!!wc~ o X,+?1r%-R@$p99v4lm[zLA\]?rArArArArArArArArArArArfF Y鷺}پѹ9aayT"^ݎYa55555555555555c/ J~ˬ]YTQq\c]\Fg!#jOVߕ%J1uw=ȥsl1JJJoݾ [$ʗH$/^k43NNN~>&lf- r1oj.\Izz!&ݤ_[LBMJJw%2L՚vÁc~:<Iz0==#=C(xWObf?K.$"jtV=/U/󟋓< ΄m3Bؐ\y:{{vϣ}8_#_JkҌm2llyH#mY|{.ݣҨmխ?ZްqӉZe&x8d(}}}l>vZ>n4)^uUfl}JrvVX狙Œ&mShjn֥P+Y%Ė'~ t+N߻O!#ݜ[qKA|^|Œ l|-Սi\b|<| 566"J;{7SQo̮m`ɵDz[z"Ҫ2…U*VVQ,'Om+]\Xym6#CH:ŷyPlmzy <:̌c0&j&<^+~DgtM&V: 9@4b S!<(:D\6l[~n]3[PU+>bg-ߝ+Ux̛;[Z#ML(v^~8j #o^p޽Cu Xɩ8o*mm@V< {=== %}yDv6-E9\RqCԠhjH.O+ _&zSa+ 7Er̒4#N_`~"10<@ F!Fk+:ݔn[[۝=nvsuʺ=\;uOC+C.8vv^lɱ?f o=zp#\pr|xw d\mg#O]+OIVl*)j ߠ.^8haff66ax[47467'OĤd`oYws=ЫWO칤CvHqlcwߐ%o#͜滄["FS,T;ҝ巰.i LpɮkVü.t2AG%-ַ1ۘbw38ߖ N9>z[얡CsFFFFޞnO9auGT[7i*)7[%b}Fb& @;Ggs5\e\0JJJ[=Oh``Q[dtכrXhPեw-58{e¾kɛjZ"+;$>/kx /tTn-ːV\}'-1=]kڼ/+QtXUS]\|f]ڽHhIYA䬔yZ'5]' )"zrrroՉmߌ⢄℄. pIDAT1{ >C#6_p"# L֫[})/ ͥeeV^P_XIesΝw .c{tPt(fTQ7w >:'ے_O{ŠJBa`pss48wFFDӟՙ/Tr# L)LbJSgҋkZkBgݺ19H>k2 |11M.G~A׫CD׫CDy_z<          zu~?j4MA偽.;DTx:DTw; h h h h h h h h h h h h h h ;ޛ)ASgw%R♚:t҇T?^]tknx];ƌ+Iskkklݺ 1 kI(2Iz0*#Z(_)X\p23 I*X8rp9RvNnRR{<-dZԴ;p)yYzNVMyD&yh5f}& gJOI bCĦsv=~{?P>n4#&TZx2a]GRDy"#Ƕ+fԨ#_ 0(U˗/⤤doo=311FVoظSsr<2~\Lt>H6H2*3ev;((P ӡSN ٹlݖ+7t[fJ>,q_RN: lH!bۏX5 %>kT7"{7x´L'8x 0cFWTTL*  ~1ёs-D?~=uĈlR^J>FsqC5b S^^ATkܶom~ʱE [=`#ZvcnzҪұiӣG5cSI8D- 4]X_qxL!(K)ë/@=ڪ75h/{}whv]slyڔp nTk;it~H#] 8oMܾcgs^G3tZО>u7mG`PpW%7NCC\CYk),;q*L5u$l#S6π`KLшqA!@RQmJ{;w [$VNѤmB {MvQPPRTbqX\pqʪjg~> awÊ*rLTR=1qT*tU]lUUBz1(½"}yDv~-E9|}DJ AhHI>>U__'{b9;9zzzkDohsea_¿d*᎜]$*c_0|ju'Ŭ;;nv}?cZT&+NߓsZog˖;cvޣ7R~e:+?'y:56mаJ2==Hjruuꕫ\k7ZNѼ~ O:_irɟ'OϞ5y<^qL%0F♚(?_.׈5O]'φ(y!l4%M;tE <u63׷?w}7oƓ:~NssOms.xK,r?̺~JT.]AҚ;#@bRR2v?x쬻9JLի'\EҥEڭYEl`cӪ!!㌍JBaF[)x ORq!Jf1?_eNySEa}nNޖ搜!Ͷ:eX3-lN꬏e\g췛S;z k\Qn+ُ"f>|j͘6$4kk<˸\# ah&:o9 ڄy|%˖.LHxe<4h i ӧ)dO<RtW'43D~\F%hD4ba(̏ut]ى:} &AJ$;w78X"ӽ{YtKkl̥yy D6/ J8Iϵ82T"kᬙL.1_VoX'0fLC'cE EZ.--ݾcW_o q?#\V%C~6~r1 56.5fYYRTGX5^򸹶,#Yr􆆆ti?a)W~g?:12ZHuw :()~5?677[,!^O=5nre݅U߾n{|>; _+:.(h4CgO28&zѣD"bvX-hT*JWk4 '5|ד|s{V`Еdڴy˱Yt:NWRz=#sf8::)0UDoJr'u]-*?=?"`#?.]ƍcZНdN?!NNo!nN G3$Id3b!nx;v1  Xv›GH$WV竭g\| G+z!;v cwwկ1:/Os|k%ʖPeXXhXڜDGGEF$[;R83y*tZa{ww78(0`a=u fC:,^Sk.J?y:vww>qz(Jq|vX8׶/c.O{<=EU6U12Erpȯ.8uhW\.4Cinn c,X0Ok%k pk궮\>>6nJhqƇa{c_8i_H+ڪiӗX'Ia.;7!:bl߱5l+`KʦA uӧX>Æ䣒k31z>CgSAJ17wxq^'(\\qZjJpY.w"\87-{՘!ns(qs%zG2x+W/ oL^6Gܱm³O?¥=}ϥh/2$*Sx<@g{֋1ch0"d7-}B''Կ$Ϟ&ZaQ\"1t=QSyGkjjJII坂MM[NUn{Rg_tjݫϸ9  lݶNC !.go { SbN KP ❂mK(hw1+#{fjlΫW}^sc vpAg( ?IDATx{TWo <&2_PH뫊Zc]j[{Ujݞ]Ǣ(V* ABL%c蘪TC&3ܜ̹ޙe6,⇼LzO2-( !dtـ3g8p ?~3g86u}__LK3h]ŨuNaQqYYBhnn1 nBӓ=̇TY=FsF#C<26Z-.NyVgZgWK5cƌ-SU.xi m466.^\Nu%#gP37 NT/~#(g_H۲!޽o$V]Vwފ?I|\wWTݭBΠ[+`DFDˊzzEbc$k>Y,$K|)>}sTJO2*M#]ipxYNzxl6{g ϰZ؃)wly:p7DG^7w #IHLMMOIO;DWuCѦhUX+[Oon?C·9[ϭ; rv7׿bhrK̏&zO.zpްCeGT7[k: }D;VO <3S>07 8p ?~3g8p )+ ?~3g8p ?~3gᇥ21޳d38pT> =8pT΍&Oj6- 3z~\` Agz^3˻tm+΍pUBtm K)ZKn^ħo1F[م,MK>r7l$Jn޼tm ̍ISRtm _X%XcgYg?hkXt>{{bBL"AT:{>rLְ٣GUpA m5x>:tȑttm ﳯ_ϻyEJUjx<ޠA 'g6E΂a ?~3g8p ?~3>F7 8p ?~3g8p ?~3gas1|y#mOՂ6ޠ9>.ޡ! bXk T&Ke cJw'1NܥJ~RT^T]^reҙRL,&[}ntrr!^iEۙ΂,g ϗqRR"m x3/N>V.\P]D˟|BHqTTdT1GXZ?RBhA\:3l^{ܶ:K47Җ AP^'scg15rHlNy%F2 %P< 40 M=I. ]T>Ḅu:JoKpWG>Cܨ;Nn{슅X"™Ntv>b}\L #2^V$RjTLBT΍&iӦ0$PCU`Wc{#BHְɊB(g2Y?vUjg2w cؾ9™;Gn! 󔷘͊(s~ 0~/Mb(LC8Q(\Fn.%P_HƞLCԜ222;;;844D,NK.%-/G8YOuוy[wD#Or(phuM#G˛+{JyE2nʴu SZZZ7%1~y|\%1X:ΌFcLrsiRw9<_RYVa9^|hIqNeUB,ubr._d!. v-Xp ?~3g8p E$B%tEXtdate:create2013-06-22T01:32:13+01:00>^%tEXtdate:modify2013-06-22T01:32:13+01:00c[IENDB`puzzles-r9872/icons/solo-ibase4.png0000644000175300017530000000243312161170337016372 0ustar simonsimonPNG  IHDRLt oFFsa pHYsHHFk> vpAg(AIDATx[r0 ENaiXZ?x( ^枏RgDa1bD!ّ|3[<*" CgxtAgxtAgxtAgx};u-]}ϳYyDl[MCtV %0KLeX9ga3gqt(`^JWQڪQë5r͙{yiTt62jzUUmpVy*R*/KP)3P^3Mj\ i<4JHnn͙Xs ^? :Ã3< :Ã3< :Ã3Bý^tAgxtAgxtAgxt=]~zmŧe`AgxD>328tGEFE+6gzз6NY2Dmk&Eaٞ`xg}jtLǣ3]yl+C3ﳽOpn |"كGyUf7&b4xtAgxtAgxt߈oD3< :Ã3< :Ã3< :Ã3<=J#ןĪ&V>D71qWRj<+OxyU~=c,\w רRx=r"9-}~U3G!k;xg;7 \Ratklg0u6C]abH,q$+ٜI5zX&YO6alw`moB! Qws&>^?cy[y6CN|=WZGRYRK-`'3< :Ã3< :Ã3<B+3w%tEXtdate:create2013-06-22T01:33:35+01:00F %tEXtdate:modify2013-06-22T01:33:35+01:007T"IENDB`puzzles-r9872/icons/solo-web.png0000644000175300017530000004735212161170216016005 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAgeN%IDATxwTE?^U7=3  &P9+״k*(Jy{:XU?jifm]v߭ys:n9ݽ on??V?B,4VSU!g_̏!QO ٴF@u翘sƺg BV0{9xBi^"A~*l&ĝ /b6l  J0 APy1W9/ {)BTUݺׯoEc, E д,0>&v}JK=ՙqm>7!hBO)0se~.X,̦p dE&ny> )%8cd~P~~gɤ<۶JKV0_x55<;C1Ɵ}iS]?B]@ 0hgSJ<7]ͭ8q2i Ba躖44_OmknF24UcBRJFČ޲,04MmUdҲl˲ɤi*d1 S4 9 CdRK&5S;LP!q>;"ҿ̠B:'@ "觜d`,evBrl;>cyaxF(CJLsS=G椈}z=PJbe)Ԅ|5c6[Q-P9f܎L-:k?EQ^K= 0Nk %*aGx~oSa ~-,M|JII|fƾ:B)h77m./k sJUUm+^=dc8SR~jںaCğcWS.B˾54$@)8.Ovgmllx>+=ܝݫjԴY^u H(yg{?$sr7%Fߜq9֯߾߇s:0^ 7 rE@\wﰒ~Ϥ&qB$I|{K>ޭ޳gz~xaԎV<IWƼ0י 1;BUOuW_Ͽ^z4Ms&$ʼܻ -U;D'^ %~ڮ7-`=:+Q 5#؇XZڡ;|=zu#!4Q7_եOaߥX7ǔ 1 BhQ#q޼sz]M$B(̆/eGNXD؜9g{ thQs]98ع=.ɲi 6+LR!P^xrǑB/A9:J))5g7|ۧwXyUU^r)_`wk:$1:C&"/^ʄ<&K:![;tb_w $B87uSNQ#VaJ94uV}.Y/li)BEG(M&HTQ\F r1+6ۙ핒u߿1ۏYqr(@(84;&k)0={ ~|SS :$s4P:?ԯyJW_fk* Q+6>d4FRQ*Bp8lfk0i''LX ׋Jp5 locy6mߍ7\!O$ýhQN8Rb#iB#1\g (!cB(%ohhHFDZ*!Jn5"FY#l)@)cll)432e_|}29 `B -yX>)a9ۙU__Z F4G"uS!B^> eYx^iĔI=*+p$ID)yӖ,^JCLkDO(UHTtMלab6MPB9:PcLY%͡fɗ2lڼ8B]`/B"+?ox**+Mt`ڽI e0Rz< {n_RbYW(H$ $Kݻu8; ,˒4pD"9s5+%%튊 M2xP]]=;S!!~+$zX,-^XZбcB /`lgxaQqQc !^I-֣im[eeٶmpFͤZmeY8˶l6M+)vNq3L0qA|>iږeٶiiYv69V!L 7a?ǜ J׷M0;2fa&u2Y;=qi`/%w摷,lP@ٕ7}݄0%#431q0HL%I۶W7zy4Mc 3 *@{l9*b`l,1|\ QO9ugΝ0iG/r%6%KM>mw\w<zUT6mYgw;/ұrľۛNd;oNVS@`>WŁ0ٳf|K/g_h 9q*Osϻm%I{|BEްac,z7]r%I:&"^^^~u =n8.5M HbKBΝasϿtх痕0YT 4McDz/Xj]:w~GAGmY6>~]<$Klcß|+**2M&hYYD"yLT(IҾ}XO7~7^eEQ@oFpӷ6%C oÏ<&ғO<ڮ٭Ix<1iS&M0 QM4 ߯BE9gCxgߣG?|1N_Ŏ ·'rwo[bMl߾GLj,3| 9Y&vOwig$e- 9~رΣ(/epBӜs27fD0y#;}л A !%]3$FAZ)zfbS-?ewk"EQmi^` DEEQ$$ւ(/IiMBA$Q4%1**(ϋ$xy^V`SX%Q8C$I(1lBDQAD&0ęEI'R>dmx'jtclYRAk464VTT%HPQ-˪L$M,5uMMʸ (x)T]]ԁslc4ciU7p*b*+ q7cƊ]7 C@PUUjkkTXܵ""8E9.mg^wڥ;7 ңz4M`$JUUpdg09q޳x[Ƹٲ;vB!gzpݻ **\QYz(2gƍ9Bm(O:%LBG/i|T4MXdퟲM w'Zaz%8 DǔPJ\*، qZ1V28!k!?B`GfZ(|43*L LjZ܈pҖB43-3/i.3vWakScSeEQmcV5>!8b&I_H$ DeqF$3PUU}>iZH5BSUŶq$qĀI613fQ'PJ^/t+ '߭]S#byysr0;AՒQX$;1Ļjire#2qȓ7ニvR+^ڻn9zl㧟.ݫܹg;/),_ʕt<ܹ:|~}Zzv3"z7|K0:uʰC[h| m!˯.]\΁0T:a'[[(?޷袯. #3VW^^|y-8LlA{_zu7(k@d^̏pp%x<0oޅHW_aVzwڵkg:!ۍ )O5+ş\rP@_xP=<܍?nBP"5ꤷv5WRJ/|[ 6}񔾅}:z8rwaݼ.qQQ3Ͼ={bqvuzAN,/WjB*_}'w>(j@ӡ?@8cv߾}t] #eҩ&/W QnC-z٧}{жmeGX+DP)++=eX,nD(Qxys觫ܰв s`xrccs2 8Dcnj?w{AeP KԒ]l/._.G%ڄ 1'?ceq\,ojl4=jwE+V|y:3@uO(W}sCKv"qs #\we'81鸿;{)R__%_z1[s ]Xq^5 vС #!Ym]]wSYY{ vr`'?ڄzXeǾ5',]ɹstܩC 2>^{⢟n 'x_J/с_6=|"%^t?zㆰx_BOk6RX؆TZ6R6|lx!1t4k !K oq\,"wkp<ϻ1{SO?km !!dݺ#GDjJUEټy޽(sv۾c/OGㅘxQF2ۣko88`@ǎ_(bEeG0/ գ4`ɣ1f0}h,Ooccӈ'@Y۶KKK?c0Ǝ]2Njp8e@`Рd5ʗ%ͩBxauY4-HbLw0SKZ4MՌbP6cH& ֝ 2L̳Rxnjה.]:?'۷/Ka-k)&ݧtY9GuwC\G쉑v$gNPܦ2yv8ߣ ^썃\/:E ) !~/=D!c3]yeͭw>R@%Nk}mucϞTwYm!]7˻,^?X֣G奥sM?6f şl۶?v !Uvxc+g|1\r`$۴&x8Yw]KOd]߫lL'5iM7.;9 %_^=u117?oGy H|>!2dBL(yk8n7MF*)iw[{ݵW rVݙӧ8qaaf&9@]K*VhlįINfMN lǼs>;{HASbspJXG-΍U'jBaB%5g[d-M?yx5 b`Q#CͶmoܸɲlB'AA8/(;TPANsz}&Ӳ^Ot KoM4!uŗ_M>kΝDrѳI|FYO|LE+iT:Wlnڢp )jugtu~sVrzTP\N~|`_~V 9`S s]ZgjE!Gm9lv"^ȚeYMM-Z!++a?g!B)BPt4.@Z1@1+4'2qiɤfYNj??}0BeEѬ )gqG\JMV~XV೻9Pܑslj$HtNVHi`3 h%BiKFAҪa:#g4kEBLh2?Œ20hD u#4sG9% uKσK'mp$bFZ@rE΄RJcdDUQeYV8W,˒()b84%IRUcC~UdI$UQmrAF %۶]__]нP(B6}rt?#!>W[&GnSӂ|(yaÆ2We 'Np-(!;Fr9gͶŋ1IU9MӴW9q\lr -_{͜* ljzJkDo9Õh M3%` ?jEm;H&5-F ҆u]k 6eUa4k NdRW_ 6TX+'gp>bb!g6cTfXശ*i!Sst8)F)ŴuCpZx#LgȐ Kp/ؓ`A fsl׮! 49 H(|lbŬohW'f b8$N ^!hv=|>a"L$nFGA zWv}왓'L7xsssC]q}fΟm .YR8yx'[xDɉ BIJn/v1nԝ;wעعsL8.ԲwN,/W]yy~}ry`DžÑÇW|M,;fӣGN8>z}@@իn\Yzzi/o{E*;Ofk{IT] .W:fT$N" {~ B,{;g zᚚ\lN$^tyUu(_qM]](:p)cf%|Ν\!ndc{1CIꡀ"{ Bg;vK/jTp{u)A^d6j\̔UU?daƦ)O͵%s/L]}߭UU_ z]١ș,]+2 -:nkh_~VR8?Jo١?6?;*\ɚhz:7sD1W˲zwk/*ǝHڵ|ѳ8{i3 Z|0 yVA{= ѽ۸c!৷<7Y?[/ڏH/Ppe1+'W,+ >or])G(K*JK;t-/s{gLO=f(λ7477{rvM~'=x,K E :Ϝv ~B!.j}6laL@_g7QJ`@XX4zWzq.q )m >k*K霘a8,7nԪDuς=|v_9 hK@`{}Ovrq܊gW!ұ~;û(CEn笴ivqQ.aeeK$(wrCJne:u2x0K! 2!jUQfL ABD.HFw1H -Fg 0dG`g#"hRljl0N,OzeYLĶmwܩwiVp8#:BffHEV~4NR[[ĊVN4!*RPP0?W+.蓅9UB*p<'< xQLW) !y^AU( ϋ qZ B(B͡'7a'b@ DuUs1CpKaa~! #a|ݻ9]6 xU/AYRJ$ݶmٳmF aEEE"ܹC! ;#^N'!S,C'bb?|'D$Ĭs'?yRɉ ;%z}Oo{P.,^l4qaa_{⥗ O=1~\CX-ݾ}nݺvgU y Vx%%yoi-,#ѣG=C&Ƹ`Ѣwuo}}~s(%2ظ海!z|L+*?xŕw)7Ԭ%,&i'O*촅nDli*9B:(9i_9Ou.dG<￵5ݺu{We̍%3ODz)e kl|WT:vE\4P/mۯ:zL}.cL~K:7c?o6\"7ٳ埕+, BN({ v㸙]cP?۹sבC&Ό,˻v^d)?#}P<֮]}hkS^GҍsӉx]M +Z|!/Dɜb9u jaUP 8'~~o^霴9G!DQ#Oj߾E"Q'ZqQYӒ|q xo/;`YD; ǿ

|iβy> %!_␋|jO$9>!C*V^z`1xUUՒ,Y"EQBeRjYi!88љiYa!Rb+=[oYJ^S-4LWq<#R˶ `jYJ%t&f:t{:LF|E.2xPǎTNؾcӝ2T@ 6vcǎEc,۶O>uR4W&ą vL]خlM⋯45 )BeO=3L~/W NdB$^w _zeN@i=͡溺zsi $ PpM;* $m755Ը464B9b @"0 pmms6;΄558^Pc*sJ) ˜J2KېT 8-~"G%by)y9'BSt3;g,˾݊-'-sǡ֝(,Qs+ߙgQoVdҧ|gP+ߙ4'ZX2+DͤD5knWn쀄8n?usϻ̳]rs6*4~wvԩSf̘fZ1BhS|[i 48D_~r7x}׮idXp[&NO8\&+&Dzx<޳g[sӚo=O֙ʾ]U\\|ݵW?SonAAA/rbE2f1gO뾙ϼXr c<~؋/O|9 p" >?+((zdӴr8Lq;}9^#c8rWO,\4d'ud>N{%߬Zͬ>tKE6ENV-}J$V$i҄nMVSuRcSӞ=:v,{8`<6FL1;9$@9\~}}}CKْ\ P ݻoÆ` nyPm}ycFR)aᇷy.Ep~Rޥkο  ' M]M\O`Vw!ƄӦ;gnkkkもп_.;uh8:D"'2šd ?0i|l.^HI>nӅb%K{<ޜa.r{tׯAqq+\y~i9^\x7oԩuo\W@rq+1È wwt~YN[q [{%K;u7x}2rnxY"ޜY[e3xX@ W+Wj>|oϞ]t...J&s0,%'H۸%Nj'ǭD/h4vƏHj=w| ɺYUҮ=]WW׳g`I- !԰7O{ON`KI,vYg4d2ٵky0p&s޻.X,޳GwG1" kq2AJHHG)ͪ.ҥCڼTzu]jf gm)NX hgOGhrIfٳ'%.!eeupdYe ĂJ0D6Yp9WN'rxRiz]9u]Ӝ0ԾPeаSye^oz/Sa}R|̊ !<[ 1\ 1LҬ\t|!LYS陽q"7 IfH'bar&!!k\mt"gauPY7mX ڶ턫P |SS5#/x|ݺpXwGDQ>F5M5mٲs$ :Taea6li+8ꖀ*+;UU6L'3=i6Ɓ@` ^6ջw2er>`$IO<ȑ'9"BޱsWCCqc NN4!V^cgԸBiSF N۴iǟ,ʩӋC-+ % eiZ#gAA/iZEViB ƄH,[Ԣ S˺c \o>Z/ h9iHZ8JGOMG%E؊4ɖss+e&c2[UU$IiRf?\<s|R\/Կy)g` TUfmoڼ{nz4-󆆆^-ҰaC%Ir Ȝ5sN )r'?oj 4?/hupֺ$'BM6obY֠AeYrw Nø_}'y۽y%bc5|E@6m:F@KTSh%۫%_B\2oݼ⢢>}{?OcR}MZ\ Ǵ?yXPgG}1@D|Y]aJ&')ycW矷v+/悷8 r}4 [ "k̕ KvW~EMM{wm,FBՅ}=~ _YQXbII;UQnN`%?SώD"Æg_?|~eY/G/TUUEBjӢtvuzݶn9J&: x5W=SÆ e&~-e]:Sr"aGzsϿ!|/Oi8+9M-3Ϙ]XXXYY%̙]Ǽ1ƿ³K-fIII.?XyxՇ>yh}QEBM{ϾƂvUr. BhRR$m mK{_d NX,z٥WVV5BΉ\3Db3gL`[Vs8|eaY3gN;,JA<wNP@#fhXQ&N8~qH$OlF^lhA0#fԡrB(>{cY X,H8Y fI ODѴb֢4ew1R>TZ?Ñf*5-'ںs 82ƧNi[#rhqMSf3=;py/qa躞Lj_qTa DY0קh>AGۂ l@R,wπRb[n34[5g)"JI >]ZTC6mhȗw?oU_o ]xN%tEXtdate:create2013-06-22T01:32:13+01:00>^%tEXtdate:modify2013-06-22T01:32:13+01:00c[IENDB`puzzles-r9872/icons/tents-16d24.png0000644000175300017530000000155212161170343016142 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(K\E:N}~&3=IzFA{2!!R\Bp.nE\7Rn FQLGbw:!=}[}ppO$57;5 @Y=oT.beZQ[TO؟ׯ8в]l//>z|ݿF~'\]~:eQ@29ܟ]L{t9.6x"I>ޭ).D?L}y+AAՔuBK$la{xs|m*T*@d(s]6~(VqqbBxD)Gi9e{~Nx4),ǰ2ξatq9<{:َgwQUQ.!ӗ.xtsw`8 vpAg\ƭIDAT(υRYn1D,p3d ͓Z5"$13$Y@oDԑQ YU|"Pw?\Jc1s]1(F0V[鷥No7&.54kQltg,3!yo_;1Q{@Ѩ%tEXtdate:create2013-06-22T01:33:39+01:00m%tEXtdate:modify2013-06-22T01:33:39+01:00VIENDB`puzzles-r9872/icons/tents-16d8.png0000644000175300017530000000155212161170343016064 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(%MkU=̙3NG͵jRlUjAW,pWE;{tYT"qUlR(IΜ9 _9I r=0 Y-ж}YSۦyER0MWH2^K<1KZ3_o__}7(h[}\ZE1%?Vu- qZ{'7n~OeKO7~rc ,,ː php>=찹KkS8nCąQ>TҰfEƋq5RLMLJ8=cZ-+W2VbhϿ~ٵ,eȂ( vpAg XIDATHǭVKo]W~sa'N4vBU-%UBbI0DB FT1@RhRiY'v|cĹ{iP+&t {Y[ƵKDg'M` Was7? f PoU3N2H$i& zllʄR Q9zXK+mWF7*!~{+YD+y2HDlv׮ݳ_~m߉ƛ=<@ qݩqT!YkTkm!1*FRֻw>vucw)MqXmJ)s-MϟpzUUXX+}hj32Yc?u D^Y]4) "@tWwqz{h7MҤh(&EP"vzz*v`,nbljݼ,RN>Gi{Y=8 `@P|@eY...,// Ca y5A12W"96NѽV=wF](#AESaBDyh37?X2ZTZ7ӓb߱I!s I(㾹q/ t,5=" !TT!AG(9 vpAg 6IDATH}VK$! 71 2ɅO-=33Z7}pwD$"Mm#_oj; lMy-FFeKN~K3U(&`PN%3z.(gn)z$9hwV`ն]sɔ tlSH2*9$Eh#d')* >pLlnAU먪,ĖQUUMK{f0S;HB1”)f#3ͬ2 ;{gw< z< fmʶxZ}BjumMnc{шxve{KWՐ̉bH=.ֲܶEH AͩfZ*Nϩmzx)0;EU]O#;(/ITYpn Ea& ~ݜ_nOD6)&X8/W}Qbg63PX=yth N;Iik Rk] ;U, 03=mUxJIк*SN-rmP`'=^A\ =).4*'g5Ԕ^V=g'STNzQ|fHP V#f}T]wM,S0QMmLe8ND\ӣޯi Snv|um.~URi_5ǀ4g 6XWmD^JȹeBb_wѾEȷpq^%tEXtdate:create2013-06-22T01:33:38+01:00'f^%tEXtdate:modify2013-06-22T01:33:38+01:00VIENDB`puzzles-r9872/icons/tents-32d8.png0000644000175300017530000000376112161170342016065 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg ,IDATHǥVo\Gٛv۱qlN&@E#U* >HU x@*!AyirH4q8Y_{Ι37fZ*9gF7~w?5rkxqsEunBoaSJyqk+mnr1%$9H&djz /QJZ Ώ }u.hߏD,+ErccOШ&[ITk5`,xݨ S##o ŀ YesG(kݍZ><כhF_rBg5޹N bo ..7 K.h)8 c>^CNp[Bmp!"ۨ՝r>M0C_gM1VV|l"ny/7P]1c(~Y{V@vJKbku!jo$hͽ;~8@A@+*ɤR*P~xMXׅF ޏRɓRAR#Lv`ffZl8,(.r\E!V:7N=79>r[gjvd4#h}_I[j-rw!YsN\+AȻRXgbY(*Q /+Zp%dƞ$Edɇ]KʎLvhgEnaJ~nl&˼YקQ SɊ,2q/ogYFCp:.*1TRRN)XNhIE$jO*8oKW߻iɺ&×Uz{p֝ 0P:=0~jhd7I|&>VeVA'^uʩiPpأu,.;mWȱ9S@\#{3h=~\:*:Z/OÊnk{\Xg^x#b\vGr3c=-y?N_@Lʷ.9f!"^)isiM hL*&iP̩X$ ;UI&mZgD~裫⨐6/BC#LF"*%2rj"[g"û T6<- /<8iaCߋ#QIbY8b^*!dT9{{"rjc%tEXtdate:create2013-06-22T01:33:38+01:00'f^%tEXtdate:modify2013-06-22T01:33:38+01:00VIENDB`puzzles-r9872/icons/tents-48d24.png0000644000175300017530000000547312161170341016153 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W vIDATXYTG?}fBew$HB)CWiS?@giY'*-YDB $̝}{̩Us{zN<~t/N?F%7< "*DtNR1ߎraA.khU5{D RLHǻQDH[f5RM* \~>c j%@Ź0˜.!"eW-^ CNc>.^ es6bfRȫ^Wcw޶TQ.{.RO&KőwWl[6ʖgsSse>;uzّ#KD:{cFy͹JtMO?vBj6E.I\Se D&|ȁ* L>1i>(ztb%CUn/pc_ظ[m Ūѽ >bCd3R%t__a``@DBS`/ȿj8=zJ6ώ/8XkEWyv&[w^ ν) oW23ώlnEkŋ~ē?JRt<|߉PaE%PUH9I$ޓAD`hp%5T8"J`\<%'VTv_j+/jƘi̅S>c7wkm6;<|k q.;zxqglxq:;=8rāuز(0Qapj0 Ϟ}૯t㍙LFħ H|s JT8buC~=+br#U]R $/$T8|y-B0͹ylfnмk$/$IVZЃL$x`+~-D♙f |RقlH>(%FW(ˣk,՘]tfͰsHي+HW ͜|s;W n҉:<#dCUYP25ydF* ȯՃ k}6˚4yZ!͚эߴmEYmRۼ}lR/?Nq4Y ٴ}TUIIE`U-=^Z͔ds j60 aE3Z*?;tRIyZuk&lZZUJ&"e <>}fpp0!?=idfKfl`E<3}زΛ^;*u !"2<^~|>[AN<)y/5+{"rA^B407aثJIѵglԶ{?}Ǻ-73g1fa=O'd=ۭ))\Pt8~ޱ+>+G+kZji >uhgLU>p}dϒqTil4##塹Nr!䳦ii(`Tx|ý*zݣ֢u c}pε퓹x(Ւ$n!"Qkس q %%&!Ӳ,)N3TƵ-R7v+ou ]HJAh,sg6]]_սco~vM5kBX"*d)dǪS|sܟV&'\3[w|_3'w [MW{W![O8FM 8:HB!_zA!U-zZiU=yiZHWӤe; 9!pvh͚ᡡ

]Z5L8j\b5W%ۃ(nVr@/fo""qWf`oztśnPF#&9@LzjGm%tEXtdate:create2013-06-22T01:33:37+01:00і%tEXtdate:modify2013-06-22T01:33:37+01:00ˮ IENDB`puzzles-r9872/icons/tents-48d4.png0000644000175300017530000000167212161170342016067 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXYQ %=WBw+5M:=S>KU/7""KxY% 7mj>ddZ6r-Y 4mC)bz)3qmImF2gKླGu})àg~r(""Ei~2͹ SPZ3FAHDQQKWHmDlFX~y3V5bP kpm!θ?%wmV"̸=z.\ٮCV&֢ .0nGH]7{b08ԌUQ'WL^xa^1H}GYk/ZӮX!^= `L?V΄O yc!.;l w8VT5JoUpG، ק"Fg1a<=xOfN5yO/K8$"̬ϳ8$"T52ήf_ӝ۸7GfJ!QνlM~)PϏSHEj̰ՙXY]oNqd\gAg?ff{ "H[gu4;@pZIn͐Tl#yyzZn䄺OB kӀ۷EF]m"}1_R{ªTk(Sb')l:y$rk.soK-_q) &@]%tEXtdate:create2013-06-22T01:33:37+01:00і%tEXtdate:modify2013-06-22T01:33:37+01:00ˮ IENDB`puzzles-r9872/icons/tents-48d8.png0000644000175300017530000000530212161170341016064 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W IDATXYYo]p;8؉gbӠ4 q"DAm"*K[_B*CVH U1C CR:\;IL|ϴ>{=wpZXsYgkK>p_%G_-@R ᗢ4ƢM_`Ч\ƭ3[M6B5QUCP\\J5JT>/lČb%\JޚwUpKXVlwڬ( \|z o-Re(7r h}˷mt_6",hr]GF$ 1e3X^~x!eg֑kZCoFuX87=^9#pXv% yU5e Xsnn>dԌp8@Gr!s]0*)SU0? 5\^\|f5\2Vy',w2Ҵ-jQAB)$#g}W %yl_d5ʸgJKJ^!=AHH TU -eZj̵8Z_' WqЬum^ԺfVT{Zxٲ{X(p+qWC{+ڸgj~['~ltpZ]>aKY~gm[2ƥ)@ j4Rʦrߊp?ڜٰϾW1O~Q6F* ~ Ŝ.N\(z3=~hoOm92m6-)9zv_GG3h(URM 8eߥO=w[>FYr鍡HDr~JeJmAԢPlz2PDԢVSF4$D uiܒ6Tf>UJ)%6ABu+ n-#[6 T2.E%YۦP,2*V5xdB' 1iKF) `Q_DCmɈGw=Xc˳[~ߡG:<A~H[@H*e\;bi|0 M'ʪ<`HקҸC+cP#1,&FwCP|v<JZ-tšL'SO/lf2jsI]-tt.mًJ14QeiEi%]Wf|N"ҀH/Ohc q}%N K[Ȣow&9 #i %sՖ=I-+qJmjo1'=-[x@&o'<*1%BKz-C%J&MѤ}B|Ե>o"L )S(&=:fl桟i$R|2RFĀ3+Evu˹CIR 1&bL IԢ26N- bf+"2u U"()-7!L,ڶoz܅\{rC=RPZb^=it]MR.XgNGn*t,čiD֭#7njR e5ϵ=-qPv }kua?zta9ڲ:^Y̪2VFkDZr]#-WTP[7=i'ǯ~t{̝YoT; mjBHmXzyCcc 4Ԛ7i?1T$TRBRjrI!ڒz J%[٩ = ԦFkGiђ=iJx(C6_٩|5퍅435SK BIz#fcU"Fp+4+׎D{zR: B[ۙ+ ֌378Pi>]84X,raO L=5ʜrz-T՟Rvk>/Ѭ:>>8 LGo[ϝ0_)DZϝڵ;Lǽ?_Z?, B?7YǧACkڿ5 J|N*]37j<ɑ=gR;w͒$l]ox?{ߛ/yƕdff*"pJ g?Oи`Gio]R?_3 4=؄Ƒ>cZZ>mh 5Ÿ^'a]1S2FF^>f0,}hVԩR/83F{~3{/;zdۆ矨+?jjj60$lK󮜹xN Y94onVN4ody'.=N +g.}koc<:7C~E?D,P֬/ͻh|GvZuhs(;iUUޜ{MSWJɟ0s߼{{ _Yx>ijoK)5m umlBlG[QPZvŸm [wD+_Hu;g1:_Z>khhlhh`vvO?e`4mg'a\M`2cEIU[o|'Oedx n?P~~xQ矐ʦO5M!7/vF_HCyx!u\l3Q-'g}?я(O8~Hq_K&w-1? \VnI`GF0 'ѲS.0 vq*4:Kho\}|]@C]ܿJru vk*Qn=]ڿJq[; pȾ!ֿjrVT[or-W]QYDoܟ ax_W2{U7+/tWx_G m˒'Ou+J~Z$ZrO,觅X~ZF駅`~Z&:Ѐ` $~x\ߺ E迅X[%4џt[ON:LA'; L1'8w0{nLJ]C[cO6Ic?pJ5> nЀ`Lƿ \ F%4`[`0L-07迅J?0pT*•R[ >G[ >G[ >G[ >Gob +o  R[ JdD#` ޅ F0 @bKO+}Ǻ w`\>,'mph!{{}4Xz/w7wKe1h3593ro5gO в|&jGZϝ0ߛC?cgvE& ^dǮ~`Skrcjcgk'/))6?KezqTXS4>,+W' ^TXSO{Sa}^`He_hݜ>NM#~`J`@4 F0 {c}8`@0 F?0]J_׺ `@0 F0 #` U}}Æ77۷رp8l4]q#``@0 F0 H~`cpAk nЀ`FCw(y륾otńљu8L˸2;3rKOw{H93J/C]kڻ:^ԉg7x \gowR^T@-Lk IJVw(szxrrCA\&hn햭@2.l; W)DZϝڵh]Qk(.-`8^=QxK l!E) n5g` Cf/!ҵ^4C-T>fJ?dH/c [h^mGfҽ_?=Ǘ=2{^m`B4ʙ_oQw3wwG~;pwdyK.-8eO|4`G#y/_4<ȮCmzc_;"ʛs}mFAi㶷5lYsՓ|!שyǬF*pc |%'AfzF&0aY0`7%d`@0 F؅N-I g`@0 F~ 9#``@0 F0 #``@0 f=6q߾mǎQF|}>^w6ל9Ҹg=Rj:-oR{Uo#M߸n-ZƿvRsK}۟jvnZ%-G=QxKnjzdžmc9m_ܯv҆7^7?O]wZ;ֻ~^Ofҋ̏?@GGǔi7GOKoV o) =*hjRjɱGM:|8y TNΥV n/}}DoӆƏ u9G~Fuvv>[n/}}0&XakN:Y)+k-zH$bŰ~7OZz,?geeM_yTSS1srrRcos45~m]qpX=6]B{<:ߨtG9pRk&z`'O~]AAakX{z}˧===`C_ vpAg@@MF&IDATxklUbeV X[QIf͚5[6owVФ1"re5ob!xk~oTݗ5xM5b(y]~ -'(R1z]ٚ)vdbh=~m^\\sGí-.^h_߹RW0i8%9dLV*9tW*@$T( ++|JLN {:),Zv~{:H59d]թyx4ܢKw>%F2>ex*-9ioo/_J}B,!c(E*kgMz޺P{3B5:{F %%+|1!Czk'ǭs1"Lm JsYLoN08 %]]tO6.4"n뒜))rx%ۺbŠĖ/ZSFz32lX=~l|rsϖ>27?ObF ӱR_\2u.$I(yɵKL?=~4747L oW%:CPL'񭻯di1Ƽ-%VVSݾpl+W}>o~~WyyaZzO)b;o+JVH&?KYN z{"ȉqC$Dr0"Iv7MAݍϋ"ivjڱY}YM;7Ǐ%@azYaJz  9ӼCMoNyޜO59:. =sH'I޷+XOuRp=g.xjc?j6O,'jC!Yv=}PЛiCAo] zMqh3 F'w* dzó]?NO:[+SvS>)A+z@`Ho"zp`z O7,PoXްoVoX;7 ,m?7 zύœvs#0]܈*l}.n0;!A`;i(6sdzao8 a +zύhs#0܈ݟр:1mci熀omS:4?w`5џ >i@Moz @y NPoXް@a߬o0Ex}~&WsXްzނ#J^џە$]IRџە$]z vpAg@@MFRIDATx]0Fsz_fg4Gc5"P]_2.Z[ |ko-Z[ |ko-p͍O<8yOa=Z|~|RJ.>yOYlc\r폥=s%䀈\oanV!{7~-itZ=Fs8R0 gal߯beϻGsNU*u0 e«T6P/pV&Bpc֤~ǿ_X^7ג T._wV\6zgo-V}|=L[|%M@O]~}kkZ[ |kAko-ւo-ѱY?5-bY|M~~H4YM:꿻b,}`t6-5;~ko-w.[ cEr[=y'2i|o9Z[ |k.?w^?ZP꿵?o)Z[ |ko-?1a <}6|C NX2i7f0Njos_p .|۟7\M 1hx~mpt}c? o-5.yZ[ |kAko->П3?wos]YM?wLߏ ׃o-|ӟ_|ӟcss1swIms|osG~`[ |ko-ܡa~ko-^[kzZ[ |ko-C #RohaZ[ 7LVyF%tEXtdate:create2013-06-22T01:33:37+01:00і%tEXtdate:modify2013-06-22T01:33:37+01:00ˮ IENDB`puzzles-r9872/icons/tents-web.png0000644000175300017530000003777312161170216016174 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge?6IDATx}w]Gu93Mn\Bs016%@b0-? 1$$@h @l"ٖeVV*gq߮Veݕd|>Oz枙33)߃[7? ӳtdL 3T 0 `?L`8OlFk6u, IDL< v.4UfLI42# eY{~[sZgEfRN2mYCC"R{3;Reg*~HC7ehTjϑf &DHg}""«BS)`@f&``y9FD( 97n-}}ifBdbp}[ـ 1xԄmAtF_;z)<^j=g#ۀֹ[trs۠HcC}c dFN@Ut%ûLaܵ1 XftfnZI`#af۶kzƍD&ZPDŽ !$ cW_fӬ)nHO\D?36: !5hԂP $3$&Ivϟ?tv!3  =O 003043~/znjaN3vfE TmdXQtc'O(A6F Tjw6krH ƧfE)ș7TqlOvѰ? c~l M %H LFȴ*&!7_}laT m0-4]Ҥ Di^]2)8-aJ61&S{Zm!AZl0 &p\Yⵝ4̐MaiE~+Bi P`ofnFDLk/)Pk6k%yƑ6wd9,"TQuDdx*Pq{t ՆL3wl y{8ұjm =ZwyPH13 6oph]H(I,LGV;16 % @sA2@Y6 zVl}PA(IfNڱnVԽ_%:BqvnT{Ι&/ص̶k\m[&HCԇ+>@H_K(u65FH[<ɛa=t14宍ߺIKsmsǶ]cD(FArޥ_֋GT5*xU x UT1"s^bY7s6_D/ )>`D i.] ל==tjEQ1@ &{{>^:Ġj"$TBP-wыO#f2Ŏi [wHpSgŔD$#P9x#ctb~aoo8Kf< FT\?R: )]B~DL}G'˗IRDIMJihJ!k:(b}o_]{ky]@EřCQYvɵ.ɾ -uY%̈Qd5 6uM P $TDp G* t4Ah8o3@BfxB! ؅̌ӻyb"Df ^hG$J)$62fMQFoà?nޙ^6NwھABR4߅L,ǙE#I2Sms?5 @CD @D@Da |t&{Gs]k.{Put!"00" c e;z{T@0Z<з7qzՁ7J|$#cfmvf(l_ymlp&OEoL@#%Cb,S/ӟ+J !m3L{96mB¶Q1Ygt3(Q!;D!N"!mo3v +Ԑ@ 5_}y_څ (5"6Z 5?Kw#PcA:P AV1%\֦DN̗/9i_5K/hrBjo j{݌Ā4".H>!ç.^p\bħ-y!oضmVkKq?4d |㉗h)qn=`p˯[~|!q ,cnmlFD?> Ơ]t)BmsO8^S%N(x4 ø{׬y%vZ1%#sׯ$\bTay&O8_77nX,?O~[2r:Sn= [:u#i|3v&NǦQX?~1  \4NTU%4s*L)a@>0Ll;ih v^BJʌqU F La@A[-n{ TB^yV /``tUyW$`e~p=M-xdI3#Bfh?zWR a_$Ra,-SB2S6?;Tq0IfΙWwa -VԜi?ʰjtmwwEoh$2rzĆ5; 00؎չe2}"23?wE[}8+N#?cAy8tb%[z.o @6( (QPvwF~2wE7fܢ]l\Re Ғn޽ip64گIsٹ?_vh F"dӒ6w֒V{ƎkO70N~)s*)RB, ✰Uf*p+Nrp8ۂd%f6`F|Ȑr9}43mc Ԃ/6WѯMfϻN,XdYG*h8q~aX>'Gfv]n}:W#9sf;+Cd(-|KpS˥==$!* Xf<~Lam˞ ϝ;_ժ5c,BH҅!`)PBڇcssv92J9Le/o{aH0аR*_x,x Nz ax,bf#!Y!P5+˟8}1v~kB,L?I59EdՉijcs\Pc 9~4^W@f0Z,yoɃ}I%)r\4vq_i~1!"oG "9(  1Aذ]!(@b$QAS) $fIEz`r# әJeHg&ƌ6;2{ax6`.92L!0*ژ 6q ("0Z7JU{ ,XO[7_t&6(;2p';ykû휹QV&e\$Q)3e[wq"^](09W..^&~dg^lȺRzՕOhTټJn}W_;4<u3|P蘡r5aؐȟ~Q=2GZ#Ŷ/=!C2 U<[)iƮao9cq@p\<ܜ -32$lhdL0Tn-K˳= /v86s,z!` HPY B0dӾ9tN!Á_`YIڲFL9nqw!$>+~cNOtY|K40]u|tcnO/̸:.O5M0:GzAh^c% Ԏ^ 5zxҸK䀴&xn9`e @i #4kPSAOs4Fl\~ RI9oڿC6 ) \s-=MXy%Ibiٹ4{%#=`)w#@a Gz Ο|d}C䒋"`L9'q(!f y ?2P`sNN, DVG}?@OgSJE Nyo\;5ۺGp]gx*:}yw 0-8IthE#BK|_4l=;z.y\QI,ǜQ>[߾ <ߛ! ϡD`U[,E눮`5@B!DE?jIZ$y&81]jTh%Qg#f@ۚ$YERad1Hbf4萙9Q (_&"8 lY) 11bsf$7{F̤13ic`&$I?OΤi6ypc$)2bZ`8ʁ-EFڽmfP65su4|1Q u3-;n"eD̽ |!#B`04ȁ57[Sl[695RgC++9]]dF5џ4 sg Thq߲%a(}6 ΁h|#z8=_X,n޼%f2bܳ Io۰Rb`}خ)z{mᏆk*;;Ni~ޯ 6}b횙9koU,,״ jewtVv{Q'bB)2!a׮[%͹]zk~- P\WW aO " y7y*-Zۇ̲oL muQ|r'm۹Ж.#s6!0 _t%7C6tVm~Tw%kKc*pl0<8hzg-85okʫ/,_9i;[^掹+hxafO﷼ u$22" SdC|VApS_83r'3@{L  ꟮ў/W+:ntz=5KʳG@=I܅KWUh{[v0]zX,.Xpҫ^3JUysB?v~ϫT*1G@0N,#tKx{ZY^`1i9Yݶo߿Gg~7Qp6 \>$lء"SӖAֱ&iH$ 9<<<%0@@"5ϿqphhiDZ)jީJkMDHY2)2ѠX&jE̬RJ7Zi)d4kLRZOL<%hYfuYT,, fL4SY)Tڧ\:ہ0HjokET n/‹/eN(ʲJT 90HdYr i#l3# 0s#!V^ fnװZ۬B4s[+G'Lsǟh)FEH\V$#wL5&4Kg앛8ܺ[BAL <䣴8s7ۼRIM9VjiB̦Krt{l{yv8y{` -|62l޾s9{"yoMk&SHPZ/ bS'dΊ$PMB7 sc6P"bB 0K_EQlK^:RfZҥ\:+DeKSBDb̆0"*,S_]U/s+ʭo-K.Vez]#7xܗʯY5lK  i0 u&sFƒJf=Kk$jbaqz-wf)`8;,XhV٨~=|KbJPO#J3{Z..ͬ'p]rɳE-޹YX:48_HRjqw%̶`M 3|xCsm4}X|Zx(rluKÙ"lkk}l̖X:2Ra*t۲bܼ]7U-.Ϊ^_P_@]V,FR|fsA4jzO!$`HB"~pц?: Cq@q"$C?[@$A-y8?;( ?LE$ʧ]U 4 #+QModI~= }'$b ?+P #0_|ˏ 5@`?rVrB4%""CY];+B !FtލCä́RO $9&9EVyL(MrFouxPwFTH4 8sezP` (L4rRYo@ʜ2M~=J(,4rM{7`g[0s>5kfT:Upw3 5S4%l$hglLfӴSR#(tSD @ *8O5ؔ4qfҘʔ 3MV?s͐X'} 0߫闄c` osgu@Cu`.賤1Ή)o:wc404]#ksz)gڿ()G`/p5Lfn]؟8MElIc7 n)M$<5cR0˕קHSFcXQ%{6p̆#DSH4B(V^ 4eƈѽ[i#m72HB5_X10 D "kfBG\=)DŕY+&5s@` I%fZ~jŕ"PX}@RŕY:?,1p1R۩X%,,T "A822I\it;>tƫ.8ԇTf,ג[461Mw"/xi PGjg*jy+zl<ڿ5gw%B "$<{f"e @ǯYzvچ'(fͩ%sN' ,6] 8N[̡Brz3g,Ikn_c3.(0T>q8·tAB/ZrW,ufj p`ZW\ w:r%WӺ$ *DS^X;*">o3f\}Uj`52s.3 /_>㎛ B% fa#Fs^z6ˢO>"j 0aX!k{#${N9 ==Rj 9q!=gw!mX9I:+U ZK' JA9t2z)9ՊB6e -@ ٠=pE adXH=V?mbv؇00( C8vz_  -!+=mI^˥D$V=h bHGfP{>qpC"`0:RGi=oqd:~B @Kќَ($YK Pd- -t|͜$ƌ6D `lnB&"eNJj}eWeDt`DQli{@ Sƌa7WȖHǦ1ԙRԟЃr LKR Q*)0(q(TH#ăZ'  j iB$qA5HF$$I2VȀ3K0Hhc2X'uq:SS7)XGBNjx,2'1j1V@Aⱑ2Ix-B.M3'6|w޵;OP'89pr&%"q;㞉yIehEhdm=qO Wf&v{?-*:MxV7npS74򟧧u6|R\t 4M3uN _}?o}}.|F"zw晧j7FGG/a4< $<דּ~g ;g_"|F3aæ_J+PO_icY_җ-߿p  $Wjuu9"|Sj^FDu^p?̒49bFR37y2Hdi"&93iuS 3K ށy̒(0 fbjMLt`q@n-HYRJKuFEpM)ϲI9]j6J\)+Rm\.9]*rٴ͠laE!2$u 90m,UJvAnĹ\X*D0K6MsͷrW6LT(m9`u'$3۪ 3, ՏžZmLJQ؈HbHD\~λ﹧E)-Mcxm+WGLgse 2ʸtk[o_i],q[aMtVȪ2Is.o66:szsA(p`p0JDa֫Å\B!#3d愴) b uV^۫V:Cz4E5/$h #}^Xidpu᧦-vNw"8B#u9KʓFEQȏl }Ί8gI1:ZonyՖe=nIu~ 9 A% p}lg?퇾Je'6x抐\;Wv?Ո&% BDD?>ɏcWi9ZkN\ @üOؐ2gْ)FiYҨo{.X\EcRR9cN53̍AuK/_t!亡ÀF.%4(MZg6->ҩb L鼐f2H3*V#43@I"e2A)3'ccތGfҤ|ԥε6Qk )SQy ` MuФFqMm!`k2'ئLH$,2[Z*FR) y"PB_m 3pP`4JRY65l#j[ABH`pY$e8w(v"\s< ԆQkʗۯv :D7Ee"Jݫ/tS5m=tv%Q2iA Pl#ش=t 6e9hame{-ؾv(2S, 푁ainf ʲ]]=gw\zBĭ7 C~&:g͜Qd̦ed;oO]DE Ll4ؿ7 PQ0 yx:l QMNm!sZs VeiAWS )qR>l@5$~4-dz͟BMMAO#gy"e\S^) H1it>]N~B)i?3YsLiXbPHg9IoRdT՚7FĖJ:AOLʥ40 sXL8|vXnmM<k2TBH(dƲ|@u/3-tm{>җ+w\$Oo\/'Ik=9Ynח_v5~c=nvƆas/7T*58-W]s.}g9it[Gq̥y^[p!׿헼첟͙I3wyW]ۛao^u?+:;wJ;>kb[{~}7],&CR?0ַiɒ?)x=m?zΞ/y񋮻߰1s'[aTZQ,cl^iEy~lڴ9sYmfwΑ#IB;v|cZNYpE8mu]w+dBZ|SgϺOw@??=y0"r׿_OI̘smoj>kDdYn}53yyⲗ}G}|BDQ ׽+.duRuchhwo݅|'pB<]0 .\~dJ)\ `}|j[ZZ^qN9Cϯvک\%{zz.%BaFGso7QJQX~p'r|!_c>*IE8.=T*ŋq`- omm cwڥy8˖-Dyڼeڐ2˗\},޳' y%\n2u$ٽkRB!tɒL{-V,_>4t* 絶;EaL/,KkaM;꺮EQ$"ܳ 2u]'OCA4_OmK) A6!mۈ Ph0 ;9֚J̫)Cӛ9ujWd2ۧL}vDf>St7SOY{Oi?bۏX"|4o||1%tEXtdate:create2013-06-22T01:32:14+01:00M`i%tEXtdate:modify2013-06-22T01:32:14+01:00<IENDB`puzzles-r9872/icons/towers-16d24.png0000644000175300017530000000135612161170345016334 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭ)IDAT(eOQg-)p 1&Qԃ11 &ޕ x`趖]v7j6_~n(Z ನ4MÑRXl0LjOs6°fzgg7ߝ&"_IS2dsVq,"mw]ΞgfgHsN) a?QqhJ"̘ytug i6LDq1仿g!bGF#`RpnR$y+1Zg56_|{ȓ=λaVR<*F_Neq4>#f!'#?roco?}|4g3K7omyR?\~B5F+F )lyxfY9eDKK <e,Hp^nQw(V!("JIy֮j}߯^/..8k E4vڭ!" eY""UlU@Ys>C%tEXtdate:create2013-06-22T01:33:41+01:00*%tEXtdate:modify2013-06-22T01:33:41+01:00ޒ(IENDB`puzzles-r9872/icons/towers-16d4.png0000644000175300017530000000065012161170346016247 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(}Rۑ0\+NĝxT'NL%w7Cð.df"{NH4Ƙé+Ԥ- q0sy0F?u谌auUDKr!֚f?jǺ ݷuE3sDqZƦ^fdXRx[k3<[@Z:JL_jj] R0lߚ翄~%tEXtdate:create2013-06-22T01:33:41+01:00*%tEXtdate:modify2013-06-22T01:33:41+01:00ޒ(IENDB`puzzles-r9872/icons/towers-16d8.png0000644000175300017530000000134012161170345016247 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(MNSAϜ9sIK q.uLL4Ɨ0H\WBqB(MФ4Hr{̙qq޳ŗ͜}Gt;mDD! 3J)-8(Ex|5k->}S̹X.vw+s Zk)0 @J D̾AB v|:"J)LSOR7!bٮT_q,l^=$i)ւC\i4VƦq4͌a~aV{e<̘V}9afg@TJ/#V*[[wD$͖er^qqP Uqr*&W vpAg IDATHǵV[lUE{sνܖPRJZB`$ cƨ!&a$?5A1@0$ H&jRJ_PmuNco?N{޶q}왵Zg *5!"Y/"G' "4cm<E( ]?5D B!˲J&`fSS[%cf4.]j[>|d 㺏zdEmm.G*n]zpp0vz mۚ>X@A㴶lnKk-03"Z*/O9z,5:v-2gٶ?d[Z9CE әcDDi2(d2II#"VT!"866H%KDx_a?޾uH)C)@ ApR@S׆׮o=lRA\̦i^ZwMzCepDEGׯMkDDktFF2Ą7}PB$ \.H$.p:QU]UfV:DM'4zA+j766DmD"J ICB:Īj 9.SÓ2aW"T_|FG{f'o PiUTڕUvUB'oNdPEjIĹG{ED-"0Ha<ɫMCo 6'0WqS^@cv kL6?~tcLJC#Nm螺'nLV^/?AC>J'v{E@ 8|O>OM2[ qO? "4!ͺYx^ʙ׾mvKkҡ D[0 @HR(?;j[N\n:DP',ۊc86{vh\. l۾vm} eUWWGU" o\@%3LS!"s~"\r%+E͞E L5HdR(JE id$oM7y,!Xmۑή˗{K"bZfGJk=::jd2i֚JT "JD"2-s#Q>שׂT˖U~RFQ9UWUf9fMFDQ)<<QGk + vFa`[/>rz -/P%tEXtdate:create2013-06-22T01:33:40+01:00! %tEXtdate:modify2013-06-22T01:33:40+01:00oIENDB`puzzles-r9872/icons/towers-32d4.png0000644000175300017530000000152512161170345016246 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǝV0l%" U[wbW=`{C0b@#@N҄(G!~$RʶmMK)4nml{O,Lv['g01UJ!u #*I8|{Z "#\#>$clݶ- 칐vhNX))>9"'D q nXHZjֱ:QJ1.b{Xg&H82p?H~~}Y888-]_J6 ocʾC 7AL햏imMU6y@n;f&"'XCU#Չ1O,9S";w\`H.m&"6I[#{#ӕlrm}y""@S 7flqCwTJEq,L>3"/f=%.2>s=n5 ^/?/a^æSٶɑLƄbHD\s.%5Y'>}c:*r)J+٥jL&ZƬ ؑahFO_*xzoZ-fo$c^O&/~)o%tEXtdate:create2013-06-22T01:33:41+01:00*%tEXtdate:modify2013-06-22T01:33:41+01:00ޒ(IENDB`puzzles-r9872/icons/towers-32d8.png0000644000175300017530000000323712161170345016254 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǵV[lTU{skvZ@ FZ(g>D>4!1bFR#~h÷h"$ RЊL)f޹ǙNXc9s枳k/fN9Rc1ZDqwwЌJّRvٜy15}sFL/"|{|Gmm)({l^Y.5S3慈a^bŹ\.5ԇN_Z7_ Kwy513Ԩ 9ommٻo|Ṯ뺮yoS}hxX,nˌZx[*/#gIDhd2s)%cltl%Ғlrp0{RJ͞>tȪU+*缆L)D2 U*FJ)WR G$ I)%WJ !Iyma!rF3g-s/FG b, VX$0LRmJ16!]Kg}?1knEG"$"5"QVIZ""D$bFE YZ)TPUHm%5ڀ1|:qW;o)VUjFaDHBW1ZGPQ$:e>9ً^7DTB[4fLh0$D 'rKPvP46ByEe[o0 5IPZMuM2wOٻ0X0`Ÿ mW?dۏه|z0<9-0HIRgo[,qp8k -*Q;׿]oKȇJk{sG;[cȍ%lP-ly!+T -6DNg>qߧzxeuF3:Tj@)ОWJ1"4cTN;{Ww e$Jc MR#pYXqK1`<ou獌?vDJ@"}B'8;481FE|W ;pj%mv|[,+r݀Ѧ91\rkT*a & R$s$4쑵+χƫM==uӃC&6+zn_ݻpXC [3K$Iv ;3'}em îDq|ew#s)e{{M04.r\my yT\u#Ef<7mHR+qj;4S'QJ9o^-6̟*VgJ*?;:斊%4c '8@ڥ) GŮDRᔞ#BqRQ&lO'$3z|bэ-okM!0,ͪo޼8A.CTU%tEXtdate:create2013-06-22T01:33:40+01:00! %tEXtdate:modify2013-06-22T01:33:40+01:00oIENDB`puzzles-r9872/icons/towers-48d24.png0000644000175300017530000000411212161170344016331 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXXklWy>7uTap#BJAR6IBhE m* ?@ZBE[mQ}UڪEPDImڎ;qwgggqk8;עѣVڝs;={`h||hD>s`-4" !(>BX,f 躞4MJyRIBDM{e 7t\i#|\.W^i"Fvc <(2g=aķltߏ~ٕd ڟZ(e~WvᄒRP*bx5;oiyǖmp뺔F4mYV>\pH ]u=\.OrrR*o !D&d6PJ%ORZ(>>+b1踞(P 0M_\.nrBR9O$G^~ W]}g'&NscT9t(6 iZoo_65MhYmt]B̋$BeN ORZ~vd޽8+u'\ϛB q9̜׼zWO>]JBqݏRyRoeO"{2seY9"kJҼzyd圧R',N\RN'sHJa(Kz"#!H5ǏLki* $= "Y["cLX/|vyS59Ju~" -)8~n .xN֬!"ĕJG !AjJﲕ M#b0xϛ#1=h S31`$b9:k=wa詘x Ϲ<^+ =ӿ{ ?x#`qSMx[i/TjRG0$PhL{'; l%k4[~i'Pwq^Bgl":)% Pۓ@8]leOli jF5%jb|wl[>y,р*rAO/(wSuwu֘BksK%roѳo0;tOv4]Q g#U ![˄F3ۣ֘J bpŻ/T{v#։Rr3 B*@JBڡu]\c?֟Lk)fEiRa,ö߻@ )5Y-2~IDD!?,P,7T#¯ JP;(c1x clL#@v3 HTU5Lf9vooD:1q:RUq* MK,3N8x%l+"55UϛU2t:n۷mɶPLJO?|%.ƒavƀ5M[jUSytZ~$O$̺ "R"!SB0dg _)q*ԈPu}M\j$H&#/KaYVFB)m{c{[<*ȧXΧ?ν{'c]v#M۶y r0 C;l/ɤ)s%L[JrhFC.5oٴ>pE9@(%tEXtdate:create2013-06-22T01:33:40+01:00! %tEXtdate:modify2013-06-22T01:33:40+01:00oIENDB`puzzles-r9872/icons/towers-48d4.png0000644000175300017530000000157012161170344016254 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXXk t>27{dN$Vڵ& Mu $nI4*;I%IRU[`Uf\ThT R")ā F1d! 13kDTgmJ)ܶ䖶suRLI Ed}O)jO}o!KRXZۋ{je>޶sehx h2 W*D3> # a9Bqe#8J{h|U 9X諂fd!>Q`ؓ9#5TY5OGhנ}2H&JS_geZ$?B l@~ ʺ:6e=:|zA SWCIMRU tUZ!?mƷY-{r L7:OɤAPy#bS a򿑂[1략l6oݫX{igPQ#x⃂E!{ BWR,#hNًwр/җQDc!{3tjkO~mѯ&u4C;DДWU]Dr6%*rTJ|Q."qn 2M T 8s3&X?F1ra\%tEXtdate:create2013-06-22T01:33:40+01:00! %tEXtdate:modify2013-06-22T01:33:40+01:00oIENDB`puzzles-r9872/icons/towers-48d8.png0000644000175300017530000000444312161170344016262 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W^IDATXíYkW>c`CvIL"!+q` RM$j~ PA ! heEC%FC?ֲKpwYHe߷o?.thk0#$̙7|T*z˼+E16nC=k׫cf<ŤGtz%-[3LP 4m#Ѓc j3 csON0a;MD!]ajD݄^ Bu]BūWMCAef|.0tMu(rj劝;_=kCeYf[u(lKRGGD,S@bTQJ9Ŀwo\{/_V4A B2EQ7cAzzzGFGLڵŋ>q|ꅝhv祐,˚fv3V$\.y(1q]k2Fȥ0!mʔ3Q"bC|4mۧϘwKa 8]w0_Ŧa?~'WU\ 0O%8&.!/oΙǿ0Q{cuR%4T*e޽  A` `lYj ;3KfZ%dVtBܲmRj{u s_G"1ƅbQUU ?} iMX24]/,;&n7a #p"ВV{Eu w-^VTbֆ(ZKҊx٫Ȼz9|y'?.SGR*B 2uU+T =m"(: q2_W\xlgZ M7eYR>uNtΊRtD[9۳Ly^vfDuU/dYp LOV1aQ 5th-qL< 4̂G%zn&3(Y¬Y%tEXtdate:create2013-06-22T01:33:40+01:00! %tEXtdate:modify2013-06-22T01:33:40+01:00oIENDB`puzzles-r9872/icons/towers-base.png0000644000175300017530000002114212161170216016476 0ustar simonsimonPNG  IHDR,,"bKGD IDATx{\w?o n"t]Tl-uӭAm/GѶjjZo^P+ZA\"  ! 4$@/NP:EGxXf= 3fر)гNꂇNь{ =,w`*o7X܂Ҳ@*tND#g9t*ϧ:7Prni);vK.ɹh)y#xNGŋBFx :2Xc{f} sE򲒒1CAf}S&GY[[CAf'f(<]qn@ " !`ܘ"ˬ̚wia-( vBԉ+_@Xk 6TkSX ?,˪:{aYZѢkc;?_hHQ<y/rjQ@X{44Ki'{ MʡؚuJ=B3UY 1, NG{?ÍPo-LPNZ W@Ű( ,z[d֪o* @qw,e郕vY4h3itnK~zܠN>⑽xgӧ2*,B'C_ur1tNly3}*^LUJ  =!o }|B@Ͻx׍mV"(-za~(ϴb6`OuNS7+g^i:ufuM1l01~o-8/!Fyc{ z;yԆ5Z !YmQʦL.,B0N>_Ef0T*BO&74hذEdkoqt9%:(Qt"GLV:3nnU]f56&:A7nbEF ھ4B>nll$I.o}>l^?ԲF/Zy)4t4!d~<0+nS;S^NXPpۃ-?Ł2]'llla\WzNEx?_YYj]\_{Ȉ|>Ł2'lA_NE-y&~!`X"Dޢo&ufzc#qwCh e{$qElLtbR mVYv+šcǎCZI,y7O9`^'PcEsמq^  j5tcE^)ƥ"4RX4LMy/4$1pt q(mS*k):'L'ģ^.A:gN-!ntS駷l0S?(|Em{))#љ,|1!ph|' ( i~wyZ_c¡P6H8 mllh~bP6H8 {߹ΎR6tp 5BDgGž?"dްƍ1dVfͻھY - IWN![k 6TkS DeNG5SˎBaYV5WjՊ]]BCLj,uJ=B3UYЉeaKcKci^VqrД[@Yn-LPNZ W@nlNa,vY4h;lJ,z[d֪o* @dw,elH3fAEIW[ !!C'cW{ o2[h rY Pyka*8 O6tO<=Kwfy^`:e[7qNu7'RT*BOqh$N]4`Aac9,e:[!$+IbV5ܹS)߰iͪ' !!zFD9w *1g^jk֮g=2Ҳ\}vUjgea+u |T6#F,BF<):yRBfY,&9cXEX[=wmgbN e26F)BtfܪjlLt$!dޟ/ 9 ?$S=!ʙ`fxU_jZBMxcC d2ٟY.¤]Willa\Wz BOd?O0;!:dNL?c'D!B_og3iVSSGBxKQuϒ$NLX 5#]{e;=mXJqD'I;v<#tG;v\*ºaT46&Z$>%y+lllƌ qA,TR8J̎ mmPjXіRStC(k*֭ #)C3tŸNTWWCmmpϚtF');vϘ ^TAN )C9nt‹!tJ7nܢնb|J{79 !#7:aIIp Bju,|@'S&GY[[CԷےj666 :F)jw{#<2;3@B\]]#t o xľ 7,Bqc:PfeּK o1'Xa'DҐt ƂE8`mju t caYV5WjՊ]]BC^Қ˩eGSEaX{44Ki'{ M$^Y"]'zB}* :p:Jro: p tNbXUoZ(\ N9e!Je;~,6XD =~aN_;&偦"N[&]Ѫo%LX,w: ^NIP~(LUlzج N2g ufuM}OᜀY~o-8/9KZPS;C_ Xbn$^NR!>qf ˝Bx3mtQo9xf*{z?SuEq#}{CAȬ.op&R *BOժoMu?^Φ[[4uoY(D&ʗJKKJe@ ?fLgwcųwx+tf{`S!:e-F4EWU*UaaQaaQZCdw?`òBL 5B;h 쟘q$Kd\\Tp$u'!⫍XϸV[`XvuL%{/?7TQGaF{y2_~y_";wݱRX{,"vej߻` E& QIH"\[_czvzJeel0z~\?)_',)1 4Nno'/ rV]v=`Աue kԵV;+;[o_F& @!0bj4şIU/q8?=V1{@L2Hc!iF)BtfܪjlLt$!d/ 9 ?$S!rjGG-6v@̰\I=N8øRGn3[ ˝{2'ٟ" !`X@ eӌ$qElLtbR mVY#,BdDŲYo&QSS{ |Tq+Nl8E"jBH :H>=y\*¤WE{Cv[ݻS SLTW;z:HR~tza|J)k  T=yhmq:NKȜ3g'gLB/lO0%epĈנxB=yhmp?xhy<-޸YГ6 7:J4pm򲒒1ɴgN(Ojmm M]S&GѓB Oy'R6!BK"dްE0,Ba" !`X"D!BE0,Ba" !`X"D7c湕dYRii\G֌1SyҊ455  1lX0ryY й}nn=-9O7 ;ՔaG2aQwHtINEGR{{yB+*ڸ 8`劄ߺq_gNEMd^JNi0 {@FbpIdy2Ö7o" ASSW !A IDATZy 3?&|N/1 JePǠΝ{2cX3/ǕȘWW$ <l?dAX&?&lOծYY  CCijR =tp]Ģ1̊񎎎;ST?ԲF/Zy)4t4T* dߩUA{h0n}xG5(yDn^~eeVuqq~wB##|t;!B3cBE0,Ba" !`X"D!BE0,Ba" !`X"D!BE0,Ba" !`X?)$IENDB`puzzles-r9872/icons/towers-ibase.png0000644000175300017530000000342012161170344016650 0ustar simonsimonPNG  IHDRffs5 oFFs7 pHYsHHFk> vpAg,,8ri6IDATx}PeM0+;!C2|E!]TvxAᅇ)uz hّDd cTa/?~kRlygᨔ @;׃(((((((((((((((g/UΚڳ5gT|w+k'OU7nh<==M *F2~ں86SbQ`h>a]L(߭ޙjcnXhVfz֖ƶk?PzU UQq)ںle4?̨&L<0q&’m^>Xv#Y|V)R>ֺ5hka&Cі;V*e*BT45JKM@؝V L @lصіϪ^fΜ9TB@[2k+m,Q&]{x獛:NImWQQ/ #=| a],+NꤴAc0&O~zˣb$+օQ%s3!ʠ!ʠ,XwH0mPVzi!V̾;UqxcbB|١øCb:\ 57mL `b q\!+*2&BCACACACACACACA㐭sI[sToGQ >n܉]/z}@+]>YQ2Q&2_4GR׊7wAVx9 d+fA*"N91]o_jK?h4='-!vd7SMO#@0|M`}VGͼIoJʾ|.7y;kA308|<"8N|!c@eg9zCZ@B< p,z}Ɗp!߷_{TWu^W:7]+_# Y[7b4`q8y}gLsFFjRezw \eg.6Lzyy,ʆF?ԙg;Pu{E]cgDV4 _}WTZs|…QU*eGTv\1 tNԐ{'LCX e6~fگ<*2eаj`:g&       }Y[04cPv\FvQ+wBY{h^!B9;k,cjZFNLg=ujC¦0((C+MM#y brv xw00s3y l5 (n^qIAd/ 7#[0%:&Pz,:MYj_[ C qcc(c>n:&[L;qbB h2h2h2h2h%tEXtdate:create2013-06-22T01:32:14+01:00M`i%tEXtdate:modify2013-06-22T01:32:14+01:00<IENDB`puzzles-r9872/icons/towers-ibase4.png0000644000175300017530000000164112161170344016737 0ustar simonsimonPNG  IHDRffs5 oFFs7 pHYsHHFk> vpAg,,8riIDATx[r0D iiJTj}>Rey |{R#e0R#e0R#e0R#e0R#e0R#e0R#e0R &s6h&*EM9߃)kR#2%l/sffNxŲh%ܝhb23-lR23kffSU,ec;~},dٓHLΙ܆QH 9=z7eORfFXhb¼)F/&xT0R#e0R#e0R#e0R3j)knK7a ɲb+c8JKRy#R.ȉ$֦7R 4LY=%giRǬub .I%X'&HJ;C'm:l@9AIܼ1bfuO PV^όȉK(8{ ? Ztb=6@e>|[~ww֌cl0 P_i1e$R[K\*˞A`*gf F` F` F` b]Fr7-ef6a|-L_i;ٚB9)B?TfKdUÙe[~(a|$ E3~Q=N36|q}H|,F %ӛ>S/2)2) 䵼%tEXtdate:create2013-06-22T01:33:40+01:00! %tEXtdate:modify2013-06-22T01:33:40+01:00oIENDB`puzzles-r9872/icons/towers-web.png0000644000175300017530000003044712161170216016351 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge0bIDATx}wxUv鐄/M"D^+Xa+"]ޤ{M =}v !dsH}9gYkͻށGw`wPJ/ !bչh5xU܄R0BM0ReBUU!fF0eJa0 0RbR!,..),,Z֋BhZCPAAA ٬ Ø&b <IC)EQMl6kb8 !}+'annn(nڴɤn$I^O}?1ꩌ`{k̘ =tǼSe?ѲE~  rΜW3f䫪ְaٷooEQbpBpzu>g~P($beB1ls~j}|%38sg?㎁V'hP x:iӖf6P[wF ^޾s皵RSkZVwd!~ ..n7iV^]UՌB()Jb`-\o((( tt]R۷.((= өi0(/gh}zPJ Ri!2 F t{Ɔ5%'')mñb B_~1u̘#1PQէy/¹izha{5kd9Ny}0cE UuĒ`=*FMc].ܹ=x(jժerr˚7 ʎ;Bc|ǐ׭(b]w6ib#lk1WX/Pi !!(Ji|\2l3 L7':th 45AJ=>:)XIEX5(1wNzY(slI(&eB_v݌2 `n%K[n(j^^1?Q ^GBy3^k2!v;|&[ӴS99yyAcY5&brssߝBPVeYIRSkyikcfM7Xֽ5ko v,MxR'BYV^8B`fy>'gUBJlD]Ͽ޹-7gD%AF?4uM61&of7nB=n;v"ĘepXpvmWX4=DjŀPlLlLh7lj֬IáGҏef'tjM֥mb/Lgl VdF90B B%_$A H{iRC@`5#K}[Y*cL0+k3!&xu7q5eKʎ7Vd%뷻ix q3{[&88K k]#?9>kcm7_@{2H! !|}ԸVgDK@Gmc16ʊ=>9%-)fR9,[ppw%ɒG<:+#㔋ҳ z`/6 BGedA7W[?ԌvM4m-pK&AU%eu0 kRnlׯmZ}(Ro=Z}ZUr\l+~cY.<@?4LUXxdO^8/ $@ANtn8&kH%݀~š'z_0CSKDA-xk>Z87[^FR *SJ9ZE`RgſlaQo۷yߖ`_ jhVp/n:/# \ o5)aYZvzBO/^~siӇ@%b򖂌ًG`J*(((\rՎX֭K޽.LʿO1 , ~gCɲsAdagmڶIlq^ԈSoܱnmzZ!nn|; UK$]?|ݨz-[FH}f#KM.颢(ii5j^)ǛFC뗝hmwjÍ4 3kX)6$it{"H$ҥKgUUy"|f͚NzcG}W>־};$?:T[jCg` ^~A$iRH A+ZӨdI`i۾V((Ko;q>W6bҫY>2bB-Rz8u떭Z;~Ă~> VCg}TJP  28)*DI'" RvwU@=le3c>?a={vgE.eLHY7oQmٷuԚ(VbDpD’sT)3飏6y@`  ^J쬔R3~f={Ͽ_yEeUU+(^Tps? TV+/OZ*:cjb+AP?LLL={.[h˵bba 0>!otSAA!i.+W+=`L1&@u>yZŠPr=]qѾpi xgzr#QBEp[~^!T%%%%==R*JJ omRUZ øNx_\\br dөy":O!WdZP,R*IRO(,9ҫ~T-~ynfZ0~ٳ&N:z,7~~10 C4E?߻w_$aeԩS ȲS+^\n߬T(%ۓ۵kԓ# ѥ) <yz':tX3 ~/Xj_}!<6Gl$ܮի&''3 ia/As+u"noJWn\|$ !˻IwY \J)pb):S̏AUUMJJza¸J욦%&&%G&ftRd-1"Fbbv0 GKC+7)<v4$E| mU>EEqB9{ew@A@ժpF@57 +UUƮvuW(֨X;[<TUScֱVG[7  kvޞ"&W16ֶ!s '\ Ú u.Z!w@)Er (2d煂 ={VQm٢aEZ"< 06pwwFvV7ry\Q9'8m̖;ܔҜӪZE1%%Rp9BF>MKޛC,**jڕ!Tl"o.X$=.mX롭[ SuHd,u #MdfKj[U=}!{rY$qۣ{^_咱O:|ӳ<0yvMU>3S/^?Oz/ $K mǵMlsBe?O=7/ elk.q)(! ذnUֱ#?:oµk9R*87oWR5,.;p )P0^;cA@Ж4r5pŅ\JMM&d߾-[6oۦM8*.$rA/oͻ-z*J0)֏;̚iQݴ=ҺZ) ~9}ܹSjjMEUQ{!Y_DM3iTS55k3]i 0j_jRчN+V2dp)%2LXfDaRjQ06j ޽*tkب@V䋅uO{gݻ#vIuh&B Vp1coݺ!䏹kJxc`(ĠrBÇ)!COϙ'JuiDW鵬<!Qb,+ii{ϚY1|]wYfD u~ !F ,,**@]&t)Jw[Q1Q.pQƚEl6EQU=x(J(/(s/˿A߳oK=/cl\bP&$.k+W޺m{|||m+aLiPMfx+b?\>I(^z(W} T69l߾"#]P*④֮С};EU}BMv|ٖ-oغj} aDQLJLEф q\b%'', f3zϧ}q,!Ԍ 1&.]vOS3J!8...-=mƌYN!3Y',[_yj\Pj[neY-dP>,*œӧ7ƸHOXsz}Ӗ-[33n>qsDpQQq-~1LĘ$&Oj(wyGQq-Cʿoٴ/33שSG6$n fۿQfqo۶!ܙ8P֪U"P d*((lP޿5-.)1w_TFJJ{s&eٳ<Q_T08GdO% JR$3!0|QzN98ks`ռ{9!ɫ_ qo~YmQywshkή AնSW,Ȋk)`]k^…GF[?RUUc5UuOoh`ۃ&Ҫ~J* @*x}!#?8yՀ{l>dcz]Dg/S7 )kygξպ:a- W9R!zw)] 15{ ܕP 3% :KA~׬ZC9ⅸZh$=$_H/' `񐖃F7鯼)LLG lV"iZ8,IR՗\<@#l)^xɎٳ+Bx|ԁ4n~{{hVku$e P]S;or ڽw6j:);΋W(#XZĵhjB֧_^QM_שS[NP:{|YMӦ~ҩM .z-^w@$_=]YUhٌ:ZY>ρYtJI=z=50֣?? ! t|8>/? Zjҧޱۼ (pՕgsSn7%LcF֭[1!!f޽nhќeYf23 #ZD稡-l˲/s^|a=UR\&8S @ɊkRBB|$1؟R^yvɲ,I+|Wi)%!lի6l@傂B˘K,zҫ~}^ڵkNxzR4o~?S>Z%^0eBc sNޭDZ cevIP8,8pp"Q!r\YZ,_䷓mL!TU]6 %(JЅm߁5bY7mG (A9ݬYӾ}{Bj2yyuֳG}\?/@_^X;պ|믿n+M^SrXTT\_ID\N'WYglg&uнN3o휼䠕-+ zc;) lBn6algEkp)b2 ##7{jR!-I5aKB 8kNJ&@'  A98IV -Bjꟙこoxvbwڽ!0;ZmRW $s:yY2X@pYoPSX<qqrx ]?/3OǔvF5p֯T:eY .ȗeSr^n[^\i؊hj U~;1w?l?ϜTkTGP j!үvjLģIk^ENtC(E'敔X2ʘ }lp(ܻwϷ|pTDIL mUdź⻥vi_ة_t^G sԫ?Ȅ`752 D,݈~u+n?=g.''}QR<Ą!IDQ)7vh7_-}ْYAs9jg/d rͻ4\5F4I),bjS5YTo)sB+w@5U[w>b^k^'K9OuѽgΰFhxAHJ6E5wbH }RJmtL~P(wk_JRAz5ߧiּY3]J@LUgל P@kҺvڽ9c!{'dFFP@ NqՈ gǎǟ۶a[< =@5~5B etB~i*!*{9"?s{WXXȲ*_^R=~RB"rh(,~+}[ t/_RO?;3]._tj^P? 6<l߾شyn/E,Zn];ra 4j6@@d~(FH9Hh.x(-CB0wԝ)a aI_= ro5qBBVaG=1B4˲>Z2BzU AfxHӪs!縼~˘HbBn{u)!<r- @xBy,k<)!vm5RMU>RHDAڻY fk-[0kBa|~;x|xJJfJ\τjBdž Ym۶͉'MBl~ݧFL\f}BBBΝ~ܔ;S(`Ѡ ØРiܹavm')2%˖;~@$3N۸i !a&_1X>i97hW^ۺuv|>M\wf^<_v];Rв9#cȐA!L87DZv="Sw*b|\={{m%Bm6(7p)LPU5j=?~b8lV !(CٿtH) ޽tvXxi|ǝ<ӣL03 AY@L(BE$)\/H)E(cӻ*2 c@0{.YB`Ti!QԸ!  (ʄGDjh,,e֢ݐhϡoL?&# 4t+]H[q\P9泔QlkքRVxȡPd$yA`i8Q\. cYVaSBɲ4 ^4ٸ;{6lP5ܥsaWUpUK.9]Vݱu͙Uk^oJJۧR:o+Wi6晧Zny W1nٿLj 7PNw' .:.<;y),ue?HF6k_4jԠ{EEE#G=d2@cijbŪb}:nup!ow۰aEB .x< J%GOߵotݳW֬]۽{@%BEQfX>]`bpnky;/bhB]YY,Bnn.hXIk]\\0LfVn"=P(殞,kf^!@0s]/a_|eǎ] 7B`RROdXzt %!4OEηYv6vH91s?g!8-x7ak/IB~q/ ?ZzgaRRbbb厏o]/"p, 9^P?;5g<ԯ_I/N(x.1Aexw~4%>>7hժՙ3gn1w3tnNs@&eY6=ЧS9%%3g<> *4MKNN~&ngYVU˒+ΜH;v48h #{IN^D"rBBЇC:?g[B5k|^A15!Ћ",Svx)@PK'"-;(,0 [TT/.K$3M)z "vb79& ] V$X >zb5M3s5@)CFwobL:%tEXtdate:create2013-06-22T01:32:14+01:00M`i%tEXtdate:modify2013-06-22T01:32:14+01:00<IENDB`puzzles-r9872/icons/twiddle-16d24.png0000644000175300017530000000135112161170347016442 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭ$IDAT(==TW m{h%X( M.I ?#*>Dh@@,ŰҒc|3Uuw8/pBD@HUTSb033U̜sf3yf-zᡪc)ޥq0UBDw+E$=~̩VUs""J)Zӏǵ6ݽ "ROpYDaw׳"j9甒ܽ78"̜s"9ޅDWZEDBĔ޹{ӧ"BLjf)~egwGDrwQaGx4x*)elf/6 IDql ~~-e(_r4 C}G?Y{X"RkSUf&""Zܺu{^Rjk@D~Rk[ero#">|xR4M̼\,T5"aPU12dJvn;cDhm"7-"_v"^[*%tEXtdate:create2013-06-22T01:33:43+01:00/;%tEXtdate:modify2013-06-22T01:33:43+01:00^AIENDB`puzzles-r9872/icons/twiddle-16d4.png0000644000175300017530000000054512161170350016356 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(υR Cͬd4rm/$1$jsw,"3[n&;;mQO3SKfhTdΉD@[[˹oޠxOp"c]~wPZ^۽s[u4 vK囉 o_-%tEXtdate:create2013-06-22T01:33:44+01:003%tEXtdate:modify2013-06-22T01:33:44+01:00潏IENDB`puzzles-r9872/icons/twiddle-16d8.png0000644000175300017530000000134012161170350016354 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(e?VWwιIJ* w+M IDBHmY;l243ㅻ1 39"?W3gf}0O̅"||x/OOOONNljdijDϞaZq჻#3a&e&"qWkg6c$"."}{[|Zk ˫+LTUD6K3fD1ȣ?4M,@kK)Uו=ۻwn|qCU޾}DHl!79eD$Պӧ?ZR[s""ѵc!;>>e!VKnVjeG"!LȌڢ:'?f;r+cja!σ/..jL+0ifkݿo0VZn 3Z#B{a)]UYTTugռk̏RfU dK%tEXtdate:create2013-06-22T01:33:43+01:00/;%tEXtdate:modify2013-06-22T01:33:43+01:00^AIENDB`puzzles-r9872/icons/twiddle-32d24.png0000644000175300017530000000343112161170347016441 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg TIDATHǭVM$G̬ݝkoxcK>aB$ 0 ʈ%6^w2*|ȞnԩY^D{BRJDMV?BHyL?uMH HW.@H,uCa;_.K!1Ek~9wMJu2BB1s$c1A8U5 `mX,{?ͼUU3Ƭ$ "B(`ZsέW^B 9缳eӴcFI/,˼yuEQXkeYik =4Bι<@)׎>7ec,f#R%IRL?eYu=7yx饯mA "nuHDڶmRB(8EsEQ r"vQ>d9$ʳmJ-ZB̲?d!R{@PJ}ODeMeI&"c1/a)i&INĺH1y~rrk"<Z; $oVdQдmӴY*oT@]7Rv*"WpRJ ɑIƨﭵ˲\.Kcm۶ic .i>˲|k{fV&^J9_뺾fic=ztz?i"ulնV:C\qk!UY{ҚʲftƘT귯2|3RM+w弯fZ !G!pƈ1kC=^)ҊHD08UJAQ;?_ѓs ⬪ι1SオӚGQ''[ӟݺ۷d,^8]˲je{3QƷc~o<ꖈvvvΪn\k>x|_u7؅E(S>k"B!FTU޵kah6x)Z$`&J2<᷿\u"G,NO# Q!jQR TY]O=Om1VRDd9aʲέ|3c&/Z .29GĪǴbfRJgX,wcFMMspu"ŕa~Mۆs~vV%i \8JNJE)4LJy<{{$8>>X{?. X=<84}晣{ Mg>il Ƿ- $Ifւsk|1Ogf}TO?}t|amaVedq7ܶ0Zk͍75|D+_㬮諸| Gxg("MY]ItO"j'ZƘ{k+Wmoe;;+kԩB8a`sΆaBi vpAg IDATHǥVI %Lfx2{ASgϘq \=&`#O^4nO1Uˡ;k>{,D{9}f~OK;^)(G]Ls %Zpc332;MEt Bc8Ҩآ ;"2119YS6uhj*ݏҴʓ**JS0 eOIau(Ň0ٸ$qѮ\q&(ӵ6i?Lr8|9sN"B\юvŊMf~L-aJ[΃O=< tvS] DTy?7<\EciMXUkqWý9UĜ#H^ 9gх2B1  A0B.TDڂIb"JA9[nJzΦ{gr"R6D3lU-r!ժ~SZ2%tEXtdate:create2013-06-22T01:33:43+01:00/;%tEXtdate:modify2013-06-22T01:33:43+01:00^AIENDB`puzzles-r9872/icons/twiddle-32d8.png0000644000175300017530000000353112161170347016364 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǕVK$7>v=v7=3a`L"e0 B!a !!,ń0.$deEfVwlan߾ G(m}Ɠmo!q bkD받d7Q "zA@X!˲,i(PO@Xs~f?9c>Dqc]GZSD( DTZ\pqm1۠d߇uB۶Dd!@wć3ER5 ZU@m?EgY sBכ2RW~s;O>I b r"_qt*}'~DsB>HR5)j6λ#C$1t6HgYvPUsZ,ej"!ܹ~/%{dkcҐs꺪c617fZ_yo~N+ŹH}'o᜗R6|?ʘR}&|e,ːkfYkd*ѯw AD-B֎-MӮ7J)="fBQatڱW:#~_~^LJ},XۮZæisRB<1vC+`{:g16"VvwiMlŢifW$SZcsu=}?O?$VbזÎy1W޹+sR )V:˳7n|k_sS}hǑb]9\X;vRr= Zyq]rO?uͮ"O0Ķ]_ه $,,~uv>N@d9Iֶm꣨ " NLA9|1 )ZO!˄z)a:S|ʭ<29' ؗL: Qpu,d;j;y?|0"ZsUUŒD}D\.z{}D}Y>{kEwdr8ceBspUUe۶BZgyZVggdE¶#x{jL3|/ Ry;MwCHl*{*DB\9\κMWE(B{bY;몊-C`dzo&rX RZ1&ޫ3Ӑ1޶Z؍=|CHA$L!F\sQD?8==McGD6y$ݏZeESڴ'%tEXtdate:create2013-06-22T01:33:43+01:00/;%tEXtdate:modify2013-06-22T01:33:43+01:00^AIENDB`puzzles-r9872/icons/twiddle-48d24.png0000644000175300017530000000540612161170346016453 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W AIDATXY[dE9>}8Òm $  $yI˃1/1H *1y$AdKM&1. !@Bv=}.uNC>=SwW__ߪӯ o# !&a:3!U-) HR8B`$!cU4Mwk(c $ h($i)!h!^dNպݮyM!Z^^(" 4XAu]Ӵa!#N^x1MPCp漦1FHpF`(`B&BizG)5 Rq$4 @D c^*9L)VM۶)ٕjլ "}?u h6`ƪ}78#XV4^qp*?>\$AJc,ⅅ4Mji8g!!(Y,;{$o<=~igJ}ow:a}j4+R kg'BH7|~=FlTUﻮ#2fViV,ko"F+?P:@ D)u]QL%]iV*J)Bcic\Qh akt`, q!$ h6[~R+7 \Ӭ(2` r7r͗Og% !(v:( 5]Sb V+ 12~n;۶3! M'Dq΅`dsrM~!qEP~(阽^iyIGUլ{J96hě cnfXlϻgvP0 # c(B8!Z cBeXBaAWۘPɀ\cNssZղl˶b0 tH\GQ(NO(wr[g 7%qX̚n!dVD3"DQ$t^޹0B.t'x2cـd4M"G$Ij mܲ( &xD{++QČS c>˗.|ZPr u0 FdL\|j5IX^$ CPMG˲c'~7l=H8S*r4KƘZ( T2TUzk@9B@T'y\Z,(j5et$"tc00MZ0\QD)ń,-uzwR=:g?s̙rqcl)J97Jκ 0F# KA1`EcI:8sﻇs GVfuq,UKI|B4M0fRX,z2>IaH68slgSK/|_h1:RzaBI v:N%eR>i !Ο~O}_9翼`-(P}m + v̘m;,Úˣq$O9iZ[]Yaq, f-Rj5Kוe$,j(nNKK_ΝiʳB)vvziضE)]Z\s=/M,,-|LQonm{ϧ2z^{MBmE\3>\RJ <-YLhccn 9*F8EmZS;N(* =ˮV(@]\\ 0Fjp8Yy`k8az`0DQ2+lSL) vpAg00WIDATXýX[ K3Y9>.KFGCx!R:vGJ)= h~c ?Y4 ^Cu sSJRaȐNODTaKF54;@R`r'c- dȉF:>אU[ S1F)o,7Sf)l{5v :"ĩV2K QK{4"c+cRqj}ɧ\?h9<:|T·x-aέ..?iFL9"K;8Lo!08%'t9kS:CzmL}ڕZ9y -&TI5-a6Fo89{C.s -z9ذ׏$7""P#3{B PԞX5!`%.5ȳ`zVsj%Œ icB[gH)6-y*F5?ݾ-"R%~Ε.Pd%tEXtdate:create2013-06-22T01:33:42+01:00k0 %tEXtdate:modify2013-06-22T01:33:42+01:006IENDB`puzzles-r9872/icons/twiddle-48d8.png0000644000175300017530000000524612161170346016377 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W IDATXÕY]#Gnwg{ޱwg{w'H"6B?!$<(!H" DAH /DAwO ? =s3yv{ބ~홮z\rBc,@o$~O r1ˑblr>S{3@-6&*c0Bȹ$srr6 )!KB߇JB1.J#4B!4¸9?|9E_ie9R1ֻH0(S*q^teq,,TYFM0 ^B4fjeYWH׵n}et`q "exe!y 8"BZjB\$C}+ƂqhUpb S=yDUJA+~ °Zڭ\= (%V+"sDb^!Jhw9me)tX5hI`,U*::zDhԱhf}_~I(=ē/ sY8NrN\׻] \5M~[n*'8x:,; !J)ϸ'F&'~aZ-u]VAW7B_x \sLĈ1u^kh>r0Zv]-4%wl|O]VUIZfZ5xL)mYV04M !uaᢔANs]7aiH0VG S aBhAkxvG\ǝ6cZL+%>z))|c)BH" f jKh !dvsΡ\7Rt煇wry4BcL)MfYjPOȲV3|*ZO)Mx BhaUGa <^Y.Vet] IO =o^ÄBl z']BO8yJ3v>WE!4|P,Fᦦ)B0X-ٕ89Ce{wsT*. C&B`({M|eӫlzZΠMC6[xc?\ 隦 }>_me4oammmь~䣛7vS~d{(i^ Ͼ뺘q ojׁ" ·ÿy`qkkʕW=f{{P1s/Ct0\D#~h ȱT*O&x&8L A~9CBih$02EcOi?eH|ŕ+_>Q8ƅBc~w4̼#H M1Pk[ \\I$K_ږS}[߬8x F?bZfPreh2 ~hn߱s͞yJš,,Kk?eH,X(riUWnaw晵OY{[PB.~{˾wnb'RSi˲ Ţ㢑(/Coj'YW~a|^(s>έσ͏ncOy5WTO0IbvTD2!Ȉ6xE@=:Org> B\KKs 79{rz1N $ܲ2!ĶK%IH$ ;q#}d^1k. !&vI,x&85R)>n0&İWuN!Isœy'_nAeI WM,rjYX-E@--0Ƣ(zÍ1) ag W彽%9G@R~7 c?(K OQj+"I.L{…՛|UoQѕiNKK33g,ض](>_,f:"tеi]]fvho9E~)z_~9.,Ʈp( Bp隦Hn<F9?adC~`œMT**$PwƌN9 ȊbT6N*| Φ 0 s4Mՠ G"ptfo$ImYh!aL\~=!6?01Ŧ>%s I, gB(J}s2bH$R* kH$zN* b.'r6m޼oŽ|DSk\Rߕ˨> T*1QTUմp( 5#l|! )G?}g-:@ fu~^r-׃1alD2EF~s˨1^st:MEp( .)ZE'_Cض v~Y͢LrLX9*єx<#f,%U#eb~)[BqMS$86&,,` d'}jL !Y38aK%Q^,)Z-W'_o֭\ x`&8'ɲ2qmޙx=BvPD0+!K_oۺiM׿p껏ڴi3{{c:Cm |`YcSZ% Ëu-7]]9&X*EdبCӴD**5%lUUUuq3oGL$+2܈]ӻSsal۶>rcb:ݨg Yɤa^vV5]w~ly}˖.Ƣ˾-ca -)cz{zKR",k%lhՕ=<6Ng@{{̲ NF +R&H4:3-TƸl ) a>YxG7~a2r\:1\Y1x7dYWiХLFxCCCC6+fiL@K\:~FMߓ!dؐdDQ$¡9sr:Y7qS@qQRAe2jm)?MQ={KE>{g[%p dY/vOnHQUSܽvkkaxcWX$@4MȶzXjf"ض9͟ޞY|m!L@(H u]h,{ 1 Ik-&?y?7$iڴgy_Yń1'ڶq9mڹx<9N˴Vos/I;;u;zHduwwET* 2ب wv|FidEQX4:Td4aL ~GIN;k嵗׮i4a1٬h6BٳmFι;/U9͝s?fiT wwu艄#6ƹ☷d2 9$‹C|oϞ}X`4CQ~~]]n$ EQ|j ($0~6tO~| F#;o_v `Lrb!4o.j9BRIi ]]3f̈#ifsBq~9 /olcL%BH5ٗ " .Uגɔ(1GR,Lt~ ƅ|qh޼qtڍ $I%9 եjZ2TUU!a?BRaڵ;着 :O>e,b`$ Omkø&C.Ɋ,`0wOWU5NZ88t[H2m<"^qХX3Qg~?Y/6~үW2́2ԍ Ȉ#~Ee:5؏wzE_ N=塇QA4UUUUg@C)sT#c?AA%}(8\~ aTM+H/I%˲mmR)otruw &*}* y6*p:~ h8Ce~Ez #e=H*@pW_W $|>(` u"cc竴cp0_L4eEI35`Q2t! .IRVUM9)Z6~aclȑQC&#~ /)Z%p0a1Ƈ 8-ϿP,>Ķ'r>_33kcC=kYkqsy9,^w=\Kf2}ˏorI5 ~3}nd29srsYe:ږ~]{՜%I:z¾5Lc db0\O*q%xL\r+/o.oi.V][U+k#/8b~?l Ġ A & L@ *T01`bPĠ A & L@ *T01lO^dTw|w@jxQ+.lƠ A & L@ *T01`bPĠ A & L@ *T01`bPĠ A & L@ *=dIENDB`puzzles-r9872/icons/twiddle-ibase.png0000644000175300017530000001203312161170346016763 0ustar simonsimonPNG  IHDRffs5 oFFsE  pHYsHHFk> vpAgtAIDATxy$U/+>zaPu56v7c5bW]up !XEA`WB\ 685 ff̪zoȡUew~o~cGNg^:>Ů;MЙ~' ѥXr-1Y"he Xe[ZgKOO A'Q EQ=[z0BzQoaE{ 눬'zA̼jrܳRt# B>0$gLV%Q4 ucG@XL #zD4_cFL&axӋT$Aq=4e%Ibh:iAnFd4Mr0\_- Bz<'J"ò|<4p7ޞ AqqY믇Ape9;7Kq1,ѹ[8Bi?d 8cYf e$*[n 绺eYt62dI>%ŃVލ ĉ /I+c{Y>u5 ҃Q v8^%*C< |G=d W^̑_?s7˗wA߿/X]B88.q$f2{0)JҊMO/{=?SoO rrLKbאuUMk6$j*Dlҗ^Ȉ;ܖĖ!yЌ$K4MP\"˕bJrd9=Mbא^14+&"YJlA ޑY?P|ϯtdaU[m[KQbr=w@?ya\T^iVBFC4?Vƭ YgGM%4b3/+tq{OϖE7X,^ !A4 MS}?3!v'T*]xT-fSUU[rҒ]?'>m.-&)<0Jl10 z~BۓbGyW ƸT,-XJ]9!aIr\G++PjK7l۶m'e-P7 UU[Ϥ (T.LiƘJIl182or&En&!4 TbŲ"7QhsƲz{(DaUUTØ4 6Dbp,ٖ34 jC@ʕbm iR 68˒eeņ1ݻz aYvrr7ggeah4L&+giFFT* " xwgPp=wv:NZ0LV2-U֊ŵDHAxic#,#|Wz Ðg1TFu" $P\lVժ cc##,RyWSզc\TM0٨ @<У\@qǵyN%Iɉ 0R뺮s,=Ububcܑ͎\tW^c $RVWY.JzT;z$-U| TMS5ȑg}o1huuS?kuvvfz^.1&mێ# ި ]sQkWzW/dYd @I)嫳\W+יS5RBL˲[dAR -U#@;VF-Hbeۖbsܖ|.WUkJ6LvLIOo6%֊]g3׿v?>c2, EjwfS^Q}=gm) 4u]U5scRBQ;v<2mo:]2\'B(a4cLHw,vؑǎz={v+2PJ%cn[.''&BAjz6EKk$1Q=7+8?8ז5uX)۲W:˲YY(jŞm˶SlE]lJI-+ehaiCTĮU?=;<;~|泟Gxݫ/dNea+n|t4xiCTn;zG_p1+˟̧ KKb+ЛzAܴ!ZNe,6?:\Tٺuw [XNɬl4Ke%xi6ފ={y{B ْUִA.b1|VS &`aBz&dWUUU Ƃ(رB(6iA_E*^>f\JL@eYofSo y,. /Fra,۲W^ayo^:l6 q7 1SUUqR:{z2a\uX\ 1SB\Qںukggxw]whPR0o5kz?!0.쒏|]򼈪aPsT/r.dhB C~WwŕW}ǎ֛֛R()r\.#FiS{5U8.+u5i}]Tԣ_mؖ!s@ %SMQ5>1&B!8,BZ3MB`K歠`j- 1MS۶aWN2Lahbi4MY+˱T6=fg!Ƴ/дz|djJQY۶qR[4Z!IJtaxxr|Rd0mKUk,ذ;0Ųz۞>O~##?}οN1'^J1fYvbb|y- iX) ~P,M4t,.Beڮ=x={v; ;::2Ї?t5 4?0`UU)15ŲttB, (cLFRPZ;9/iv+FF0&yZ4 %I2˲ îJ%2&Mř6iZ6 '<亞n< E]iu Q;wl%S4!x) Kb5۶T1T~~9>驩-cT* |PXe+rQ: 2)Y9ynPp\OQJe:ˬj@y^>Lz*[سg7DZ,v5WqkMBӻvRr޺[P ύ ccc(_V!%1Tvх$?p X)+!Y&EP7 d$qӰLDAQF #m+e۶k,|Y_Rb cYؖi[  8:2bYVR1-0u XbS5=kc hm۶e I$c҈iRq|ג\Wd!JI yE ۱bYβ_?iZ֮;Ⱥ+n`׮8SkN&L:Q'2qܐ A;ï\3L1$6Zs%[VLrCH'ޝ?}mg}.]k唅8\r`42B4qlӲ2$wn7og~& NCfLeĖ /۲u_Ne};rqR(Ӑ`[N$91K+矷hZ}5; v4Gܐ$?sNx曳,۷cwY&ŠR&@V#1cfeKBtUS]%hy 4=11hۿk' ?+8p1KhRb`AQT \^C I=:zWo)>/ |edKÍftkp\cѱ'E^,:l3 eK5ܐ۷O-ye6Kz~ϖmI VZ_m)"7$㢎rW} ?kKbNEӲǩi*ǮE)ZnH*/pWO}1Jrڕ5ǜ8[Ny !iJHQ-|M|QZtYs;׀PTĢ96a9QNY A8 Ѓ rJ?[ Y+d54-7d[9e$bpD&Xx[qLmϦ(* CUUf:1.:, %F:;:+7 ɓZskv?-G]*iY(npR&ABFMUm^=+GcnmM178 ժPb>f:,`c!V3ka3ߐ0Iq#%tEXtdate:create2013-06-22T01:32:14+01:00M`i%tEXtdate:modify2013-06-22T01:32:14+01:00<IENDB`puzzles-r9872/icons/twiddle-ibase4.png0000644000175300017530000000237012161170346017052 0ustar simonsimonPNG  IHDRffs5 oFFsE  pHYsHHFk> vpAgtIDATx] .]Kӝ5i.hyfnUC {zѯB뺦_J8_!:Z6;ln8к%AK-utptq~$Wёy͋R..sX;Kp "XR9"XZ_-Y@]P|]bevSH. ;bfX|>۶u?'7smk۶DGI28Q2\_(Q";e2I+C"ӰJI 2:"`I2DF8 Ρ2~+ \vUE D*[LLnM#,jwYL7tb]_N;eay9 Yd^ RU/D4vx R^ggE^u@Pwf8~K_>t[;Tk:-RCF wt\;=Q(vjE!q% SrMj8NbU/+)]K4QǴڽY.E{faSO4)uQAearwߤ5IpE|e;&OdYgbZ,cJ ut$sSJy%yL=9 @d<]s^1L+mj[*eeu<" 8,&q by?[pYMώ+XJpa\Gy,o'&?{Z `Pd8Ⲷ#Z q˘;,)삡Ja 6~15+94pnȂ sS.N]~$>.c19I-dY2~.ꍩ!^L]vt2(ޫ[1EӤ^j NeOUJ8—˔{/dNȂ(2=bIeVGzcZ/2^dh-wo+yLP1#53 gm<%tEXtdate:create2013-06-22T01:33:42+01:00k0 %tEXtdate:modify2013-06-22T01:33:42+01:006IENDB`puzzles-r9872/icons/twiddle-web.png0000644000175300017530000002561512161170217016464 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge*IDATx}y]U|ΝoݚSC !LbZZATgk?[Ph[@FAABBJg9{?N,J֭>/ܺ}i~ Khs~ <!Dݰ1aL)clÿP!q> @v>[.m6]{@CBTE8nſPr?1pa29ABB1.c 0v !;Ji#PIx80)";N%0R)W(1Oܮ_ 8].BxF* )Mi$!EV|Ϝ $Bh^ !4QEQDYQB x> ! Ð0c16stfum۶@Rq ].Wˆ;(cL @@0"$ $ɒy^A0JaGׄS  ܞ~Ey6k]B[TL:,p~yA ,Ky~S0pOJ {)B_$ۡl6+*ڎm[C;;K %IIDEQH{{>ry>/zgSJEɤS纞vj31-moz]G"w>\/R t:-ZV E{( c,}q,˪U}}t:ͦi;$_b esϱ=›Nw BswMA(l; (Ȥ3e@Bn;-m k J}qK( ` !s\LB(L o8@`Ĥi(by'!q]'⎬Z0(RTsm\]10Ɩm?|W^GqI' BH)ma1덺(}C$pt4M(b 8qj}01))ap( ( s|/L2 *Z,]۴aԼdY>~p(zq q\TTEB3),aF=H|iBmť )cǥR@eqMwAqjK.w&XE?W6oز?vg4fH8US,˞繮C)5XB(RBWk[mI0eR*¶mca jClrr2J=1 j:0ba^TeYu\hLcͼIH5:p\|C$IQq)2RQZmiz(Y**pj:~3tBAl6h4BB β8.QJ}6܏1qzO]M^9CO{κ0ycmj86 8EQx4MUUq !ݻu(ʲwޟW*7 /#+jԜ1Vj}lP#uOHB<ǡy-uwQhZBo|ҡ ìug ͽ c\l9أ[<9xoBdQ VsG9 W sY*3@6TʕdwIh!a&˹(BB>C{01>av"q0858V¼dzH…d/XQL&ߧ* `YV)B!/n  EANZàQׇr,ZRڔWX/8:s:긟u75ǽSܾ}$Isu0.J ]k  78˗l4l@3Lıϯa͛_ݼUzsu0si\[flZXX,@F#"Q[3A{ם|?;1M# ` uAcfۦ٪ו(OZE R)|s!M``zu<$66lzWl 0i#C't;.@ZE:vjS-'˓3K 8ebJJ '&\ $i s`-*W*+W,/ L 4DЇ?/e99m`?=o﷔n퓤뺶7☙]NU4AFיu=TEA:€mqZ°y#HHVZE ˲hc=Ƣo@KiZΜ,S15M8EdI^>ܴdx($ !4}$BȲryhOO/eZ$ Qv8Z}C8e˶6RJ ضu(o~˛#">J!qX`{bfEQTEYbE]oԪ0MxNUUA0텉R!E^~|2hвw\|磣#Ua+}:G@'+͸C`8 SzTzўnMSm#l( ıgv6xz1G}իyW6wm8αu-;!2#N9rZfyFCdYo2z1 uCN+Zj/anݶn[n{.eaϻ*ZV0@ILLSBqxhTRZ[^3Bu˓eq!]dJe˖l&s՟dUʕ6h/!j8I9ÄKT*veY]oi;" &H!!^+Rqk9)f}ɧ T*a~}?ҩEw LÒ$qD$Ad)nmY8A$bKWFGFGHh;Ej=(w{饗O|_EwuO_ta9(; J]B I|YK]t:mf0-[y)""ϋK!$$T*)-58؏9ln/cd)'4911zyvB$ؔY C#y!!2JU,! DQQu>lHX4n/8Ӳs?ͯjPӴCY۞'yO`0A. eLԹ|P,*hoBabjyrrl=?ذaА$ q `UUqRj GF (WAHb7=_UTֹb+cJYf w1zݛ a3?{:c\0003, H3ieӶj*"G mEijNC/ ஻nۮʛN8_CC)ި*,C۞\6dzZH׍逝}.mq"lASKf˿^{ͷH1}ݑ!ڳ)ZaGGGAd5"$A|>kZĤV4\N>zR񺠔RO?slw[w;~tO9DV(DQ\b:eA2˶kzRAWjR$IHV5 I`RyRY}Yguawyro`R\qZT=Hmus(bJ]+V(I ZڶE$2/\EALrJ鶱1?*JvpA419E(#þۖ[(0,ȕEޞRWdyұ4Ud# B0MpKB#Cb\ͯlN"Z[1F0MJիB{W=H6c{.EyHeqmqWRc̵Ozs]o,a|KBq֬Y>tu߻[n(iOַFt\>(cm{Ɋ̷=AI, uuty^Uq+1%La /87&IRѸm߱ 6Nqǧ1D]]].=k`1q|%Iy^Ł~q ]lI] IjH1vG\Nݿ)'Cvۏ9,q۷oޟI [ݻy^EIHض5I- Ir\#9Y>Ƕ 2MCuIeI$i$c1fV.L,a ]{p*7%uԺ]yͫ$*;}?GP#=$?$JYzCeY$Id00ƶ\.ʬ1NE)&x>c۶׽o7nHմ\588$7{D)emctcBaA 4vZ/ IvK:ZEQ&ӑC')"r $ |˲A'3vŚ#D aݤ 6uuFFFrٜmAt6kBHDQ((jTkU00%I$h1gVVrFESyrrm?| i%t]朳ya>T-8;L6j޸e^>2ʉby۶yR=HP H(mzU7 1IǑiڌt&0Aܸqӗc3~vW"Nw$Jm19le٫:0ZAH8$=(7[@XK/䒋9] BQZ%15\%K]wSO>DoɳiٲAE[rSNVP(l+$/_(5j4_ǚ_~`xh8oߜS((mж܁~ƚYsw~۵}뾗tՇ?XhySp1K&.@Z[TzziL,˚nIr|oy]xJmܸq8 aFQOk`zq㦿z)$|߇P?5}g~rhhȪ]:eYcc>~7q[r0oݺdkTݯW6oQUBަM emݺudxh/eEo SE  Nhj{2JI!҄·!SQwglq lO'!Y6cGKO~ #/#o:t]4fyweWT*38q܅8=f=殻feA!Fr;Ial{{{{? 6_"*ӟ{G:3N?uBз US壊t"TNͯn?"+/_ 4bĄOs@QJoO?횦Y`&)OT:3u;zz{:5vLR6~xꩧO:ķMiB!aH,`mٲG~=0yڶ180)'}ܱǔJ%UU\}'oMrsiͨ.:&fɝ?]{C֮MIjwwe6҂j}1[0'tbwd,Ȫ!k>uIoTqǟx_z;7 c_dh4 ~ǝl6}uE]I[Wb&'MS/b˶LBgzliJpi>/׿bCaHyq?~3zw8b)N5qyr8 6$ňnSNZ' ɽAŸCcRg(uQ_ w{}C;|߿<+zBF2DQ7*4M۾{W\tхu^@BPM{a?+xw6ݼU$cS~ܱBWH\ /8϶mB8UQDQG3IP$5ymg4(k֬y. X3 ʕ˧KqAg'y vpAg\ƭ1IDAT(mR[kQ9=6ؤPA,}ꃊR ć6m[ii.93>lբ~osfi*Q?B̙Y,R7Ɯ6oH)nX,@Rʣ~ bffYOAzBʥZq<rD̲h4Uj*Օ :|Ez}9NGL r9_]\ZRLѫoJwMU4|ӞW Vk{T.3(%(__g=>>y$I;mfvk9wkDh2WS*RcZ7L*fNꪾ @AB EӺP(4h&^^]%VerLDLm|HB{<ٞ_Ru56z?k3kng3DJ\XK|O]zeFDߘyT²Qd>y|e{MD$fef"-cGL?9e( vpAg\ƭIDAT(}R AD:qr$ABmbG9C qDDTk]n2 DgՁID%cw33Z3H6d~H[x^}GD%yw)8@UZk9v8X:<\^#))1.jKI@k4TTk 6'%ИC[F!4 ِu%tEXtdate:create2013-06-22T01:33:46+01:00}$%tEXtdate:modify2013-06-22T01:33:46+01:00 yIENDB`puzzles-r9872/icons/undead-16d8.png0000644000175300017530000000136412161170352016170 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭ/IDAT(]OSAggfw)^)`D/K vpAg 1IDATHǍV[o\^k}.sf3NIZUB6BҧV%PC UQ?h+JPmI$vX&c̜{g&Sf?>ZeBGf{B)y#F7ƤQ^$I66`g~+ڱt??:D'_fza`WrlYVXZw^fYV_D$ɕ+uh 矯{O^z^u6Oo/yo\Nkݧ]o߸ysjr"^͝;KJefvN _(Μy~kke&A/,<^ ]mq<7;lZCJ)H`N5=~XAE{ "!De5"􆂈^&^[DFhg-0 b̜>2'D 5y»rф"Bj٬:1 f0" q# "T*=ԓA $1^&%@6>V:0Wk2u< #G<2=]}FY>ҝ|6{hjʱjղ,0H)m~/=أ?xL֑(Zmy8Mb& įLNJ!$ ٶmDLџ{ʪʔ"۶(뛛1:޾y0lʇ'r|F$0<{}uu.Eq_1f0 D88QhJV!_ffmk~֩'ﯬH);ӯAJ)U!DJD$Z?t1&Be[Jǿ[Lfg36nP8iA@B J)fF"@d϶520B; it k6BG. ȶ-5Q$mQ\,eYvPEo.ARF$IXBln+N c۶y^܊,{ Gtbt[h8ڰ$ZUo\8S]Pk:?NeoqUh ss7̹vQ䟭Xp?`^ ~™Y/-…`fӅ-G:t4TwJ0DT>FGn|L37Tr=nG֠vt}kSH5"]~~Vrg"20u(NvB=Nčٴ E6i<;@AF*~7f޽;c0R[]'SR] -ٶm0T߫:uc0ZqR^WDlR$DD`9R*PAu6inN=f]VHBÎvx?{պ0B|%tEXtdate:create2013-06-22T01:33:45+01:00L%tEXtdate:modify2013-06-22T01:33:45+01:00=;IENDB`puzzles-r9872/icons/undead-32d4.png0000644000175300017530000000154012161170352016156 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǍVە0xo%"t"ɺԉ]`d>|0 xR#_3﶐f=qg#rfohDFGQOP38VJY0hUUx踪r{1q`]}}.QJٶ Ϻf~ځn+nv϶o:~ Ůo/,@px)y'1FQ9DQ,&?g-֚]ns|y)UUfgg>Kgeki ()T-Mߦ5DUE@733kx}q(O:RxEAh qufN$>A&^)9"B1Ua)+ Vܖg:~'&:eǜ[ޟ<PpﲹW{e+g-wVbH@5JBL[̶mg۶}߉4ck*zZl8)V {ZHc֙Y*uuBPcZ4af. @ƐyAk E{'U:1 "g!1 nĩU밌Op\4חTN~00~ߩ$yw?Qha] "{s>3%tEXtdate:create2013-06-22T01:33:45+01:00L%tEXtdate:modify2013-06-22T01:33:45+01:00=;IENDB`puzzles-r9872/icons/undead-32d8.png0000644000175300017530000000312712161170351016164 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǕV[lUwΜ٥vemЄ , L0jALxDcT@&*b^X{RXZvvgvϜˬnۥ0odxHCU(ԖVRq΋O%H$ڲ NGn̲ !0 4F)-= ^븢ύk7il8ԉN SJKy~wz0MGa;wvo̚VEY&FG)/^<!ĶCec@$z/[~ReuB[XW[[f`psSŵK iZሆY 9`նu]W4uK/ݲ-"@x\)XJOHyOP+ir%tH?Lf gl/4P(w4BJ0ggg`r2^[[Sԗ7LӰ,cEc*w'3g .:]o{vNؖJd.c1l^N}rzr2^}8H8xk Rx@x衷9T_η?5R*\x@CcW ݹ~#!z]csceW^h"HGUK'YQBaáFBK5}%'HBHTT4'y KKb%~]O%tEXtdate:create2013-06-22T01:33:45+01:00L%tEXtdate:modify2013-06-22T01:33:45+01:00=;IENDB`puzzles-r9872/icons/undead-48d24.png0000644000175300017530000000520212161170350016244 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W IDATX͙kpU}ι\r!{~8\n?$'ok=`׋ cч3cO>ضM⒓ iRJ""RZu])KgDD1)eIEQ% B)3 9#IO2*$`XLQ\"Oywwi yɠRb_l+ksBD""AJ` ũE4}c{KnuKI#Rn߾3Y_c'V76{^IK)3;n߶.˲1a{㍯+dKX,3ok7m̞JOe/x|ގ7hYG:ٱp{>N'}myoyeeۜ3\{߾z7:oѡ$8/4|JJtPqM??mvWd*mA̫h{[E2i;n"Rg-JײII)+z_яiЖtUf{\[VO_ܹx*u=U]]=+b E=a[b&Z8$.?sk|D@]]oC4%|߿xɒ˚@+!=k.9o^jȱ׬#sbVȑO>ʤ)CѮqz,ՙy3gl'~yf!C@g,Ruu9FZ%$5O}~AG[b@1˲Z[jNlr3U==cwFF8CI I\}^ɱߵ"?\ozI}3}dLJad2aYV0 ɫ,J55uWeOV,:{<"3` tRESjP@竺Jft;00ޞf_~eK'PXzus9t$JRڤT(z\i&H)} t>DDww;v??_UՉD"R9{F$0zy#R!sfia787pNH16k6/WLd騞dAkii~zFB՞憇Ѻz-Ǘ-_r? Ca$)о#O642v+[֭lf /5}XkE⊕+:{Obzӊ+{UMMc ;Cfv]]VNAޯXްv'=)7eWH)׷s|m Id֫l7LN {<(d(p}wޞ(#81l1 "]FNFD|h@=Aqu]qH r-DXse%lt%tEXtdate:create2013-06-22T01:33:44+01:003%tEXtdate:modify2013-06-22T01:33:44+01:00潏IENDB`puzzles-r9872/icons/undead-48d4.png0000644000175300017530000000131612161170351016165 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W IDATXX[r! s:9GP~h!Ӧml6KkZO!#wZ)u PJI!xJ~n$7w=@@r%Rʩ3ʮgHP8_+Ж\f%wT^A5pBa=Dnpw:E|@zn?yJ2 ʋI2XkժV8YGGxt!5 o:_hר7O#ld⭗1jΰA֕zOկddc#ds9jochwGS'ɰhR6NeŒ,KgY:,{i+pOZ vpAg00W mIDATXYkl>;w^;ޗmq!RMh6(-M+hCj(Q ji@BBQ ZhI$ 488mڻww޹:jk}|sv;YGSJG/8 (oрtC +.7s1u4<'9CN&ԌiHEU̲h@rθPnp]w$SePJME Kx|Rgܲ7|nãTVLMӰwwSO?53W}<$2G= RS Rh1}znudL6^}{D2Gؼ醙DRNW9diM9<bQBH 9-V^G)B;+:lۣr}KNB{'=5g[ I"1:6yT*ffggC\ֶmw {(ȅ5$Mc-7[%ࢥVh+f3NieXFpQ (2K.^&ex|K/u})'ݳR7t !U״N }2س*^\=#!$( !P&ikmqr*q.,mY,VҶ%KxQBDSD.!)e?iB!PUuYbx9|lKܼ .=rHL:>~l0 D.AMU''2{` _=;:&+5ԳeYMm-:cB  äXUeժ!(hίLE!5 Bk R1rD4@EaM] GŒ^\.rB~d%W4wc-ۢ8ga5zo$$lfu[]w@OV-V$Uֱñid"O +h9;n-TK?ys׫`*.? >9z?ܶ~)˸:XԲl'TBkjK87Ѓ¡樚66 ?xP6W1 'lXf;|b xqu#G]{xݞpnhjZiCgFTsO3ό>Yj2i'WRC0GY6m†~E⨾>8TpaY.h]8W=w7M٣G%ہ I87-)ϭEdn1=1m7T l4(/_)h8i:5v/Yf{`px5ͣ(s.PT GiW{g')wZ?NNZOPHqfz{_uɵH2D}❹%KTƚNwvf1?-k̪kl 3^;wY籠J=2aOMNcpeمu+{Aж/Y#WXAm?7ϑ qptu^\%+~(d e^B+W .0֯cBvcCu BәʪtaQ Y 4<晦*ߊsJaq;Juq0h.%TT: ٵYTi":hwj?Wt]o_ MJy@v%tEXtdate:create2013-06-22T01:33:44+01:003%tEXtdate:modify2013-06-22T01:33:44+01:00潏IENDB`puzzles-r9872/icons/undead-base.png0000644000175300017530000003775612161170217016436 0ustar simonsimonPNG  IHDRl .bbKGD IDATxw\ T"lAQ7օZm]_V8պp[GUUd80@H cI$wG'w>޽z2x).@Sp@, Bp@, Bp@, Bp@, Bp@, Bp@, Bp@, Bp@, Bp@, Bp@, Bp@, Bp@,ˀ7=}s˗E cF :ԄG(,>v<2b^j33)'kЁ JvmMMF"H+}s7" c:TGCp*p1f5[yOήukōs߿ÇC];ŘEKKJJB*LW\;l/{Okmq?|TeyeXwV[?ځDbbuI78%K^mkcea+x't3pq&&&&\AڔMJڰ:֓qE۶q>bb;Q]ҍ(=^ВWݶ0xNmS#w hG-.>!칿b3SSc#ztdߤuz?߼q#D"QPMaa#GuZٲu{Nޭ[W^|B;F Kqmvjğ];?Z]T9zgVrrUQݎ^]ʾ;+;dƄ 72sD$:m߾+nu^aqq4jdקw /..~0%y{i`*{tH6_tĨ2dv$y99vvH|PڵTxF|^9ݕ+ї]r[׷ؾ['pxu҃N-ZtjhNX]~Z{{_{UڦQFwjZGpe<ˑJ(񡱜K;مXV-Z<. 斖e&uQX Tu|GC rh=dȠG~ViAAsԟ#ǴmۦFsPg/ll"w Ztg~h-Xz/^uHKJ7:mV'REGlJeVJ7n\\\z1Z0Tz +gmob-{[UҼKۂ}o 4cpJۘٚS,,gWϙ2TH&7o[wx:(*T*,M<{ oo9*1rg6u?V]=3⪎ge-\:  AU8LVTT^~2PȟU,--Cɒ 8ʄ+_gtSeukLdQ#Wj:lsy?z@Ž}Sܬdܟ.\ߣG6W9>M|}7H3 DFIHJ>l;o'!KNr_ #~ukյsȿ v,w[Gp n߹{݆ # ҐoߝuŃMST˖eg\[(J__?tYmֽ{CgRR{))˥st.=#sws}^^N5+K˝;v~-[z罗[ޥ3?ZEU/gQU/EQ:#{'68yVlڟI,//v+9^z=;uj۪[ |{y7>mjӲeFֹrҪQPt?SGnGuYEz-5&W\mxxiv4{~ߗ!}=Ù9q97l\o߁6n400W Xp6VVee;߸ifΙ:U&Ry<ދb_ػ]ڻ$ISNm ]X0u2ަ- Icpp|3*!+KK+YdѼ_~);9:RYvvi.GF&_|ӦeggQ ˗/YJƁp2tٵyWErVfFy?߹TZZznҼПPr@tuՕKm#\snRjjZF7YYo߾+,,,--522_ɩoǯoH+5ϯc^=~WKxe@W]|w׮ko߱#ˀs/ׇE~~1ʏW{ͻ5\ϳjgn̪+C|MRh ɒ>OL<9zÇ}}O}Srg%Ad`Sߢޒٳ%[oܘ:ܩS۪y]v]۽ӿJ˗mڔ[TU[Xr sjTQ^,~[.jO9KӧN]pݱi۰a&Νoߞ_?+qܹ9֯[MګyFf&+ɯ$O1(ŵ5,?r/[vٳ3rv|8|}41w3gܶW^%%%~^%OxYO^_5m~*Jy3.>)ٳ ,̬l,h/0_Gp@,db!X8 b!X8 b!X8 b!X8 b!Xx}=7۵2x35*WVV}sB .u@~!CKLo߾KMKKMMKIMKII_WJ'ca2N]DOOoE?71a{-srr&M!--$Oϖ#v4l@UHq3&`a8jPeec}?Z+..0;FQTJJI߉D"-Wb_~q7㢢O]|S --}Zb|8W'Mڴvsurss.\fFP(ivÆX\!=&liA_8y:lJȽN8J Zzyyzyzzy j+FD8`ad4#?oLd0(0`⿿;>!d]_b͊{40`[>[gzIܧw/I Z (b 8ۻ5](w7RSXK5EQ,Y~X7KW% _lԨ&֒RM@QKV@_YY$MЏz{A++ c|;tNu볖?h4ƖxjV\k/8+ -I"Acc%KYrZIBVز,| 4oNiGrA}}sLK-?SذW &NG_9Jɮ%lͲ% a EUÇXF28j XRf?HKJJէO/ ?'嚛k8úG_&uHȐeiG6҃yy?JORM+ ~}1泖;j&Е}rQ觥sXPvYYYYIoܸy1PxѸxkkk_: $ۗVDjKيl՞K'cM4EA7WW\!,?̇i&&&p;}pl8{B_]gggw9.r :P Bp@, Bp@, Bp@, Bp@, Bp@,zH^ @*y X'}9z~P?%hX8 b!X8 b!X8 Q~[߈}+ĤZ{u۱e=+ՈSկo0KCRllܮ{ I%"QH15-eL}6v CKJDVN-(//3wxwFps ;,..>s|^vȤٴq7 ޴q-"vp*|ׯqVůC23_6mN!peg +YLM-\S}}}S .TS ڔ9&;;Gnnb8#O2}.5--55-%5-%%-DŽgC~;~R~~~ee~k.. 4W/7?ޥxAZEUR6rs'LhCCCrO71WHZq90_? (z7o***w>J7hX~݇޿5"b)lع"uC-o1M X_XYEGD I. mmW/\ͺ+'OgdCC¢"s3|ݻ׽{+*mF4rtCƁ\EƦ@ 4hP_k_'AFa_Bub$2d ssw}T{x־"1Đq 'od}]|R־tCƁN^o&MK|m۷BaZo:U2M b4EuvT}6r|}cnߣx]ѻwڲȑ Oף~ PQQߋ٫Kݻ$.ݸknn .Ϋjc_ lUko?.=(ZÆE9S¨3g 8thǪUK̑N7GFGwM qݺe >;:uAq};ZIDAT֭[mܼiS[  |Ӹwm|ں99UЗY|Rҝ{, fk\][$$$^=E@ B_.Ef.#FuFgZV]?CՉE_v%݅8Bp@, Bp@, Bp@,=6B@GZ7۳_ӈ.A Bp@, Bp@, Bp@,]U?&޺u3vIEB537wl%зoǎT?!eQP(rMM;xz1~pȱw6ء_۴hadnnQaa޳g|o۟g~Z}F Obc9xNrӎM6lֳEm̍F"HT({gOnoU?*tqIuwZ+zMҷEEOΝ{gi۶mSo,&%/["W5K_C3CKJ{h#kS^?buj[p5PRR| W/Xг_?JOOO:c'gN9;g kڪJJJ^uJzQ*OcǓO&!g R?@M$:Qpą .nzt顠hQYY:YY_ 0mą.]TL7 =}=#/|(H8lJ23_5aĈ7z>F7n"dXo33_B23_5acݍ̍j=#sb2z6r9On7~1c2cegeeg?ƴR [mc1'j~Z۷RRSRRRRRוL8ZZZ:y,ɓUl]/ϟ:kNJEii?xNtٺw)/:kz#/؅ZyW+ѣ>gѣ?mڲ}}7je5Ks!æ-Q3V`]TEx'_hzՌ?kiЀ3k~:B)d r4vIIDž ^/YKco`bqտhhuD.j Zzyyzyzzy zݻwmaQ@#K#"Wycդ}n>S(Wi{.]7owkwT%,5458&oND|L^hނy-b׎3uֵaÆ##!qǔb*sϦuo^۳iBJA>pHQu|PCDd ?2aYHx}`Zkv`+]T oCz]l^ƩEsCCC7_ӿKrgb{= ]=ZTd`js}ϋ??|= L jF~8H>=z۴(>-w&*705m&!ᖺob`"}'O>?|`{_Z {7Qojf톾~2#rr$ O<#zW7MTg@Loh׾+cOk_]rF_ĸ:U 8RyOldFZHe rܐC,RvmRy<5NQKuUЀȿNТ;Hki1[7T^Z*|^dZ5k&3}GKϽD#33DVzkYU7U|L-x[8~hRիLE @ |F2#'O;6nx'<ޓs~;6{3QeAG_hd!2a٥y$.ͻ$sUi7.~V}j頨䈈 _[aeAʝ퍸\@!,,!\F6{_o.|#sUiiF\#@W3f0kګPT]U^vc$D)W~d20u%%\}pdjڬiދ<}]^f^SǦZ:!Z{ʹ- ^82M־9A~x 0L~^˿CJ}};hj注48 O@f .Y3痞]?dT}ŋ/X5iJ|;#$NH`GȌ+;ҩTmNΚ͛7{Ŗ3 V}իS,؃/7g欒GUd`j0(jp~ ft `F~AQTƣÏʞΞ>C% N%-DmĨ2+0b=O<:ԠìNpg Ӄ:b=9[ p ?13gpi2Nfy aۧxVJWFvKސt}DnW͓>8·:sƴ-8IƝ>瘷eӑvV@UK&d2Nnss}a!YY74ccKuǫ/EF o͉Ԩׇ{\d;=U##dgWO kkHHL-njIPg=ǍYnVf\c:84khfkʘklda$*J_|;|ǎ_UWt!~ w NUKA$Fw n28~CSb35[&ƥ^MF̌k֬CKWo Ka e؂@4"`b!X8 b!X8 b!X8 .ދ tgŧ_Q?tEb!X8 b!X8 b!X8 gϝc{śΞ;?g|[L-~2 P^^NQ8&ؕlE+//3w>{^?! ޴q-.#^?1pPlO03 /oLw{ݵG/ gbbBo@/y{qFP9f>zo-,,ڷk;d@gz+3ߤIͿniޯc3u~铒Eq8MҒOoP4n0z͛&xIw!@3뱽کI׷| EݺuоK RSV^|O&.>A~l(-p/Ӧ6v]2`{2-3~}ѣG,X*n]0qHzyf4L}=ׯ޽zHp誄Y>~[n `&o~$-ݚJpr7s89g !?.^]:|T2Ӽ9:\/%0%7W@؞l_b?6lU28q¸WE1'Č[ںyӀJ|j?h ):|芕k$F_`1+$N,=x%*`{F#G/[R22$lb`h9h.=%]:#(xeTVVjEU14rKK؅!GF/^&IW\ΐt{ȑ#Ǥ5jDW%:lEGFE.^)B0gM΀٫_׮۴iݢEƍ3_BouLtFE Dor +tscBfӎ]Ԇ xyj%Mnbd?3pq7[!{ƴtӝg]WbfB3N19F:}%槛y?n\sggݺ1^= z"o;-&FX ) zB41vI_UnnӮspu&FFWy ?U7nwءEf [%mn$Z*P#؞nb,3)X[[s8Kz;:W._JW812M/9dd<]I&ƮW`QQ] 3lYK71b|A.לJp &Wrc\]]*gQA&#;Ę}ճ{vZhnkkc``Onܧ]Epy,3c!31]H71f4 Di3/:D ȅ]Tt'Ę`iYoͦ&tEZ1k][QZ%&ƄJ߱7?>>Gy<>M~~~yyCvm LwʕEE:1W]QGYP?(ĵ07㢢O]|ٯ`(*--}ZP?(F_v՟ގ !zPSSSf'NBJKKCzLn/]Hz}ʴ[JP?(+1/[2xמ---q8mG)+4]$AG433(?>|HHEGJ~P {40`⹰[nKO^ғTSM~P 2g[Upw.7EQiiTSM~P 7KW% _lԨ&֒RM5A)m޼M2h:?p1׿_@[yvKS p- [qQ:$EQTaatKSG_|d0$dHز4֣ [}?JORM5A);t/@:dЪazzzV}/%ʒԴj J!xd%at :xO7<ݥ,=+K=<>!P?(Ȩ>[ȐU+3HUvx)c>kٱ#<bǪLQ'~Ja!CYg*gee%q#DŽBG$㭭}(P JBsq󒤛R)5ZO-lݶsm̙=jBjOtcɓƻ(hKP?(i&&&p;}pnxJ~P $*&~_gΥ \}C60`bab!X8 b!X8 b!X8 b!X8 b!X8 b!X8 b!X8 b!X8 b!X8 EV/IENDB`puzzles-r9872/icons/undead-ibase.png0000644000175300017530000001463712161170350016576 0ustar simonsimonPNG  IHDRݾP oFFsPcG pHYsHHFk> vpAg>IDATxyXW,!1A6@R TEw(uk*jQ@(*\e *QV /0d& >>>s~s$/3'3A/W9&B,еJ}įZ[?5~x_&N"ikkN8a=xQw  (P *@T@ AP A (P *@T@ AP A y'п`0g_ ƻϟ?M0~\ sy;)i׮XluҊsXUEfޱk^|*^No: ]ܴy[kA␐]jk\]@ `2Yܼ'O1ӹ0S}?\잽)i\.ymG=ᷘ+Ws߯ݾxCC4]aeP4E>K8\)1k + oÆO,Ohnn|2C{́r\3cD X[X\OH036rC=n@zSn횘S'rʓ'O+*+55OyVhi%DGkkhz칼+OrJQQRBSC#62rUO]#4 c2EE^fb޼977oJcab2ẇl)Gr@~ wrrN%&_hXȩĄ;99Ĵ>0A/ҳ(@)icI$ѱ#seҝ;w#=/AL;6ɩW=hlP^t]xSeiIK`رq 7U~ƌr+yZͺ=6c6ʸ࿌D"/++2 Fh2#{vWo)S&?L2yGHZ:ǓxecP 8\n#2}{3:<8wϘs#Hba89R=@qva:LKf51Ce?LGQFo uW`x(@V䲊 uWVQaiIbP(Gjq̺/*rvvYw(@.NEϟqE~>Qe6PbXw횳lYBVB8fhr`.9F_j DrP<ꦞ9C"uV[[[lkkKqcF3"f)z{$$صi'| F$w{؍1~Ad{[یx'7?=n V&$"QDbX,6"g5s襤(+u =v % $$xݺx55re%%o9;vHpp۷彮aLKD"FFfܿ})KQ]pO8q2Z'? sGEݺSռYnذq'O;f6jT+޼Yaǎu 9ct3Sju?*+:5\_/뉵טĴꪵAk(@ǣ|9#;S猙=gab2RS~UQ⬠P a9rtlrhǴiClLlrӧϜ]___ziC{?,12UH}}5dc-[HaXaG9{d aa(}}Ԕ$%ī7oWSO75T=?E ).~? _hԩ=\3μ{l!!vvcQa[{HHn^~R弼qN-̺HWV=zg$gL< n)m@`2 eeoX,ֿI$SQdyB:+nnߺ}+D ;` (P *@T@ AP A (_TN?܅AP A (P *@T@ AP Ap. C>Q siolE &(GG !δ htZf6HD5z !''K=6ttУ8ɚ8"G"X,XYNO-.|8qBI. ?*6t4ԣu4M5qDx<6{Յ5'IP xwġ6 b,on.~ŹsZBmǏC9,i c1Bup/O 7˯8Bc'Z=ݼs98bl0humm׮8>mgV}qNsl ЇMP~<@;KNkk3M >D3'+9yŵu 6mE}0Hiy'9d篠UUU_h"Gq(Ղ#ݎwYUUl1Zdv GIs;6wd+@uuNضm̒%48vR-[U~Md F2~I`c5Jh˗% [@ ->&0|, hijZ~SjB4vژ24UR.)r41pd4v/-~~:miޖ`8l}m?>mk).N/qKHw ͸}+i=N/qKo%vfW,r ޮ,W^QouLQVSZ*)*fkk;vk?'O߳]&O-770i|NsSWXbSGF>}n,63܎2y8|U*bc˽s亵?M?zzz6k??~ssosz{ܯtjxyxk?3q Rk?孜ք gR3 >ňo}&Y).dR}'Z4&cʔÿDcʔ11bAo6mZ~~>ţXw]T*O lAedeN; *J27gPƼFTaXuvR4)& >}V|(.~7W mڟNE&!aL_ڥĚԔaaϟ% FW]y{{mK<&WW$hA~Y)u>|؟̕+y"?T@&=ӓǐF'MOE MPvKvcғ%@u"x #/f"6jjjv>}&:evJIJ ֈ╤$'M`;0.edu-N*uG/YRrBw1sO==uװZ HܼKyT##='=KMM5NcZX-H;Q e"I.U /pJ}į@V׬E&Ka2ry%Ҫͬff`IG&[:P(s!AQG^B(P *@T@ AP A (P *>s"%tEXtdate:create2013-06-22T01:32:15+01:00k%tEXtdate:modify2013-06-22T01:32:15+01:00aIENDB`puzzles-r9872/icons/undead-ibase4.png0000644000175300017530000000457312161170351016661 0ustar simonsimonPNG  IHDRݾP oFFsPcG pHYsHHFk> vpAg>IDATxѺFk7ݨT7a] _jtM=MCB{H@#{;Ј& &[Hom1#ʗsiT T& SL!U.GaЄp596Rs,4{8[δT5alhfx$Pͷ4zD~۴s鰇dϐ)6gx<#쎂YιRXCA1sB y8_ Z"TTE]CzyuR2p =J ؓ )7@ֺb3L=D 3o "iOCG~{HU㛪x#GWRBSwwsirK~(eIĽR+ej 3!QmE!ј^(W9g$NT'g2cϢ9OxF ]YB D@ @@" 7 y`& @@" D@ @@"0֗otpo/G1>R&}Oǫ4KciW 1oʿ],~e̶@[h16|+P1r}^&6=#?t4+ @߻ZOǜprwizV9GcEQ -QGϿ&ZU.w[%%Р33`܅}.T č#>6iet:p>[r|riȿ&] M0TLoRvS'MspuA@雗R߸+#V.6*IS-c!B!cZ{'L9-qj@UaskH9l cѽ cH;.,I?h6PxP<֦C*N8cjF{B ` |t!3ƒė1Sg$&_\@A@ FAa#Dh'[iRN·oZv@֌ M~CQ46gV/Sh? :!P*Lsܔ3YaFe0m< +Zl%t:m ɒ8;9͞l܅Torݦ֜貢Oҡ?ɷ\NS@.֌:L 'Bk5 Vw$Y^G//Ӹ3?c]Fb*%HkcWWl@i*ZZKEQ@@" D@  H%tEXtdate:create2013-06-22T01:33:44+01:003%tEXtdate:modify2013-06-22T01:33:44+01:00潏IENDB`puzzles-r9872/icons/undead-web.png0000644000175300017530000003263712161170217016272 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge4IDATx]w|TڞْlIAJP(+bAU(`A6ӑ"ի"EP!$gw3'{N~#;3g3syg>ygP]е!jBBaN!ci&h1(* Bh)隮:Ზ(s<Rt  PŅdFA@EY12 Ng  T` D_#1`98_\L|LX ^Z5{ OtKy+o҃3ӂ) Dviڤ ~h*..`-[\̥iyÇ{XBB( B1$Ic/})C36}3֩ B!r +1%)l&{a||> ƚ<ݽkڠ;114;aϧzC ݂}CBcܥKE gK|ّ#G@▌p8bN=h&hZxN^骪~C)ÆV$Ec.':y*)PuӴaC$XoA'OJKk1$i͚5$!x^hOK>EE".6vw@~Q6#w5y>77OU+i}k8|UB:Puxrjj\ٜ+(,t~== Ş>uW1^x1.ܩc(w-$0|}Mݰ.)$Oн [6ȒҢՅf{<СSGsό[h>2 4e9]cr5 {'Eah1c.WjR|bƌM <0:yg֭ۺt$Rӎ53=YV ujX/>b |lQ}>ߢEK3GPk( !4_J?]J=9fdӟE4;{bFg58zHd@]DŽKJtٶCf_bYveًnN9ߴaJ 0oX}aIJLL+Sua|e 0m끢@Yv[ϫ:;}y@]U؜R ߴᲲyy; 5m{G_籁9w ޽2}cϞ !MS4(JpBYQX8xpP4-K0abbyyUUΙݱc`0xB!D k+YD#XÈFM5h: ) 11a OYI^B"G~2d9!-jڐ$CU!BI-Zn#pH(8=w!gӦtՖ(iZQrBM}XI0;>5#oܹgORBB

f:vÇ5OXo+ G B/ˤD}3gwzcQeQ:TԆrA2bDQj(dB 16h.))ηhy[IqM}0n/9;c_{?_~^~s^N^^ۖ-nތǎ\(^z%6޿z7&ƫ vo_rq2'7,{$fphɡ}y{78g͜2!D%6.GLW׍{gbv킆ѸkWAuff$&޼9_'KaYӦo'ObJJʝ5mzs(ˋ$KWX ^vطoߟ}D]wfdb/vBk&[2UU1={;O\,|InӭC[EE3e0TMRV-T⹰Px´Fe3|?n %YSpGK].pX7B0b(']NIqĉ/:|޻ީiyz< !DR(Mh xgKğxYPa(x >fH^/ҋ$IQ/1 YRAۿ8nE>X{:u<6wVݾǎRcl!VYava1!rx5i.IR{Y y/88/@VL;4n"-VK !Π(B݂SQ5IY8.𼃡P8\7䝼 #2 !Ē4^ NP Q:%IB⯵6g،B0!&c!"&F'1I( j#TIOetBT}*oDQ_su}w^0HbkyBD^r b5|)RMԁvFwPH4Onzbbb(RنnB\.a0(t<cB50g>3˲w-~FFJII.~0\dٹsk@~}eY|! MƳ˒47ovp6~QRZZ?5udon\o$U È7~7JHϿ׬^q>e6%o1|PdFllu>:U+ff#[!5%%7//))qݚU ԏDfP(QΙ}poǍ9s'xGƘcǎoٺm1GC-_0tFN˞8a<iii*?w}`vvRSmDSEQee<7oܒ#eK]>ԩHUaINN4\D `XEZP!Ir7?ϝ]?5+!% PTT˳~Wx"h꺡(Jſ[eg|>%˖}g)Ę$&Ŀ//?vLy0` ?|39EQNh j 0Ɖ V~򩧟|L{\U 1N'?ᢙ^+xP8\L BUU7n8rŋ/\Hs\DOqf,r,ki3k֭쉧yW^)I7={En B)S$"HcbmǞiJC.11fTD^}KKRRu]/++?6jdVyyymZ}k0w=TUeQ,qq[Zkܹ33#5 z=4Eah 2E;:E(*'׫gu3>j\@yJ4X=Hv)e9€-p BBmZs^ B0 ʲӬFPPOVHmRqHbY:7BX #ڶ?M{DeB o uŰ)4# qn51 6R#5!QV]Ta@ 컂h6-7q00[3lBUM= ۵mӦ%Wbm^ݷ6;k:tݠ3999Ea@c8,B6  = aN>vApZFH̳ذ =Aܩ(6}c\}z{^0ʷm>vP(lC1x=[}bG :1` ŅEEWPH C 2|0cYJY)((D_9_EXV0. #n^r_PPh/1e "Q: mFL't:BEQdYBG !(Q(/›V-]C!B4MG8+!hBQ<>0 w,w▌4(Zpr^qAN5 IIIPBǍ Qhn[2 x.7mlvastоt*t]z=O8x؄ǿ8+6g 3g-o8%9 Wwk]ǎx=ׇa~?Ĕ&5 cw2b山16 6SN1 6-(cƌ>btnnm?z^e/MHW!v|ss&xio;?]0Gm⨑q9wnӲe$}m۬Y/ׯ+?joqkԸukVZO{ݻ+++=:kΫ/Y 7FJ!!Eb8twtvr]?|ܸݯ65j7հa@ Pw+>`/&¶!$6om֬'ʒ̰llЁ qqב"pǎ>da`Qqu+ܼYn0|b5;`B( ծMkxyЄUQ~xhbE,BH4M@](QUAM35Xwf WL)BkoB aIa`ry6&BCx7 B@qp#ɒY =SP!EYi(.&9Im$q„p}(8yd(1:tV0Aػw_L4V!${Ԕdcm?༘;pX$u&S/)raفLEQ`0''&%9Ye{p\fz㪨y !a$'׻V (#q;e˖y6*syǞÅ2KZWbcz<@4GG!TU-%J"Nh* +TfMVE@05A!AtXTU$a{R%Ia)l[0,+V9%BMͪ31fMLt"D;/|}VBS3(B0A 8JdMѮP2BXuvsfb_{˿<`*^3nٳ?8u., CǶm|S-aU7L(w9yj5n"4xKw\S{,SȲ,0I#?*xx[Bs[W$BCmpg_Na8FD)W#G4*lI[C;)bY(]df,; s ]OOOw:#JV q9elg|ò ,iJ5?7>C!E]3\ (tKjҴ);bnZhm8)bDZ7c,8ػۗb@q(BS4Psy;g7fXcmtSzX7 dAiGx'UoHM3 >h~7ͷޱ)4)2{tP䠊(J 7;w6OovȒD!dPiS-_nْ qW䩓O_zl\.(F4I$tOf><1&q #R pA{x`:#PqwOdUSN{n _kgbbb\[Wgky3g9JȀMX#dIB߿(Pm3!\BŋoXo73O?տ_- 63Y\Tl#"C2s0l͜<~^&|R.θE)Zfcbs"ט{p> l|z|8LZjTLPcK% i!%%`v 0"Q A5]'`Pf7N7yTVи!EɾpQtZ<!:]nܧ1+RB ^ ͠YP@-|.Lk` IIy狏͋CܱAEAZ²5 @X)RezKB~4ē1 pXT1(^YNI8w8%Kl(p-tٻooe: [͚6;vt154 :0?CtY&Bu! 7?{6t]7gϭ Qh oBŋQ+ZK7/]T0ɧ0lWu],A4M E.(,\ĄAJNNFhtO 99Y4o ,]`G&My0lj!ܺm޽EiٲE~})ao+Vlh_hB?>^}fa΃4!6Y4o/k3/p'Ȭxv!Q k; /xٳr.rWiˀ>3gL_p 3^tJz\s ! >xw&?'.ŠchɒGf͚nB ߲DQMo曝Sz4/?&ڪsw]LQRR:G!̞C,2 p{N~xG͐ozZs0i͛7Ӵ(hgT-(EEŢEQeePDBQM2{ dف|Τ)S'M~`ϺR/Og;Ӻ5+9$.BHQݏ9pђiO=}M Xiio-UQbi_f9\{Ҕ3_^hյk2~e`<RA)y-^fB`Qw&ٵn8{?+6j&DK]7𑣉 ΝG,+oaUTe2չ3$kE3yyovoӟt:/|?1113lsM`کcZuUu^=0絗Cp'fq!%%ןa^ ÄD1c'@C5@Q9J5DQ+>N%(aM.(biYQU1ahbe@/)eY;`Yf0rD3ȯwTyl"bii$KBpE#h°v?|I # Q=3?# &<?q8UQeE'bYsN#Kr58gϝ۳g(v"8G;W@Tjz뭭ƌy钝Zc бcK>ߪ8a>{z^mKnω1x<>ڵIEd*ˣ1`0Xϲa7 Ce`hA^L ɲ,l~ D41B0<_FAD!6~BʤEQZexyf3Ms!DUAU0a`+~T*9~7BB@SaL(*7`Oh=`cNbccjJW|_mҹK}_%>b3_r\bA_/Ɩ\7.j !ewO|eQO?|Gڷk۴iy1qÆ^jsgXXy^A#x15Ӆ aSW\ڦm߳}GMhq?0%3 Ư~Gͬw陙3/\Ćz øI]t{~v펊ͼers΋бcUUEF]%cŭ[6v5bqS2{t_}uJ=a_WCiޭkm7ɒX |]v1K4=,cŇ۶m=&Xə-|ߌl_TFg|~/̘UX}1|>ްO>-xx$ɒ$P1\[dqڶ=vBuvT6~~{h3QU7k .yMu]wNWl>t:p̋[ѼsuMTE۷;n=+)={X83sf>p[^f<0p~}k3fd՟nhOQ;0rW"k?tфK{f(--T~i?W+mh d[f8111t@мTPckU߿dfv_&3.\رf͚:ucSSJRF|w/xiq}uEQ/;nG+eވ*d͟8W"dcppO)pL7*jnBPΝ9uCp:oF/jb.{NX޽z^[1NLH䳍?gfꬿ= B,CQTYYMCB-練oTtBxRhz7<`X)[[v-Hk|#!Of-Jd$3ko^~|ŪjCxa=Ap|,ڒaw]Z 4!t]X.x0ܣ`ck֕U9C8\NEY`X={$I` 9JӴ)ۿ2z>ys̊Qe[sG@B e@׈YhH={G40ox< AhKz "P( H41CU k5ln3տp?D!v5LWW\H uC!˲N'0m̋KN'*!8ئ,t:,DP*((鉉 ~ӪpBԮ< 4/¼C ݹ^3ge ///?~eUXmfRPKaϞEwGn䳠(ߡsry y "Zrs .rWK.Yr ƌsEIٜ )))1r\Jj:7?B@\{zl}"m:kp`@ :Cڑ?{.1<(..9z\Rbm_ܸ/u#!10$1!pX`P~}@m'NHMMza`Ñ\PXxV@RbavڣBQT8,M3gkG~*@ʲ▌WMzpJqqI^.`Anݻw|tE8+0 3Gqqɔ/Z4))1##]UX)(([4Mm[{iүDUTkܸѦ^2gBtBUUoak֬p!Ǧ3R'!F-8~\?֢-R۩!k>Rq}B~a` X5 !N3 b(!!b#pmK¢bv uW)0 ]uAʬ7@QVT11!P:UuOP_f Zwu#jX۪H5QVS#vMƋltŲ]#%tEXtdate:create2013-06-22T01:32:15+01:00k%tEXtdate:modify2013-06-22T01:32:15+01:00aIENDB`puzzles-r9872/icons/unequal-16d24.png0000644000175300017530000000143112161170354016455 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭTIDAT(MKTU*nW[E :2@3_` &Iϐ082h CTcMx EQu{^ ½?- YBbL BeQA>:P,&K_]4?{vleFzfFaiO>>ufuWubz/~U;j9G৓jznA AjpwROJIU(TBP$@UEH30%'뚦),f oFfo8bXu=h0B%> )13GkEWUSRqSB͛?^B$Zg9ͬ,ʇ/?BFU(Y>yٽk*5!c.^8st:! "s׮<L.%`s#%{fVeYP}|;ף/;GJH;3QJm;G{QPa-%tEXtdate:create2013-06-22T01:33:48+01:00-oG%tEXtdate:modify2013-06-22T01:33:48+01:00\FIENDB`puzzles-r9872/icons/unequal-16d4.png0000644000175300017530000000055412161170355016401 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(}R 3L; LlabSHBRDO T=punGyJ$5f*I߻3fDq:+$-NU #P?Y)!d { fpDK*IiTg vpAg\ƭUIDAT(=OUϽzgVF[?{2&. d+ŭF7#2" JB衧߽(lOι\/@/ c (c$T!j(99b,۲,1 K6E(BFUUף/z>? U8g?S, UE5z_|x$MBћfQ#Yb9"CΝeì?w0?xQ1vlN1㕖ջ]{YI9b]]2Jնa)%fͦ])eԸ5۲4:"j)ilGդ9gCXk^`aEڶ4۪sf2lzԶ];;ܯQu{c6}]ODvo/}BʙٰX|Y5 Gŷ|w}FD2p7og:YZSJ}5Fp+>~oeZhӧN]?NaO}dldE&'=zB0ףvQ#[0%ї0\)j5<ָ6<-).%tEXtdate:create2013-06-22T01:33:48+01:00-oG%tEXtdate:modify2013-06-22T01:33:48+01:00\FIENDB`puzzles-r9872/icons/unequal-32d24.png0000644000175300017530000000325312161170354016457 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǕV\W>?}ǛݙMvic*VUh($6GZL+EQkW[[,UؚTZ*kJAU% &ffgͼ=?la`rswAs׈AB "i @z G $IgKH l^c /5;^t6UcLca\D֬0T(("b~xf>hV0 (b695Uw]ZDUz ̵d5dޕMɒM%K%=U@7rȲ?]"/~y+Q997^g?vn , aKF0GEP(0/6wjuBz `'vf`2Ka`AH~+_W@D9vGxlvo;ڄBLꫯy׿kU%n7ePpȦnz=.?^@ED/>Up@TJߺ`@^}Ŗp8OIɔDe@UU5$[TU1˲ң><{Ѕ -b^G &n_x#itkcmmSPU2zZb1PcmC77zR2djh DSU R${FjTyCRUfϷDD@ƚ ##aOߧxԋ' cbEIƘ0,6q %Q7`juC̨ ^O~/f =/zYZK>d;Nn8D$Juj" 339?5uxJg0sgڄs>(((k$VLN( 㫪` paP%RFQe bm)mf(*{e6fY1Xl >uYHſ}߿O{GGz{#n#7ݲ䈢R2OaouaݪZ '~疗WOFc }UFK_^jSO;z\4lr4U$z{viXUɹ;dʗ`b_o$aX8s充"~7 cWʟ|s!Go4㸗ρ^e<2uDηm Q/l8޿n8ܚ dٺY^DT1Q0 v7m!@pC+TR %tEXtdate:create2013-06-22T01:33:48+01:00-oG%tEXtdate:modify2013-06-22T01:33:48+01:00\FIENDB`puzzles-r9872/icons/unequal-32d4.png0000644000175300017530000000124212161170354016371 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǝVK lS^p3fx2”Cd̸H@73"ھI3> t9^ .S?z^Pۺ>j"e> иO_w> vpAg cIDATH}VkW~swٹl4!/6"MEZRhjmB1TP4(^ ?%mYw;3}3eWϏソCK.RfLH!ILPDeֶўVM2l;[ݮ ""fv `"j4R3XJ*@ RX(t64`(0q,TJa@D`A&P`zhԵ3c0LI@)QWʙ֥0.PA03:c6;Z3z㬵J),/_jf6dZG"z?\֟9k1D46$!|*sBJGq$:^\TvѾ -CpIzgNz׮-*CIdsGB_*1#'l=s:o*kIԥkmY' 70J w@Dd=p _}~[Z\Qo) AM(_n1sXX\|g=zQk+K__.һJ%j@DcΓikf/?O?zNuM~'ng}5׀ϿZkq!jwp*ȷn4¡C} 7Z:_ rT J;D 6,/_ժRAAյ466 :ba lu=k,ja3!z utcNTc3 8WmL:}l4PHԤ}X,[f3c Wb>{} icL_Rv.PXp94'aW͕g_~ZR ?[]Wbϕcg\[o}{߹O=8vcbH%Sʷ/g6v6*aŧy]HD` z#qtםw~ș'I,"PJau}g6YWH)w\nC? BJJJ35v]Hn<7g|j:& <ڿOCK.Zqv5A0bx0L3L!( Cf Ch4a0SAT(S9+";*̭%tEXtdate:create2013-06-22T01:33:48+01:00-oG%tEXtdate:modify2013-06-22T01:33:48+01:00\FIENDB`puzzles-r9872/icons/unequal-48d24.png0000644000175300017530000000313212161170353016461 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATX]lU9wvwf.[Z(EILx#$ h &` ÏhQ Pb|h+Ķ@ 1<nwٙ{|XXvmLza7s?^z-`xh9q_2R;89,v]Y4]xP GdYvoJ%N {nn0hdW: Dt]y&CDHJzz̜Y O3(PP30_2DTR%CC 鄄)J' C> kYEH8Re7#A DH[ҩ AhT)U]]m۶',:{|f,[Z4cIµ!ƿTn54=z2VJsv*rw76.sYU> 4/oٹc_v:R:<|v5 Z;m ~_ $4Ms;5Ml۾KJ &~f 3K@HuJU^e~_D\+I1K:+VN>~+=A@mBJRJ]fׄ+Ç;;%$,//m7GB!q+rN\* RɸwyN b?p߭+VOǎTTTض9!!C;;;8sNGp *w$vM>v^\[+BeS)) oWw̅'esᲮ@`Ps)f 񅖖Hdжm!p2hl\>< R±̔Jvϫ'\Dzh, Y" D`BJ)]IP4M6f"Oj|&̞ɡ J4u0hL[ 'GPp81)heqW(?U RJf0+.Ɨq2iZ(4eYˊS8`r߹woDqI"aH<*w1m{u=oXlj;f3o0ݻ 0 eYs? \*8r]|>?߸ֶFV;q)kZ,kkϟ=s-7:M.9t8g0."!":"oe)o@fCEe/Qߍ͗識9RHD Fr`_6LӤ/-Ƥ/{r2_Xm %tEXtdate:create2013-06-22T01:33:47+01:00S%tEXtdate:modify2013-06-22T01:33:47+01:00IENDB`puzzles-r9872/icons/unequal-48d4.png0000644000175300017530000000146512161170354016407 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WpIDATXXAv %iȜdz͘(]gE4ybXDď brDge XGCjm9,p!A)!m:8-kZS6Q Li砇TeȄ.|񓈨M=ሂjYM:fMSBcEg};i݈q+S1TixAD?uR =LQe۪XU.3$VN0 N@K杲à ;I#[mefň9xKEYRFreS u ;>F83.jA~DNg%tEXtdate:create2013-06-22T01:33:47+01:00S%tEXtdate:modify2013-06-22T01:33:47+01:00IENDB`puzzles-r9872/icons/unequal-48d8.png0000644000175300017530000000463212161170353016411 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXYM~UMwO]c~C$^Y%ƎM!$(DDQrERnˀOda9XJD (,8D(`SU/YV9ԡW{}xq}d찎3= rݬ@J@о1ABDT<7\jHMk̎Sp'kA81yipd2N@MMwM8]Jk~4V[< R 'VBӎ8^h08N$`Ώ_Z'Nl 6a㘐B2ld B:ÌA4q~AHDs@ aL+*_DdidFB7R2  aDy8PRZ+W'JiJζܲc<ϝ\nÉ'JK剛Qb,7>y|GMOkPD笕DZm江9v|pd}ΦU1; 啎Qs̰CDh 1{`4l(@6MOuvK7XB_'٢XքBQ3a٨gt4H2Ɛ zS RJFDQT-;m1P,9)M0IKj!AjG 'eCJyn+ #d q:J+lf)ey ^8]#gp:bI ZwU3<OA)~+PJk7NAhN]oՒ )]*H/`a?^m'> z2f+0 t^1':w_)@7HJ sie=>YytYЁ3ݣ'μ,%E-]hɣ(;; "28 eVO2 !q)2 !ja$DGqH$ Zbѳ8I$e6-]rA# zJkcB m8n4֭ ! J0sA`wVzg]QT?[>w|W60:pԻwAsݏ?õZmÆ>~v6 B[˿xeddx֯;6l7h;]f6J\֬sJ[Mܸ񟟾~khC)`Uy5S/ !` ]}}Μ=r7S_׷hٲ ˝SSDi5;,cdn TXϝ TgϞ>tBi[dY0ոjXúW j$;ZtLV+.s]{'W>7k=J/y9Ps@0bxŅ>Bn)$3#RZPI<ϳ H.vd;NB_62@ԤD9ۘ~ R.0icشvuk6Ur*PZ -uYu-]ϐleb)Z Kh!I)npR3R |]J-0du-L u`'AH/eqG ygڹq77o_y ~DLj֦C!Z{2d wEWf+uDɼ@n;_ ?%tEXtdate:create2013-06-22T01:33:47+01:00S%tEXtdate:modify2013-06-22T01:33:47+01:00IENDB`puzzles-r9872/icons/unequal-base.png0000644000175300017530000000410712161170217016630 0ustar simonsimonPNG  IHDRx#bKGDIDATxOlUZU4.%N4PѨlkx0BBBjBj !bb"wI.Pb`uad,{yRq~3ݙ_͟+@Jc [j]:|opI[O갞pZ[*DDDDD.5W}Q'7;V ȏ{$(m۽^K: { ={RLf4?rTpЃ'W[Td2C'َWFwvxd~~^)m&"^V7# |;JUO4qxc(`JM8AAAAAT=RMV'\zHbK((mmn~>ѻCFi|^7wq^:n!QiUwg~/0=Q.kWơ,O]G9W.iE8Z6t4?]sk\ ξ[b|zWX:TKs_<ͽٺcݳkINg|]lp'MWs|5gyp'MKs6|lK)U*{wŽlεB1?2d2ḗS95p ʍiM}sDjSvMٽZ?TKmWck|8¥Umpv?iKQmRtݣC`K(((((zBpLbK((m4ԛ[멑":p-tB/p{7&QڂȔ^tn!Q vpAgd9aIDATxAHQ. iGC=V:h&&CҮt@hC:Tf`y /ieXdtp9o] -}^Vza'F80p`{ajҁ/4'kjí F80p`&̬XSV:Zx~Yu3L$M3ənN:7 CD:-+tvp1 öՔW4OraÉCv3Cf\8\K8Fp+;?viڮmۭm^OgtT1z:[e:m6 cltd/%_D+8>pRCjuS;>%&WZ>W\/5pN٨Joߪ>p`#ΪRj>!sV#F80p`#F80U*8nU0p`Vr|hIͽ~gs|60tW(ܪ?GW\MyY;Z`VY}/\mqcwex|`ssSDN?6杺U<,"+*nߺt}Qr2Rprw"Py#g_D, BG ꯑ:F. D`&CW[4p?|p8HE".ydSrK[ZGDN}h Š$[Tl(F80|pw<F80p`< `ܪ`#F80p`#F80Y?͜MA%tEXtdate:create2013-06-22T01:32:15+01:00k%tEXtdate:modify2013-06-22T01:32:15+01:00aIENDB`puzzles-r9872/icons/unequal-ibase4.png0000644000175300017530000000137712161170353017074 0ustar simonsimonPNG  IHDRhh~" oFFshhV pHYsHHFk> vpAgd9%IDATxkn0݊h>HQJ m?/s5G&VEqAQDqAn轾9_z~zn/^-ǨBQDqAnEǒʷ;sYR; \kZJ9n,:_\ιJIS6/.5rWs9;x ԴE\Z] qi)wĥu܅~]DWkUe [^\%UP[Z;Y{;ri[! &EQDqAyuTP,Q(8 (8 (8 (8Ua=F8 (J#Cpz 7x>.0cb+tϐvw2l$gCJ }ĝ[l&!sTD1  ?YӐNl UǍDq#C_,}E5QG5N< (8 ΫBWU (8 (8 (8 |.4%tEXtdate:create2013-06-22T01:33:47+01:00S%tEXtdate:modify2013-06-22T01:33:47+01:00IENDB`puzzles-r9872/icons/unequal-web.png0000644000175300017530000002107012161170217016471 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge!sIDATx}yTŹY63#0̰]1@5f|&Q+ &+wC\%כ5,n, 0 0338C3 }fNp]}ުzNmonX 8:;o"գIo+;EQ="Ӳ[QƘk\$$YroeeO* b:bS,|l0JKCdfꓩ[@PT |BdFc(7HմP2A!KD$riI,Kir;EQ[>{S"UUw|yNF%˻t9,#~ۗ~33v1^,,H-S":xn2x<_޻r՚޽zU{ƎYѽ0 ZBO^ηј$s#?Y-+kn`Kǒv&ZzݺN#CzTVk]Vܲ?vX4[`0ɖc!!ih,ËB nF{"RU3H:|$X,BD^;nh,bMh5Yi=#DIb6,N\Ps2Vu$R5122-eۆK PFDbB܎bMֈV2jD%d92w Wi9f WeJUU saĈHiza@ I딋K^  >V ~^oO ,wXꬳ睝\ 2C5c}Lp!{eG_ X?]޷og?NRb4C8- PokUj'dVz^oWa̸kyo>/7B>Wԯyi|`\.MWE͸k}~O=9wxPH@ ) U` P5w^РAX^fS(Hd_vhb>EQ3TD$Iw3;G:AdRqYf'{w4xnKng_;13&SNvrd(wO=gH$,Y &!3iԐ$ " B(xggÑB,f>5T! { LT]>lD: Ͱu=8\]SS;۾r;%26`@띿|"[;oiEK8(9}*"6m# z)9̈́e%,3nEfܬOwvAZs\suuMmŗ+~ȼ|r"K+Ľ%x'4oókosMV,!sS ߜ||tȐ )" $þҟSkD$yrl%I~_}gxqGVS>PސCPܿG#WWGF체cfi)5,h+pG$5i߮J ,hCl7g*nDvǔ'ڳcFj1DN6x YWQZB!hSgGev6ۻ&j&biBl 8ZR@j vZ !ǣ:L/1G}>t|}o@E PUZ01{OOѦVw:3:OƦeYt[ a 8+WADe  q]iGUw٣e[&;x_u-aR29a{i~1˽^O4"'&DTVVZZi>FC8R_ߐ~{nG0kjj0$˕-4DTUeш   &I^ 0LÉ3DUƤvXeVַMQdYΉ_afMU"0 %lIjg)I1Ö4dr۶\p^˰ǡo1F[Y GD9P qw͘;2[$1nuʖ-ۖ/[TS_QhΎ0X㫯?Ћt߉G&  8p/]%,3ZqxOݟzb!O|:Q.2H$r/+***!pD!!~…neSԵK|_iߺO=)**zzvYs @ ?>|cǎbK \ !bX ch+ldρٞrٚ9.rodhuNb:?h}~`0  XcStzUf#₏!ca"(1|H"̠xy?lžZOsY6'߹ ,6{싕EA'8ɢ؋GvZi/cTK.tl,T _x"51|oKPUlٲ=kcxb ++KX¬IDwG s_}8J|Hk D$An""At2֚N}C Wely'Њ=Ks8H/A e@InwLv ,QCx(+jF9q' KX,W|phh(l KXq+{$A.{?B@tYK}mhJxnBPj2.q餾W5F)LO՝9̾h֠ЀTCx|@!C7MF'(La 009iP)l'vI^AB?@a `)ُVH;\͙ITT3۵(hm*]9npt)@%UUUthЌ($UQUEA.AB*ؚ"؜ E˲ZغÑƪ}ՉD݂@kh|^'HU9]:6HU` /6b">"jhWO$T G{lJn񢢠]n (1VVVzFD(߲e;c.4|lT٣"\"ճGުpFAYC-*++]n˭+EEAnV8" 1xt+ R)=~HaCieE&wqE!D*gII$IyIc]OC?#r(fUF"\~D$4MEQL3XĢ}U׳ԻsdұX*E9',yO?xyTq8Vѡw`^/cOn޼妛ozVz;OWa{V7o4bH͟i6{΃Ywxg\ߊ,˺=v$zvd u())n] zEׅ99\ zeeiSo4hLd* ?瞙wgVKks`;ԡ |H$uv$X,&H4)8$Ƣxss9󴲒~O?['nne!\pIv֭VU˲.̊"Շ׭(I{77]p?-qXDhA [Uݵi]o6}Ū ?'s:YͦO?[ ,-Q*QY^[c{K>VY/4#N/dY./ZY0 [>DKKK*+ؒPqϏaaDad)lho@@iD=ӣYeaZael׵Hm-p[#sX۟Kh9ޫV "X3[~DdFx@}yur?c̲6ITҹnpC/A W?yk/Ɍwע F4O""MV\-+ 8 uViq}W֯~MVPޯ׾%ntp.^,x@%ߴJhyBm{U;R \x bt?ѤDjȲD177:>eED¯vzy`4X rA%^Ԃ疝-x~4Q׍o.|˔ ,fhLn1S !pm?l~Up1П1y_ "˲n| *>u 8}|VV l*! %.g6>WX<{wڰI-˺i v[: . ֎h Sa{G #y?v ~6! y;6i4}]82x*ku'QQhhU'j[?L:lO'T)̼Zڰuƅ7\ѣFnED@ <ṪHUi価3/82OYN 3W^0%X,?͘1= q Dy.$"moɗW.4_cg6jnRO^q0:;ˇs^K'箉, ]z~IG`ۧ؀x " ;~ 4$.RpNs=4ҁ/'!CIQ"BA S$dȾ[bS,{$3`)+dDu1SO|h˯̟sCvĉ8ft3ĸ "Q;%'q0u0j$dEjQ4(#ό~<=I?M+E~`貏W9?iڜٳ&\9>"!l 53{M\pū@Mv#![>aQP f>&wtxl i%̸ *H77缴䡇}jeY ㉬Ĩq΁$" >)-!5cû}{sOF6"y޹>ģ%aÆf%d#FW_umDZVB4Q<=t:MfS>LalHH$.ےLӊF Sz֐ j2.N TIBˎ1lll6E3Ą_IIEAL/0[yC\fclۈ~'J8d.cgYv9Alٮrh[A: .vwlPմއ.ӗej#.wYs.}*WeP֭43 8B+(-˒$)k;BVd8T,v6/ZED]tb z#HH?N$!rt1Oj/!is)o^r̻-* :. WD!y<;:eϞGo{M6fx)t2Qhkތ1|6uުms6m|-S 6MF&ѣGnٲ/\r#JJM$"Ϸyn}ǎw׌;- 9(^'ǎmێ/\j#F*y'߲kw=3n@G 4M?Q;v/Zf5om׮'N=3UDzB/)8b]x"Z~Ӥ ˜FU<;w^~c~}uhEAQ-[7x5Gew6KQѻWcFƢ1&f&vBPqQpa$گcWvF'$]p9ݳ e| HQlnzAԿ_1bס" R)!Dn@`F,98z\KB9IυnF{"PU3H8'x" tӛ"?]7bK0 ZNV@&1.[Zyzwc7.9-&fg/ HDWStbƲT,cd;zrr?00dQ|O(BcfĬBfq8D"4+- z5+Vol$ο_E,Ӛ960k~tU?(l T5n"NqпK?U<g=ݷ_jñc.VUE‚c0 qQyI7UV%J^"qC{:{=$i&IPֹ.Za^0/_/Y~}%DR7 !:jV [>v4Ct4ȇ~w?Gg}V[W HԌ  5=<4ѷBGz$ vܨ/nݺM_ھ}$mV)΁\ӿmoH8"ɇB9)P xr^SSGeãz@z-(ӴIs(r]}+%S7;_o>'8 'n nռB@@Yk$(2cs/< <܋>bE" J<%xWGR0^IjEhsaY] !He߯X(궦*va]JOy 7GܷE2no>Ys Ls۶߮H$*E(lLx߿R )^M`-ytB1bx˲ʻuM%[UUeL` a^?u(!'/P"K-hh4sq\K 8D2(J(l#A0٠sвd;OUbx,(l <"Zosp̅1VZR]p$[SEE@9~1MMQDմP2B4j7ҒYF[eVpW:"Ӳp,3rr[I`Ę$dbBd"* X[?haL'a2MW7е"ҹ2JDzUN9x*_xR}Fy%tEXtdate:create2013-06-22T01:32:15+01:00k%tEXtdate:modify2013-06-22T01:32:15+01:00aIENDB`puzzles-r9872/icons/unruly-16d24.png0000644000175300017530000000156612161170357016355 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(-MkW94$4Q6,FxEe0$P}SZHA馋?p0^i_`01ݤ!vlY׌f.ͻzyx1cfffc6 ,$"RZf2o}y^Zէ}xUXt>} vpAg\ƭIDAT(uA C>Eo6fd̢8] $|lZkk ]q6\; !19;ݖ%$N@bv]9'zbRn.k 1RY0"s&-撰R!|f;lFLۈ' n_(<1<^2% ׃6w=+łHoSe M~Bk}q &%tEXtdate:create2013-06-22T01:33:51+01:00t)* %tEXtdate:modify2013-06-22T01:33:51+01:00tIENDB`puzzles-r9872/icons/unruly-16d8.png0000644000175300017530000000156312161170357016274 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(M?OYg{fe9 1&4D'q)rR"RDԉ.AH*W| qݡ88)vʰfߟ+"HSoSc?Z)M `&):4G_`X~`LF]s'KG/_&''8{2\.wǧggJəY!I!OMM/w߾"6}iQJT*l6uyyݻ?wvv|~r20lJ<M&'] ZRZ,N&G]?Wգ r>_XXXZZ*rsf]enmZezT*RNOO9NF+e|x7/.. ι1n:><< ds΅!x&!!cm+%0 h^NGiZ1!0 `:NqnJJe8ömݟ4ðq~H$JRk=dq,КOo*=R$h{{xnn4P"yV50iN$FB9kMӤۗ%0 woWҫ8LӶ?Z?[/%tEXtdate:create2013-06-22T01:33:51+01:00t)* %tEXtdate:modify2013-06-22T01:33:51+01:00tIENDB`puzzles-r9872/icons/unruly-32d24.png0000644000175300017530000000342512161170356016346 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg PIDATHuVKTk?9UB8[qh1C܉h !($3E h+v7v:\S!{P]goݯbqj01d |]1.g?ceY%"zC\[#& ZDdSJR^1&9eY'R2dǜsƐ!!(%EQwF>L!n)@DX<b׮]F~ۃ~?ƈ[[[Mq5Z)9WJ"Eb".9'1ccZ !xĖ$CZbFk-liwD̲( !D!)-=RfY97p<. %vz6DEd2wlB@J|4v(Is^~իUUu:Ǐb>߻wΝ;srrѣHZ,˼Kg$X,c$LmllEQ8,sNb@<766=c4,[pW"" !8BҊ:Oζ7-[!9cZƴfq>r:@'z; \Yd TESJu:,cx?Zj dB[9ι/_8x|uBNmh? }IvΝ;WWTs~nzOL~*l Z"(n2Bj51ecnm*3B/RdXMH)I5u Ɍr˗/BJi%/RLȀ1ϿɉR*Ƙ7Zu-e[M͛7{^oi5J,Dd,x_5&"(p8y^Uc1&9wή9@ƀsF۪p5Z-xs99~MAʤ>(-ET(X眵R7d\QE 2V6qT͛7{…K.9MӼz*R! =XJ {-;ZJ)@[k_~cR+Wܺuk:R5}yHͣ鴅[r`jљҢS1ƴ<5 MX'V;ւ\=UY/BrU-=~ BD)ť!DUշo841ŋRJ6{{{>r{{6IAJi8mmmYk;ޞ1&ƘeٻwI9YQjҘ-DEs^5k;x ;:'ε)%>Fm5I)wy^L&*ԫ u:8XMs]6}DTJ}_R,M RepΦlÀ1)ELX5\ "z6KKV!RTZq]Lkh4]~v vpAg KIDATHǕVQ0 7s9Y!m_[q,, ff"Z˝޺ZkoT!20"f 8;m UPZ8GJ^W2J22g' \ըAO0DSZ{̼bi$$Zԭ0 UH9Ҝ{{ws13{լY?J\ %KO=ksdPs5QBDdO\ΐ;F=NiVCj oj̬7DLc1u6mɀ5p!?lU0TSBaj5kfS.qED,H@ #c ځki""xXKx3-vN2ŌjP@4X5s~ An8 ֹr @i5}r>IVA@cU(<" r8hu T猟gJr0p \nl29cbI:^=7U}O@#5UmG=8-2=Vns\J{̀gcGZvy-eC%tEXtdate:create2013-06-22T01:33:51+01:00t)* %tEXtdate:modify2013-06-22T01:33:51+01:00tIENDB`puzzles-r9872/icons/unruly-32d8.png0000644000175300017530000000354212161170356016270 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHuVnTg}a\e,ORZeر yD (H Œ`,,YvU= uEQΠtnkٿ~{9BBp1L)2RL1-1Zc9ZۯBcO)B);Y4>"g+++ ^,~LJBZF#!BJ)X1x,yevvv5tw &ggJ)}UU+++BcV כ +++t[n-` [k1,ˤԷcc)v16佷2Ƅ8KBYyeY!,K!,Eȑ!`,xƆNHB0zi9es!c }gYUUe sJ,EQuCDDԊs^e G:ƈȅۭ! `')Mp9R:y!삂vvv4OcbPץUUeY)߿V4Z듓ׯ_+b˗^/_3ui}4p,vww 'O<}t8rI)ϵÇƘ7o*RLDe9??S"êRJ>$d29۶-buu 0yN%MR!!BJIOZ۶DRB==}"s1ƪ@PJZzVDWAkM*h/Ykm[=seY]/c^{ PJ9_6ξ$Xn=SRx핒IˎM#δmK  !x)<\0dz3}ݶmիWt"YR;w\^Su=>YL.eMJ[׮]ე{_Ǐ,K)y..v$ ӵRu]_As7Mc4Xku114eM{OOG[Amy1N9G #^RΑs3#vMc |2\\\4MC1ZmllH)@9ڶMY* HyJkTeFrΑc !eK,MK WUyEp:}L:pR~_#Fˍ%tEXtdate:create2013-06-22T01:33:50+01:00^!%tEXtdate:modify2013-06-22T01:33:50+01:00IENDB`puzzles-r9872/icons/unruly-48d24.png0000644000175300017530000000433512161170355016355 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATXY;o>}p\Q`ˍk0,jB06ߢҕap& W0$˼*ʺI-}3)R")<}3sR{_>ix!*oR͹(ꀤEQ|SmV(Xoe^ 8tVwk/?߲,SJ{[8JA r{| `{{A@EQEvB?_%lYePkS8:΍7R|xnsn,K\n:礔eY*֬mmmEA6WHZ( eYEy4UJ ch6Rj8qy h A(5-qIIkMI)RfLs^OJt^%7y֤ѧ9W΅rch AEq;p8i}@^K(T9 H$ C2k ȑeY&IE@ia4==::L&Y)V*MӍp'Iϟc=hssǨG޳gshOA`#aEk֊oCRk-kcsFQ$X$/am=2VEn !pz^A1b.$3A7оp{^9pSj %a\YP%D Ԝj$@)Qw hjJ)k-E t>n" 2CJI{"5I3eZ繘g(FE9`0PJu::8+g[eggg8#R*I$I8~[]]MӔz8LJ<ϑ.f2м޽{oβ  =88 ݜsB( +UJ὿v͛7kݻw&LO>}U!QA@(1ggg˛N,ʖy(97˼-v$.5Zۗe`0yin9 1<}I-,WHي5ơ p0A@-CrZ-ТZP,ĽNֆ r΅aX+aр,q(ʲ 9!A<==Ea/_ty} }- C, nZKY~<<ϭTJEn\eW˾I1?! *ܯٶx+O|¬y!y+J<EB6e8r ZHC(z Y@EŃ`ggp:ƒxft/J?~y{{˿}ŋ#{}LdL6r?7Q "k-8_~rr~̊J7Zk/2)f,~.CrTSe vpAg00W IDATXYQ KoVs3zС|$C6@!s}HTJ)!9W~;J@T""9f6I2s sSJ 2&ߍ|>ǎ5%FJP924|SB4խiy_)T*._J nEqqoe/>48G}KZAS{ =[jx QF 5I)rY#rr-fTQ,sΣC15+K4ᄩN)К)RJ-Dze37CeHu@)pBSg*k ee߇ 뫲2G!eEEJ2+@.=, pq1J[k@7}7C DhD"K~@ ܒVQHV1VBZݿBݑ!_? h(fԭ^a=crD4_O/65%tEXtdate:create2013-06-22T01:33:50+01:00^!%tEXtdate:modify2013-06-22T01:33:50+01:00IENDB`puzzles-r9872/icons/unruly-48d8.png0000644000175300017530000000517412161170356016302 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00W IDATXíYϋ~gf!Fb/: kA>rp.)l>7B %6Wև=hYj~twySӳR|Hꪯ^Ո_B|Bv_rӻ)%)%WL4dr?17D()){ƔڶPE1vWLDMR*7eYjCJk"A5MoT!|[RJo9ؔRQo9|7ot΅WѣG'''ZR{sq-<{*K s޴m;L^{cqR*g|>z !aL }o9W{ݻw1 hoo/]îV85SJZO6ϥ]'k-d2)C%a|>Gl^AYg4MDF$ 0_eD7U>ܞFbҊB "vA ك+WbbWX(4R21Rn=( W9瘋@ J#:T׵s4JIC;дm{||\Usn6ckk)^x\.!!Tr:޻wp:M]OmwyGrYk1D$H'0N/pFp8u*I8 |2\5M.6cu[(wnc!@?@RQ:rp0j "O)!Uu]JXJ)<lU(#i0~kPXsJ(ʲ,acs%L)mRB N*UX[\o`큪1,w~~~tt411|8uBsSJJiHZ{yyS_ΪM˅Q<;8?dB$P4y뺾}v/PC`4Lɹw`0X!Xkꫳ(\.Z !|yڵ" !Ս 4WpSU`0`wc,BABkzBϔb>1'+=8B~EXvGIk$"C.̻+;.qR_" }-.tVQ6KW^9ΕRR '|4}ak_"hVǘ9ǣkS \OŢmrDdy]A*ʲ ٌBhEy*l7<{J뮃-y; ZJ8z.ލɐ$ֵe'%'W!|ץ5t% k|3rJz55(,_ȏU-,QJA10.p`›pwJ lB]Hx8bb%fB\,iz5@QI)ݻw' sѣGUU1DcǏK.CUUݹscA`0ަqe!8'ń,hBk1136溇csxbULAgG%" 1l Xe611m'N>{`3#+2rN8Ȃcg5Myܬ5sŕ m[ߎ-%tEXtdate:create2013-06-22T01:33:49+01:00ld%tEXtdate:modify2013-06-22T01:33:49+01:001OIENDB`puzzles-r9872/icons/unruly-base.png0000644000175300017530000000210712161170217016512 0ustar simonsimonPNG  IHDRObKGDIDATx1nFQm<^lu &؃ O`) (f>Ax |׷|;@/7_(i%M 4&PJ@I(i%MzˮŸJ@Iߏ~N}ޯ 4&PҮ#vu]o@͝-r>7vG_ϵ8I(i%M=W!lsē&PJ@I(i%M 4&PJ@I(i%M 4&PҞʇq6G|ͽ>NA %M u]O߾qw,Ζe97exJ@I(i%M 4&PJ@I(i%M 4&PJ+.r ah$m^Q#4&PJ p|P Kx;(i%M 4&PJ@I(i%M 47+*T=UIENDB`puzzles-r9872/icons/unruly-ibase.png0000644000175300017530000000210712161170355016666 0ustar simonsimonPNG  IHDRObKGDIDATx1nFQm<^lu &؃ O`) (f>Ax |׷|;@/7_(i%M 4&PJ@I(i%MzˮŸJ@Iߏ~N}ޯ 4&PҮ#vu]o@͝-r>7vG_ϵ8I(i%M=W!lsē&PJ@I(i%M 4&PJ@I(i%M 4&PҞʇq6G|ͽ>NA %M u]O߾qw,Ζe97exJ@I(i%M 4&PJ@I(i%M 4&PJ+.r ah$m^Q#4&PJ p|P Kx;(i%M 4&PJ@I(i%M 47+*T=UIENDB`puzzles-r9872/icons/unruly-ibase4.png0000644000175300017530000000174712161170356016764 0ustar simonsimonPNG  IHDRO pHYsHHFk> vpAg]Ug"IDATxQN@@Qk]Z;eK=P 73iRm]z}&PJ@I(i%M 4&PJO ';?i%M?<qW3c뺎~!VPJ@I(i%M,O"PJ@I(i%M 4&PJ@I(i%M 4&PJ 7c۶Mv[(\x>_!xJ@I(i%M 4&PJ@I(i%M 4&PJ@I(i%M 4&PJ@I(i%MG֌9-4&PJ@I(i%M 4&PJ@I(i%M 4&PJ@I(i%M 4&PJ@I(i2烎>b'M 4&PJ@I(i%M 4&PJ@I(i%M 4&PJ@I(i%M 4&PJL0p>k?:VPJ@I(i%M 4&PJ@I(i%M 4&PJ ysqNt۶wӲœ&PJ@I3 񴂒&Pn04&PJ@I(i%M 4&PJ@I{,y ![%tEXtdate:create2013-06-22T01:33:49+01:00ld%tEXtdate:modify2013-06-22T01:33:49+01:001OIENDB`puzzles-r9872/icons/unruly-web.png0000644000175300017530000000741012161170217016357 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAgeCIDATx];~N]x,K ٖ,! !)&pf˽܈ȉ~  4 Bk!V»!`g ݾLOuy]_骩^]s_vw A] Yh Mꪃ1Vs“|E4 u??Iή&TkaQ5[)0 k8LLcֶmXxpRJW4k3P .JꢸLyWUC<ؓGd2W^*@.wppc/8aGkrq&g Xe$FqPyxBH寋va#7D^0\׭\@s8Nm,q5mf}+K^XD^y96T}&_bxfE2!y`޵9__$gJZX 4CS<4CS<4CS7f慑QvRi/d 3sVS8p>n/#J ̍ﯯ. 8mQ궚|4C5t~N^,-(:J)xuDk`}w@ ! u],9Ju-{8믿pcÇ/^xZߺ:wܹs0z*zU_X)WadY8Ϋ|?vhl:<*M*M*M*M*M*M*M*isʮekC7"|Ѣrot 5JpV5u p@(c"kC:ǧd5gΜ^0vwweliAO?3,k4 =tP[۶82~ E'9dGJeY2Sqoo=p,{DFk8>wZG 롓*ǓUS֍!3jΟ?Ǽ) p9[Z r\JiAmE3lskjE*Xq(.B( !=lm$/T"n6AEt;S9R o\NP^a/tG$,H 1,BTI,MjA*M*M*M*բP;"pδQ|UO0PFse#`#i#,h"k9Zgbt;ueVqVeD.3 Y_t  0 Lcׯ_G{P;;;2u "AN{{{ RU|ڵk[[[h%?@ o~g\uݫW^lwwIz{Wɡ;޿0(B+t b\yv%{f&Rj3tW'Seʗ1VgK=88Q.QJ<_ -D亥x, !ABjc܃O?LY>6in4[; @+=)y2P"tگxNֿ2M=xZB 4CS<4CS<4CS vpAg\ƭIDAT(uRInQڶI@cI99MքM2{C$oU*7x3_G1Z!Spz>~IE۶"g `-|t]_92)D^og{/%m`VU2̹mD*J,Y\JP3n`B8r6uQ޶{{Hڲ\֫ EQL&nBL4&mjRcPf4M1HΙ,`10TZgZkdfDdQ\J)f"KD7M]7RJJN f@W/M3$IX,0,af)(3HD ݮ:(s0 m5MMDQq%9BZU Ets19h$<< V߷ oe( <Y.{^87;89o^P֎_/9 %3!?. ָ?%tEXtdate:create2013-06-22T01:33:54+01:00&%tEXtdate:modify2013-06-22T01:33:54+01:00WLIENDB`puzzles-r9872/icons/untangle-16d4.png0000644000175300017530000000053312161170362016537 0ustar simonsimonPNG  IHDRh6 pHYsHHFk> vpAg\ƭIDAT(υR ,Lfh $Ab?w3 MUTuÑ'E;V.sN)PƄChi$4e8RIZy2fK_|U($ vpAg\ƭIDAT(uRMr0~zdKɸLp 8ܣ#ؤ,`pI,ˑ%pHɂoCnpF-sU!Mʽ}JaZ~ eY8?7ժe弯uOvl/!{yZmwq^fE'-kET)%~DiJ1c:)eQYe;~4 AD砪h6Oi,{D0TjM|<ΌzD+5]Jޅu*2DꜣJ}J; QD~FB$Zw̏B$w;mItRfE1K9{_URkP\s*.c>>t]VkHDs𣌿Y$}@1iZpp BpggLlyx23F[|Cȿ`(3\>a%k14cFӱ}N%tEXtdate:create2013-06-22T01:33:54+01:00&%tEXtdate:modify2013-06-22T01:33:54+01:00WLIENDB`puzzles-r9872/icons/untangle-32d24.png0000644000175300017530000000300412161170361016612 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg ?IDATHǕIo[UϹo{IvM VUH|OR6 6JJ 26ghI\'ӳ{ҽwcjD@1ƘgOL3P'lⱷIוwD尦i8Eםwݯ?wJi*H) X-BFh``s{H~ݿ<zCDPR8f=;".2"SUTZ(ީVjUHyq?J@R{T,LMMej/dɚnX2m{gRT.aXOV[( " PɹD"¸mrp3Ѩz_+(p]7'=ÇM@v3E-K)+JJ~=؉DdB\6l8f])-/\=AI)Q5.,.>i6\.t]}϶ioGhudt.@+'P'lvT4Z(^iDvD5mll.-=BGG/v}J'&& zQѨJKƵˀ01Qd׃ zJV;88PUȈ(ѳ\.a/RW&UE% hBBa<༭!O?n}_F[o_!rH"p quhw]T.ﴦ \} 2EAA7ou}ݽ=MSiIRӴv}kkGG"Ujiuzj2LApxxhxqo gѨaD$hѓt1(-WBD"18xQ sRjay҉QiJ)=А뺭gYt:nѨj*q+HMyGz; !jiPM$!"2 })eYds"JkX"!"mkVUU|25ybńsŢx\H"o0VJ&;mxHČ[ں CDDBz9D".9Qdkkf114M_7~47ۣ9S{\SuY[1I)XLH!0F4kN*e"$OS:@]GGh4^3&w37nݺݭo (*4?uq~m64k1:LD#B){6j5/?$^hcGD15Z "Du6Se%tEXtdate:create2013-06-22T01:33:53+01:00;#%tEXtdate:modify2013-06-22T01:33:53+01:00냟IENDB`puzzles-r9872/icons/untangle-32d4.png0000644000175300017530000000123512161170362016535 0ustar simonsimonPNG  IHDR  pHYsHHFk> vpAg IDATHǭV[v `gpeZδ|AD?DD机օOD&xZ}R“u>>xhc fx%/ nnlR'&V!2{AUJ:Oڰ06ּtHY~xf3y)i}ҽ3G+_]~zpv^&A\H@ T#"c "҄g%N …[ZK1Y|@2W2dP vpAg KIDATHǭVIo[U>wxS<wRqpxnvC^w\ݭI!gI !( }HHYP`(ItBdcE"J)Bn\X\괽L&N `5VfR)4 }`J^ ijuqqyl&NQc`ܮd\6Mh:clcs!rl(<60GĈ1O a! P0B4MRmln.?#O@:G_;::]\]Jt:l6덃x<6{iR*LJ!5M46?!Z^ pю4KksW(qppȅP(:y(%Onage%qm+8Ni4G"i9?]4 J,!BݝQq8Rx4H$]jZNp$,KJk:Qι!hTZbi@0au" ٶ9wVo`0 S_b{.p1ƦiRJlfexuUH,+Z51>v^o@8l'Eg HB8CښǓB!,˚'&ƥ&BxwϿ]u9a(cB4''&i4_~F˱10]˪--ӧ;X<5RcBaRbqOf;?O]t78ae9Wnףf2jN$b )127 p"!9_/`t%F%tEXtdate:create2013-06-22T01:33:53+01:00;#%tEXtdate:modify2013-06-22T01:33:53+01:00냟IENDB`puzzles-r9872/icons/untangle-48d24.png0000644000175300017530000000464712161170360016636 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATX͘o[})S)KDn%;k)1 uQ EHɾ](4m-4-IF-'eJ[&%wfNl-h΂ 79!_S7ҝ?} DBG Bmۄca=BC>.t]YYYep@B`0|peWү֭)MS8D !-EU3'p@Tkonl\^\|wOP(~B0gfJޞjz "8מ0\_dʕVWW Pe,DTeuuW/87?q:$D">4tJik&HąZPz`oH$yޱ!33\LKr)5nllD-,'2tZm|(McyZidn27niID$Y֯_,;ZT9"\y +˓L!wUuvu^m{oQJ@>cuH$R7nOEIBD%s OPJ=Bpؙ.\\˒8He"9jD׶Rid{5?2u.4B܇GQUUbqr2n:ݒL4i*4[qSqLesY!BBk׆E1cZ@cEB/,pƁw{ֶmٔgȲ:W_qGB >,SS}qXh;ozZeSJBcLӴ>Id$'s]׶mIdYDjJ8h4==$`Os~wHvwwjj۶OCBc|b Ky:@k>;K3 I."К=I( Dž_ht]#H"ohgvqxRbO|xjWVV3l6TW[ @@V-}yJ'B荮02mKKs ىP(T*1p}qjwHxgۙ(ZVTRPVɲFR)4 !_}u{m}\. 0 ?|LC-eYs]X,&I":DO~LۓmOT0eba c~:ν)8ΙχG/]m)=s\45R,ef,wlg BH3SZZ48dcɦTJBaifvV4˲L !+1ӌ[]=smpȲL]9)B:#D"J%-ʅBannNU5ӊY }=lW9--o ^~f w[c,L&B0(i, z۷޻U(-Dcaii/:66*K>YXc"y^/nܼ)r,֐5M{y<{dlT*UU*^cKMc]c744tuv\S|/q ?y]0 BA}`3Nclw&݁$ eΪx#`@.82(!yZzsX(r4d;11oەo.,,.IS`0$?B]ޭ2QUI@Iɮ4ˋwdoOښC5}L>""JdY4\0̾ uu7(?jq1s8BTP*$"mہ@…S==CCvcznJ`䥗pZs/; aR)(r]YgG<p U3@@)0G?]NY%tEXtdate:create2013-06-22T01:33:52+01:00E0%tEXtdate:modify2013-06-22T01:33:52+01:004+IENDB`puzzles-r9872/icons/untangle-48d4.png0000644000175300017530000000123512161170361016543 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATX͘[ Ou_Jwl!4UK0p&"OvϹMFuGb "( < VD&M [B Ņ,4ViKnD"d֝):;ŹarZI¨("$ <$+P-][D Ub~}2̔)JZܗYՇLCu(0ԡϔi(>SP:A]LQQO'2ٯJa߀v- edbu ]xz܀x`ֺi GJ 6UPeȼdw\RIyM$ukPyxdԁt- !k,vLفp+X5Bۏjz3b$6pwO5 [f]"vKGFhܼe/B7@Jb\'s}V)}ܱٞÓZg?|Wv;t^%tEXtdate:create2013-06-22T01:33:52+01:00E0%tEXtdate:modify2013-06-22T01:33:52+01:004+IENDB`puzzles-r9872/icons/untangle-48d8.png0000644000175300017530000000434112161170360016547 0ustar simonsimonPNG  IHDR00`n pHYsHHFk> vpAg00WIDATX͘[lËd72׈m%eqf(a@lÀoÞviȊm-MI'4bY#[r$%يDR HQR lvm'Ya Sk D>@Vz;AUUus YU,uZ8|2 ֶÖh}N.oCij{Oͽ6TR&0rҴ|::ZJ$aR$ 0ߓo^VDQ_'vu ^h!zͅz4WQԋo2Q] ‘hB ՙ@x0s. ɲa>0 dj?i*˺Wn |i چ鲬(jfEjrm-Mu8DH9W4M?T*O04t{ 'FDW~xPC۲%gHxxAZ]TBq' I:H].\dF)WN^+<9>8Iϩ_w RƧ68N`Hl[3a]]{~I/s2@o!s;m2{.kF(|VmRki Z(H*^IaznFUK$IR4nއpgj!A$)dp$}L&L Ve^iJbDbo^ko_'r4#Om1 0tcJ5?:`h2ˢrX3==|MJ{{<0UK rM  X+HkTʲryA2 j^ef?!p[-"5X ݌9'Y%tEXtdate:create2013-06-22T01:33:52+01:00E0%tEXtdate:modify2013-06-22T01:33:52+01:004+IENDB`puzzles-r9872/icons/untangle-base.png0000644000175300017530000003740412161170217017001 0ustar simonsimonPNG  IHDR@@B2bKGD IDATxy T3caRi}d{(ʒDBȚ=!dߊ2n ҾjTZ{)Yf̜y㎷= k w00`a @ < x?uZFƙkΏ"+d-- e/.`0`(%J1UL&I@P+SijsÂ1>3>RELa2!h* BT&ӚN[YwHÇ-eWB" "N/,+kjyʀF i47ׅ;:\8Z d ҀTzVFZG0h XD!MZEDsu@-++ a!fІԙ3g1* ``dTSw;:k>lyb%*AhމLJ~>+;FqV`@yvYYEee.,, APآ7toS{}NzFvCcC[$$mo߾+,,*a2Zt'O} _dE_>L&MwL%N(PKkGbz:+;BEE|?0$ $%i|zFFf타`˗l˗Syed--VT`0Ob2׮֞]lCW7ϲ3驨Yo 0\Ǽ|BXo^KKcx`(*9?QBbL&ҥ+YO>jhQQёu 0oh訪.--ysGXgw%]!aFf ioj*)9y5/W;a::ײ"6[]o߾=~xf cU=psGVO4IGGK[Kc̘1,V;lk#ގSsNL6|:ŵ .?***MG[@_o֬lQ8sƌ}{__&##lz{{_IYII__WA^ˑ%2},^dlqill̹sޖ͛6K TVQfSSZVQWw.PK̬jY-Mӧã}iYyiiEWwz]SRɻV ղܼ'O]nim%:2`8U^^umQMGGKv2؟F̝;E%9yƍ4ߦ̙TSee%ikkᄄ?CNbag0g֧gfmjb:׫ׯ*KqB8]]-]m.SSsĄ#lƍ6m`jyȀfjjMaaɓO55z<˗/$ 80ŋY9ٳdwJ 9y99=b#cS}?>}>_pɅ [oLr8}}=M 2o:wd=eYٹ8!! 32I |򥼢⟷ouuuuxrŋffpk&y_9/_45ټiAe(8UVVyuk֬齅/pppjeҤpUC@eU3gL nV“-v촶ZfeyOvNnqI:5Xva@ÇUEŝt-ӦM(KHL{. lꕰwN 驫?WTTz 55U=n8&׮ (υfYo$w CEGJ媁SlۻFQG􌬖G&[6Bt]wWy[\RZZZb 4Q(v U.Z=̮;etL@p`PLq??[ͼߠ/Ҋ--d%R022_~w!im}{ByyKm-⧌HHÇ-fSTTgW.H2fF;}87 :yO9+#®.RH9E&ќ^dd?}B~ODDlIEE1tj.Gr5!iff:n8ݰX|cU *>,g>Hd]]G@AA^uGmWk0+((544]0|r$X^^ {[š[Ϟ?ԬT@lH >Ȯ֢Qk+]\ݩڮnV}b4Wc~|B 'K$$XFFZG[7_l tu.]bh>)!dU'O+{uwP4XR's$hPp܊<:s(l஁֬{$AxzqZZsf7\=] EE%Q=2EUcLK 3n}m;{'y4.h?eA@wpN,*:a YKs.VMT<|آnzJ /6-4]ݱU$wׯbbCy*ܯҥˡ!A>RUG(+W䉕F ͎F-?t`rV#4MDd$k/eQfFJqyKISV%xroo/+\ᮗI4-0(5,4WEt03&LtKvfed&$)|.]`Nn ''{ te}C⢓J. :46qm[Mm5yz$~uuU 2i,&3fn݁ׯaQW6T.jC7KAL4lܬ'OJ ijsʕ 0_Eמ[oIDDPtuv <IIK 3K W_S(IDu2|R^KA^>X*W-8$Ȱe$y4xkh40:evy9Y& P'jF\~ɲJ ƒO466mNkVϞ?Pj|m'$ŋyD-9ׯY`>]: x(fΘaӪLA1QQ_@"Y;"wL&:N3g6n2Y0^!BC1k];w| ۷L&j /^y=5%k߰ LDb)RRlZ=PjId2qpW_/|ŗ/_ط,82!!l+ƜmfHKHKv{Buqu`0dQL3G ~~eK655igϟ (;;20\( 0{ࡘ3Gf'G{P(T=.|D2%ʕWY྾̬]vn6ċٸZLysϛwwUQ{pBd.%5Twud1[=9/&*zDIIm`ðj wx* nA&͚5ce|_hv۷{ s4l 4'a0E.Ze[)T+]cD54d:/45Cݽ{)N(:o~ρse OY-{b+d]鱯Zjbj1y$ 2DRgá N>BªzAl~bVx{֝8q|۶ef}%]`+ F4ZgqI^A Hhe$QmđotjB]9cCiy+~99rfl~ŇŮ^׻l.G6mf='#%H^~ECcPC;a&~~3 30s ??5֬Y{ hBl 2HT?~BM;vlKˣAKHhĭ۷BVȲ'~-M4*`ϿNq:ŵ߾}tJH|œ92d:ۇWn.",.BPv (Ԑ-M38!!64%4Z0 ())*))\xB[0I]Mu?~h`WQ3$񵽵M~~> yyL%Cz{TQQRQQEJ5PT̢ 44Hj*W]>REL߁2tʪ oqmie0",XPP`4JWwUQ#"/YL"Θ1*A¦d: u(2kᄄD5"QJ5? O8PUU  #F\ i4X8L+ + -F & DFɋeUPNN, 0"~&""9T 3 0x5| L 0  .{;!:N!΄5 ߑіԃ_l tu2pBG Hh+c0Wb0z %nwh 1Z[߻s?|( J(G}by[JA#Q۷={]7mܠAп.p =\'Miw!`Pq+7ĝ;weJ " CSc)i9Y8Z@EC{W7ϰЃS~ZPqL {!lr<ܵC30Lo9sd̶U<4 CZzo2S.L܅.8)$FT,1_xy85XfJ{G^''K] 0l0->L&[A^nkFЈ[;;ܣ\.!J-.)-`P`oM8\-'%ŏ;ZC=pӃ.|cys>uܵ?,vLuK~w=(** w-k p'A}i/)p  vE ۷oN{m0WSUPNpǍeBȗ_p[9`:ACQ}nv=hRNV'  .Q<ݻw.nӧO-liaz*kF%_ЬY,̷]F|-#Ls5+'#3L>7յL :M~~Ex􌬂9`kCrp*g:i AS!H2tʪ8 d/^zx:VCÖtz! D ʚZ=2hN.v6Y@9hq ~5rC5!!5q `FT~cpRBbǶO^ppb:?BЄAHh8T ՞;}DXUv6iϡj8'1#(X^^ uႅ#w;98;-\ZNCZLp;?hrz}V} >eut,##-@Pǀ/6(P+Vvtݵ0T:2.{. A8n A/!^b0x|-q8!!S͔uuU,,/_U5g##B? ஁-](k+ .//oin|UTRRRx:^~$i)k. -Go@ wFaedi`0ΞOJNc0vXYjX>}ddlυH7:*\kQBGm6X,V]]dA=sNXT\˙ ۷o{[ ?âvMnvJ-m'{zz.Ga{[ t> ٔc !]Dʡl*oN.lhl {u`,d2GK/=xȭ[II)Cr7n܌=,J=Α?̑9KM-#m>{{ppa)B0?7ʃ̚938?DΗ/_ut #Z[qb=N{nV\\.jgљ B^yÒ#S$[s;W ?}!$!1msY1cČ7o3켇[+@O7_;q}eEiw?|^ И~$& P |c׻\QfGuUŋlv;8y6 kOI,<,0??o77J,]f};w5]mav;ޱ]Nn 㺺9БfME::MY^∏d2=Xpf bFi25|8&# cIJN}/v HXXkji55H瓎OaeI-`hEeWZZvwHo,,,LfIC0k!?|X>Ѓ@0!?8`qJZ.'I$M@@ *2۷!,@ >,3 {{{ Jt O:RQQb 8...ƍq?< Apgg';),NK˜1c Yw!J $%o۶]LLl ˏ4T` VFvuu<=C-dmرi&[-ƈ_˾"4QQQ_p2+;Wv9sdXrM81-%JTLTUE=<f/_d?fͪ4PIDAT?g̘gNDXDAA]\ "nñZ:y!ANosDGGu{*<[wa:z:靧 6m*K˂8w% =zM`P1-.<1y$v7\J\^x w-Pq2RmXVzfرmd4H;VE3Twuч'._bbb\Y^,&&Fl񦎎cƌPC߿ 19[f[| jH.ׯvS脊{`A!Ay붝^yUf<~{898>:, -,lܼTT*JLM6㄄8_h`0>^cu[{ uᯋ,Ct*ʋ6m4mА zWׁ`!ڠ"8PL&liTTS`.>ٳ(k8 z{{+*)ǎ PUQft?̶'&쀻C྾W^WTRf̘줸v (dbؖ-Fpp۷Ҕtnرo#hk]v pWwӅisddÃsl%)9991VDXDEE rBZ;;; Nef,Y8.ys!jj w]5{q Ȇ_~MHL&k޿0%9!pdz!"k`XpAXAww܅Obb5׿~&';=<젔l~ǽO<]xYEť)?m&""…[s>;3MRr2ǫ矷zڅg'JH 0(67' E{~ձڳu JN7߂!M'D-[|jeevKC^ ӧ'Kx񦊲Ockm\fǗ_lmRSC$ፇX8[Z픖(qvp37W3g:q|ǽ읖.]B,nA g# 8<->.fADi"#1ULfI|mor_Sck=mwwϙ3ERg m@&TltStt$>Xy|NdZCTL5~-$4Ngfhh^}(1g~i@Gh⅑{>xZacÖtz# \@>h*xi)V .3]0 , ф4OSnʼnV8,'B UwdL8rb%tcc6h;YpZ^lv+۶O*RF < m&dv@ĻX`aqz?hVaaqVu7\U9.]aX`yy v&T:{S>|dUCc ۤI].DzۙNQȼ8Su -vx =t &88z\eі hS;`<5U۷߽w`fӭY9߼aU%?&$ {("ap 9<=45p0dz A='n: T{;O6޼hԴ/^`L{G] K 0䯢\*&Gw;Ɩ ~~իWq?̷nܜ, ,1B|\L]Lk~d/IaoY!+;uꔟ~ 5;v"$:&##=ʪX#3n83bbb7]#A$QRR66;=]c_-"0a^m蘸 0M8X5pVWS=ᯞS͢ CF d"}Ք`σ{(*] KCӒB.] w-`{Ǐ|`8'Κ9?}neU5K>ct sOϚ>}Z\|* ===ߛp`6!lwql0 M$Q&  xv1ERry^neddRS3U޵g,ڸxN[0vqol޼}JMMb}J޻`IӧM lmcrێ -o57xdMMW6 !>NjobTY^<}8+Foytw5ZDT9ߎА으;wcSVRT*S,/RStp[\\} .%p.Du5jM_Ń!#ƌcw4>J g$m]O. k@>,6(З`xys1c7$eKwN뿏D ]ؘCgk֯׽t銆]>~b[~WX DT$ ACY)kwfbK.qL* F=s]FTv ?=`!`j5x{;w_Y)#V f{*J"<|d7nqDRZl& ^`~_o߯uEEECBv@AActyv++9p4VLL?Q3(8j3:GjiiXm:;`UNv2}=eLAZjD C \Ҁ6o24Xock DR* ]6?bUhC4kLO͜>}ZLl .gVklv/^6aObf~>+ ;7nܦǒR*˔)k88--@bч={w-[Xl3&~?-c^ؚ 2Hv9 fwrCbR ܵ l~2黕 Z~NPHъ=|JOO dMv'Oikk%RSKJ/$3rr+yPvr3ENPv EGEmؤc`}<=4bرS+அ`>,VUUy >2qy^ꝇp8"Q-"<\|۷ m$%'''G;w- CD&%͞>w'$$xfգG6l61KKf3.A0p4`Ϟqc&$/>~~lԿ@`:/o229C ,\ ,䠻~,-T0_UEW `4_>x\^#Rb~unn.Z[[%$&b r>l`]EC$..gHls V]v:zGrb**J.{vڳv,?V)>ZXX:I;8$ٱrxZ\`aaW*\Taj-G>ޱӛ{~xmQaAjJ¸qd7W3g:q!70m:a>7@{AI@P+Sijsdic/+fP88wc+ ^zMVSk߼~RA~7Gpv2`:!|@N A4ɴVV  &`@!._bNb"xvA~JLJQV%yy՝e`y~*^Ax'eeMK:a>^xǣ)MUDdPpm[M<<}H LTTtzݸõ5_ kZ؟???rx6~)r_]{_$ Ao0|--Yga`nG22srNjiӿqwM8`ڵcb38[,L&;55gk|XL\0Џp08QRb P`N8\"""v6eg 5?4p?]v杀@70̒ŋ\(UX,͋D yP0xyM4ox oow4…;o6(((K^bb$-%APK#wg\Q5ZA#H˖.zFaEDX$88Qu{ILlmuuuE%YsrKhD'Ȯ0] ޚ_y$Q]MDT_!'ӹ=wϚ9j󵌌3׾GϧqVtOUU1LeϏւ^xIT׼yFMULRw띻/_~ڏOQwᯋ1{(՗tu0ߠ/Op~Ϟ++#WT~VT,C᧌C`0(CQ1 ŔNQQL_i)NԻwjj몫k vpAg@@MFIDATxg@' 6P`;EE&`i E) қ *(*X@:])9zT<Tp$>!퀃Ԇ@CͣZGCgӓg UJm% P[ N·}{* `,6B!~Ei4~{'=׮]lŦRG̙dWCPek'„ieeAHIJAm&7iii %%)0ƈH uLWU._X.@g0))ߨY0[BB\g J-8[JK de;Y_[j9ĉH8lm;ȯ\+N` 8 Y8#QKvtvv ;QZZ>uT36|˺w D,yTzH55ٲr^tz~AQbR 4WRZ@ -^|eee'!>Mn6o  (/O3榚d^?oC3:G8H"R(!&D\x`Fyۮ\xpȔ0{X(8rἼ g]ڨބ'v#b 2SdW%&;t= -ݴmmmP}aeUp?'D2l钨O=UԍKl <~$""**H؊a[[G\@xXHzZO"OE7D]}g͚aW[@@sɜٳ}ΟYGwKƯ9,;*7؊bh\j+9ŸY̘.jfj•`+6 KԔ)t7NpǑ?|d~ϛ[ >Fusi m mb^aeRrʩp,˭>a,6W$"ʢ0w%6=b?׻ b[PPCp8; rɯ=xr_Uu hf225bpXhCׯh2}{c2$ n*lF"t:^#((i޵ O<]ߨ` <=xb$.|zADjkihiWT܊KH:can! Ыew;/Jz-K2@ŭ?x}:tK S`JsKcEE{LМ1edMƉDy?f\jJBPoii^FyZ{@aJLt^~쁭أ+Ɯ:^YUy”::4L b D ԻU=\(D…P)Vl0*]^< x[xLaԩɉqH*Jgϟ?Ϝ=uQ^~MJRܹs9Y3b,99Nr)`/ 3DMMM'"u6566ffYi .8y2[]3N؜d746mmmktᜯyV\m%8zY_mb@ ^xMDdS \l>~4@2_믏 7*noߖ3qgmmk #8X,Fc+»w'$߿ȠU!!!mC 65%aܸq:b;_xUl|'u&wz=J,Mm$+οh z'uOmlee rMJp<`/!!ac0={XB]YYejnu萇ba5c#C,@xyzL?('8Bz߾sx_&=Wo£:> Dh^^l&X,d2oG^ziFBP(T>| g= P(X>&X,666jnظDk+Vl:s}~AѬY3GltB"w'$c -a(vgg畫) &CEY jx bw:j+iٗSHHVUU@N"**mniMm[[[.\JK?lҨ-TUUCm_7onT ;{G<fonn}ub|Lĉ0"qth))ɐWϞ={t GDdֆ_ΞI ?FAQvgCc6뷔+Ws4/deL@!|@YYJkm{&-g1&_Qo圬S D0\v8+lni[ZgMޫ3b9!1-eO8qSp8vlnnbwlϻ&?B,~6KJb7Nή{_v= |搋ٳ8wvvR>}fgaeEf{Dp; @ |}pXQwO믏ff&a!hÔhTGǯ.6@ ;ln}o߿[Zh rj6 B^l&ֶLSS[q:`X(f0%eqIH$zwbXW@ 4b*'$D{ xS0\q5͖j^:;;^OHL6u5y 4"pA쁲?xࣥۅޑs%)%uy>} ]..ݝ-GLe[ێ­l克٩igHIE<.xƊXcN~M궶3.gg//(v\\l8r8t9* ;:e dz/V` :LA>f5ag' m!@Nf'[:/ ~jSeeAYXe]w>O"ZO8ƈH `[e+eldXSSdEFLxO8} s涶>ZحrI.^qâqsfRWWSSS]+ U͖@ɓn}Bъ+tuuUVVPW#Μ)] eyi?ɷ`D򩮩 .} )&&hbk̙?ǿ'N5yW,_>ct_Q[Xa0kչ!/7}H$ɪac``Mq.؂ ~ikk{64irML!*"N~.cIů^=uo$!Ҕ#,<[XT`Ĝ9y%r8TSUuNGG!DgDlCBC A7 MOꞲO_u{$a ?D ~/e?~R7N$/rr/~Td8:魦VؿrPH_xuP*%<?glo_MF-*,,,'+c@a)g?cQA~ ?xH"$s5G9<'߸PRZ/vM@`0}Ν;gYi)SL9~RIY޽]Ol"X^^1]TDLL '+;aqn֎?n۷oP󽀠Hw&G#`%b<R]HYI ((b2W#dneH+V,ߺummξ~۷Hhuu -6 VpWEEI+xrJڭ[wxW۷%%e%ee/^6SL,8nczMd.ƻA'MޱC?91ՙb3^zq_36?{~:֟b9O/US cǙLfaaIHH㺺ΎE1c-_)~$++QQ拗r4u?P(\ݽ˘8QFzvEEƆ؏?bQ|@]]m;9fMY.qkDH/̙=si3gEDF+*=|n߹ mEgdORrO;$̛7[(2Y[|c?%%^q:uڵk0'o|6~8k+Ad\3rztc ٺ'XHcײE%^VXV ?⩜ok\:?x3G3\p>䒕|#;455W=}fj uzy@-6PVHOM5k ~ya`j9YU2k.۷g7KHk~ 6m<8|r5KJYG3---7oTUU\B]]MYIqx TKʆlZNV[KۏL;m hH$]奅zz:7oұksRNSSW@l鿿]ː-m?ιphK1O 4OyD*m{;w+'=C TƗB{-NuL5k޼}~CεO ! ik_HVY2xD5 8@ 6;eEIt%%%k 7j4 1bSVDV]3SS&vfh w~y=dYAA/`%D(RPPplyf6w۬slNvBDEEt4>͢D,v':@㰋&dMgmy,S!Wki/<v"h@JMͣ55T7a*S-+W.ن`EE.ZvIn)6n\YYNGG@5bOGGK v6֢<~Rp9I[#;Y1􌍍#4{Qa颢Cš~l۷m=s1H R[o0VfA lmrs:[OMb FaQ_t[I$󡶗nD"4/ӽ8t%EuUj4`9~Pln9GECt@ JWAm&P!#c66FP 0{H]]9{؀  X-_ 8^6~?L%tEXtdate:create2013-06-22T01:32:15+01:00k%tEXtdate:modify2013-06-22T01:32:15+01:00aIENDB`puzzles-r9872/icons/untangle-ibase4.png0000644000175300017530000000434612161170360017234 0ustar simonsimonPNG  IHDR oFFstx pHYsHHFk> vpAg@@MF IDATxu8 @-ĥwbRI##( 9{g"|4圓~#J?Oe4qM"nk5P_I?Q `ܻMuDUd"O+l4/ɷA|vRjJ+~(l=_2/tR|ޏZUaW6%}[dNXClD_K3:e+;Q+Ȇ9眛9==s@!0NY=7޲𚹒?dGtO4=*+Al:S('D ͱ;dMeV^.(͔P+ q;e)&r:b+謜4Q~:'ZdՃ'0ٗH3Zg씥͍Ǖfol\;e)ڶs(ߝLY݄?#~:,j!{a4mݜT߹ ? ̚#}ztٚeʻ>&FVKE)Y5W5\l{]K:%܏#ݐ8j[r۔pђ*M(BGNȞK5j8AhvSlpgDf}w`}K·>ImUM:hNf-[.Ƶ+&Tst,oMBDsmgd5GX ,hLCi\\vm;7#v4%f9[vy<ř+TWn\@d]d+Ӝ[5KLs ѷfٱ5˽^+b:7 mmvl㑳٫5ҏ#&(ewXS s6[ 5Msdf gmCiͣәe[4_$=lENӅ.(KB~٦²MsOd7zvxdNKne`zNKGQ69"󧥿Q_M0\la`1*OK.ҥF;L`NK.7l:c10Tңf.lW<-}5o>RZ_?~b3l!1C=;qCai5Jbp{otE+ncn@뤊{0vs4 uflo9NK! *NMx?նXmdoiWk)OKZ:L%!{gHX3'4݊۸9vJoN=#;fWRWgJ3s _W|>o^>/_ >^#KKsg2d;TvxOiy5|'ٹH;m-_R([X1ĺvo?baE._+Kb2~]Y\2]QX.a4]9[̉ܘ]aԑعbjztMw,cg2M)Ҽ#Z-Q,!En:# Xe.^ʄ_,YL , +XHX]%OMZ+%#b\uti>#Z +d6÷WQ.U7]2Cn\.qs ̰]+N##9qy0𦈴=ZJen=ZzιmAu6oxD7~k[6X]ԂÃkE6UhHTKĜXL+\gF2 x!w⯙ZEQ\]+ҥns)1 t:fcb*Α'v4C/1\iwidHde嫑zdJفt\=vxY$%16'-_`2 ƺٰ93sk0~^$ vaU[8' i˿|~+>j:[l}qʘ7nboBټ#4:ju (Nه#PZzLⶦAlcj,W)%tEXtdate:create2013-06-22T01:33:52+01:00E0%tEXtdate:modify2013-06-22T01:33:52+01:004+IENDB`puzzles-r9872/icons/untangle-web.png0000644000175300017530000002754412161170220016642 0ustar simonsimonPNG  IHDRc pHYsHHFk> vpAge.IDATx}w|GnUu9)gPil43668}oo؀H0ㄱJ$%@$pSU c0 !tg?A>NSe[.fp_8_Ƅ !DE߁1yqXׅPBAZM;wnmًYƦ(]E!xy^~8BVb|5M" Bx:DNQ55m,kM;wkD~aWT/)Z hFQTgwC,k2DQܹsM]]n8;+fDcBQUkZ[4ow斪JQ}:WX_YQvC(`l)$Bh6Lmj0LA~~~~nTT$I<;ՔbA &a}Y[o߾jeY$ ^s`!vgђiA9߰qMpX% c;\'4M_;j 3BfFĉ8zaBOq^o@Uc1+ (*++(/|k#B D(j?EQ8;285dYc۶kVDx23n$ _]Uy2tTSp9{]dnE]蠐(zfEzĥ@X,UU55;֯߰bʈ\: $pc)'& !g_l޼( @ pAKL!BHUT!MSC < Kb٪+'LjظVDEF:N\Ą}hߋ(BT@EUM!3wZKzPvIknooohb RBt BIR7mj Bqtf[ZZ@  Gn? )4@4M4a4q¤IZ[[}\lL~~^Fzn4T4F 9m(ap";rZ[ۖ~iRbo}~ žEhO8iZ@Ucʔɓ'Onim_lyBB|^nnFFjմ;%PJOܿPA~ѣ]:V!VEQ?XkW/o 0‡f/psi@t:.:eI]]۶ssffL&K]YKOzLL|lL*Dh۶~Ezzo}BC82B/K]z)]][m[o$r[Z{n]㺫mKN^߶ >_d~EQub-)j(R(ɷ~?r^y%ZWw֭V^WybYO[ R!DFn'q!n+e.zqD40͂p)cB 5 7*2,~pRuϏ1)))9;2G&Ί0UG   I[N4MH)BVL)I=,q !x"+q:@%ILK˛1֡i yZN<@]A06чk.嚚)odP2ÎpȊduY2Bw޻yjeDAY @G Rh2Q{~I zz!!.GxïE,t:`add_eM<-?YQ1e?but~#G̼㶁RщS8`Ό /yX3fBÜ%v>_w?;,yV(  üi0*<3##OBuñ`{)ɓ'MzG86PS#,<+t]w_}{"ʲLQ#,<-t]٬[7>aB?ƍ yw%s0g.m;(!޼kFa\" I (\ͯ1v Ǝͩ8:v L!FQ'+t]w\~i  s2hZ->-y1ؖ+]vۺ 6oio1i5 acb2/^8w^>3w/ϽBEd:|أ vQ`mtih~k~,BRFӓWek_p d]_oN"<`c eB麆1FQyv1,4 \1o/)_R|O]hx#iUU#Ui]ߣ(}l6YR㯻vQ:‹0i BPXPrutcVUweg/x۰q#q6BiDA׮رg/r3sBjϿjSOMu[Uk֮(////9.8|m1fy޽_~O>6].(B!4G.7>A7+33/7wǎ+Vo(+_YQnA8OѨee f̘vAxMBj0(I,{T,KEVSRzݻXjݺ j{0(h~fg~iBMuAA^Eşa bv˄WFY^_\\ܽ}3__|@:TLUu<.וwʗ5po,x;gq3y,]IHNMӺ F-+V8xh^49M85TU:[g .Na31X,;Cާ(j{gv "5MzVeM]7'''O2)!!A$#b|5`wߟymVu^B09fy7\7uʤں;u䔔dYVxqB`V\vԬfsAKBHQchvU&MoS&ڶq-˥כ1e䋨f% jy >!tMPظnqqqQQ䕫#<}DQϿF}BLVmu]|x]c~}>\ҿ8ex7ƗRN᧐)$EzMeV:S:dM7Z,A|>`E)b6}䡗l싽pB4Pݝ Cwt\r-//& WCv"DIx<>p__yINN᧐iUU hjmm_j*+Jdz,+8)AHHΊ莢f1jm\~ZUUjjǗQ-,xm6EQ@0##}-{駞٬+»烰QhqZfþeֺAM4!(!Bs dP(+,D9s})eZDXN6aшqٲ.fdA\k)z!Q|y>QLu(y6MQƵucTVuuw#4B(dP(4gO=^,FQn%i5k]=~|%&LZ ^~b,ѿ_9{܎;f=@ZZt951X~qv=o p^2o✽r>porr7*4=\=B^;nC C°?!p5M˯}n޻^, xtpI=>Å],1D u](tHɧϝΙwyGTTUڤ(J״0n@`Axg_\i3chuUUX5_}M{ǎ;&&Z 1#hM'$=#y|e^,iASHZ#f+::MD6#G>]:srz3hP]1sM&SMuEQ>8 !<}'ڿgObd[g~x]]}K~oG~v `ΰMQA_ rc?K&pcZW߰_ѣ>߳O ̌뮻p72 BXG~襹/M̌Q9 -xg4@F+cgq5\!z}FQ9?쇛FzEE)"2}{{X,ٺGxuLtBFHQ4s0^K8Q!B $%%uƱ4z>aP8;)(CӾpWop8fc|iYb½'@ 5_ϝ@ qdqBd{OuFlUݤ뚮帧[gfM+λmݺbۭ 8 x[>_qqѴzqpyA@  k7+3zu B(dv;PӴSE{oFoԠprߴt=11{C.IKS\\7V3qbain=P6UTBy樇E N!"DQ ++.h|Ia\dU/.1~FDŽ 6c4FoRi,cJ{̓9g3 RNر8-u(!M].kOktM))[ӆ2K]%Iuj-͵{Ř.hӮ,(e&Ti~r(Rc iºuK $eee?p(AMM _Pzrx]$M,KvvVIIqFz(Iw q8.Bcl<~"-5n߾}TW -鬭k2r7gQ111?![ccb&d.UUX,e%imи]nnR%CBH㷷nTUU)IO=mQ0\vB`zz$`y...v⥕e΂歑Gi/ٜZ^V lnظU4rF!0*uYQQSAE!!)aoPX1쪪ŵIII}}omoq qza+OR$]׫*ۗ@0p*=+~DTw:ԿqJK'BAw~yddAR4=d[؟d2e҅khP(@ewk3tioMPm|qvtt,:f̈I0c$ M'''+,gYvnoiz}vt01V{ly7ߪjJjdE[,Ȩ%K?,g^EUsr Y:#N ŎWPT4t߿a݆yx9cx<1cRޚ ''rhH!E+:㮘uV8.%%a?0&%iKEyiPر,G<*2 ::zܸq ##"8~u,a2Jd)e966::&fw V(iSNNJd mM5CBlN![P<\kk-Ǻ~eʲ!xN& h600N; IhR00hX n^(Va(0` $nqrY[\FGHgjkTVVXZ[}\]^bdeacdacdacdacdbdeZ\]Z\]bdeacdacdacdacdbdeZ\][\]bdeacdacdacdacdcde[\]cefjlmhjkhjkhjkhjkilmbdebdeilmhjkhjkhjkhjkjlmbdebdeiklhjkhjkhjkhjkjlmbdebdehjkgijgijgijgijhjkacdacdhjkgijgijgijgijhjkabcacdhjkgijgijgijgijhjkacdbdehjkgijgijgijgijhjkacdacdhjkgijgijgijgijhjkabcacdhjkgijgijgijgijhjkacdbdehjkgijgijgijgijhjkacdacdhjkgijgijgijgijhjkabcacdhjkgijgijgijgijhjkacdbdehjkgijgijgijgijhjkacdacdhjkgijgijgijgijhjkabcacdhjkgijgijgijgijhjkacdbdeiklgijgijgijgijhklacdacdhklgijgijgijgijiklacdacdhjkgijgijgijgijiklacd_abfhidfgcefcefcefegh^`a^`afghdfgdfgdfgdfgfhi^_`^`aeghdfgdfgdfgdfgfhi^`ampqrtutvxx{|y{|vxyruvlnolopsuvrturturturtusuvlopmopsuvrturturturtusuwlopsvwors~abc455VXY\^_dfgcdecdecdecdedfg\]^\^_dfgcdecdecdecdedfg\^_566bcdiklhjkhjkhjkhjkiklbcdbdeiklhjkhjkhjkhjkiklbdey|}cef_abhjkgijgijgijgijhjkabcacdhjkgijgijgijgijhjkacdcfgZ\]beeEGG_abhjkgijgijgijgijhjkabcacdhjkgijgijgijgijhjkacdehiEGHLNOY[\^aa@BB_abhjkgijgijgijgijhjkabcacdhjkgijgijgijgijhjkacdcfg[^_rtuXZZ_abhjkgijgijgijgijhjkabcacdhjkgijgijgijgijhjkacd"##~abciklgijgijgijgijiklacdacdhklgijgijgijgijiklacd[\]FHH^_`eghdfgdfgdfgdfgegh]_`^_`eghdfgdfgdfgdfgegh^_`qstvxyRTT466012EGHmopsuvnpqtwxsvwsvwsvwsvwtwxnpqnqrtwxsvwsvwsvwsvwuwxnpq~]^_cefacdabcabcabccef\]^[]^cefbdebdebdebdecef[]^\]^cefbdebdebdebdedef\]^cefiklhjkhjkhjkhjkiklbdebdeiklhjkhjkhjkhjkiklbdebdeiklhjkhjkhjkhjkikmbdebdehjkgijgijgijgijhjkacdacdhjkgijgijgijgijhjkabcacdhjkgijgijgijgijhjkacdcef^abbdehjkgijgijgijgijhjkacdacdhjkgijgijgijgijhjkabcacdhjkgijgijgijgijhjkacdeghTVW<>>bdehjkgijgijgijgijhjkacdacdhjkgijgijgijgijhjkabcacdhjkgijgijgijgijhjkacdcef]``LNObdehjkgijgijgijgijhjkacdacdhjkgijgijgijgijhjkabcacdhjkgijgijgijgijhjkacdbdeiklgijgijgijgijiklacdacdiklgijgijgijgijiklacdacdiklgijgijgijgijiklacd`bcfhiegheghegheghfhi_ab_abfhiegheghegheghfhi_`a_abfhiegheghegheghgij_abikloqrnpqnpqnpqnpqoqrhjkhjkoqrnpqnpqnpqnpqoqrhjkhjkoqrnpqnpqnpqnpqoqrhjk( @ `cd]_`[]^cefacdbde]_`suv}\^_bdeacdbde]_`uwx|\^_bdeacdbde]_`suvcefjlmhjkiklefgy{|cefiklhjkikldfgz}~cefiklhjkiklefgx{|acdhjkgijhjkcefwz{bdehjkgijhjkcefy|}bdehjkgijhjkcefwz{bdeiklhjkhjkdfgx{|cefiklhjkhjkdefz|}cefiklhjkhkldfgxz{]_`cefbdebde^`auwx^`adfgcefdfg_abvyz}^`adfgcefdfg_abtwxsvw~xz{tvwx{|wz{x{|twxtvwx{|wz{x{|twx~Y\\<>>GIIprs~~`bc\]^cefbdecef^_`uxy|]_`cefbdecef^`asvwloo123bdeiklhjkikldfgz}~cefiklhjkikldfgx{|~\^_`ccZ\] !!bdehjkgijhjkcefy|}bdehjkgijhjkcefwz{mop234bdeiklhjkhjkdefz|}cefiklhjkikldfgx{|bde]^_dfgcefdef_`avxy}^_`dfgcefdfg_abtwxvyzUWW799BDDilluxyz}~y|}z|}vxyuxyz}~y|}z|}vyz||}|}\^_bdeabcacd]_`tvw~]_`cefbdecef^_`uxy|]_`cefbdecef^`asvwbdeiklhjkikldfgx{|cefiklhjkikldfgz}~cefiklhjkikldfgx{|IJK{~acdhjkgijhjkcefwz{bdehjkgijhjkcefy|}bdehjkgijhjkcefwz{SUVbdeiklhjkhkldfgx{|cefiklhjkikldfgz|}cefiklhjkikldfgx{|]_`efgcefdfg_abtwx^`adfgcefdfg_abvyz}^`adfgcefdfg_abtwxsuvx{|wz{xz{twxsvwx{|wz{x{|twxsvwx{|wz{x{|twx(  []]`bcknnnpq^`aacdtwxbde\^_oqr_abeghoqrsuvdfgfhix{|hjkcefsvwpstsvw}{~oqrqstrtunpq|;<<[]^prssvwtwxprsorscdedfgwyzfhiacdrtu,--vyzdfggijy{|hjkdfftwxjlmXZ[~vyzy{|z|}vyzdfgnpqruvtvwfhihjkz|}jlmeghuxympq[]^acdmoppstacddffvyzegh`bcqsuoqrsvw|rtutwxuxyqsu(0` npqY[\FHHjlmTVVVXY~[]^cefacd[\]dfglnohjkfhi`bceghfgh^ab^_`mopqsttwxx{|y|}vxyruv566cdeEGGLNO@BBcfgXZ["##]``ehi012EGHsuwuwx]_`_abTVW<>>ikl            !"  #$$%%%# ##&#'$  # ( )*+**+,*-./*,*,**,*//-++*,**-0#123444432552444444675634444867 539:::;94<<8:::::4=74:::94=% 53:;;:;:3<<>::::?:4@74:;;:;:4=  54:;:;;:><<>:;::::>@74:;:;;4<%  53::;:::3<<>::??::>@74::;:::4< 54:::;;;><<3:;::::>@74::::;:4@ 53;;:;93<<3;::A::4=74::??;4@ B>;83338C55C;88888D@5D?8888;D<% =EFGHIJK9LL9KKKFKGELMEGFFGKK9L  N,O*LKF7PQ,,QQRQRQQOSSTRQQRQROU 1>VWWWWX<563>8>Y86<56844Y4867  =VWWWZWW[<@>::::::4=78::::4=  T \ IZWWWWWZW3]>:::;::4@74::?:::4=%?L*2^#3WWWWWW_W>::;:;:>@74:::??:4@ ;`a^#CWZWWWW_Wb]C:;:;::>@74:?::::4@ cd6eKWWWWWW_WfC:::;::>@74::?:::4@  $g::;:;:4=74A::?>@# # 56jWWWWiM5C;88888k<5Dl;888?D7 MFJVmnEK\LGKKGKGL\ooKoppEq r( ,TRPs*tQT,SLQQQTRRLSSOOOQQRQTS 168>>>>8675234>333u71u33444327   58::::::3<<3::::::3v7w::::4x e  53:::;::><<>::::;:Cx73:;:;::4= ?Cx53::;:;:><@>:::;::4<74:;;:;4x ?yz! 53:;:;::3<<>::;:;:>@74:::;;:4<#3Cate54:::;::3<<3:;::::>@74:;;:::4@#  Q{53::;:::><<>::;:::4=74;:;::>@# #| B>:88888>77>888;88><7>????8;>< <}F9F:MM:FF:]@:FEF:]$$( @`bc]^_dfg`cd_`atvw~bde]_`uwx|acd^`asvwhklhjkxz{ill{~efgy{|vyztwxY\\<>>IJKprsloo123z}~[]^ !!mop234UWW799BDDdefz|}gijSUVjlmx{|suvvxy                         !"#"!$!"""!%$!"""&  !'(()*+,,-./00012  "'343'5!'446'7'64689 #444'5!,444,7:,444,;  "'444'5!'444'9:,6448; !'(,1*:-'''1;+1','1< 2:+:5#=*555*">;9572" ??$+@ABC%%+&!!:D%+!!!+= "EFFFF!'('./-','12 G $HIFJKKFLM,443'N'346'9 O+-(PFFFFFQ:,444'9:(444,; =R"HSFFFKFTM,444'9:'444'5 "UFFFF,&''')5/'''. %;VWX6>D.759;M=*797;" Y/">>!Z%N!:/=%/::/[ \&'(1*+-'(]-./',,12GR%"(433,7#,333'^'466';O#B7O#(3_4';!(444]9(444'; D`%#(a_6,b#,343'9'343,7  \&-']').c18,';/1',812 d9e5.#=& h600N; IhR00hX n^(Va(0` $13389:./0\^_SUU?@A123'))EGH !""&''suv]_`PRR:<<QSS;<=:<<79:LMNUVWx{|  hkkJLM\^^456MOOTVV#$$:<<&((678nqrY[\twxjmn *+,)** uxyknn'((,--vyz9:;'((MPP?AA[]^ acdABB^```bbz}~kmn]__TVW@AB\^_022@AB]_`|cde!"#[^^@AB]_` ,-.*++*++*++*++*++*++*++*++*++*++)**())())()*)**123JLL{~133suu!""@AB]_`acddfg vyz@AB]_`VWX '))/00@AB]_``bc,--OQR>@@PRS022@AB]_`()) vyzBCD|SUVFHI@AB]_`ACCx{|qqp777@AB]_`SUVacdUWX@AB345kno344@AB]_`uwxSUV123twx } lno344@AB]_`Y[\123RTU89:lno344@AB]_`acd;<=GIJ678lno344@AB]_`vyzBDE+,,tvxlno344?@A[]^ehi hjklno344CCCaaahkl+,,lno344())<>>EGGOPQ123:;<lno344 '()twx;<=123QSTlno344]_`LNNlno344lpp lno344MPP133Y[\UWWFHHfhilno344looz{|!""lno344IKK lno344 accjlm233>@@EFG`bcWYZ/01.//UWXqpp777466iklsuv}( @ 123233011@BC$$% !!,..EFG577_ab[]^\^_\^_\^_\^_\^_\^_\^_\^_\^_\^_\^_\^_\^_\^_[]^egh244UUU]``344GHIOQR`abBDE566qtu}z}~{~{~{~{~{~{~{~{~{~{~{~x{|{~}qtu123]_`PSS577]_`MOO&''#$$89:8::#$$WYZ-..kmnZ\]$%%$%%%&&>@@pst-//vyz-//eddwz{prsrtuqtuqtuqtuqtuqtuqtuqtupsty{|QTT>??lopAAB\__wz{prsrtuqtuqtuqtuqtuqtuqstorsnpqwz{QTT?@@9::ABB^`a,..jmn_bb,./_ab$%%$%&_ab ?@AFGH""#UWX+,,EGGDEF*+,_ab <=>]``fijfij_ab688gjk;<=())_abhkl122_bcMOPy|}|_abruv012fhiFHI{}~}^`aFHHvxzlopwz{())|}eddvyz}}233Z]]BDD,-./00345}}ACC9;;wz{&''kmn}}JLLhjk456}{~456!""355}z}~z}~abb788|z|}NOOWYZ345,..nrsvyyRTTIJKIKK()*-./(  qturtu~jmnsvviklknnilmruvruvRST}loo{~prswz{z}~x{|z}~(0` 344 79:/01[]^UWX:;<'()EGH"##  *++rss^``NPQSUUy|}~fhiKMM678mqqvyzkmnABCacd]_`|?@A./0WYZtwxFHI      !""##$#$#$$"#$"#"##$$$$$$#%&'( )* +,--./012 .  (34045#6 7289:; 4) 5;)  3!2<= ># ?@# A0B   %AC?/# DB%A  (  E FG/5F#H E' H6B/E 333333333333 3333 53?EIJK+7)/H!?3EI8!%%%%%%%%% ;-62&! E JC4HHALI G,  ?LI H M9(L)-(3 EI  A0J8EK * / EI E#3= EI 94,G0HOJL B  EI )A *3P"K38J B EII *<  Q,> LI H R  = @B EI  A 0EM A B  E? ? :<) : BEH /BN24#% B  ) ( B #!# ?A*8B )1F ; B )B3 B(0":  > B456!B CR1,B,1FH )#BL1HO?% >=,G G-JG8@ B5&60+/ 7G.4G8  ( @344/00BDD$$% +--FGH788_abWYZ\^_eghUWXFHIMOPcccnqr}z}~wyzqtuRTT:;<jlm?@@OQRlop fij|KMM     !"###############$ % # & '()*+  ,-............/.'-0 !12 33333333333424#)34(5'66"7#4889 :,;<<<========+ /95*+85/0000000000/>:? ;#=/00000,00,,/1:@6 ;!;;======<;;; 7!  *;;9 3*;5-A:&% 5!;8B:!CC4*;= =C6DEE45!;FC D!)-.G355!;80 5=$&GG35 3!; D&04?/4DGG35*+2 A/=A4-G3F "  (53-G3: '/7 3GG3H72 E  3GG@   3GGG*E+2  4GG H" 5-G5,/1&&B5F( qtusvvknnikljmnilmRST}loo{~prswz{x{|     !" "" #$%&&'()* +,-./012*3444(#5**673 8"9: ;*"!<= > !!'?@A!B"C *DE:  ?FG'HI4J+KH8L#M!GNO7P?QP41+JRSRPPH(0`wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxGwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxwxxwxxxxxxxpxppxwpwwwwwwwwwwwwwwwwwxwwwwp8wpwwwwwwwwwwwwwpw0wwwwwwwwwwwxwwpwwwwwwwpwwwwps?wwwwxwwwwwww@wwwwwww3wwwwpwwwwwwwwwwwwpwwwwwwwwwxwwxwx8xxwwwwwwwwwwww8wpwwwwwwwwwwpwxwwwwwwwwwwwwwwwwxwwwwwwxwwwwxwxw@wCwwwwwswwwwpww3wwwwwwtwwwwpwxwpwwwwwwwwwwwpp4wwwwwwswwwwpwxwpwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwpwwwwwwwwwwwwpwwwxwwwwwwwwwwwwx7xwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxpwwwwwwwwwwwwwtwwwwwwwwwwwww4sswwwwwwwwwwwwwxpwwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxpwwwwwwwwwwwwwwxwwxwwww( @wwwwwwwwwwwwwwww7wqwwwwWp40CCCxwwwwwxwwwws7q%C%a!wqwwwwwAwwwwwwww_wxR08@'wxwxxxwpwscwwwwwwwwsw8www'gwwuw7www(wxwwxpwvOwxwuqw'wsspswwxwwxoqvwwwwwrw'wwwww8wqx'Twwwwrw7wwwwwwwgwwxw'wwt7'wwwwwwwwwwwwowwwwwwwwww( wwwwwwwxwxwx87wxxxwwwxxxwwwwxxxwswwwwxwwxxww8wwwwwwxwwwwwxswwwwwpuzzles-r9872/icons/cube.ico0000644000175300017530000006117612161170230015156 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $u==TUMN   ( @ UVƼXY Əđ¸22¸./Ż//// ŸŷƼ¸·ż ¼  üüüüü¼¼üüüüü¼¼üüüüü¼(  hirsjkjkqrfgYZcc]^}~_`""=>8845bcэ[\ ф[[ Ǵ__fglmijst//   (0` u==TUMN                   !  "   !#  $$"%"&  &' ( &))& (*(* +,-& ./ )012323221441233221562322222789:; 3<<<<<<<<==<<<<<<>5?@<<<<<<<><><88<><@@ADEAFFFFGHGIJ%2<><<<<<<==<><<<==<<<<<[\]Z;  "*3<><<<<<<==<<<<<><<><==<>>><><<><3#p]iZi$Z qkhr""*3<><<<<<<=sjh ] ` ]tW(!"(3<<<<<<<<=%q  (""*3<<<<<<><=n""(3<><<<<<<8% "# 2<><<><><8 %%%%&2<<<<<<<<=u ;vwwwbG8==8=8E4   x q0F=E==EEGcfG==E===Gfx&  '&  xqy<<<<<<<<<<<><=8<><><<><<<<><==<><<<<<<=qy<<<<<<<>><><=8<>><><><2#!  q2<<<<<<<<==<<<<<<<<=($"%"qd1yyy2yy|0}|y2y2yy3~   , qqqqqqqqqqqqqqqqqqqq$%( @ûUVXY· ÏÑ22////                        !!!!! !!!"#"!!!!!$% & !!!!! !!!'())***+, & !!!!! !!!-./01 & !!!!! !!!234 !!!!! !!!25  467" "#7" "85   #  96:&;  !!!!!<  & !!!!!<   & !!!!!< & !!!!!< !!!!!  467" =  =><<<>9#  9     !!!!! !!!!!< !!!!! !!!!!< !!!!! !!!!!  !!!!! !!!!!<  !!!!! !!!!! 7 #7 =4 4  & 1 ( ijrsjkfgYZcc]^}~_`""=>8845bcэ[\ ф[[ Ǵ__ fglmst//       !"#$%&'()*+(,-./012(345(6789:+4;<+=>?@A++BCDEF?GH((IJ K G7L((MNO PQRSTTUVKWX DI((TY(MZ [\DI((;((M]^O D_`IaI`bc  DdDeeDeOO\(0`wwwwxwxwwwwxwxwxwwxwxxxwxxwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwxwwwwwxwwwwwxxwwwwwwwwwwwwwwxwwwwwwxwwwwwwwwwwwwwwwwxwwwwwxwwwwwwwwwxwwwwxwwwwwxwwwwwwwwwwwwwwwwwwwwwwxxxxHxwwwwwLLćxwwwwwLxxwwwwLLLćwxwwwwẅxwxwwwwwwwwwwxwwwwwwwLwwwwwwwwxwwwwwwwwwwxwxxwwwwwwwwwwxwwwwwLwwwwwxwwxwwwwwwwwwwwwwxxwwLLLLćwwwwxwxwxwẇxxxwwwwwxwxwwwwwwwwwwwwwwxxxwwwwẇwwxwwwwwxwwwwwwwwwwwxxwwwwwẇwwwwxwwwwwxwwwwẇwwwwxwwwwwwwwwwwLwwwwwxwwwwwxwwwwwwwwwwwwwwwwxwwwwwwwwwwxwwwwwxwwwwwLwwwwwxwwwwwwwwwwwwwwwxwwwwwtHLDLĈxxxxṫwwwwxwwwww|Lwwwwwwwwwwwtwwwwwxwwwww|L̇wwwwxwwwwwxtLwwwwwwwwwwwtwwwwwxwwwwwtL̇wwwxwwwww|wwwwwxwwwwwẇwwwwwwwww|LwwwwwxwwwwwwtLLLDLLDxxwwwwwwwwwwxwwwxwwxxww( @wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwȈw\̇wLLGwwwwwwwLwwwwwwLGwwwwwwwwwLLLwwwwww\wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwLwwwwwww|wwwwwwwxLwwwwwwww|wwwwwvLwww|wwwwwww|Lww|Lwwwww|wwww|LĽwwwwwwwwwwwwwwwwwwwwwww( wwwwwwwwwwwwwwww|x|w̌wwwww̏wwwwwwwwwwwwxwwwwwt|wwwwwwwwpuzzles-r9872/icons/dominosa.ico0000644000175300017530000006117612161170233016054 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $]_``cc`bc`bc`bc`ccacd[]^ '''^^^xxxRTTVXY!""89:$%%WYZ^^^ttt8::dfgY[[SUVhkk;;;wz{MOPeghTVW---(((233acdKLM;==$$%@AB  !""Z\]PQRWYZSUUadePRR rrrmmm##$qtuVXYGHI 777%%%"##}kno455lop@BCQST MMM!""PRSIKL[]^NPQx{|bdeSUVbef ZZZ!"" ###!"" !"" !"" !"" !"" !""lopacdknoTVW 111UUU!""twxy|~123CEF?@AFHI 444"##x{|fij`cdwz{JLMTVW qqq!"#NPQLNOdfgNPQSUV iiiYYY""# 788!!!##$+,,ACDGHIFHHHIJGIIFGHEFG  677   lop`bcprs!""acd zzz???666888pst444'''EEEwww #$$`bceee555pstJJJZZZ!"#`bc333kkk___prstttwww***!""`bcFFFVVVprt@@@>>>###!""acdKLL112QQQ"""pst[[[$$$ UUU ##$cfgsuv$%&/11 "##$%%#$$!""!""!""!""!""!""!""!""!""$%%#$$123TVV"##"##%%&"##!""!""!""!""!""!""!"""##$%&"##""# ( @ ;<=.//345345133+,, GIJ222JLLhkl[]^;<=`bc***eeeKLMOQQuxywz{cccVVVKLMwz{ NPQ|uxywy{y{|x{|twx\__DEFEFGHJJBCDtvxcffx{|~z}~ ^^^ XZ[OQR|||ttt fij`bc~hklruvWWW    fhiors}[[[qqq GHInpqRTU||| dfgvyzceffhijjjlnoTVVXZ[VXYQST~x{|vyzx{|wz{vyzvyzvyzvyzwz{y|}z}~ruvtwxy{|x{|wy{vyzy|}ehiEGG_bbUVVMMMooo@AB ___]]^ggg_abbbb(((///@BB///xxx 444YYY`bcsssDEF[[[rrrUUU^`a  ?AA .//122,-.022122122122122133012/01())9::011/01-..123122122122123-..022*++_ab(  122"## \[[322jlm %$$z}~nmmqstmml 333bdeDFGruv355344FHH  223""#+,-211!!!iii2349:;LKKBAADDCVXY%&&"##!""PQRhjk'((!!"$%&$$%789(0` ]_`acd[\\ +++twxSTUWYZ"##788rtteghfij;;<NPQ/11JLL?@A jll}GHI344lopwz{FGHBDDMOPyz{          !"  # !$  %& '() *+$, - -. /.01     "23444444444543 2 2226 0   3*73 * #(8 %9273:; ! < 9. 8.=>0 ?/ */,*@3    633 A B  6     87 -7 @9@"=CD :C =< %@7&3 @/ 5?# ,E*7FA3$E $ ,,4"3 ! FFB FDCC;;CC(1GG1"BAB!A 8  #4  H2) !! #5 =2 C2  B =45 #4 B?/( B   =' <8 #4 <5 2   CF #5 (D 0G   /=   #5 B  I $   # ,-< I=4< I2( @:;<,--123 GIJKLLklm[\]cdeOQQtwxz||TUV|vyzCDErst_ab~VXYfhioqr?AA/01         ! " #$$%&&& &&''''    ((()#  &*+ +$'   ,+-./  ) 0,*+ ".   1 1 0,23+!1 ) ' ,4 3, ' 5/ +01 0   #" ..+6%%%%%%%$4%%!!2270 8, #  3 8  0! *  97 8/ 9  .  *  5 )$  / ) #*  ,999!999( 211"##!!!\[[333jlm  $$%z}~nmmqst344bdeDFGruv355FHH234+,-hjk9:;LKKBAADDCVXY%&&PQR'((789    !! "# $%%"&'(" $)*+!,-./01 233,4/56+-780 +9785: ;<"=3>(?4@;=,"A6B*C50D0EFG%G>BC>H>IJK=L8/7/DM/7N 0=O 78PQRS7-TU0 V@W. XYPZ[ W.\   (0`xwxwwwwwwwwwwwwwwwwwwwH8wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwwwwwwwwwwsHwwwwwwwwwwwxww8wwtswwwwwwwwwxwwpwwwxwwwwwwwwwx7wwwwwwwwwsGwwwwpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwws0Cwwwwxwwwwwwwwwwwwwwwwwwwxxxxxwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwx0wwwwxwwwwwwpGwwtxwwwxpwwwpww0Gwwpwwwwxwwwwxww7wwxwwwxwwwwwwwwwwwwwxwwwwtwwGwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwpwwwwwwxwwwwwwxx7wwwpwwwwwwwpwwpxwwwxwwwwxwwwwxwwwwwwwww4wwwwwwwwwwxwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwwxwxtpsxptw@p8@p7pxppxwpxxpxpwxwwwwwwwwwwwwwwwwwwwwwww( @www wwwwpwwwwwhws7swpww8wwwrw8w7wspwwwwq`awwwwwwwwwwwxwwwwwwwwwwxwwwww8ww7wwwSwww8wwwwwww0wwwww8wx`wwwwwwww8x7gwxwwww`wwwwwwwwwwwwC!0t0 axpxHpxp pw0C4P0%`aw( wwwws7xxpwwwxwwxwwwwwwwwwww'ww7wwwwxxxw@/04%wwwwwpuzzles-r9872/icons/fifteen.ico0000644000175300017530000006117612161170235015665 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $qsu688566y|}>?@;==QSTPRS*+,qtupst)**nqrfijWYY456ilmwz{svwVXY455_abtvw"##HJK577,--BCD9;;Z\]egh<>>UWX%&&=??y|}-..#$$EFG=>?HJK678nqrqtu/00kmohklVXY !DEForshklEGHABCWYZmpq[]^=>?DFG''(CDE( @ 89:IKLsvwqtupst/01dgh;<=VXX^aaWYZ?@A<=>TVV789RTUwz{~RTUiklGIJ012dfgZ\]jmn?@AkmnOPQ(  knonqrUWX(0` qsu688456y|}>?@<>>QST*+,qtu)**orsfijWYZkmowz{455egh"##HJK,--BCD9;;Z\]UWX''(-..EGH !DFGABC=>?CDE       !"#$$%$%$!!!!$!&''((((((((((((()*+,,--$./0123456789:*'/;;<=>?@99A*B/'&C1 DE8F9A*G'-HI J3K?9LAM#NOPQRC JSTC"MB'U%/VW>$X.8E=AM#0YZ[\?3]^O_+,-9LAM#5`_ aTG'OV9:*+97-9bcd0/V9$*86,8Qef+O9:M#g)ZhG?6\3Y0O#''i:M'#'j'MMkklkll,+ll+l+l+,,%$M'LN_kmmmmmV:M#N,nNoooo(p$MBO-LLLLL:MBN/'qL"O##'''''''''''lVL:rOMMM MMM M MM * 9!!MOO/O/O///sOOO/OO/OrL'9%''''''''''''''# n!n-'9 LVHtu1W=veq! '/KIwJxyzvP% '{HK&dh!|/q!( :JH,-a}y!~v_#c-(~'9!~(~9~9!  #'''''''''TVV012RTUwz{~iklZ\]kmn?@AOPQ              !      "#$%&'(" !)*+#," !- . /01"  !!23456789 !/:"/;4<=>  !?@<*A$!! !B9C5D; 4  ! E9FG5  ' ! < H433  2   I 2   J    K LM222333 2 !NOP2333333333 3Q  !R R +STUVKN S& h600N; IhR00hX n^(Va(0` $,---..,-.,-.,-.,-.,--233EFG%&&/01,--,-.,-.,-.,--/00!"" !DEF788!!"y{|}&''*+,*++()))**())be79MO]`"#npqOQQ()))**ilY[wz())=?dhqtorSUUWDEFgijUVW)**Y\hk())%&UX57233_ab)**eh+-lo())hkhk))*uyru*+++,,!""oqs!"" *+++,,*+,*+,*+,*+,**+./0345./0/00/00/00/00.00122""#,--011/00/00/00/00/00122'(( +,-*+,*+,*+,*+,*+,**+012 =>?)*+cefade*+,`bcdfg())`bc^`a)**\^_bde())tvwWYZ`bc^aahkl)**ruvXYZ]^_bee())=>?`bc^ab`bcNPQ)**>?@]_`bef())]_``bc^ab@BBfhi{}~)**^`a]_`bef())[]^GHI`bc^ab~DFF)**XZ[IKL]_`bef'()^`a^`a()*[]^bde+,-knocef/00hjkeghY[\Y[\Y[\XZ[XZ[Y[\XYZcef'((RTU}[]^Y[\Y[\XZ[XZ[Y[\XYZcef#$$^abgijfijfhifijfiifiifhikmnTVW|}PRSiklfhifijfijfijfijfhiknoRST011+,-,--fijz}~ TV,--9:;SUV_abPSĥMO,--y|}QSTFHIK,--577ǬX[á,---./!!"~y|}mop"##011/00/00/00/00/00/00345kno*++""#HIJbde,..,--bdecff,--â)**\^_bef,--jlm<=>TVWz~lp^a)**kmn%&&]_`bef,--EGGqstEH]`)**acd]_`bef,--'()[^?Aâ)**qtuNOP]_`bef,--x{|}ǯ)**\^_bef-..,--cefcfg*++ !!FGHbde$%%{~()*011,--,-.,-.,-.,--.//%&&233122122122122122022567npq( @ 789VXYRTURTUQSTWXY>?@z}~VXYHJJUWXRTURSTTVWKMMFHH|~fijSUVJKL_abý¿PRRZ]]CEhkwz>@`bcBCDLNOy}fi[^^DF?ArtuNOPMOPcfQT_abOQRSUVFHI'((UWXOQRLNNMOOVXY455>@@\^_VXYUXWVXYY[\FGHSUVXZ[UVWRTUXZ[RST!"#QSTPRRKONLOOVXY788Z\]`bbsuwTVVoqr[]^oqrmpqPRRx{|mpq\^_hjknqrdgheghPRSy|}mpq[]^Y[\npq]_`uxyPRRacdmpqbdexz|VXYorsABCKMN>@@_`ailm9:;}suvqtusuwy|}cefhjkuwxuxytwxuwxy|}fhinqrdfgdfg?ABZ]jmfhilopw{ilmUWXacdmopnpqmpqmopqtu^`a^`aABCeghjmniklVXYorsdgh`cdz}~loPRR^`asuvmpqeghEFGQTnqPRS~mpqfijQSSsuwnpqgjkRTUnpqRSTY[\HJJVXYQTTQTTUWWNPQ""#QSTPRSNPQOQRVXY899nqr(  twx~¥lnoruubee~}y|}}ɮtwx~ˬɘmpq|(0` *+,./0/00DEF'()"##788~z|}be79MO]`$%nprSTUhkY[vz?AehnqruSUfij57133^ab+- <=>bderuv]_`jlmWYZY[\>?@OQQIJKmopegh}áSU}KM^aFGHFH[^Ǭ    !!"  #$   %$%%&&'$ ( % #)% % *' %& +, '%%-./0%%%#1234#% 56*%-789(%' #:;<=(%(>??@%&A6&'% %)87('% (43B(%# ?C &DEF%'):&%' #I$#(J %I$ (IKKKLK5'''HJ M  NE E EM  EMO %%%PFQRR%##PPS %  FFSI I*%&& IFPT% UVJ FFS WX* &UY%IZPT% %[\ FFSF]* %&[* IFPSI  F FFSI [F*% %"V FPS % V^_ FFS*%&Y^`FPT $$ VVSID *&`&$$&"ZPS IRaPQRST"EIRWAS YYV6YYYP6 X**-\\*D\\X*\ ZYZYYYYPFRNAAbbAAba6c X\***\ \*X*\'6WAAAAAWW6 dSSRSRSe RKdRdRSSRe  \D III\X $Ac*X%fgh\\ \6F*\IIifi\\ c]R*Xiij\\ %E$X f2f\\ *X \\ R*RkR\!5E EWRI __DK_J _D\^PTI \ %FPS T*\#f\  K FFS  W[Y*\h=l  WIFFSI m5*\I%nol*% %&F)FAS k k\\#p;f&&U] FFSI !X%_hq* & &H%`IZPS R\RkSRT` %PbS _%D_LXDX$DD*$mPT MEEEEEEE5k%%( @789VXYUXWRTTZ\]>?@|~UWXFHIOQRKMMfijbde¾»DFhkwz>@_`a>@ANOPcf?AruvQT'((455]_`FGH""#mopnqry|}BCD~ikl9::eghZ]knQTnq       !"#$""%" "$""&' ()*+$,-.$/012"34$ !5)6+ $789$/":24;$0$&<+="($$$+6>=/ ? 1 @22A1B C  D2BE "F:GHEIII $!=!JI !K/KLE$ M!NJI =OPKLI==0= = NLJI = !JLE=0 I$0 QLJI "FRS!=FKITM?UU=V"$1WNLNBXIYWK:: 00:: : ZLIK[S[HH"IESIIISIUL\\=]3QZH 1MI'\=Q^_IXK S\$33QXHIIIIS ?0JKKJ:0NEE0\"FF"$ F1?LLGZJIQXIE=\=Q$JIEH RI=3_Q$0:!?JIEHI\=`a$ FW NJIX?I$b]$  !LJIESEE= FQLKI G D  B KE="=Q=$$$$=$Q( twx~¥lnoruubee|y|}ɮˬɘmpq  !"#$% &'(#)* +,-./0.1+.2!345/6!789:;<&57=>?@ABC)D0C?E!F0D!!GH)6IJB570& h600N; IhR00hX n^(Va(0` $JKMHIJIJKIJKIJKIJKIJKIJKIJKIJKIJKJKLDEFHIJFGHGHIFGHFGHGHIGHIGHIFGHFGHGHIGHICDEIJKGHIGHIGHIGHIFGHDEFFGHGHIGHIGHIHIJCDEIJKFGHGHIGHIGHIMNOVXYMOPGHIGHIGHIHIJABCIJKFGHGHIGHIBBCY[\UVXZ\]BCDFGHGHIHIJBBCIJKGHIFGHMNOY[\hjkikliklZ[]NOPFGHHIJBCDIJKGHIDEFVXYUVXiklmoqSUVXY[DEFIJKBCDimpIJKGHIFGHMOPZ\]iklmoqjlmZ\]NPQFGHHIJBCDIJKFGHGHIGHIBCDZ[]SUVZ\]CCDFGHGHIHIJBCDIJKFGHGHIGHIFGHNOPXY[NPQFGHGHJGHIHIJBCDIJKGHIGHIGHIGHIFGHDEFFGHGHIGHIGHIHIJBCDJKLGHIHIJHIJHIJHIJIJKHIJHIJHIJHIJIJKCDEDEFCDECDEABCBBCBCDBCDBCDBCDBCDBCDCDE<=>GHIhjlabdNPQhjl:;;@ABTUV?@A?@AGGIjln@ABJKLDEF>?@GHIlnp\]_EFGHIJABC?@AXZ[TUVDEFGGHKLN^_aXYZAABbde>?@JKLDEFXZ[\]_OPQCDE>?@KLMXZ[XY[Y[\[\^AABKLMGHINOP`acQRT^`a=>?efhGHJ^_aXY[Y[\XYZlnpqtuMNPZ\]moqfgiCDEFGHlnpXYZY[\XY[^_aGHJQRSZ[]moqegiDEF@AB{}AAB[\^Y[\XY[XZ[KLM>?@_abGHIcefQRT[]^EFGFGHOPQbdeAABXYZ^_aKLNGGHDEFTUV?@AOPQ\^_XZ[FGHJKL>?@lnpGHI>?@DEFJKL@ABVXY?@@ABCHIJDEF`bcTUV@AB:;;hjlhjkFFH?@A@ABhjlGHIKLMjmnDEFABCBCDBCDBCDBCDBCDBCDBBCABCCDECDEFGHtvwgijJKLHIJHIJHIJHIJHIJIJKHIJHIJHIJHIJHIJFGHUVW>>?\]_IJKGHIGHIGHIGHIFGHDEFFGHGHIGHIGHIHIJ@AB>?@EFG>?@KLMqtuIJKFGHGHIGHIFGHNOPXY[NPQFGHGHJGHIHIJ@ABEFGHIJJKLGHIMNOQRS_abIJKFGHGHIGHIBCDZ[]SUVZ\]CCDFGHGHIHIJBBCiklABCDEFNOPZ\]Z[]GHI?@AIJKGHIFGHMOPZ\]iklmoqjlmZ\]NPQFGHHIJBCD?@AXZ[`bcmpqnpqcefOPQIJKGHIDEFVXYUVXiklmoqSUVXY[DEFIJKBCDXZ[\^_QRSPRS\^_VXYimpIJKGHIFGHMNOY[\hjkikliklZ[]NOPFGHHIJBCDNOPcefkmnjln`bcWXZ?@AIJKFGHGHIGHIBBCY[\UVXZ\]BCDFGHGHIHIJBCDABCEFGZ\][]^MOPDEFABChjkIJKFGHGHIGHIGHIMNOVXYMOPGHIGHIGHIHIJBCDeghQRTKLMFGHJKLHIJFFGIJKGHIGHIGHIGHIFGHDEFFGHGHIGHIGHIHIJBCDx{|OPQ??@DEF?@AHIJFGHGHIFGHFGHGHIGHIGHIFGHFGHGHIHIJABC`bc@ABKLMJKMHIJIJKIJKIJKIJKIJKIJKIJKIJKIJKJKLDEFjmn( @ MNOIJKJKLJKLJLMJKLJKLKLMCDEIJKEFGGHIFGHDEFFGHGHIGHIABCJKLGHIFGHHIJSUVJKLFFGIJKBCCJKLFGHHIJVXYacdYZ\IJKHIJ@ABJLMDEFSUVacdgikUWXGHIAABJKLFGHJKLYZ\gik\^_KLMHIJAABJKLGHIFFGIJKUWXKLMEFGIJK@ABKLMGHIIJKHIJGHIHIJIJKJKLBCDCDEABCBCD@ABAABAAB@ABBCD99:DEFhjl[]_NPQhjl::;?@AVXY<=>?@AHIJrtv?@AKMNTUVLNOIJKWYZDEFRSTRTU?@AVXYTUVvxzx{}LNOHIJfhiNOQOQRLNOx{}vxzTUVVXYQRSefhRTU<=>IJKLNOTUVKMN?@AqstBCDSUVOQRABCgijVXY?@A::;hjljlnDEF>?@BCDhjlDEF~GHInqrCDE?@AAAB@ABABC@ABABCBBCBCDqsudfgKLMGIJIJKHIJGHIHIJIJKIJKDEFWXY;;<WYZJKLGHIFFGIJKUWXKLMEFGIJK>?@=>?DEEHIJQRTqstJKLFGHJKLYZ\gik\^_KLMHIJ?@AFGHQSTfhiefhBCDJLMDEFSUVacdgikUWXGHIABCprtSTUSUVjlnJKLFGHHIJVXYacdYZ\IJKHIJ@AB@AB]^`acdPRSDEFJKLGHIFGHHIJSUVJKLFFGIJKAABZ\]GHI@AB??@~IJKEFGGHIFGHDEFFGHGHIGIJ?@AgijBCDGHIMNOIJKJKLJKLJLMJKLJKLKLMCDEnqr(  QRTJKLLMNCEFz{}JKLKMNY[\>>?uxyLMNY[\rtvDDEy{}CEF>>?DDE334wz{z{}uxyx{}wz{WXY}QRT}JKL_abZ\]RSTjkm_abJKLz}~YZ\@ABz}~WXZfhijln{~y|}vy{uwyCDE==>CDE567ors^`aWXZLMNY[\rtvABCPQRZ[\JKLKMNY[\<=>|mopGGHeghQRTJKLLMNCDE{}~jln(0` IJKGHIBCDFGHY[\NPQUVXNOPgijiklVXYlnqnpq?@A>?@_ab^_a<==RTUegiruvz|~acd            ! "# $%&& ' (!&)*!( +#,-,##.& $&/& +'..#+.  ! +#  $$ +#, + '0 + '#1 222222223"+ 45"6"""""111111111''+ ++++++ % ++  +7-8+ 11*32(-6 +  22& ++ ' 2$22 * ++19 31  !2 7(27+1+:  5+1+-3 (9(92 +10 7;93<,0.1+6 7(-97* 1+= /< "+.' /(721+1'+);*% 2>+','1+ 279($2+17 ?! ; #''72(7$ 2!1'+ 29( 2+' # )/ 22$2 '6-(3 70+1-!2:&1 + & 2  ++++++++ 1+ ++ & ++ 4@ /"" )11111 8 =% +''+, !391++   2433$/+  ( 241 $7# !  2+&2$ 31++ $&/& 2 37//?-+. (!&)/!( +97(+#&  %&& 2++$?/*7(2 +  ! 2+12 &+   $ + < 8# ++>2 2@   + ++?2$8 ++'& ( @JKLBCDFGHGHISTUVXYbdeikmZ\]?@A;;<OQR>?@UWXqsuNOQvxzx{}efh~nqrgij]^`             ! " #  $  %"$&  # ## '() ) (((*  )))( %  %""% * *""" !+,%")%%'-./0' 1  "0 .2/" . #"$ .342")"562+" ) 243. /56'0" $ "/22-0%1 .+ 5)/.#'%0' 70""""  %""0*%"89:(  ) *; # # 10)"% .'.,"%-8- : !!#8/<< 0" <#("396 &.#(#=+8 #("!##9#(""#;2#""(:00( QRTJKLLMNCEF{}~KMNY[\<=>vy{rtvz{}DDE334wz{uwyWXZ|_abZ\]jkmABCfhiCDE567orsWXYPQRmopGGHegh     ! "#$%& '()* +,-.(/ "0"-1234#567 8..9:;<=> ?9@ $A; BC2D E@%F@& h600N; IhR00hX n^(Va(0` $./0 !"%&&+,,]`a?AA78989:89:89:89:8::677ACCNNNEEEFFFFFFFFFFFFFFFHHH>??./0Y[\uwxZZZ-./XZ[kmn~YXX-./XZ[lop~ZYY-./XZ[lop~ZYY-./XZ[lop~ZYY-./XZ[lop~ZYY-./XZ[jmn}XXX-..VYYwz{[[[*++^`aLNOBDECEFCEFCEFCEFDFGBDDQSTcbbXXXZYYZYYZYYZYYZYY[ZZXYYYYY+,,Y[\SRR!!!~~~~~~~|!""www-./TVW111>@@-./UWX...678-./TWX-..688-./TWX,,-688-./TWX---688-./UWX../688-./UXY0009;;-./VXY||{+,,123+,,lop/00+,,+++)****++,,,--())&''344....//.//.//.//.//001!!!...-//~WWW-./~ZZZ-./~ZYY-./~ZYY-./~ZYY-./~ZYY-./{ZYY.//ZZY,--}gjkilmhkmhkmhkmhkmgjksvw8:;YYY,--!""cccttspppqppqppqppqppsrrhhhBBBHHH.//.//GGG-./++,EEE-./+,,EEE-./+,,EEE-./+,,EEE-./+,,EEE.//+,,EEE/00,--EEE .//.//-./-./-./-./-././/,--,--.//-./-./-./-./-././/,-. 333999788888888888888888566333999888888888888888777;<<***( @ prsHJJNQRbefprsSUU{~||z}~IIIgijrtvsssehipstppqehiqstrrrdghprtqqqhjksvwqqqgijtwx<=>gij^`a`bc_ab]__gij=>>wwwqqqrrrqrrtttfggwxxhklsvxYYYdfheghorsbccehinqrffgz}~ehinqrggh{~ehiortcddeghors^__qtuacdIJJ_`a\]^^``[\]XZZSUU?@@effabbbccabbffgPPPfggehirrrehirrrehirrrehirrrfhitttcfgpsuqqqbdeJKL[[\fhi\^^ehi[\]ehi[]]dfgZ[\lop_aa233lopdfgehiehifhibdecfgfhiehiehiehhfij--.vvv}}}}}}~~~{{{uuu~~~}}}}}}|||III[\\(  wz{|~(0` /00"##--.^`a?AA688678 JKKDEE999YYZ{~lopVXY./0wz{TUUcccTWXuwx||{334ikmgjkrrr             !" #$%&'$(((((((()($*  +,(-(((((()-.$* '$(((((((()($* ',((((((((#-,* '$(-(((((()-"$* '$(((((((()-/ 00,* 12',(3(((((()3((40&,5-#63("#+$((((((((7- (((18 9(((:;<=,$>$$$$$$ ?"((($>@200 :(((2:'''%+%%%;A B7 &>(((((((- 3)&# 0C,(7?@?4(3 2  "##,(@B((@#("# #,;(?(3("4(#>;(@3((4(  ?#>("0 @(-C#?/,;((3B((- !#?,D(B733)7)CC C CCE??:::: F< C::: ;E / ?',(3(3--(()3? '$((((((((#3?'$(((((((()-/ '$(((((((()3?/:'$(3(((((()(?+$((((((((#-? D$(("@)(()3? #-;$(" 37?)(7-? 'FF*GGGGA!$(?3((7@(0+4  :=HH*FHHHG6(23((7?(? #(((((((-7("@(-@B( #?)(((((((3)((@@"(( @?#(((((((3)((((((-( ?#(((((((37(((((((( #?#(((((((-7(((((((( #?#(((((((37(((((((( #?/ )(((((((3#(((((((( @#? #(((((((-7(((((((( # ////////E!!!EE!!! ???????? ?  ""( @twxIJJRSS_`aors|}~hklrssfhiwxx=>>eghlop[\]ceeNQR?@@233--.|           !"#####$   !"###$#$% &  '"#####$%  (&('"#$###$%) * +% *,"#####$$-.#/(+0)(,##1'0""""2" 3,#+/4-(#%**!'12/)**5"1#.# '  652"1%#$7  5)31$#$25231#,#75 52"'#%/$$48 449*:77797'"#####$$)  !"#####$$)  #####$%) "#####$%6("#,#$$ ""$#..* 9 *#/474$#####$#,#5) 4%####$%##%$#5)4$##### #####/7  4%###$$%#$###&/3 ;#####$#####<5'=37)7))))>???" 4 !!! ( wz{|~    !"#$%&'() *+,- ./012.34567  89:;<=>?@ABBCD **1 E/F6= GG "/HGI/9JKK L0'M;GK1N:OP,QRGGRST:;U*VWXYZ$/.[\\\\](0`wwwwwwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwxwwwwwxwwwwwwxwwwwwwwwwwwwwxwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwxwwwwxwwwwwwwwwwwxwwwwwwwwwxwwxwxwwwwwwwwxwxwwwwwwwwxwwwwwwxxxxwxwwwwwxwxwwpwwwwwwwwpwwwwxwpwwwwwwwwwwwwwxwwwwwwwwwwwwwwxpwwwwwwwwwwwwwwxwpwwwwwwwwwwwwwwxxwwwwwwwwwwpwwxwpwwwwwwwwwwwwwwwpwwwwwwwwwpwwwwxpwwwwwwwwwwpwwwwxwwwwxwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwxwxwwwwwwwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwwxxwwwwwwwwwwwxpwwwwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwpxwwwwwwwwwwpwwwwwwwwwwpwwwwwwwwwpx( @wwwwwwsww3wwwwswXw8wwww7wxwwwwwwwwwwwwwwxwww'wwwwww8xxwwrwwwwxxwwwowwwwwwwwwwwwwwwwowwwwxxw_wwwww?wwwwwxxwrwwwwx?ww8ww48H7swwwwwwwwwwxwwwwwwwwwwwwwwvwwwwwwwwwwwwXcSwqwwwxxwwwxwwwwwxswwqxwwxswwp(8R8'wxxxww( wwwwwwwxxwwwwwwwxwx?xwwxwwwwwwwwww?xwxwwwwwwwwwwxwwwwwxwxwwwxxswwwwwwwwxwwpuzzles-r9872/icons/guess.ico0000644000175300017530000006117612161170250015370 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $;J,-[^ mpkqsi?@A?@AVh>C*-mj233]``]_`355½' =<PWsy`:;<:;< !63GOuzWknokno=P',inǿln445_bc_bb567 /0@B}gwykUnqr orspMxrl\Qlo^`jc}wxz}RE<#B(<#[PFXZgsuhni_@$R9O6R9=!yq6u #lkuwyH5M3L3L3L4J0UD:LStz]G4M3L3L3L4J0UD9KStzZDFGMOPf\A%Q9N6R9="wo3r!ll223^`a^`a455OA;#@(:"XLA SUgvwii !" !"ssuwzvR~xbsudfep]ijrxq$n hvzo3sEGXZ@z|xS*rtk uwywy{ p"yr~7NPWYp'yr~<TV]_688Rvre:x{n|')124UWXsvx5smskCy&oqvkUgjz}9:;rƽ}tl[z_\KCrt¿Y\MO{}UWOQA.?%D+>#G6#$35moFG[NF*P8M5Q9B&k^%#bg|üx{}x{}F2M4L3L3L3K0SC51LMSUJ8M3L4L3M4J/XH @?X[adx{|wp; P6O6P69II25!!245^aa^`a456g`=+?+=,rl9<jn+. |! PRSPRS´Ƽ¼IL-3PRED5/XZ@6`~=7i o$[5./ACռZ\Ȼ s  4/JQ.0 EE^jkn|SU>,ad psRTC6!ž( @ ȼŶ5*dg%&GI78)*}k&Eso79º}px-{435!""cfgc>0 }QaF~t{~aZ)( kt}u9:;%&&ilm ^@B (%XZE&~|acdĺv~yrhB)D*YJ uOT r{rs?&O6Q8@$y>ftU|v@&Q8Q9D)ul2XgJsx||cegRUUZL=$B)E2ȻU/3n~sN879'((kmnʼnz}lƾ½^z}eha9+s7{XIK;<sz*8MXsz~lvov:ľ}12w}c?~_aDFqq_u|' fh;<>|} ~W8y!{}PR<>~orsxξy{umB*E+\M²zUZ%&ux?&O5P8? ǂAOtwVX@&Q9Q9D%s|1A cfHIsvxZ\]WH;$@)B-ȸÐQV+0HJ459$%&iklȎuʃhſɚZJzte`PZJu?K0>{v<*`VPA~=]y|>?ʿ4df?@ _D\^%#ctwMO./xH2(  r˴u{YB3'Vj^oLOOphTȓC9'/#Pgjkзi[٫@ppX[P{ui.\LN~Urҳ`Jݥ4d`INBuwy{ y"zQP;=&3NYVr>/wP UW|~WH{~bdWYĹԒB}{CLzÄ-$03Ӈ{Bpk,<cs ik!!Z\\zZN~h][\DE47Xj]}moZ^26(0` ½8J()^W!iplsgIKL?@AUe>C+-i345^aaž $85RV{g9:;LSYknoSfkln& 46EGtvkors ¿tGoo^e}vx{UI<%C)WJC UWi^>#Q9L3A%xq!"xz|H6J/UE9D0P6!RTU9 !"wy{zxRfgjjv}s%o l6uXZBzS%y q:OQUpmtwygjl[KC35GHz}un;"RTg`=, ż.25/pռ    !"##$%&'&()* +,-./011234566789:;<=>????'@ABCCCC,DEFFFFGH5IJJIKLMNOONMPQ?????RSCCCCCTUGFFFFFVW6JJJJXYZ[\]]\["Q?????R^,CCCCCT_1FFFFF`W6JJXJXaZbcc d%?????e+CCCC-fgFFFFhijkIJJIl!ZMNcON\m'?'n"*oCC-pqr2FF1s*tKuuvawxy[9yxwz{|{z}~/w3HLtY d){r8"w'?&)wFF 7uuKY <dd%????{FFFF15IJJIv4bb<9????? GFFFFF`W6JJJJXaq d????? 1FFFFF`W6JJJJXaZ: :%????'{FFFFGkIJJJv#MNON\}w'?&whFG!76uKYw^ {wi"b"d3$wLqr2FF1sHw“FF1É[[ dĽŽƝǽżgFFFF1iFFFFG<]<jŽ^Ž GFFFFFhFFFFF LPŽŽŽŽ̭GFFFFFV1FFFFF\^"dмѹAҷEFFFFGgFFFF2#MN<ҾćjѼwhh1h29\]jdӅd=wrjwrPw#*/~z`ViH L *SCCC+ q“FFhqGFF1sL +CCCCCf)FFFFhjFFFF1rq]????'@= H hFFFGΔFFFFA%????n????mj ]L dGdGgdz&mw"%&n"]]"djP"Pw z= dwd( @ŻźŶ¸4*dg'&DF45''j&Eso;5}r|-{77:#$%befc }UdF~u|aZ ~u'((jlm_Z\J&~VYY{~ytjB'C*ZK wXR##rk?&O6njx8fnU@&Q9ylnz}~=#D0UNvydg2u7{XJM<>xy0>NVs}lov;ÿtc_aDFx_$}|}y~ors̿wpyǂAOSUyzIJsvxÐQV./ʃhɚZJz`Pu<*`VWA=?@cF  !"#$%&'()*+,--./012234556789:;<=>?@'ABC---D3222EF5555GHI::J*)+'KLM--CN221OP55QR$9I;S=TU@BVWXYEZ[#\]^_`abcde0fgh YijhAklA mfnopmdq rstuh/v-.wxQ5y_z{;9|}~c+ s---V6555;::a't---VQ555G;:I~?)gut .-C !55:9=TU@' AlXYLi /  _ 4556 Q55!+cenQ5555Q5L/pP55Q#555\= [#]_\d~cjA~ ƃdd rsu2x65y5))+ 222ӿ6555555zp*)t3222EQ555555P)g ut21Y55۶55=TU@Lݯ Ox h~εۤ_455Q55,--WC--+peQ555555C---X-C-M_( 55Q#^555"lM--CB---,*hhzPx6wv._.,~~L  _~LYh'~h  ( r˴u{YB3'Vj^oLOOphTȓC9&3#Pgjkзi[٫@ppX[P{ui.\LN~Urҳ`Jݥ4d`INBuwy{ y"zQP;=NYVr>/wP UW WH{~bdWYԒB}{CLzÄ-$03Ӈ{Bpk,<csik!!Z\\zZN~h][\DE47Xj]}mo26  !"#$%&'()* +,-./0123456789:;<=>?@ABCDEFGHIJKLMNOP$QRSTUV: WXYZ[\]^_`abc7defghijklmn3oDpqrstuvwxyz{|}~ J!}z9 + ||-pHII(0`wwwwwwwwwwwwwwwwwwwwwwwwwxwwwxwwwxxwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwx;wx8wxwuwwxWwwwywUUUWxR(ww;xxXUt52XwwUXwXuwwsYqUUxRx8ww;rwUUUWr5RUwws7xwXXuR8www8wws6wwwxXwwwxwwwwwwwxwwwwwwwxwwwwwwwwwTwws;8wwwuU5wwwwwwwywUUXxxwwx7xUUUWxww̃YUxwwwwxuXUUXxqwwLx7xUXv44ww|w;wqw5UXqaSwwwEws8wwwwxXXwwxwwwxwwwwxwwwwwwwwwwwwwwwwwwwwwwuXwwwwwwwq338wS33xwwxww38y8k8xxxww8883s3Ywwww353u3389qwwww;3s837xvwws38w8ywWsxwwxSwwx8XwwwwywwwwwwwwwwwwwwwwwwwwwwwwwwwwÇwr8wwXwxwwwwwww|wwswxwww{gxxww\̌Lswwww\Lrw8twwv*7xsewLwywqCwwwȇw(wwwuwwxwwwwwwwwwwwxwwwwwwwwwwwww9wwxwws3wwx87wwwwwwwxws7wwxwwwx{xwwwxwxxww;wwwwxx7wwwwwxsw;7xwwxwqwxwswxwwwwwwwwxwwwxxwwwwwwww( @wwwwwwwwwwwwwwwwwwww{7zy7xwwwwUCwx_7x]Hw܈4Cwwwxwwxwwww\sqwwwȇwUwwxw|ȻXȇwwwW1Rwww4wwwwxwwy8wqwwwwwwwxw8swww8xwyww9w%xwwwwwww|wzwywswwwwxww\ȪwȌ?Rwxww8wxxwwwuywwwxww7wxwww7xwwww5wswwwwwwwwwwwwwww( wwwwwwwwww؈wxsw{wXyxww{x؈wsyww8wwwwwWyxwȊxwx7ww9ww{wwwwwwwwpuzzles-r9872/icons/inertia.ico0000644000175300017530000006117612161170252015677 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $y|}wz{vyzlp:<=?uyw{{||$%56}{~9;PRtw !46 ~twx:; DF{{x{|y{}ŹǺx||KLMVXYkmfcd\ avuwky{`|^TVVehi]zuwjwx]YTVWlnp\zsuivw]Y]~suewxZXDFG\^_]~suewxZXDFG`ccLNN\^_\zsuivw]Y]zuwjwx]YTVWlnoavuwky{`|^SUUeghkmfcd\ LNOXZ[*++x{|JLM %&&~twxkmoDFF466 ""#`bcz}~ #$$twx MOO`bc./0@BB}|<>? }MOPvy{9;;{~( @ vzsw rv<>!"-/uy bey}LOFIquNPQ''(~gh}VXY=>?ztsybde;<=xvvwGHI.//xvvwHJK)**.//ztsy]_` ]_`;==~gh}OQR<=>TVW)*+PRR:;<qtuFGH:;<knoGIJ 9:; UWX$%&*++gijTVW|(  46&' dg X[ǻ"'#ߊމ ފމ***()*!"";<=MOP 455lopnpq(0` y|}wz{lp;=uy|~$%569;PRtw !46 twxDFƺLMNVXYkmfcd\ aztvjwx\}Zehi]vw]TVVlnoy{`]CEF[]^`bcvegh*++#$$lnp466;==MOP     !"#$%%&'"  (%%%%%)*+,-.%%%%%%/012$%%%%%%3453%%%%%%." 67%%%%%%86,+99 4:%%%%;< -=> -;;?6@  ,ABB0 CDDEFGHI@JK@LMNOPQRSTFJJJKULVWXNOPYZX[ST\JJJJJJ]LVX^^XNHP_ZX^W^ST JJJJJJJ` GaW^^^^XbOQZW^^^W^STcJJJJJJJJJJdLaX^^^^XbOQZW^^^W^S cJJJeEJJJJJdLVXW^XNHPQZW^W^S  JJJJJ`@LVWXNOPQbX[S FJJJJJJ]LMfOP_bST\JJJJg-HI@JK@EFPDPDDDDDDDDDDD" 9@@@@hh9iJ"j k1hCEKJl G>=k  hmJJJJJn"-99 k  h9cJJJJJJJ@ok  hpKJJJJJJJlek  h JJJKKJJJJJlk   h@=mJ,qKJJJEGk  hjeJicJJJ`1 k  hrJJJJKG9 k  `JsDG k  ,9rJ,1@  >qtttttttttuCuottttttttou( @vzsw!" uy<>-/ bey}LOFIquOQR''(g~VXY;<=yubde wGIJ+,,]_`hUWXqtuFGHknoSUU$%&gij|       !""""#$%""""&' ()""""*+ ,-+.""/01 23-45678 9+:;       2 ++<=>,?@A-@B?CDDE FGHI1  IHGF?JKDLDDE,MGNOHI1AIHONPFQDDDDDDR'?MGNOHIAAIHONPMQD>SDDDR'?FGHI1  1IHGF?:3?@ABC9:>& h600N; IhR00hX n^(Va(0` $%%&!"#"##"##"##"##"##"##"##"##"##"##"##"##"##"##""#$%%vyz$%%qtu""#ruv"##ruv"##ruv"##ruvĥaeORgj̸"##ruv()./ Ŧ"##ruv͹~HJ"##ruvͼDF"##ruv|46 }"##ruv{vHJilǭ"##ruv}xξ"##ruvz~zz"##ruvϺʷн̸"##qtu"##vyz122z}_ab[]^pttw{{nrr"##~vyzJLMvyz<=>?@A"##prs577JLMBDE}uxy#$${~QSToqs;<={~ruvuxyx{|y{}z}~ruvqtupstqturuvruvruvruvruvqsty|}RST$%%vxz"##""#ruv"##"##ruv"##"##ruv"##"##ruv"##"##ruv#% }=?"##"##ruv`c|eiĤƪ"##"##ruv â"##"##ruvģ~ŧ"##"##ruvƩ}SV"##"##ruv˷;="##"##ruv ! QTz"##"##ruvHK/0@B"##"##qtu"##!""jmnilmy|}MOP~"## !"svwFGHFHIors|"##"##678pstdggbdeikl"##"##456wz{vyzNOP"##"##uxy##$"##()*012()*!"""##"##"##"##"##"##"##"##"##"##"##"##"###$$&'',-./00#$%"## !!!""##"##"##"##"##"##"##"##"##"##"####$( @  ruv02289:-..345-./456-./456!"=?-./456{-./456}sw-./456ȱY\á-./456ȱ}_bcf-./456|w{-./456-./789OQRfhiz}~|698./0456WYZy|}*+,122x{|vxzegh!!"'(("##9;;=>?577>?@577234234456456456455677.00/00678-..455-./456-./45689RU68-./456Ǫ-./456!"79-./456-./456uyŸ-..456ä=>35mp011566244;<=345fhiknortu,--Z\\455VXY|244788 123122677,--,---..-./-./-./-./-./-..011 $%%244567/01/01-..-./-./-./-./-./-../00())(     lop!"" swfi OR~ VYƯ !suvhjkvyzx{|rtu{} !!  ˼'(  cf lox| &&' %&& $$%"#$  !""$%%%&&  !!(0` "##vyzruvģadORfj͹+, ŧ|HKBD{46vHJilǭ}н˷/00z}~~_ab[]^ors|JLM<=>?@A678BDERST#%<>ƪRU ! ilmFGHcffNOP)*+344                  !    " #$%   &'#( &  )*#+,   )-./0   1-+23   )- -4,  '  35656'  7& 89 99   :;<=>?' @  ABC D E FG:H      IJHDKL8BC ' ?@78879RUǪ35mpknoZ\\|./0  !"#$%& &'()*+,-./+,0123)4567899:;<=>?@ABC?7 D 8@?E?F/G>E<<<<<<<<<6Y8 / /Z XVC[X FRJ/BA\AH(   lop!"" swfiOR~VYƯrtuhjkvyzx{|{}˼'(cflox|%&& !  !"#$%&'()*+,- ./00$&12345- 6/$"#2&789:; $/"<=>?61@ABCCC$DEFG=HDIJKL LCMEN=OP QR"=HS2T6"Q0#2&'(UVW(H6"0"#2X1YZ[\L@QR]"#&78^_( `ab""2X1cdK`efg6BX=ha#!"i@` j``@k @(0`wwwwwwwwwwxxwxp/wwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwwp_wwwwwwwwwwwwwwwwwwwwp'wwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwpwwww(wwwwwwwwwwwwwp_wwwx*"'wwwwwwwwwwwwwp'wwwwwx(wwwwwwwwwwwwwpwwwww"wwwwwwwwwwwwwpWwwww(wwwwwwwwwwwwwp'wwww""wwwwwwwwwwwwwpwwx(wwwwwwwwwwwwwwwpwwwx"xwwwwwwwwwwwwwwpwwwx""(wwwwwwwwwwwwwpWwwwwxwwwwwwwwwwwwwp/wwwwwwwwwwwwwwwwwwwpWx8wwwwwwwwwwwwwphW(pwwwwwwwwwwwwwwp'(qwwwwwwwwwwwwwwpWwxwwwwwwwwwwwwwwpwWxwwxwxxwxxxwpX((wwwwwwwp'wwwwwwwwwwpwwwxwwwwwwpwwwwwwwwwwpwwwwwwwwwwpwwwwwwwwwwpwwwwwwwwwwpwwwwwwwwwwpwwwwwwwwwwpwwwwwwwwwwpwwwwwwwwwwpwwwwwwwwwpwwwwwwwwwwpwwwr""(wwwpwwwwwwwwwwpwwwx"'wwwwpwwwwwwwwwwpwwwwr(wwwwpwwwwwwwwwwpwwwww"gwwwpwwwwwwwwwwpwwwwwr#wwwpwwwwwwwwwwpwwwwww(wwwpwwwwwwwwwwpwwwxx"wwwpwwwwwwwwwwpwwwz""'wwwpwwwwwwwwwwp_wwwwwwwwwwwwwwwwwpwwwwwwwwwpwwwwwwwwp'wwwwwwwwpxwwwwwwwpwpwwwwwwpwxwwwwwpxwwwwwwpwwwwwwwwwpxwwwwwwwpxwwwwwwwwpwwwwwwwwxwwwwwwwwpxwwwwwwwwwp( @ xxwswpwwwwswwwpwwwwwwwwwwt7wrwpwww(wpwwww2ws#wpwwwwhxwwwwus7w򏈈wp7wwwwwwu8(xxwsCRV0wswwwqwwwpwwwwww(pwwwwx'wwwwwwwrwwwtww22wpwwww8wwwpxwwwswwswxwxwswp0( wwxwwp7wxwsrwwwwp7x77wwxwwrpwxwpwxpwwwwp puzzles-r9872/icons/lightup.ico0000644000175300017530000006117612161170262015721 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $:;<##$!""!""!""!""!""!""!""!""$%%moqY[\[]^Z\]Z\]&'(Z\] Z\] Z\]x{|}Z\]Z\]Z\][]^Z\]/00:<<89:89:89:89:89:89:89:89:9:;689@=>yqrrrrrrrrsp|Zno5qr;rs:rs;pp=rs>xy>yy=st;op:rs;tu4jk0)d1*]BG0)^  0)^0)`*2yyy0)aR_xxx0)aHT0)_ 0)^[i0)^NW1)^&' 0)^(112=>0;;0;;0;;0;;0;;0;;0;;0;;1;<-::<>?uikjjjjjjjkhtgkjjkllkjjji' YL 6dI@)^JA)^JA)^JA)^JA)^JA)^JA)^JA)^JA)^KB*_JA)_PIKJJJJJJJLElgELJJJJJJJKF]tELJJJJJJJKJJʾͽ̽̽̽̽̽̽̽̽̽ͽͽ̽̽̽̽̽̽̽̽̽ͽͽ̽̽̽̽̽̽̽̽̽ͽ˽( @ FHIACD;==455<>>567fhi;=>456799_ab;=>456TVWuxy;=>456;==456$>?#=>#=> =>.:;ZJMLLLMKRpJMPMKPOF{ 7A T c+7 M^,7 M^,7 M^,7 M^,7 M^ -8N^,7 N^.)+***,$HP#,***+(3_'+****,"l(  344  455##$##$},-.$''$''$#$#$$1WXEEBBCC6ZZ hh.?1OOO dd#KKOO~ deEgg "(27)'$1AP*"5*$6)"5)>)9 I(0` 9:;&'("""moqZ\] xyz},11@=>yrj|Zno5qr;rs>yy4jk1)d  ^E^ *2aR_HT[iNW)' 1;;-::ihtg'YJA*PEJJlt̽̽        !"#$ $ %&%'''''''%()* +,----------./0122223322224567888888888889:8;;;8<<8;;8=>67888888888;89?888@ABBAC88v88888888888sw888888888889F88888888888.:;ZRpF{ 7A+68,.$*_l       !"#   $%&'(''(()*+,-...../012345678932:;<=======>?==@G=HIJJKL=0(MDN;<=======>?OPQQQQRST(U!F<=======>VS(QQQQJWX(DU!;<=======>VY%QZQQJ[\RMDN;<=======>?=]ZQQQ ^T' E ;<=======>G=^_R:`a=/RCD;<=======>G==OYS@==b'DccccccccdeVGGGGGGfgGGXVVXGhijkllllll=mn=k>>>>loflk>kk>k=/( O=======pq=======>G========0(r>=======ps=======>G========0'r>=======ps=======>G========0R O=======ts=======>G========0'r>=======ps=======>G========0(r>=======ps=======>G========0(r>=======ps=======>G========0( uvwwwwtvGfvtwwwwwqxwtwtwwtvy(zzzzzzzzzzzzzzzzz!zzzzzzzz( 344 455##$#$$},-.$''$#$1WXEECC6ZZ hh.?1OOO dd#KKOO~ deEgg "*27)*$1AP"6$)>9 I     !"!#$%&%'()*+*,-../0123456789:;<=>?@ABC5D7EFGHIJKLMNO7P7EQRHSTU1V"WXYZ[\]^_`abcdefghijklmnopqrst777j777uv757wrxyPDD`D77yz7D7w>{|777`777uv757w>{}~nnn~j~\r#(0`wwwwwwwwwxwxwwwwwwwwwwwxxxxwxxwwp7xwwwwxwxwwwwwwtGwwwwwwwwwwwwwws7wwwwwwwwwwwwwwpGwwwwwwwwwwwwwwtwwwxwwxwwwwwwsxwwwwwwwwwwwpwwwwxwwwwwwt7xwwwwwwwwwwp@xwwwwwwwwwwwwxwwwwwwwxwwwwwwpwwwwwwwxwwwwwwwtxwwwwwwwwwwwwwsOwwwwwwwwwwwwwwt0xwwwwxwxwwwwxxp@@8788883p;p;33ppx;7p;pw;pw;pw;pp@p@;;p8p;p{s8888x88{;sx{;x{;x{x{{;xx{s{xwwwwwwwwwwwwwwwwwwwwwwww( @wwwwwwwwwwwwwwwwwwpwwwwGwwp7wxwwwwppwww7wwpwwwwwpOwwwpwwwwww{www@p pwp {wp7 {pxKwpv p7u {K{xwp ;qxw{{{{ww{{{;{;{w{w{wx{{{wwwwwwww( wwwwww@7www0Ow@'xxww33Wp{ppwp{x8{{w{{{{{wwwwwwpuzzles-r9872/icons/loopy.ico0000644000175300017530000006117612161170266015413 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $*+,688456456456456456456456456566345&''788455456456456456456456456566567Y[\"##   hjkmop  }orsnpq788  ,--  gij())CDE  z}~fijilm&''  rtunqr  \^_ACCWYZY[\         IKL          567 566 456 rtufhi456 vyz`bc456 =??456 NPQ456 iklEGG`bc456 mpq{~456 455 789CEE\^_EGH())NPP EGH ruvvyzhkl ;<=X[[MOPqtu`bc JKL678@BB IKK688{~RTU >?@_bcJLM~mpqEGHacd kmnikly{}  GIIHJK\^_acdMOPJKL ( @ *+,TVWpstjmnjmnjmnkmnjmnnpqFGH\__nprjmnkmnkmnkmnilmtvw"#$kmn;==OPQ9;;IKK9;;nqr`bcJKL9;;mpqx{|JKL9;;-..JKL9:;|IKL=??MOP+,,ruvpst<=>`bc688>?@LNOIKLJKLJKLJKLIKKPRS FGH788NOPIKLIKLIKLJKLIKLLMN*++IKKtvw@ABilmABC>@@GIIkmnACC^`akmnACCkmnkmnABCpstjmnDFGoqr}577svw]_`677123DFGABCABCABCABC@BBHJJ uwxRTUQSSIJK|SUV>?@RTUJKLsvwruv^`aJKLjmny|}fhiJKLceftwxmpqIKLLNOABCrtu}788%&&=?@9:;9;;9;;9;;9;;:;<$%%(  orstwxdfg~\__cef\__~twxuwxfiimpq{~]_`}(0` *+,678345456&''688X[["## hjkmop ~mpqqtu CDEy{}fijkmnruv\^_ACCWYZY[\ IKK  fhivyz_bc>?@MOPhklEGHacd{~789CEE())NPQ;<=orsJKL@BBRTUJLMGII       !" #$%&$'()*++ $ ,-(./01 23( -4567  23( )89:; <3( -8/= <'( >,-8?@A  B3( -8CD6E <'( FG ($ 23HIJK4((4H*((($LM<-K*(((((((($NOP-33''''Q9NRS-T''U'3'VW 4XYZZ22222BY[ \YZZ<<]<2R^_`MF$\aM F* 2Fb H++4  cFb M-Dd'  Fb HeYfg   Fb (Th   2F* ijk$ l 2F$ 5mno4l  2Fb C6p  q4  +$ XK4bbb*bbb*$L9r st)MsuPv'FFFFFFFFFn>"w l4xYyz vYR]2c22cYn {vX`z!sB$g2'$ *| H|+4<'$ > L)D  Mvem}M<3b  4~kL *YfM]U$  $ga 4j2'$  $[s9 ip$2'*  4Thf G56no(2'$  A  .m?Z'$  *+ 9+(4 {L99L-K488$448$4$1-91[)9`,PJF)-)))-)-qol4xYk \Y XYR]]O^\1xs_444LH( @*+,SUVpstjmnmpqFGH]_`nprkmnilmtvw"#$;==LMN:;<IKK9:;^`aJKLx{|9;;-..y|}=??svwrtu`bc688=?@IKLRTU DFG788uwxACC}677\__123 QSS>@@fhicefMOP%&&      !"###$%&'(!))*+,-.&&.!/01)  234 56 $.78)#  294+:;<# #=>1)# 23?2@ABC# =>D) 2E42#FG )$ C>1)  2942HIJ" C>K&#  L &M N)OO(+C&P0Q/!CCC+C2RSKKTDD1UVW2MXQDD1DD1FHLYZ0[[[>[\]^L###_`0[>>>>0 &abCbb&c7d+!PPCPb)ce $   #!fQ&## #""# $)fg&# M7 & L+fg+# WVXH&6 )fg& # 6%h&! A +fQ2 " & 2 C_iN2&&&&+2&&+  j WkRiQggggdjV2lWmT"####Lkn_fffff7oe!"#$##pW LC++)+2cqr) #######&7D) ##jp#Ls\ C0D)#6 tRuRC #v;6 b[D) #6 WwrB& $LHxr2+[1) # " WTyHLeHb[D.# L2+&&bzS{W2222!Wh T  &fU4|V}4V1A#~h"66#j2L$66L`M999939%#L&!!2!!  ( orstwxdfg~\__ceffiimpq{~    !"#""$"%$&'(&&")*(+%&"*##'), -"""./0 -"""12&30456&789%9:;;4 %1<..==>&$4)-"1?(@"" A%B#$C@""%9&&D(6-&" E<%-.*#)$ $F9")GG$(0`wwwwxwxwxwwwwwwwwwp40wwwwwxwwwwwwwwwwwwwswwwwwwswwwwwwwwwwwwwpwwwwwwwswwwwwwwwwwwwwtwwwwwwwtwwwwwwwwwwwwswwwwwwwww@wwwwwwwwwwwwwwwwswxwwwwwwwwpwwwwwwwwwxwwwwwwwwp7wwwwwwwxwwwwwwwwwwwpGwwwwwwwtwx0wwwwwwwwwx7wwwwwwwww4wwwwwwwwwpGwwwwwwwxwwwwwwwwwwwwp7wwwwwwwtwwwwwwwwwwwwwGwwwwwwwwwwwwwwwwwwpwwwwwp48@444wwwwwwppwwwwwp0C0xwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwHwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxGwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwpww8wwwwwwwCwwwwwwwwwwwwwwwwwwwwwwwwwwwwwx7wwwwwwwwww4wwwwwwwwwxCwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@wwwwwwwwwwwwwwwwwwww4wwwwwwwwwpwwwwwx0@@@wwwwwxpwwwwww4043wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwtwwwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwxwwwwwwxxwwswwwwwwwwwwwwwwww@wwpwwwwwwwwwwxCwwwwwpwwtwwwwwwwwwwspwwwwwwwwswwwwwwwwwwxOwwwwpwwpwwwwwwwwwwwwwwwwwwp7wwwwwwwwwwwpwwwwwwx8wwtGwwwwwwwwwwwwwwwwwwwwwswwwwwwwwwwwwwwwwwwwwwpwwwwwwwxwwwwwwwwwwwwww?wwwpwwwwwwwwwwwpwwwwwwwwwwwwpHwwwwwwwwwwwwwwwwwwww( @wxxxxwwwwp80w7wwwwwwwwwxwwwwrwwvwpwwwwwtwxwwwwwwwwwwpC8wwwwCS4xws7swwwwpwwwwqwqwrwwwx?wwqwwww7wwwrwwu7wwwpwwqwwwwwwwwwP!Cw7wwxwwwwwwwwwwwwwww8ww7wwtxxwwwwwx8wwwwwwwwwxwwwxwwpwwR 'wwww( wwwwxwxx7wxwxwwwwxw8wxwwwwwwwwwwwwwxwwwwww?wwwwxwwwwwxw8wwwwpuzzles-r9872/icons/magnets.ico0000644000175300017530000006117612161170271015703 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $@BB:;<;==;<=;<=;<=;<=;<=;<=;?;<;=89ln]_`89:<=>;<=;<=;<=;<=;==;<=;==:uxY[_<$%egUW79=:=?VX89>  : @B UW78= ors:VXX[9:>""#""#z}~:^`Y[9:>%&&%&&"#$ ! !! !! !"##:OQ#$VX79=!!" !! !! !! !! !!!!":<>UW78=:*+z}VX78=<VX78=ABC: 9:=;<=;<{## ""!!!!!!!!"" !!!!!!"!$!$ #!$"%!$!$!$!$"%!GJ:<̾ms8;ɶQSHI :;\^cf,-TVHIci7;bdbdʷjmDE67qs"",,sv))RT`b dj7;wzwzʷmo<>-.sv))ux!!MNbd ci8< =>-.-.=> ʷwz!!eg  ci9='('(ʷ56gjgiABMNeggi** ci9=ʷ66ruloDE  RTbd **tw() ci7;qsqsʷ9:qtjmFGTU`b()sv00ej8<||ɶ@A$%DEEF-.=>ci8<23fh23̾msEIz~"%"%"%!$!$!$!$!$"%!$!$!#!!!!!!""!!!!!!!!!!"# FH&'   Y\BC ""%&Y[\$$XZ[ !XZ[BDz}vyvySU`cdwz{rtusuvsuvqst|466Y[\ikl[^_BDz}vyvySU`cdwz{rtusuvsuvqst|466Y[\ !WYZ$$XZ[""%&WYZwz{@A:;;=;<<>=>;<;<;<;<;<;<;<4;())$%%$%&'()245tw;<ȿ '()$%&$%&&'( !!3XZ>?ǿ1ACǿ^`b0/275964764764764964/64]6464645431536443Ŀ}@A  !! ! "Ƭ45,,<=>?78EB89ru Ǯ!"[]**[^/0;<VW 9:WYOLmo ǭWYdf&&01hkLN OL,-ǭWYdf&&01hkLN OL,-Ǯ!!Z\)*[]./:;UW 89VXOLmo ƫ 9:01""ABCD;=#$GC;<ux ̾     efӵϔИЖЖЗИϖϚϦϦЧϦϦϦЦѪvp{uxswrztztztztztzztzt~zt~zt~zu~yt}}xJLL~8::$%&'($%&$%&)**789]_,--,--;<CD+,577CEF@BB@BBFHI5669;;9:;CE(  <>> #$'*+!01'(67#$^a&''y|}()*,-1{JL-.^`a 456JLM$&%02"w24@BVX^BDF^aeMOR43+-bdcJLLo}styp~nq}qkxdref8 I:JLvDF3434<>.&BBC-1D6[]''OQGI̽2*$$+ ,-"" WKkm+,SUqu{zyy~uz#"20Q**,$'E79)swv799egy|K348%&&+,,"#^ CEE(0` ABC;<=;<4;?'(66*!6@@@@6ABC& DB#E&'(6@6A@66)*!+F-&'(7!G3H"&'(*!&'(* "I*JK*77777777*LMNMNNNNNNNN<*JJJJJJJJJJOOOOOOOOOOOJ PQQQQQQQQQQQR STUTVVVVTTVVTTTTTTTTTTTVWX YYYYYYYYYYZ XU[U[U\\U[\\\\\\\][]\\][U^_`Z a\[bWcUVcde[UUUUUfg[VUhbW^i`j4j"Z a\UUklh[mnT\U]UU\gog[[bpUcf`,qZ a\U\\_drkV[UUU]UU[goVsp[Uci`EGt=tG"Z aUUUUhou[\UUU]]UUU[gvpwUUhp:x==  ==xZ a]UVhmiilcUUUUUUUUhsiig[Vwi`B>=*?@$A?&1=>>>>( B C&' ()D'   ( 4EF333FGHI.......J1 ( K>-")))))))$C86--")))))))$J&9,,, ")))))))$Ju#: ")))))))$J: "")))))"??*******; ( <>>&''*+!01'(67#$^ay|}()*,-1{JL-.^`a 456JLM02"w24@BVX^CEE^aeMOR43+-bdcJLLo}styp~nq}kxdref8 IJLvDF3434<>.&B)0@4[^'(PRHJʸ BC-1D6[]''OQGI̽2*$+ ,-"" WKkm+,SUqu{zyy~uz#"20Q+,,$'E79)swv799egy|K348"#^   !"#$% &'()*+,-./01234565789:;<545=$>?@ABCCDEFGHGIJKLMNOPQRSTUVWVXYZ[\][^_`abcdefgYhi]`ij_klmnopqrYstuuvwxyz{|}~}565+% p5.%%(0`wwwwwwwwwwwwwwwwwwwwwwwxwxpp7pppppxppwxppwwwwwwypwwpwwwwwwwwpwwwwpxpp!xppxpppup%x wpwwwwwwwwwwwwwwwpwwwwwwwwwwwwwwyw""""""""""""""wxx""""""""""""""'yr"""(""""""('yr"""("""x""'"'yr"'(r""""(("'yxr"&("""""'"'yxr""(r""""""(r""'ywwwr""""""""8""'ywwwr""r'""2""v("'yyr"'"""""g"""'yxr"r""'"""r""('ywr""""""""""""""'ys""""""""""""""7yxb""""""""#""b"wwYwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwy@wwwwwwwwywwwwwwwwyxwwwwwwwwyw8wwwwwwwwywwwwwwwwwwwwwywwww wwwwwwwwwwwwyxwwwwwwwwy@wwwwwwwwyxwwwwwwwwywwwwwwwwy'wwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww( @wwwwwww5axxpyspRyx40pwwwyxwppS`xx0pywpw )qwwxwwwwswxxwxxxs""*"***wz("c"h""xyr*#*22ywxc*cxwyws(2"""xwwwv(h(yyzjb""&r"#""#xwxwwwwwwwwwwwwwxwxy7wwwwywxwxwwqwwwwwwxxxwwxpxwxywxwxwyxwxwywwww914Rwwwwwwwww( ww%p`7p!r!yxp_xxwz2##9x(6s"&*x?rb(wxxwwq wwyxwwy0wxwy9wwwwwwwwpuzzles-r9872/icons/map.ico0000644000175300017530000006117612161170276015027 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $h}`wYnYp`hffgaaxg~f}f}g~axccWoXmawif}f}g~ayagfffffffffffgg_yPiPj^geeh`_zgeeh_x[[ToTmZra|eeh_y_gefffffffffefh`zY`eggheZe|gfge}Zz`zZuVoVpZ\`yiezZegfffffffffgghg~`zaigf``Ydygege}Yzcyj`yQj~Qkbc`{f}Yeffffffffffge_aia|`ifUTnOj^thege}Yzdzgg_{Y_f``{e}YehgfffffffgdZ_y`{bxZsZbTVk\vUm_xhfe}Yzdzgeh`xaig``zhdyY``efffffgeYexhg~WlSlToSnWoZtYsWqTkff~YzdzgegexZdfg``zgge}a|[w`gfffgdZeygef}\sYuZtZtYtYsYsZtWqShYzdzgffe|Ydgeh`_zgfe~c{\v`gffgdZdvfgef}[rXsYsYsYsYsYsZtXpRstdzgefia{ajghdZe}gdzZ__effgdZdvTvxTvvf~hg[rXsYsYsYsYsYtXqStve}gegfayZuZa`_Zdzgd|ZdhgfffeZdvUwxWoWnUxu`yaw[rXtYtYtYtYsZuTm`yjghf}UwvSlTnTnToToRmTifi`{`hefffh_ay`zSk[uZtXpSmWmZoWoXpXoXoWoYqSj~Yraz_y`xTwvVn[uZtZtZtZt[uVpTkayZt`gffffg_b}`zTnZtYsZtYt\sTlToVoSmWpZtYsYsYsYsYsYsZtWqTlPi_hffffg_b|`yTmZtYsYsXs[r[pWpUo\vYtYsYsYsYsYsYsYsYsYs[uUp_gfffeg_a|`yTmZtYsYsXs[rMh[uYsYsYsYsYsYsYsYsYs[uUp_hffggiac~a{TmZtYsYsXs[rSmZtYsYsYsYsYsYsZtXqUmPi_gge``aY\uZsTmZtYsYsXs[rRmZtYsYsYsYsYsZtWpTkayZsajdZazYuPjUoTmTmXrYsYsYsXs[rMh[uYsYsYsYsYsYsWqTke~j`zZaYdxj_xTl\vZtZtYsYsYsYsXs[rTmZuYsYtZtZtZtZt[uUm_xic{Y{_y_{e|gfe~TuvVn[uZtZtZtYtYsXs[rZoXpYpXoTnToTnTnUpPi_xfY{dzhgfffge|SuvSlTnTnTmXqZtXs[r^___`Zb}b{ayjefffhghg_x_y_y_xTuvWoYt[rfhhgia[u[sZt`ye~gfe_x_x_x_yghghe|TuvWq[rfffgg_]y\uOh{TmTvve}h_xQj}VpVpQj|`ygfeh_wSm\s`ghae^\v\tUn\vVnTvvg_yQi}UoVnPh}_whefg_xTm\s]]Sj~TmXrYs\wTmayhbz[tZr`xY|dzgfi`yTm\sYpZuZtWqTmPh}Zta|b}\wa{jd{Z|e}ea{YsSm\sSn[uVpTk`x_x`y`y`y`yeeh_wc{b|\u[rSl\sNi[uWpTkfhgggggffg_y\v[wc|azSm\sSkZu[uWqTkffffffffffe`y`yg_xUn]tZoXpSn[vWqSjefffffffffggi^tNiXpOjXrTkefffffffffegdyZ_aOfggffffffffegdzYehgd|gffffhgggghdyYegef`xgffge|_z_{_{_{`{_xZegfff_x^uRj}XpWoWoWoWoWoRj\wggdyZ``````egffff[vi`ySl[vYsYtYtYtYt[uNia|f}Zehgggggffffffcyaza{hffUxuVm[uZtZtZtYsYs\vOjd|_z`hefffffffffffg~ghffgf~UxvSlTnTnTmXrZsYs\vNjf}`{`gfffffffffffff~efffeff_y`ya{ZsSmYtXsXsZuRle}`|`gefffffffffefg|f}f}f}f}f}f}f~hgibxWm\s[r[r[r[pezayagfffffffffffg( @ k]n[vhjkgf~kjh|eaZmfykjh}ekjjjjjjhne|V|Wdfg]b{ef_x[VSjY_g_z`feeeeeedjfz`jf_\a}ghb{_izYzSsbabx_hffffffh`edx]eXTkWmhgaz_gg~^ce`dz^ceffffg__{h|WmTtVyWoZuVob{b{_g~gcx_ge_g~e~`}]fffg`c}fjZsYrZqYtYsYsSj_g~ff|^ghd^hd~_^ffg`^xazfk\tZu[u[u\uWq\yggf_v]ca^f~d}^dffg`^xWv~Tn\xg~QkOkPkPlNhSvyi~gc{Vv}UoUqVsTpXnh_dgffe`]vVn[uTn\tqPtxZ}VtXpZtZsZsZtWrVlVyefffea]xWoZtXs_v]qUmXqZtYsYsYsYsYtZrVzeffgea^xVoZtWr_vWqYsYsYsYsYsYtWoUxdgedc_\uWoZtWr_v_vXrYsYsYsYtUo[qZ}fc_\TxUuVrYrZsWr_vh|SoZtZsZsZtWp\uj^]^g{byTl[sZtZtZtWr_vWnZrXqVpVpVqSlc}`}`zfyf~fg\zTnVpVoXqXs_vZbb]`v_h|gfgf~gb{`{a{Y~zUn_vajg_\Vw|Wy|a{h[zXxWx}_{hgg~Ux|^t[cjl[sVu~XoUre}Z{TmShX~yhehY}]rkVlXrZtTk\vf{]uaza|`~gf|V|y^sUoYsXo]u^z``}eh_}bt^~Uo^uOj[uUlfhgggfga}[d~Zr^uOiWqWq[sfffffffe~iUm[sOi[sgffffffha|\ftc~gfggggh`{`fk\xhg~a~```_`hdjp[tQl~TmSmSmTnOine~``dccdgfejvjb}iY||Xo[sZr[t[uOj\tahggggfgejb{dgfe~X~{WxWy}UrYs[uLh\ubgefffffdjg}f~f~f~fg~f}fzXw{Zp\tSm~]sbgffffffej(  mfpjoi`illnnluf]Yc~`avZ__bde`hazXtYm^|f~efddy`fe^o~[sQkSqaxb|Z[`|cef_Up}e[yWrYpXoWk^hf`VobxZsYsZuXm^d]ZVpc{VoZvXrazbe_yWoUpby^Z\bxe]~b{_}b{y]zUo^xXtbdycQjaycfd]bu`x`zhfgb{e[vgcd`ohUsWnSmx\dcbljiid`zVocnmjs(0` h}\uYo[heahc{aw`eTmfaycNi_hfg_zhi\w[[se~_xhfY_aZYz_z\uWqZ`hiUe}fZ_xZsVpc{i_dvUlYTuviZqYUwxUxuawazQj~Zt\vY|Oh{]Of]w     !"#$$$$%% &'($)"*+,- .( /$)"""*01+ ".(/ 2$3""*455+&'")$$6+72&(389"*:55;:)<<(6=14-88->""*4505=?(/$6454:88888888->*:555:?<"$ $6:5500888888888@:5555:E:F8888888+1554E99><(($:G8889H B8 >HI:7=D-J888889>:78888822KLLLMKH9998-888888>:+8888B2NNNO8-9J88888888888B$$'++88888MP88888--888J>%($::8888BNQ>888888888H<'C,I888-8NR98888888>>!" <6=1+J8888888BN88888888#"S++4544@>88888888OK 999>-9#S:5555554@>88B2MMMOLL2K '/ :555551515777+@888MMONTONNO $/!I+0554777+55514@>B2MMM22UO $7,V@417H99H:5541782ONNM2$( ,,J>@47H9H+;45;78222O2W(XWOYH88J:1:I!S4541:8222OL22ZZNO8888>VIG:I"S45GIB2O2222[>8>###"#::!!B2O22P8 >""#II"8OMN2J8->""##9822OUMB-J->"V92M\NOMOOKOP->".\MOMMTONNP]". \OTONNNOLO2222") MKLMLLKYNL"###)$$ON^=,HB9-99-HP_)<$$$K^`1+8888888[_")$ G::155E-8888888P2<(($05;5554EH8888[L$ 055;5554+::I8888LNN<$$$$$$$$ :0::0::0000GH8B8BBKOY#  22( @h}Zt\tkjckaZmfdlc|V|Wefg]hf\u]VTmY_f~_za^i^|i}Y}|Vrdaih]ehXUnf}___{h|UxZoged|_`j^h``{[sUr]c^Xw{]uOiUu|i}Xo`\vqWy|Vya\y_[s]|\h|i^b|bt\x]\il]t_|`~^~Um]teQl~v~      !"# $%$!!&'()*+%,,-./$012&334*+$***5678299:2&;<=>-+!!$*?=3@8:))2&<;3A* BC$?&D3@8EF&;;?@%ABCDEFGHIJKLMNOH,PQRSTJUUVWXYGHZ[\]Q^_UJ>A`aKH,[b(bEcdeafg;ahi\b]](jklmnIopq[i\[rstuve)\w\xy\bz{|}~~[xTSVsx\!@#ih+RQii66i(\( (0`wxwwwwxxwwwwwwxwwwwwwwwxwxxwxxwwwwwwwwwwxwwxxwwwwwwwwwwxwwwxwwwwwwwwxwwwwwwwwwwxwxxwwwwwwwwxwwwwwwwwxwwwwwxxwwwwwwwxxwwwwxwwwwwwxwwwxxwwwwxwwwxxwwwwwwwwwwxwwxwxwwwwwxwwwwwwwwwwxwwwwwwwwwwxwwwwwwxwxwwwwwxxwwwwwwwwwwxwwwxwwxwxwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwxwwxxwwwwwwwwxwxwxwwwwwwwxwxwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwxwxwwwwwwwwwxxwwwwwwxxwwwwwwwwwxwxwwwwwwwwxwxwwwwwwwwwwwwxwwxwwxwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwxxwwwxwwwwwwwwxwwwwwwwwwwwwwwwwwwxwxxwwwwxwwwwwwwxwwwwwxwwwwwxwxw( @wwwwwwwwwwwww7s{x{wyy7sXwwx8{ywx78{7x8syw{x8{wx87s889{x88wwx8x8Sxx?wx8X7ww8y838wwss8wwx8wwwwxwww88?wwwx88wwww8wwwww8wwwy88wws3xwx7wwwwx8sswwwxwwwwwww7xwwxw;ywws8wy{{xx3www{x88Xx{xw88wwxxwwwwwwwwwwwww( wwwwwwxssx88{x8x8xwwS;xxxws8s8?wws8wwww8wwswwsXw88wwxw7{wwwwwpuzzles-r9872/icons/mines.ico0000644000175300017530000006117612161170300015351 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $Ÿľ]_`SUV]_`^`aQSTTVWTWW`cdWZ[`bcBD{UX}\^`svwGIK(((*+zcf\]NO>?~#$|wNOO8:4PSy EEop}@@øQSLN !>@-.Ľ}RT[^@AMPos}VYbcz** Z\gi35#%RUSTZ\qtCE{~jlm012&'',-.%&&}µ=>?'((,-.''(MOOYZ0168}]_c~vSUV@A#$$(("#<>9IJY[uw{ _a+,!!z{23xy~UX]_MOWYZ\DF[\{{ú\^ !giZ\tumn@BBceeHJJLMO249;NPCE<>л::GHKL44֭9;sjmŷñt,-݅##Ѻ()OtwJMIK}AB$$FGyGJ 9968}wgj{м<= MO%'"# ,-&'XZϷ//**êĬǶڨ׵( @ ruACRSDFI}457ɼ'( !STMN>?LN[_an34nq__de9:23:<x{ !^`rvfi$%|XYzUX45CE]_ɾ|¿hjkqruprsoqsikplnswz{QS89ruv{~ù&'JKJ#$$ {}NPz''KL_`eg$$ru`amn&'PR]_df%&68HH÷jm^aUW[UW]XZ\žíıѡФқȼ01-twxKNx=?Ǻ)*zRUvw BBйUWbd,-CEFH''֥ru°ƹŶ!!ڕvy,.be;=ABPS9:RUmoاþ(  ĸ»np_zwytBD[[CEX[nq¸zzvxTWkl|DH~twSU]a{~ʺ66ȹMLȽsv^~qsioめؗNQ~_bóFHøqrԟ׍ƶ(0` ľŸ]_aSUV^`aacdWZ[CE{VY| svwGIK'((+,cf\]NO@@#$|wNOO8:4PSu EEopúQSLN !-.RT\^CD NPosbc++gi35$&STZ\qt|jlm012''(++,%&&}µ()OWY0168{|v((=><KMY[uv _a!!23UXmn@BBGJJL:<л:;GHKL44֭jmƷ݅$$twABMO gjXZϷ--ëڨ׵                      !   " " #$  % "   "#  #&'(((()''#('((((*  +  ,  -- ."///0/* 12(34354)64478729:;<=>1?@*AB@+(8CDE12FGHI. 2JK;L 1MNOAPQ(RST. 2*UVW1XYZAO[*("\%]^\_`abc 229de:9fg[Ah@+(ijklmn*%opqlrc 29stW;Uu1v?wxAy2(z{qlmnv|}llrc 2W~U1*1** xB?+(__"*(p\2* +% *(c,,1* ..((()(("_2 v *2  .*22v2 -. - F2 '  ** #IS2l2) 3D% v*%1 u4v 2qp+( .%hP1'2lq2)!"QABA zc2 }2#{ll%A qllc2l+(pqk%AX[v rmlic 2!|* *  __""2*22)#) 111vv112()((*%.F-9   .F1vuu*22 d9dd + *2v+CH7F2~u*:/ 1v 2)0F*V<v )*QTvw BBUWbe,-FH$$Ŷڕ,.9:moا               ! "" #  #$%&' &%&%(()(*+,), %-./01 2345678+9:5;<=>&&?@AB%CDECFG"HIJ$?KBLMNOPQRSTUV%-W& ǰ& 2!D  '% >2( ĸʺnp_zwyt»BD[[CEX[nqqsóvxTWkl|DHԟtwSU]a{~66ȹMLsv^~ioめؗNQ_bFHøqr׍ƶ           !"#$%&'()*+) ,-./012345678 9:;<=>?@ ABCDEFGHIJKLMNOPQRSTUVWXYZZ #[\]^_&`abcdef ghijkl#mlncopqrst9uvwxyz{|}~!v:X}5wm.}]}  A(0`wwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwxwxwwwwwwwwwwwwxwwwwwwwwwwwwwwwwxwwwwwwwwxwwwwwwwwxwwwwwwwwwwwwwwwwwxwwwwwwwwxwwwwwwwwxwwwwwwwwwwwwwwwwxwwwwwwwwwwxwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwxjwxwwxwwpxx`ws68wwwwxwwwpxwwwwhxwwWwwpwxwwwwwwwwwwwwwxwxwwwwpxxghwwwxywxwwwPewwwxywx6wwww|wwwwxwwwwwwwwwwwwwwwwwxwwwwxxxwxxwwwwwwwwxwwwwxwwwwxwwwwwxwxwwwwuwx 'wwwwxwwwpWwwwwwwxwwxlwwwwwwwxwwHtWwwwwwwwwwtgwywwywwwwxgwywxwwwwwwŇwwxwwwwwwwwwwwwwwwwwwwxwwwwwxwwxwwwwwxwxwwwwxwwwwwwwwwxwwwwwwxxwwxxxgwwwućwwxww&hwww""wvƌwwwwxwxbwww((wwwwwwywwwv(wxwwgww|wwwvwww(wwwwv7wwwwwwxwxwxwxwopwwWwwxwwr2wxx(*w|Hgwwwwwwwxwwwwxwww|wwwwwwwwwwwwwwwxxwxxxxxw( @wwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwxwwwwwwwxwwwwwwwwwwwwwwwwwwwwwxwwwwxX#(wwwwrwRwwv7wHwwwwwwGxywwcwwsxwywwwwwwwwwwwwwxwwwwww(wwwywwttwwwxxHw(wwywutwwwwxHwwxwwxw|wwwwwwwwwwwwwwsCww|wvw67wwwxwxwwrwwwwwwxzwwGwwwww?w8wwxwwwwwwwwwwwwwwwwwwwww( wwwwwwwwwwwwwwxwwwxxwwxywwwwwwxvwwuwwwwwwxvwwwxsgwxwswwwwpuzzles-r9872/icons/net.ico0000644000175300017530000006117612161170302015026 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $~rqqqqqqqqqqqsqqqqqqqqqqqqsqqqqqqqqqqr n+,hfhvegdfdgdgdgdgdgdgdgdgcfUXpegdgdgdgdgdgdgdgdgdgdgegUXpcfdgdgdgdgdgdgdgdgdgegbe~eg}fffuxurr%##$jkfrurtxf{}hj~i~i~i}hpr[j~i~i~i~i~i~i~i~i~i~ijpr[|~gpY[Duxftv1tsgg jj jj jj jj hi jj jj jj jj jj jj jj jj jj jj jj jj hi ii oo KLtxfnoqsNhjluxfmny{LpsjtxfmnvxKwzxop)''(ii"fmnvxKfnoxzMdjkopCellstG]``'((fnnxyLcfg(()fmnwxKade&'(fmnwxK|~=LIIHuvKade&''fmnwxKqade&''fmnwxKrade&''fmnwxKqade&''fmnwxKrade&''fmnwxKqade&''fmnwxK " qrade&''fmnvxKvwDvxDade''(fnoxzMFFdgh'()ekkprEyz;yz;^`a-./eklqsFz{<z{<fnnxzMFFfmnvxKDDfmnvxKDDDEFKMMJLLJLLJLMFHIfmmy{NFFjmmfoplnCz{=z{=[]`oqufvx<sseehhhhhhhhhhhhhhhhiicc{{{{cciihhhhhhhhffooCCRRnnccvwfWZXkmkfkmofikl ~fhe( @ ()|"#n()r'(q'(q'(q'(q'(q'(q%&n'(q'(q'(q'(q'(q'(q'(q'(q%&o'(q'(q&'p&'p&'p&'p'(q%&p/1u'(p()r'(q~^'(qU|~R|~R}StvJ|~R|~R|~R|~R|~R|~R|~R}StvJTfgE^'(qXoo#}[|~Q}StvJ|~R|~R|~R|~R|~R|~R|~R}StvJSfhF_'(q|~R\^'(q|~R|~Q^^^Z'(q}S}S%&ntvJtvJknm'(q|~R|~R<=>'(q|~R|~R=>?'(q|~R|~R;=='(q|~R|~R;=='(q|~R|~R;=='(q|~R|~R;=='(q|~R|~R]=$^<>>'(q}S}SY=>?%&ouwKuwKxFQSS'(pz|Pz|PO'(q}S|~RR'(q|~S~XXikn'(q~Ust=~~m@wzlPRIz'(qst)op/pq.op-pq.>=}=>?}~==?st2rs-^_(BBwx/qr4'(q[^c()q-...//011%&'orq'(q/1u(  VYnpkmmohkkmloloknhkkndghkrupst?gkmb~[|\^``]sub+1lo_d&Uhk|\jm^lo`ĹĹlo`ĶĶkn_ym}hj|~[lobvy{WYZpsulnZ]dnfm9:*''oknRTR CDCqt(0` ~q nhfhvegdgUXpfuxust%%mnrurtx{}h~i|~gpr[pqY[Dtv1tsgg jj KLsuMjlmxzMpsjwxKwzxop)ii"lnC~stF^`a&''dghadevxDJEF'()''( " ++,z{<DEFJLMFHIWZX oquvx<iidd{{CCRR       ! !"  #$%&'())(*+,"- .#/0000,1/0000,0000,01,234555567" .##89::::;<::::::::;=::<9>?45555@7" .#ABCDEEEEEEEEEEEEEEEEEEEEFG555567" .HA*IJ!!!!K!!!!!!!!!!!!KLMN4555567" .##A*O/PQ,,PPPPPQR4555567"- .#M*ST$$UVW)BWX+,"  .M*ST## !!!A"- .###T*OY##$,##PP#######P,P,,PP#P#"  .Z88[\*]"[88^[8[[[[[[[[[[^[8"8_"8888Q .`a%bL*cd"%%""-eeeeefffefgfeehijkeeh-,- .,PT*Sl/mnkop-  ##d*ST%pqqqqr  oskt /- .Hd*STauvvwxSh/ -oskt- .#PM*STap>y5555@2 -mskt - .dVS0%p>y5555@2z {jkt /- .HdVS0aap>y5555@2/ |j} ag .#M*STap>G5555@2z {n}t -.d*S0%p>y5555@2z/ {j~t/-  JVSTaapV {j~t - z #J*STaa uc onk-  TT*Sl##z|xx|/mnkmip-  "[\*c![""88uu%-je- z Z""*c![""8""Q8818"881"8"$ .H,#PM*OlP,pwwp,#P,"  .#AVST%pwWr i%,P" .#M*STa r::p- %,^8#" .#A*OYP, iWwiipN+#"- .HMV]8+a"+ .H##ACC' .##%++$$^_"N8_  .##,,,,,##tpp#N^,b .# bN_,"  .##! a""""""-  H,####P,iiii,#,,,,,,,,"  Z""""""""""88h88""""8"[""""$ h/i ( @()|$%n'(q()r&'p/1u}[T|~RtvJfgE^oo#[fhF]jmn<=>^=$RxQSSOYst=m?wzlPRIzqr-op.=}=rs3^_(BBwx/[^c.//011%&'orq     !"#$%& '((()(((((((')'*$++,-. '/0(()(((((((()'1$++,-. (0233 !%+,4-. ((567--78 '(669.  563))6:66;< ((<=>?@AB ((CDDDEF>?GAB ((G"HH"GIG?>EA ((JKL++LG=>?>EI ((JL++LG=>?GE= ((GKL++LKGF>?GEA ((.MNOPQON=?>AB (( RKSRA@T?@>UB 6))366VIFUW(CFAXYF6Z=[Y M.((.MM\<]^_BX6M6 ((EJ`SJEY (' JRabRGcddde;f 'ghihjk=lmnkno8pqqqqros htututvvvm^wwmxtyqqqqz{x 9.99J<<<_|_Jc9}qqqq~f [TV >YTTTT AAAI=AIU66BBBBB( VYpslonphkknkndghktUg_~[^b_]sub+&modĹĶoyruhk|\vy{WYZpsuZdnfm9:*''RTR CDCqt   !"#$%&'()"*+,,+-.(/0  *1234+56728 !9:;<<=>?@A8 ,BCDDEFGHFI ,JKKCL?MFIF ,2INI& h600N; IhR00hX n^(Va(0` $pr`bWYYz}~lo\^RTUwz{lo\_SUUwz{lo\^{|~RTUwz{gijlo\^tu)*RTUwz{{~x{|lo\^vw01RTUwz{lo\^tuf}~|z{ruvRTUwz{uxylo\^tuAVWXUVKMMx{|RTUwz{x{|}lo\^uv//TVWz|~nqqlo\^tu))RTUpstruvlo\^""!!CCRTUtwxlo\^SUVy{}ln]_RTUwz{loZ\RTUwz{nqLN_abQSSwz{ln]`011\^_wz{lo\^123uxy~}}z}~*++~lo\^/01CEFPRRMOPMOPNPPMOOSUV`bclo\^/01~z}~lo\^/01vxzlo\^/01ikmlo\^/01lo\^/01|~lo\^/01_ablo\^/01twxlo\^/01SUVx{|loZ\./2RTWvy|knhj673\^Yux tgi[]\^[^`cJK|;_b\^\^\^\^]`LNZ\]_\^\^[]eg$$N35adf[]\^\_[]`bruknlololoknory|knlololololnnqlolnlololokmvysvkmlololokmpr}WYZz}~_abruvjmm`bc~vxzx{|ikmuxy( @ _a{~]_y|}]_|}dejkjkcdy|}\^&'y|}[])*sZ[yz|}y|}[])*jk{~z|}\^&'twx]_mnQRXXXYPQvyz]_z}~\^y|}\^knoy|}]_lnoz}~~PRR~]_jmnwz{\_jlm[]jlm[]jlm\^jlm]_ilmy|}_aprp[]Z\cy|fipSU_a]_]_VXY[]_]_]_\^\^]_]_]_XZWY]_]_\^_avyzz|}(  US'(tu#!77QO"#st~~(0` nq^aWYZ}hjRTUvy{\^}~gijlo)*uv01y|}f}~ruvtwxtuAVWXUVLNNnqq))""CCNPPLN`bc/01_ab232~*++CEFMOPilm\^\ux teg`bJK|;$$N35aqsy|vysv           !"#$%&'()))(*+   ,-. /%&0(111112345 6 -778/&0(111119:6;"! <=77>>?/ %&@(11111ABCD 6==," %&0(1111123E4 4,F7G %&0(11111HI7 $J$ FJ$JG$+;KLMHMHMNO>PPQ PP %$OI$OIO+ 4  %J%   >RE> J=SJ> 8+ Q%! 7TU$ VW%?XYZ 6/[Z %U\T]^Q____$GSU%U\T$ ! UJ,6`ab> %U\T> $\]%7T .>?\\Y"%7T U`"e "Z.%7T UbS%\T77 >$-aUG/>>Z8`-7c7,$8 ]7?\6W;d=?7bSY">77Z$,=-"-7?,@ %-AB !4 &'CDEEFG B%'HI J  < 2K % HLBM$ 6NOP5 J?@8A-&B' -CCDEF'GH I3J 6'KLMN?LO @PQ>6RRRRSST@UFV. W B&DXY%@(0`wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwhwwwwwwwwwqwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwqxDHDhwwwwwwwwwwxwwwqwxwwwwwwHwwwwwxwqwxwwwwwwwwwwxwwwxwqwxȈxwwwwwwwxwwwxwpwxwwwwwwwwxxwxwpwxwwwwwwwwwwwxwqwxwwwwww(wwwwwwwwwwxwwxwwwwwwwwwwwqwxHwwwwwwwwwwwwwwwqwwwwwwwwxwwwwwwwwwwwwwqwwwwwwwwwwwwwwwwwwwwwwqwwwwwwwwwww(wwwwwwwwwwqwxwwwwwwwwwwwwwwwwwuwwxwwwwwwwwwwwwwqwwwx'wwwwwwwwwwwwwwwwwwwwvwww$wwwwwwwwwwqwwwxwwwwwwwwwwwwuwwwpwwwwwwwwwwwwwxwqwwwqwwwwwwwwwwwwxwwqwwwxwwwwwwwwwwwwwwwxwwwwu7wwwwwwwwwwwxwwwxwpwwwrwwwwwwwwwwwwwwwwqwwwtwwwwwwwwwwwwwxwqwwwxGwwwwwwwwwwwwwwhwwpwwwpwwwxwwwwwwwwwwwwxwwwwwwwwwwwwwwwwqwwwrwwwwwwwwwwwwwwwwwpwwwpwwwwwwwwwwwwwwwwwqwwxsgwwwwwwwwwwwwwwwwwq0wwwwwwwwwwwwxwwxxwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxwwwwwwwwwwwwwwwwwwxxwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwxwxwwwwxwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwvwwwwwwxwxwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwww( @wwwwwwswwwwxwwwxwwwswwxwwswqwwxLwwwuxwwuw3wxwwswxwwswwsGwwswwwwxwwwwwwwywswwwxwwwwwwxwwwx7wwwwwWw0wxw7wwwwswwwxswwsww7wxxwwwwwsywwwwwwxwwgwxwwwwxq8S53wwwwwwwwwwwwwwwwwwwwwxwwxwwxxwwwwwxww7wwww7x7ssxxwwwwwwwwwxwwww( wwwwwwwwwwwwxẅwwwwwwwxxwwwwwxwxwwwwwxww7wxxxwwwwwwxwxwwwpuzzles-r9872/icons/pattern.ico0000644000175300017530000006117612161170310015714 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $z|}rrr|}}|}}}~~xyyuuv~~~||}|}}{||\\\ &&'%%%%%&#$$,,,///###%&&%%%'((jlmopqrrrzzzttt~~~PPP WXYlnorstssszzzuuu~~~QQQ YZ[LMNqssssrzzzuuu~~~PPP XYZy{}qssqqq~~~~~~xxxvvvQQQ Y[[rstzyyrrr}}}{{{{{{yyyNNN XYZprrMMMXXXVVVVVVWWWRRR}}}]]] KKKMMMMMMMMMOOODDDxyznopnop^`a}nop_abnopnopnpp   opp'''''''''!!!dfgnop  XYZrtunop  XZ[tvwnop  XYZy|}npp  XYZnop  XYZpqq222888777777777:::<<<666777777777999444`bbrstzzz|||UUU YZ[qssqqq~~~~~~xxxttt~~~~~~|||PPP XYZbefqssssrzzzuuu~~~PPP XYZ\^_qssrrryyyttt}}}PPP XZZsuuxxxzzzTTT XZ[ijkXXWggfddcddceee```]]\eeedccdddccbiiiEDD   []]jlmoqsjlmdfgfhiqtu_ab~qtuy{}^`aTVWpstdghWYZ[]^suwtwxnprmpq( @ z{{~~|}}^__$%%"""'((++,!""&&&\]^{}}wwvzzzxww}}}OOO EFGsuw~wwvzzzzzzRRRIJJuuu|||yyyNNN FGGwxyQPP[[[YYYVVV___ DDDJJJKKKAAAnpphjjz}hjkhjjijk jll 111//////111*** 000///122&&%`abhjj  FGGijk  GHIhjj  FGGopq)))&&&+++,,,&&&'''(((&&&  MNO~~~~~~UUU GII}wvvzzzwww|||OOO FGG||||||SSS FHHuwwhhgsrrrqqmlljjjrrroonxxxGFF DEF{~npqqstuwxuxy(  {||>>>&&'CDE|||rrr|||&&% *++%$$333BBBLLL MMMmmm444EEE *+,233zyy~~~+++ ,-.kkkuuuqqq|||%%% *++(0` |||rsttwxYZ['(($%%...jklnopSSS WYZMMMXXWEDD_ab~dgh666:::fhidddnpq          !"   "#    $     " "%%%%&!' #### """"( )*****!*****+,-./)**********+01,2)**********+03)**********+--)**********++*444*)*444*5##$# $###  #6)#+-### ! -$# /## $## 7877788778787##### ####0  &$ 9:   169   !  "#1     ! %;<<<0 <<<<(#$ ## =!,&&&&&&&&&&''>>>>>>>>>:'?@,A? -6;?AB0::2-2? 55+@-C./0=A0:/  > A A:9B-?-B-?( @z{{|||^__'''!""(((,,,"""\]^z}wvvyyyMNO DEFsuw~QPP IJJ~NNNFHHuwxYYYVVV___AAAnpqjjj{~hjkijk122///+++`abxwwSSShhgrrrsrrmllqstuxy  ! " #$%%%&'%%%()*+),! "" -.%&%/01/%2345677 ! "8%9%&0%%%:);.<=>?@AB&C0D(28EFG4H3I%%%%JKLKM%N&%OKLKJHPQR)3S%&9%JKLKT%&%%OKLKUVWRXU3S%9%%JKLK@%&%%OKLKJ3Y%/&&JKLKT%/&%OKKK3Y100CZ[Z\000DZZ]^)I%%%/0%%%01%%&0%&%()_ GY%&%/01&&C%%%/0%&%2)I%%%&/%%%0&%%&'%&%:)F000`0C%8)H6 77 # a=%&%/0%N%:3GXbG6! "c >.%&%&/%&%:)defGG6 gg g hi%1%/0%&%:);jklmIkm"(%1%&/%9%()nbMMM@WMMebhbobbpRd)q3)q333q)HVrs4UtPW3FHuvwrGxHyVfzH{V|GG}|5e~~GeQ )H;^o)^H^V( ~~~>>>%%%CDE|||qqq *++444BBBMMMmmmEEE*+, 233zyy+++,-.kkkuuu    !"#$%&'()*+#,+-.()/ 01234)55567()89:/5/;<=>? @A8BCD4EFA7GCHI JC..K.L.D'MN OJ&...DPN QJ7..N(0`wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwxwwwwwwwwwwwwxwwwwwwwwwwwwwwwwxwwwwwwwwpxwxwwwwwwwwwwwwxwwwxwwxwwwwwwxwwxwwwwxwwwwwwwwwwwwwwwxxwwwwwwwwwwwwwwwwwwwwwwwwwwpwwwwxwwpwwwwxwwwwwwwwxwwwwxwwwwwwwwwwwpwwwwwwwwwwwwwxwwwwwwxwwxwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxwxwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwxwwxwwwwwwwwwwwwwwwwwqwwwwwwwwwwwwwwwwxwwwgwwqwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwxwwhwwwwwwwwwwwwwwxgwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwww( @wwwwwwwwwx?wwwwx8Ow?x@wwxxwwws7wqp@xwrwwwpwwwwr@wrwwwqww7wpOwwwwwr@4a!` wws ww7xwxwwxws8wwwwwwwwwwwwwwwwwwwwwwxwwwwx7x7wwwwwwwwwwwwwwwwwxwxwwwwwww7swwwwwwwwww( wwww@0gwwwwwwwwwwww0wx8@7wwwxwwwwwwwxwwwwwwwwwwpuzzles-r9872/icons/pearl.ico0000644000175300017530000006117612161170312015344 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeeeeeeeeefffffffffffffffffffffffffffffffffffffffggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhgggggghhhhhhhhhhhhiiiiiiiiihhhhhhhhhhhhhhhffffffffffffffffffffffggaaa^^^_____^__^__^__^__^__^__^__^__^__^bbb_____^__^__^__^__^__^__^__^__^____^^aaa```_^^__^___^^^YYYXXXYYY__^_____^__^__^eeefffffffffffffffgggaaaoppdddgggfffffffffhhh^^^uuvbbbgggfffffffffhhh___tttcbbgggfffffffffhhh__^ttucbbgggfffffffffhhh__^wyzttucbbgggfffffffffhhh__^uxygjkkmnjlmjlmjlmjlmjmnSTUjlmjmnjlmjlmjlmlnobdettucbbgggfffffffffhhh__^dghttucbbgggfffffffffhhh__^fijttucbbgggfffffffffhhh__^RTUade^`a^ab_abJKL^`a^ab]_`fhi.//dghttucbbgggfffffffffhhh__^lopdghvxyttucbbgggfffffffffhhh__^befdghttucbbgggfffffffffhhh__^dfgdfgttucbbgggfffffffffhhh__^dghdghttucbbgggfffffffffgggbbbNPPNPPnnndddffffffffffffhhh___dfgdfgttucbbgggfffffffffhhh__^dfgdfgttucbbgggfffffffffhhh__^jmnjmnttucbbgggfffffffffhhh__^TVVOOOONNTUVttucbbgggfffffffffhhh__^ttucbbgggfffffffffhhh__^ttucbbgggfffffffffhhh__^ttucbbgggfffffffffhhh__^ttucbbgggfffffffffhhh__^ttucbbgggfffffffffhhh__^SUV......SUVttucbbgggfffffffffhhh___iklhkltttcbbgggfffffffffhhh_^^gikgikuuvbbbgggfffffffffgggaaaUVWUVWoppddcgggfffffffffggg```Y[\Y[\qqqcccgggfffffffffhhh_^^gijgijtuubbbgggfffffffffhhh___dfgdfgttucbbgggfffffffffhhh__^dghcefttucbbgggfffffffffghh___VXY9;;dghknottucbbgggfffffffffiii[ZZ799DEFEFG688hkldgh !!HIJACCBDDDFG89:;<=DFFBCDDFG:;<ttucbbgggffffffeeeiiiYYY&'(fhittucbbgggffffffeeeiiiYYY466dghttucbbgggfffffffffhhh]\\psswz{ qttxz{ttucbbgggfffffffffghh___ !!wz{ttucbbgggfffffffffhhh__^ttucbbgggfffffffffhhh__^ttucbbgggfffffffffhhh__^ttucbbgggffffffffffffeeeoppuuvtttttuttuttuttuttuttuttuttuttuttunnnttuttuttuttuttuttuttuttuttuttutttuuvpppqqrtuuttuttuttuttuttuttuttuttuttuttuttuhhhfffffffffffffffffffffdddbbbcbbcbbcbbcbbcbbcbbcbbcbbcbbcbbcbbdddcbbcbbcbbcbbcbbcbbcbbcbbcbbcbbcbbbbbddccccbbbcbbcbbcbbcbbcbbcbbcbbcbbcbbcbbcbbffffffffffffffffffffffffgggggggggggggggggggggggggggggggggggggggfffggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff( @ ffffffhhhhhhhhhhhhhhhhhhhhhhhhhhhggghhhhhhhhhhhhhhhhhhhhhhhhggghhhhhhhhhgggfffghhhhhhhhhhhffffffffffff___^^^_^^^^^^^^^^^^^^^^^__^a``^^^_^^^^^^^^^^^^^^_^^^^^a``__^^^^^^^dddfff```^^^^^^___ffffffffffff)**|ffffffffffff'()ffffffffffff#$$ffffffffffffffffffffffffcfg *++ffffffffffffY[\ffffffffffff]_`RTUffffffffffff]_`dfg+,,ffffffffffff^`aacd'()ffffffffffffOQQRTU !!ffffffffffff[]^_ab%&&ffffffffffff]_`acd&&'ffffffffffff]_`acdppokll%&'ffffffffffff]_`acd%&'ffffffffffff]_`acd%&'ffffffffffff]_`acd%&'ffffffffffff\__`ccWWVYYY%&&ffffffffffff_abcef&''ffffffffffffTVVWYZ""#ffffffffffffTVVWYZ"##ffffffffffffacddgh&''ffffffeeegggTVVXZ['((ffffffgggaaauxyffffffkkkRRR:;<ffffffjjjUUU%&'OQR!"#"##"####$!"""##"##"##ffffffffffff+,--//ffffffffffffffffffffffffffffffffffffxyy{|}{||z{|yz{z{|{||{||yz{uuv{|}z{|{||{||{||{||z{|{|}uuvyz{{||{||{||{||{||z{|{|}xyyffffffffffffbaaaa`aaaaaaaaaaaaaaaaaaaaabbbaa`aaaaaaaaaaaaaaaaaaaa`bbbaaaaaaaaaaaaaaaaaaaaaaa`baaffffff(  fffcccbbbbbabbabbbbbbbaabbbccccccbbbfffccccccfffiiigghjjjdddiiigggwz{kllggg 9:;fij|||iiiggg%&'DFF^`ahhhggg$%%@AB`bchhhggg&&'hhhggg%&&hhhggg%&&Z\\suvhhhfgg#$%@AB[]^hhhhhh#$$FGHorshhh\\\ACCNOPFGH:;<=??hhh_``hkl9:; hjkfij`bchklsuwiiikkkjjjfffoopuuvwxxsttssstuurssqqrqqroppqrrqqrtuusttggg(0` iiifeegij^^^VXYoppttuxz{vyzlopdghTUV^abLLM...NPP9;<CEF466 !!&'(         !"  ##$%&'()**+,-... / 0"1.2..3'! "1....3'/ 455565507 01...8/ -92* 0") '/  " 0    !5 / :0 0# !&: ,:; < < : &::,   0   0  / # # / / <6 / / '..'  $:....: ! !  =>....= / : ....8 / (..; /!  4774 /!    !! :9 9> !   > >   > >=>" "> :>> $! :9 9> !!   0 ! / $ 0 "  9$ ? 0 @$#>-#9> ?AAB :0 C6AAA??AAA? / D  E ! B 0 11)E 1:'1;;;;1),;;;;; # C %9"# " 555( @eeehhhghh_ab^^^+,,z{|'()#$% WYZ]_`TUUdghOQRuuv:;<uxy        !  "   #$$$$$$$% &'((  )* #'&((+,- ). /&!!0'1 23&  ) 45)! 2 6-") ) 2' "-7 /-!1 2 8&- ."! 2" --). ) 2 "-) )19  "). )! ((!-   ) )+(("   ). )&(("   ) ):/*  "; 4;') 2' ,)"&/ *&" 2-- 1"&/ *&-"0 2&--- 1 ) 45') 2 )"5/ *) 2) - !  # < 2  / =  /2$ 7)222#  -    "''" 9 9> .( fffccc`bcbbagggdddiiigghkkkwz{kll 9:;fij|||%&'DFF_``#$%ACC%&&Z\\suw@AB[]^hhhFGHopp\\\NOP:;<=??hkl suvoopuuvwxxrss   !"#$%&'()*'+,-./ 01234%5678/ 09:;/<.=>?@>ABCDE=' FGH:IJCDE=60KLM6NJ6O8 PQRJ SHT*#8GUVWX3YZIJT[9:\],^_1`ab2c`de/f)cgh//i/H,G3U=36jklkmkmmmWmmkg(0`xxxxxxxxxxxpxxwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwxwwxwwwwwwwwwwwwwwwwxwwxwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwxwxwwxwwxwwwwwwxwpwwwxxwxwwpwwwwxwxwwpwwxwwwxwwpwwwwwwwwwxwwpwwwwwwwwwxwwxwwpwwwwwwwwwxwxwwwwwwwwwwxwwxwwpwwwwwwwwwxwwxxwwwxxwpwxwwwwwwwxwwxwwpwwxwwwwxwwxwwxwwpwwwwwwwwwxwwxwwpwwwwwwwwwxwwwwwpwwwwxxwwwwxwwxwwpwwwwxwwxwwxwwpwwwwwwxwxwwxwwpwwwwwwwwxwwwwpwwwxwwwxwwxwwpwwwwxwwwwwxwwpwwwwxwwwwxwwxwwpwwwwwwxwwwwxwwpwwwwwwwwwwxwwwwwpwwwwwwwwwwxwwxwwwxwwxxwwwwwwwwxwxwwxwwxwwpwwwwwwwwxwxwwpwwwwwwwwwxxwxwwpwwwwwwwwwxwwxwwwwwwwwwwwxxwwxwxwwwwwwwwwwwwxwwwwwxwwwwwwwxwwwwwwxwwwwwwxwwwwxwwwwwwwwwwwwwxwwxwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwxwwwwwwwxwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwxx( @e8HSe8wxxxxwwxwxwwwwpwx7wwwwwwwpswwwwwxwxwp7wwxpwwxpxwxwpwpwwx7pwpwwwpsHwpwwwpxwxwwxwwxwpwx7pwwpwpwxgwpwpwwpxWpwwwwwpwx7pwwwpxgwpwwwwwpxWpwwwwpwwpwwwpwspwwpwwpx7wpwwwpwwpwxwwxwwxwwxpwpwpxHpwwpwwwpwxwppx0wwp%wxwpwxwwwxxrwwwwxwwwwwwwwwwwxwwwxwwwwwwwx88( 8egwwwxxxwwws`wxpxqwxwxw?wqwwwwwxgwxxwrwxWxwx8wqxww(xwwwwwxpuzzles-r9872/icons/pegs.ico0000644000175300017530000006117612161170314015201 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $֯FF!!89ۙ׫CC !:;ڝ٣>> !?@٦ְ ޏ׫ ܖڟ٢KL&'DD,-669:"#Ժ <<55((*+ۙstݓ{|߈ލpqvw}~ခԹԹԹة>?11ݒ٥;<34ܗڡ9956ۛ۝6788ڟ׬ ߊاݑڠܗۚڝHH$$AA**::013367"#Ժ >>77!!00''**--ڞxyܗݐ߉ݍݓ%% xyގ## }~!! ⃄ᅆ !ҿӿϼϼܙ-.%&߉ڞ߇;;$$ <=%%ڠވܜ11))ߌ( @ ߉YZ၂ abpr qrԹ ၂׮ލڠܢۜYZޏgh}~ۙ٤մ ׶ֱ@A/0ߋ׫==22ݒڞ6777ڞԸԹӽӽӽခ OPxyWXop_`ghghն|}׫٢ސۙܞ۝Z[ސefpqz{䀁""ڟاԽװշ ֻԷLM;<ݕֲII=>ۛժCD=>ן֤@A@A֤ճתeeBCاツܕmn%% صн]^QQԩ(  jkֱ߉ဂᆈ^_DEӿٰۧЧܗBC77==>>ႄgh#$tu˻deIJԶ`aTUҼΪڥܞ䑒ߥ਩βռУܳ߮(0` ֯DD!!;;ܗ׫AAۛا?@ְ ߊڟ٢JJ#$++44Թӿstݓ{|߇Ⴤޏϼ    !"!# $ %&'(""""")**+, &)#&+!"!- $.(/0!12( *.1&&.0&&3%)%++%(+(++&#/ &#  &# &#4(/    ##  %+&'(&5)**+, &+     0( 3+/2(2/&.6 &02 &22& 2'(%(('(((78898:8888888;8:8;8:,9<<$<<<<<<<9<9<<<((("",!"!!"!-"""""""""$!"!!"!-"(-"-(() *2,"-2!"! ,=""""&5,!"!- ,("-#* ,>(:8:88888::::8:8:88:$$$$$$$$$$$$$$$$$$$(( @ႃ WXddprӻ׮ߋڢۛYZgh{|اշصֳ<=/0֪55ݒڞOPop]^$$JKAB=>֤mnQQ          !" ##$ % &'!!()* +,- .,/0 1//(   ())( 2345(+#.066#7$ 8%& ++9,0+9,.:; <:;<= >?@@?5A>?@@?ABB5>555C5555>555((=(B( =D( EF"D!"  G!( ""   ( ( +. (E:( =H& $ EF"0I$  H"(8)B 4J.(CACCACAACKK?C 5555555555>( jkֱ߉ဂᆈ^_DEٰۧЧܗ77==gh#$tu˻deIJԶ`aTUҼΪڥܞ䑒ߥ਩βռУܳ߮   !"#$%&&'()*+*)*, - ./01!23456789:;<=>?@A3B CDEFGHIJK7LAMNOPQHHRSTUVWX-N))UNRYZ[NVR3,\N-$]1^_`7RabcdefAVVVghiNRQQQSHHSL(0`wwwwwwwwwwwwwwwwwwwwwwwww|wwwwxwwwwwwwwwwwwwwww|̇wwwwwwwwwww|wwwwwwwwwẇwwwwwwwwwww|wwwwwwwwwww|wwwwwwwwwww|ww|wwwwwwwwwwwwwxwwxwwwwwwwww|wwwwxwwww|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwww|̇wwwww|wẇwwwwwwwwwwxwwwwwxw|wẇwwxw|wwwwwwwxww|ww|wwwẇwwwwwwxwww|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww|̇wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww|wwwwwwwwwwwwwwwwwwwww|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww( @wwwwwwwwwwwwwwwwww|www|̇w̏xwxww|wȮwwwwwwww|wwẇwwwwwwwwwwwwwwwww|wwẇw||wwx|w̏|www|̏ww|wwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwxwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww|wwwwwwwwwwwwww{wẇwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww( wwwwwwwww|wwww|wwwww|njw|njwwwwwwwwwwwwwwwwwwwwwwwwwwww|www|wwwwwwwwwwpuzzles-r9872/icons/range.ico0000644000175300017530000006117612161170317015342 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $#$% !!!""!""!""!""!""!""!""!"" !!$%% #$% !"!""!""!""!""!""!""!""!""!""!"#Y[\MNO!"#XZ[KMN!""XZ[KMN!""XZ[KMNHJJ678TVV!""XZ[KMN+,,9:;!""XZ[gijgijKMNcef!""XZ[KMN&'(122z|~!""XZ[KMN~!""XZ[KMN<>>MOP!""XZ[KMN|!""WYZJLM !"^abQST#$%89:012TVWY[\XZ[XZ[XZ[XZ[XZ[XZ[XZ[XZ[XZ[WYZ^ab9:;SUUJLMKMNKMNKMNKMNKMNKMNKMNKMNJLMRTU012QSTJLMKMNKMNKMNKMNKMNKMNKMNKMNKMNMNO #$%SUURTU$%% !!JLMJLM !!!""KMNKMN!""!""KMNKMN!""!""KMNKMN!""!""gij KMNKMN gij!""!""gij KMNKMN gij!""!""KMNKMN!""!""KMNKMN!""!""KMNKMN!"" !!JLMJLM !!$%%RTURTU$%% MNOKMNKMNKMNKMNKMNKMNKMNKMNKMNJLMQST012RTUJLMKMNKMNKMNKMNKMNKMNKMNKMNJLMRTU012QSTJLMKMNKMNKMNKMNKMNKMNKMNKMNKMNMNO TVW012012TVW#$%QSTQST#$% !"JLMJLM !"!""KMNKMN!""!""KMNKMN!""!""KMN9:;899KMN!""!""KMNKMNXZ[!""!""KMN$%%VXYKMNMOP!""!""KMNTVWKMN!""!""KMN577789<>>799KMN!""!""KMNKMN!""!""KMNKMN!""!"#MNOMNO!"#!"#!""!""!""!""!""!""!""!""!"" !"#$% $%% !!!""!""!""!""!""!""!""!"" !!$%% #$% !"!""!""!""!""!""!""!""!""!""!"#( @ svwPRS^`a[]^vyz^ab[]^)*+*+,ilm[]^_abEFG[]^/11hjk[]^gjkRTU]``wz{SUVHJJEGGEGHEGHEGHEGHEFGJLM./0789bdeZ\][]^[]^[]^[]^Z]]_ab()*SUV]``[]^[]^[]^[]^[]^^`aPRS/00_ab+,-y|~Z]],--z}[]^,--orsz}[]^ilm,--456z}[]^*+,,--z}[]^+,-y|}Z\]/01befz}~z}~z}z}z}y|}UWWIKKy|}z}z}z}z}y|~678ors}z}~z}z}z}z}~~knobefUWW?@A}/00ade+,-y|}Z\],--z}'(([]^,--z}y|}HJK[]^,--z}$%%[]^,--z}~UWX022[]^,--z}~[]^-//_ab -//,--,--,--,--,--+,-/00/01+,-,--,--,--,--+,--//())-..,--,--,--,--+---./&'((      ;<=CEF@BBQSTDFG &''&'('(( !!>@@kmn     fijhkl  tvw  ""#!!"  (0` $%%!"" #$%Y[\MOPXZ[MNOKMNHJJ577TVW+,,899gijcef122z|~~<>>WYZ !" ^abQST89:012RTUJLM789                    !"#$%&'       ()*+,-(   ..   /0  '11'  2345  66  789   !   2:;<(       =5>' ? @A9  (9 BCDEFF9GHIGJKI*ALM%IA*N  DKOPQQQQQQPOMHRIPS  TTTTUSPLG  @9AQU@@QA9@                 Q   Q       V  T !    W.C6FQ  TQ !XC.  .CX  TQ !6C.  !    T         Q     Q   Q  B Y9A@@UQA9@   GLHS  TTTTSPLG  PMOPQQQQQQQPOMHQQQ*I%MLAALM%I*N RIHGJJ9GHIR  *Q@( @A         Z6[           (+<9   (!        --9   \&         (]\  >         (?  !&^9         )\%&' *          6$_;_6! N                    *       N N  ( @svwPRS^ab[]^vyz())*+,ilmEGG/01hjkgjkRTU wz{IKKHJJEGHJLM789befade'((UWW~+,-z}~Z\],--orsz}456y|}678?@A}022$%%./0&'(    !"#$% &'()*+,-./ 012$3 4 0567$8 9:;<=>$ '?@AB$ CD 009E;FG9 0 'HIJ2KLLL2M5NO PQR  5SSSSTUVWXPY?Z+%[\+44] $%^_`\4 9/+^+Fa`b_+ $$ .c^)d$[\ 9$3Z$^,[b+ @$4Z%+%?[\%++] $445e_fU>geO%heYij\\bb\\kRJ8ll\bblmnaV\bbb\Vaop[```[[UORU1)<[`1pqj)[[``[)rse,,TUkfTSePD'Z@4%[l<t] +++ ^4`\Q& 0t^`\u/lJ_ 0& vGw^X,[b$x# 0yz^X[\,jRs)& 0S^+1\D $+^_&&epjW&{_P'=^Z^^^^-|oi|^^Z^^Z|}*^^^^^Z^~(  ;<=CEF>@@QST&'('((""#kmn fijtvw     !"#"$%&&'()*+," -./012%+!3 45% 16$* 47/6.8$9!4$$658:$$/;<=)2)>2*?@>*A*B4$8)C3*//0-!8.2/6)D4$8$)EF) 84!AGG).CH44-4& h600N; IhR00hX n^(Va(0` $011""#/11-..-./-./-./-./-./-./+,,-..-./-./-./-./-./-...//*++,--.//-..-./-./-./-..011!""234Y[\y||^`abdefij_abTWWFHHikljmn[]^wz{dfg_bb} vxyacd123dgg_bb#$$NPPACCdgg_bbsvvikl9::qtu[^^ACCdgg]``~dggjlmfhi)*+RTUJLLKMMJLMKMMKMNKMMMOOJLMOQQNPPNPQNPPNPPNPPMOPRTT>?@^aa=>?##$&''&&'%&'%&'%&'%&&&''#$$X[[fijz}~dfg{~dgg{~dgg{~dgg{~dgg{~dfg~fiiqtuuxy[^^qtuuxy[^^~fii{~dfg[^^{~455011uxxdgg234{~567ruvdgg345{~!""dggjmn{~mppSUV`bbdggz}~cffknn ;<=RUUMOPNPPNPPNPPNPQNPPOQQJLMOQRNPPNPPMOOMOOMOPMOOUWW-//nqqy|}bee{~#$$566dgg{~:<=svwdgg{~{~)**dgg{~kmnTVW{~dfg|egh|fhi   ( @ lop'((Z\\PRRQSSRTTRTULNOOQQRTUQSTQSTQSSUWX !!BDDUWWQSTQSSRTTOQQ$%%pstPRS/00)**nqrJLLhkkDFF-..{~z|}[^^'((nqrKMNPRRx{{-..|<>>&''npqJLL[^^pst,--|}QSS&'(qstVXX122}())jlmTWWKMNMOPKMMLNNNQQQSSPRRPRRPRROQQTVWy|}&&'cefqstnpqnpqlopwz{+,,122VXYOQQPRROQQTVWy|}&''[]^loo122}'()QSTacd,--|'((RTUbee-..|'((RTUbde,..|'((TVWdfg-./~(()IJK[^^)*+svw%&&MOP_ab+,,wz{&''TVVcef-..}'()nqrPRRacd]_`ACD,--|'((mopOQRbeeMOPx{|+,,|'((QSSacdfhi,--{~'((TVVhkl/01)**MNO244+--+,,,--/01 ##$/00+,,+,--./)++QSTIKL{~~||}wz{{|~z}}!""SUUhkk()*RTUaddUXXJLL'((RTUbeeDFG&''SUVcffuxy())RTUacd'((678.//022011011122-..-..122011011/01344)*+'(('(('(('()%&&'(('()&''%&'())$%%(  hkly|}~~z}~TVWacdsvwtvwgij{~twxlooaddnqrnqrgijvyyX[[QSSbeeRTUPRSgijy|}_ablno{`bclopvyz\^_iklvyz]_`ilmvyzfijnpqruv[]^fhiEGGikl^`aQSSbdeehhGHI~svwtwwfhiVXYpstjmngjklnoqttTVW(0` 234 -../11$%%Z]]~vxy^aafiiSUVFHHjlmdffNPPACCruv;<=egh KMN>?@{~|MOPnqq    !" #"! $ $ % $$ &$ '''( )" #"! $*+, % $ $-$, ."/!  0 -$1-. ."#"! - % -(((23$ ."#! 4,5$ 0 '-$43 6"#'   $- (%& 67,    %- ($'( 6"##*88882228222222229% 11$!"    9"-%%%%%1"::::& ' ;'( ( ."# <$ $- ."# <' (( $- (  6"# <' (( '-$ ( ."/ <' ((( $- ( )"# <'((( '- (( ."/ <'(( (($&- ( ( ."=>? (( ( &$( ($ 6" # #@@#@A # @@# # 41-----14&---% "'A @ A # # 41----1&---- "== ($ '('&' ()"A #<'(11-($-( ( '."A1;<$ 4'-$ ((( ."A /<'(4$$- ((( '."A $AB'(-1'?-$ ((( ."A ,B' ,*.('-(((( ."A B$( $-( '.">:&'' '' ''@," 1  7""7/ /#5*CC222228*222222*";    4"A <  ; 1;;; '."/ B' ( ( 0(( & ."A <' ((((( 0 $ 54$ .7A <' (( ( 0 1B )"A <' (((((  D*B ."/B ((( ( 0 $$$ ."A 7 (((( 0 )">7777777777777777777A-%%0-000%%%%%%%%%-%---%--%-%%%%-%%-% :>::::::::::::::>>>>>>>>:>>::::::::::::( @lop'(([]^OQQRTTKMMUWXCEE%%&qtu/01illz}}nqr+,,|<>>}VXX122bdevzz_abMOP]_`fhi678 &'(      !""# $%&'()*)* ++')",#- *+'()))./01! "# 2'(****./3')45$)6/ 0*)  "%73*((((  *8 %9. 57+*((0(  ###:- 63)))) ",, #8***.3*))*  ,#8 **./*** " ,#8 ***./**)*  8 ** .3 **    ;*&&&*.9'&(' $   <;*0).9)0000   48 '&* .3))))   &=8$>0.3*))*   ?@8 *+./)))* " !; ?AB.+**)*  4A$ 6? $ 7C... D6....7 " " #!+//39+/??3+  4%$0B'  "#8)*)'))  "#8 *)))')!E  "48 *))&*?9*) "" ""#8))'0)) C.6666 .6 6.    ( hkly|}~VXYbeeqttehhnpqacdvyyQSSRTU^`alop{]_`jmn[]^EGGPRSGHItwwfhipstgijlnoTVW        !"#$%& '()(*+  ,-$ ".!/01&,-234#/+50607'789 + 5&'9.9!:1  ';<&;7 =2 6;&>%? .@A0BC4!!D%-EBCF9GH!!I DDDJKL:MNO5,PPPQP++++5(0`wwwxwwxxxxxwxwxwwxwxw``wwwwwxwwwwwwwwwwwwwwxxwwwwwwwwwwwwwwwwxxwwwwwwwxwwwwwwwwxwwwwwxwwwwwwwwwxxwwwwwwwwwwwwwwwxwwwwxwwwwwwwwwwxwwwwxwwwwwwwwxxwwwwwwwwwwxwwwwwwxwwxwwwwwxwwwwwwwww`wwwwxwwwwxxxwwwxxxwxwxwwwwwwxwxpxwwwwxxwwwwwwwwwwpww`wwwwxwwwwwwwwwwpwwwwwwwwwwwwwwwwwwpowww`wwwwxwwwwwwwwwwpwwwwwwwxxwwwwwwwwwwpwwwwwwwwxwwwwwwwwwwpwwwwwwwwxxwwwwwwwwwwpwwwwwwwxwwwwwwwwwwpwwwwwwwwxwwwwwwwwwwpwwwawwwxwxwwxwpwwxwwwwwwwwxwpxwwwwwwwxwwwwwwwwwwwpwwww`wwwxwwwwwwwwwpwwwwwwwxxwwwwwwwwpwwwwwwxxwwwwwwwwpgwwwwwxwwwwwwwwwwpwwwwwwxwwwwwwwwwpwwwwwxxwwwwwwwpwwwwwwxxwwwwwwwwwpwwwwwwwwxwwwwwwwwpwwwwwwwwwwwwwwwpxwwxww`wwwwwwwwwwpwwwwwwwwxwwwwwwwwwwpwwwwwwwwxxwwwwwwwwwpwwwwxwxwxwwwwwwwwwwwpwwwwwwxwxxwwwwwwwwwwpwwwwwwwwxwwwwwwwwwwpwwwwxwwxwwwwwwwwwwwpwwwwxwxwxxwwwwwwwwwpwwwwwwxwwxxwwwwwwwpwwwwwwxxpwwwwwwwwwwwwwwwwwwwwwww( @wwwwwwwwxwwwwwwwC8`Swwxwxwxwwxxwwwwwx(xxxwxxxwxx'wwxwwxxxwwwxxwxwwwxwwwgwx7wwxxxwwwwwxwwwwwxwxwwwwuxxwwwwwxwxwwwwwwwp'wx'wwwwwsxxxwwwwwGwwwwWxwwwww7u8x7wwwwwxx'wwwwgwwwwwwww񈈈wwww{wv0@!%wwuwwxww'wwwwwxxXwwwvxxwwwhxxxwwwwxxxxwwwwxw0P!!0'wwwwwwwwwwwwwwww( wwwwwwwwwWwxw8wwx7wwwxxwwwxwwwwwwwwwxwwxxwx8wxwwwwwpuzzles-r9872/icons/samegame.ico0000644000175300017530000006117612161170325016024 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $UU55<<;;;;;;;;;;;;;;;;;;;;<<56SRVU45<<;;;;<<45VUSR7765V[NR8746\WPL7856\YIP9822gg&'$"$"$),%,'xu;;''" %"%" $*-%.)|x:;8< 4: :67/ ::12B8-(+- OJKE,!/#?=+'(1(3!,AL?L!.!,BOI@-",!LF>G"..KN)15C$)#W\&'}|-'"!!!||"&#'|}-&(3;;&'{z,'"#!!{{"&#'z{-&'2<<7='6.)""3:4>75:<04>=06;H;;FD#+'+( $$*'+'#FC=G ))FJ@?&!# KE>=)"(IO;;''&'$# ||)$)&{}&2;<((%%$# z{)$)&z|&2;;''<>  =93:==48@?-7;J40QP''FC#$C@C?$#FAA="!FB==% "MD<<# KE3@!' U[''$%{| " !%"'${|-&,'|x;;''&&}~"# !%"!(%z|-&,'{x;;'' 23$%}~ ":;><'%49E>67F@/5RR''$%}"&#' AB %)7=#%"$%#%"%"$#"$$&TV((%'}}~{$# !%+;;''$%y~}~&$ $*<<@A33?A"#!"}$#;<@E6;!" !!!!!!! ;;!"}!"!!!!!! ""#FB7:@L;;"#}}~~~}~{-&'2<<!"z~~~~~~~.''2;;@?&$%#%#$#%#$"~,'8>"CO2: !TV;= !  !@>}~%.66>E5/RR!"'$}&/&,{x;;!"(%{|$.&,|x;;B@ (&&%&$==% $FI?9%!#GI9: ( U[6;:< ?G-3QP!"{|%# "'2;;!"yz%# '2;;EH'(&(HHEF''..+, '/&0%FU4?#' VZ'/*68C3( PL'/&,{w;;%.'-~{;<A=($( IM&,~z;;+3 3C&,~z;;{|$.'.~{<<|}%.&-|x;;:<"$!!BJ;:"$! CH:6#%SU( @ HH**11............,.7,jI%+3011#,iJ92+"N$0(4L(9?[J$UV  H!H! &%i 7~ x0/C:'*! o4,9#$-(YD*;,D-32cOK }-I\+0?-  (/" 1P0_K( C)=9""6! !{J %T H% 70/vx?_kj4 **ff]^_`^^dexp4ekowX,NB[noodr,jD? U,0'00 %ffII 6' g( hr*./1! ]^BD8"~g)hq& !,10 !_`_v[h64yI]*N$pF}d'~g+nEM!]2JsZzM _`BE!I"T&H%7 0/ _`EFw!) S&W'T%B!:1#$--ھܴCE{ &}HzI^uT 9p;"7>-LJ$+7-HY##66BDx~H{!&..89 HITWG}&'<   /0|  !V? )'6#11|G% 7 0/Z8DvJz#Iy"Oy$/ZPK1QKK)IZ"' T- &M(:82W( $L8/1c=Q!K-;&ui+gH}z3Gn[xM~!I(8 1/yJu" ; 4/ũž䟢ީhi0[!u0Dvf_L▗ h' 8/1䐓k( ;{/4+xA4H 9|02v $G8z/1)=Z-,8NFI(  /9###'"4".g-:%$R\-1kg#(  0YxU+mc!A`++> 7588 OL9;@ ~!$88㓘am2'6 [~*34Dg@#jE 8: MO-.惄|PKj|uj,l"zr䒓 I I"  / t"G$3 {Wa)G4){l5$ C|@ %>+<C w"˹䑕f$B.DûI'=#Ʋ (4#L8*(0` UU56;;A=SRUV44;;V[NR\WPL\YIP;;13gg'& $#%,-%+&v ""%#|8<4: :67/;<,5B8+- OJLE$%,%B?(2AM=K#-DKI@-"LFCA&2,JO5C&,#W[,)##  '2=='64;6>;HFD)&!DB?AKE((( 69>GQP#FA@>FC3@U[!!'(33%'<;RR?GFUEF87;D#GI EHHH,+$ 4,$.IM-53C7=;:""CH:6       !" #$%&''() *+" #%&,' -./0)123344567889:45;$%<==> ?@AB%(CC!DEFFGHI#JKLMN0COPQRA,,&S)T',TU V,,AWC3 @''&S)T,,MU V,,A/X3 Y&&ZS///0//0.)[\][44^&\ /._3 `+%a(/bc)(%SdefMMG+gYAhN4 /iiija)V',A/a&,,L4 k/)+l',A0%&'mL4 090W00$9[&&n0/9[&&o35pk/KqqK;rsddeYqTt9+)guTlvk/T,,Tw +V,,A/S&,' k/x,,Ty al',A$S&m' kd)/z{,m{|""|*+[V}Bn00s[V]~k.{mmmTTlfW!OUPRk.{,mm,m'TW!" k.{',,mm,T)!" s0*zV{Tmm,T9*0e1"Uwwww c{,mm{qTqlTVGnW*3 c{,mm,m,m,m'A0X3 cT'm,,mm,mmmA0X3 cBVTTTTTTmm,A9)/1c9/)**).VmmF"3V]~c)aVmmL" P},' c)aV,'F" P}'' csda+aTqG;Tvc[&&W(WW/W$.:"5pcT',TWX3 cT''TWX3 cxx!(!X^PRciii0C:32}=> cj*!3 P},' c/" P}m, cs%3 P}m,c]\" P}m' cV',F" PV,, cjV,'F" P}', ccc@qG@~cccccccccccccccccccccccccccci( @II-.11,,7,jJ$,1092+"N$0L(9?[J"UW!" I"&%i 7~ w0/C:'*!o4,4##-+YD*;,D32cOK }-I\+0I( (/" 1P0_K( C)=9""7!$|I %T H%7..owXekg(ef]^_`xp4,NB[noodr,jD? U) 6' hr* .BD8"~g*hq&-2_v[i54zIY,N#pF]2Gu[yMBER$0/FI!) S& A92ھܴCE zI^uT9p;"7>-L+7-HY66{Kz#89G}&'<   !V? )'9!Z8Dv/ZPK1QKK)IZ"' %M(:W(8/1c=I);&yg)H}3Gn1/Ju"ũž䟢ީhi0[!u0f_L㓕h'k(/3+x4H :$G)=Z-,8N  !!!! "!#$%&'()*+,-./01234516789:;<=>8??@ABCD#EFGHIJ!KLMN?OPMQR!S=TUIV!WXYZ? [\]\[^8_YX`abcdefg/!h4[ijkjl mnopZmq=rs!)+\ttuvw=xy=rs!z4m[k{|}~xxe@\khM! ==U%NM\k'!pgM 'Q? k'MM"!!!!!!!W? MI!zhPP!)PMMMM'=UI!h=)h?mmM!DM^T!M)hɌmZφ=>?I!'mI!څ)t!MtI!'N!'!'))h)( /9$#'"4".g-:%$R\-1kg#3  0YxU+mc!A`++> 7588 OL9;: j,l㓘am2'6 [~*34Dg@#jE 8MO-.惄|PKj|u"zr䑕I I"  / t"G${Wa)G4){l5$C|@ %>+<C w"˹f$B.DûI'=#Ʋ (4#L8*  !"#$%&'()*+,-./01 )23456789:;<=>?@ABCDE0FGHIJKL@MNOPQR1STHTTlmnZopqaaTTTT>rsPtuvwax>TTTyz{|}~aaTTTTTUa>>TT>laaaaaaxTaaaaaaaaHxx(0`wwwwwwwwwwwwwwwwwwwxȌȌȌg|hzw|șə̂"gxəș̂"|șə""7xȘX剈̈8wr*("""(̉x"#̂""#̙r"("")"(̉r"(̢""&̙|̇wwwxȈhȌ7xwwww|̂",""|wwwwŵ"h""xwwwx",""xwwwww:8lwwwwv"&"(̊"|wwwz"&"#̂*|wwwwx"""(2"|wwwww"(¢(̂"xxwwwws""(Ș|wwwwwwx""""șxwwwwwww"""*<ə|wwwwwwx""""ə|wwwwwwv"bǙ9wwwwwwwwwwww"""("(̉xwwwwwwwwwww""""""(9xwwwwwwwwwww""""""(̉wwwwwwwwwwww"""""Ywwwwwwwwwwww88"(wwwwwwwwwwwww"""wwwwwwwwwwwww"("wwwwwwwwwwww"(b"wwwwwwwwwwwȣ"wwwwwwwwwwwb#ll7wwwwwwwwwwww2"̙wwwwwwwwwwwwb̉wxwwwwwwwwww2"̉wwwwwwwwwwwwh(ẍhwwwwwwwwwwwwwwwww|ș"wwwwwwwwwwwwwww|Ǚ"7wwwwwwwwwwwwwwwwx̙b"wwwwwwwwwwwwwwww|ș"wwwwwwwwwwwwwwwwx6"wwwwwwwwwwwwwwwwx"&""wwwwwwwwwwwwwwwx"(jwwwwwwwwwwwwwwxz""""7wwxwwwwwwwwwwx(62wwwwwwwwxwwwwwwwwwwww( @wȌ̌xȉ#|ə̙"ə̙xb(̌6<ƙr"")"l̙"2h2<Ǚ|w̃#̆(xw"æ̇w̆c(̏s#\2/|w"",̢xw&̢bo"b̙|wwr""<șw#",̙w"""Ιwwwwwww"""̙:""<șwwwwwbijwww")"/wwwŵi2wwwbșwwww",̙www"<왇wwwwwȉ2wwwwwə"'wwwwwɘ"wwww22owwwwww")"'wwww""wwwwwww( w̉i/v8S7sLiş|ƌ#vfg|wr& h600N; IhR00hX n^(Va(0` $~mpooooooooooqknpoon~nqpppppppporkOhZvqppo~nqppppppporjPiMeLcZwqpo~nqppppppprkQjMeOgNgLc[wrp~nqppppppqhNfJaNfNfNfMdI_Xsn~nqppppppqe`}cSmMeMe\xc_}k~nqppppppprruXtLdLdhusp~nqppppppppoqXsMeMefqoo~nqppppppppprVqJaJafrpo~nqpppppppppq_|WqWqirpo~nqpppppppppoqrrqppo~nrrrppqqppqrrpopppo~okghnrmjqqlefnppppohYshQjir`~XsppoVp^{qppppoa[vxVqkcQjPiZvpt\xesopppojZudSmitcZvsosZvcsppppo~onlUohrnmpqlUofrppppoorsfnqqrqqqnpqqqqqp}lompnnnnnnnnnnnnnnm|~~~~~~~~~~~~}~~~}( @ wvwvvvvvwurxt|pnonnonopWsNhkotqpqpppqpYtLcMeQjjxqpppppq\yNfOgNgOgRmsqppppppmoSmLchmtqppppppqqSlKbiqvqppppppppUpOgipvqpqpppppqonpovronpqpqonoqpnvqb`~co[xkq\yeqpnvn]{eagPibt`~irpnvrj_|`~uhqq\yjrqowqqnjpqooloppnvtprsqqqrrrqqpw(  xuvxnj}smpjPjKdltopq\xUqvspqsidxnhjlkqvk`diemuvptrqsw(0` ~nponlOhZviNfLdQjI_Yte`~dTn\x_|juVqrxl              !" #$  %%   &  !   ''  (( )))& ( *+& ($$$  * ,$  '# *-!  %"% +& $ !% (  .)& /!  ' .01* (     &) / (( ( 1222222222111213, 2211122+22221121334  4)11&))+1&)0210)).22**+0).)001**22*++22&&&1+&+22&*22+&&&22)225655722+&89:81*);<22&;<22*;<+2&+0)8;:22&69;2+*022....( @wxvrp|nnVrNhnYtLcq\yOgSmiqc`~tkgn^|s            !""""#     #$ % &   #$& '  #$    (('     )(' *+,-!- !.  /0*+*,10$&  2' -++1. !$&  (33' 1 244222,$ / ''')'''''''%'''''')') '4'(4' 4%3 4(%42'%25 2%'222 2%'% 42'' 67'44%89'22 89''% 69'4442 '245     ( xutxnj}smpjPjKdp\xUqvpqhelkk`dvptw  !"#$ %%&'()*++,-./012--34(1 567.48 9:; )<3=>?@2;2;0A:B2B CDE ;FG0H IJ2;0DKLL2; :MNOP Q RSTU 1MVW20) HXX@/K(0`wwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwxwwwwwwxwwwxwwxwwwxwwwwxwwwwwxxxxxwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww( @xxxxxxxwxxwxxx|xLJwx׌w׏w}|Xxww|xȈǏww͍LJōxxwLJ||xw}LjLJw|wwwx،x}}wW|؏www|׈ww؍xwwwxx؇wwwwwxwxwwxwxwxwwwwwxxxwxwxwxxx( xwxwxwWuww܅wwxwxxx}|mwwxwwwwwwwxxxwpuzzles-r9872/icons/singles.ico0000644000175300017530000006117612161170331015706 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $fiiilliklikliklikliklikliklikljlmdgg_bb899+,,.00CEE^`a*,,QST|244OQR]__ !"WYZBDD_ab011MOPhkllopMOP012*++(**$%&,--prsjmnMOO355CDE{~hkk:;<JLMSUVmpqikl@AA022;<=XZ[NQQruv}dghmppeghnpq122Y[\fhi011QSTXZ[x{|;==%&&SUV#$%[]^_abIKL|svw&&'^`aMOO9:;!!^``y|~234GIJWYZTVW9;;BCDx{|wz{add=??688GIIruu{~uxxSUU())133y|}DFG9:;Y[\_abPRR122hklfhi%&&KMMbde]_`788012 }LNO799GIJCDE"#$$%%befLMN\^_QST&''QSS{~knoy|}>?@455TVV`bc_bcSUV8::^`acefbefJLM678 KMM`ccruv344()* !!|x{{CDEqtuVXY ~577355!!"!!"OQRrtu:;<ceftvwDFG89:*++234.//SUVmpq789}<=>Z\]UWX !%&&?@AHJJACCWYZ9:;012.//]_`ehhKLM)**Y[\svwuxy89: !NPQ./0566SUV%&&GIIHIJnqrY[\89:+,,hjkWYZACC!""!""OQQIKKfhi())ilmNOP)**+,-EGGors+,,TWWehi677-..BDE~NPP:;;>@@`bcMOO688233=>>^`aorsZ\]]___aalookmnhkkhklmopgjj\^^[]^fii{~<=>+,,'()122RTTTWW012|acd&&'()*MOP577^ab'((HJKQST%&'())@BBY[\#$$BCD+,,-..vyzIKL./0678012/01gijegh\^^9;;456oqrwy{]_`[]]>?@Z\];=>012}CEF678tvwEFG<=> 345,--"##kmnMOP+,,z}}BDD.//cfg789-./!""rtuJKL011466acdJLL799677vyzHJJY[\lno:;<KMN%%&788345QSTx{|ruv#$$466&'(RTUVXY&''$%%JKLacd#$$jmn<>>FHH)**!"#ors789NPPSUV122?AABDD<>?344z}~)**OPQ344@BBACD;==456z}}svwy||svw( @ 89:=??=>>=>>=>>=>>>@@678^`a}IKKTVWRTTNPP344wy{befOQRBCD_abMOP@ABbdez}~}uxy788\^^hklfijcef<==]_`ikl022orrlopCEE{~_ab9::XZZ`bcDFF=>?789RTUnqrsuw455\^_ors_abwz{~cef678.//=??tvwZ\\!"")*+y|}\_`TVVHJKz~cef[]^eghOQRNPQadeVXYDFG{~\^_qtu566456`cdCEFmopx{|ruvfiiQST89:X[\@ABBDECEEhklLNOsuvwz{{~567ruvZ\];<<MNOHJKehiUWX_abacdvyzXZZ]_`567HJJuxy}fhi?@AhjkQSTBDD~PQR[^_KMNUWXvyz]_`twxMOPJLLEGG677NPQeghVYYY[\svv_abUWXX[[VXXnqrPRR-..?@ABDD799:;;DEF9:;122PRRNQQVXYHJKehiKMM~[]^*++JLL}`bcXZ[pst9::]_`lopehi012RTUWYZ&''hklrtuvyz{~MOPsvv}122XZ[ehi RTT Z\]pst'()nqr\^_ ^aa|QSTRTUvyz,--QST`bc677Z]]XZ[[]]SUVKMN/00RTUNOPRTUz}~eghTVW'((OQRwz{eghUWX}ors~|orr(  '((##$&''VXXVXY HJKvyznqr_bcuwydfgGHI !!011hjkehi_ab[]^@BBGHISUVnprruvhjkacdhkkRTUQSStwwZ\] QRS@BBknoSUV^abCDEmpqdfgoqr (0` ilmfii_ab:<<+,,/01BDD]_`RTU|244OQQ  !UWXbde&'(ruv~JLMnqr?@AZ\]MOPy|}GIIWYZvyz>?@789'()678#$$mopegh./0   !"#$%&$''()!*&+,-!. ++ +# "!/01! 2 #23$-  ',# '4!45$ ' 67,!81#! 9:'!;!-<3-4 8 8 =>?!>6; ';@!= !,22#!-;A 43?B2//,!"'CC!4%?!!# '@AD> !:@E"/*FGA3/EH@>+D $I-- 27*;J: )>>>>>)>+>>-$ K''!,G+'A $' K=' - I2.)$'B*7 ;&'&&.I2IB$,@.L@'9" ,**I.MGI+**7/$-G+ I 1#N/A*3>OO+M'-K!-E &30(##4'J$B%KB3-*$3$!G.NB)!'00 8-G4$-1F;(%2!'971$.%0 '"$$B(-$'>.P17 =N%  IH !,D3E, G$!%+J?B 2A&78 ); D..% >KM.6&&A''$ $ 2 =H A/ 3( )!! PG4 -++$/G9* )++ >+-> + /G9 8;* ;;  FH !!*2  N$!R*47GNJ+'1M)E7 ;8C1<#2 !G=O E.!3K=$J +H / -D2N 2B> M A*+K -B6FA NK8+.6!G7'$ (FF@!! N$ <9'  @3/ 14""/ @3/ ( @789;<=?@A_ab~JLLRTTNQQ345vyzadeCDEMOPz}~twx]_`hklehi /00orrlopZ\]ruvegh678,--"##'()UWXVXY|     !"#$#$%&'(')*##+ ,&-(./ 01 #%/2"# 3!,"$/4056 7$,8 39'4:; <"=+%#&8.> 2'?#$ & ## " <@!"""5"#"%#)?;$9""" ""#  ABC 122!?DE."999993 "!F.A?9"""""""" A ')5FG(""" #"#H$")&.3@ 0+= $$/&<)#F106I "$? )"&&$4""*5$#@0.@$? "'$A$0/",+$G?3 ! 1 $87"$A 0) =48F&=9"$3+$""H91)+,)2""$'"$!)8&*"AJ?1!$)"F"))H?H;C & & "HA!''"?"C5' K!J,8@ 3F 2L '426'+ :JE%4@."1$:? #6I%2-$%)#$?; #E; "3D%. #F%$, "C=  ?#?05?999'9: 9 .86H9#E++8AH;229K;29F ( '((##$&''VXXVXY HJKvyzoqr_bcuwydfgGHI !!011ehi^ab[]^@BBSUVruvhjkacdQSSZ\] QRSCDEkno  !"#$%&'()*+,- ./'0 1234566789:;<=>?7@@ABC00 D  EF1GHI@J&K+ LM7N:8? =OPQ/PGRK9 :' O STU0EVFW/X+YTUZ(G/:5[A8YU&N-=@H&"U5\\:[=E[EEE[E=(0`wwwwwwwwwwwwwwwwwwwwwwxawpwwwwwwwwwwpwxwxgwwwwwwwwpwwwwwwwwwwwwpxwwwpwwwwwwwp`qwxwxwwp`wwwppwxwwwpwwwpwwwgwwwpwwwwwxwwwpwwwwwwwxwwppwwwwwwwww`wwp`pwxwxwwwwwpw`pwwwwwwpwawwwwwwwwwwwxxwgwwwwwwwwwpwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwhwwwwwwwwwwwxwwwwwpwxwwwwwwwwwwwwwwqwwwwwwwwwwwwwwwwwwwwxqwwxxwwwwxw`wwpwxwwwpwwwpxwpwwwwwwxwwwpwpwpwwvwwwwwwwpwwwwwwpwwhwwwxwwwpwwwwwwwwwwwwwwwppwwxwwwwxwwwwxwwwwxwgwwpwwwwwwxwpwwwwwwwwwwwwwwxwwxwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwawwwwwwwwwwwwwwwwwxwwawwwwwwwwwwxwwpwww`wwwxwwwwxpwwwxx`wwwxwxvxw``hwxxpwwwpwvwwwwqwwxwpwwpwwpwwqwwq`wwwwpwwpwhwwpwxx`wwqwpwwqwwwwpwwwwwwxwwwhxwwwwxwwwwwwwwxwwwwwwwwwwwwwwwww( @wwxxxxxwqB0%x%V7wwwwwxwwwp?xwwww'wwwpwwWxwwwwxpw7wwxvw7wxpwx8wwwwXwwwwxwxwxxwwwwww8wwwwxwwwxwsx?wwwwwwx7wwxwwRwOwwww(wwwwxsxwv'w'wx?uq7www8wwwwwwxHwwXwwwwwwwxC4wx(wwwwpGxwx?wwx7wutwCXqw7www@"wxwHxgVwww8xw7xWxgpGp Cwwx84x8wwxwxxwwxwx( xwwxwxxp4xwwwxwwx7wwxwwxwwwxxwwxxxw7wxxwxxxwwwwxxwp8xxxxxxxwxxwpuzzles-r9872/icons/sixteen.ico0000644000175300017530000006117612161170334015724 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $fhiknoruvruuy|}z|}WYZtwx_bbikllopZ\]~{~}|x{|bddeghruvprs~`bcdfgtvwwz{SUVy|}_ab|>@@bdeTVWY[\mopdfg=??bde|kno+,,/00orsehiGIJy|}>@Ay|}ruunqry|}ikl_bbtwxy|}#$$pstGHI[^^MOP344*++>?@ruvbefikl~ruvZ\]fhiGIJuxyfijvyzpstkmn{~WYZ[^^|_abgij_bcy|}z}~^aa( @ suvx{{orr~~vyz]_`uxy_bboqsLNO{~tvwhkleghpssx{{tvxqtu~acdy|}uxyacdsuv(  wz{uxy{~(0` fhimoptwx|Z]]_bb~eghrtubdeTVW>?@+,,/00orsGIJvyz>@Ajlm#$$MOP344WYZ           !"#  #$$$%$$%$$&$&&'&&&&&&&&&&&&&&&(((((((  '&&&##&&##&#'&&&&&##&&&&& )* )+#&",-).   &/ &##&0 -/& '" ##&-&0- & &)123##4'567  + ####&8 . )3 (#&&&9&'#&%':&'%'&&((, , , ,   ,,(,,,(,,,(&' &'  & ' &&)'(  8, &';+4# #<=10 '  ''-9  &+>, &)9 '84 ,5+ 9& & '?@   %  ', %', &', &'#  &7''''''''''''&'.''''''''''''&&( @suvx{{pss~uxy]_`acdLNOy|}tvxhkleghqtu      ! "#$ %%& ' ()* +#% %%% ,,,,,,,,,,,,-.,.,/0/%,%012#  ,%3#00/24,%567+8595,,,22:5;<,#=>:?*@  A2BC,%"DEFG@ 5 .%!A!E,H..+I*J/ 222.%%0%. 3,7 0%%##20%#I-,,.,.,,-,.,,,,,.*%@,%,@#,%%%22, %@ K6.H443,(0" DLMJ6,#9NN?, @"C!=D(, =*?OP   @.+Q.% R,6S@#%, # %.% %"""""-"*T##0@@0@07@@@@@@@0% %UU( wz{uxy{~     !  "#$%%%!&"!" '() %*+, - .&%%%%%/(0`wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwxwwwwwwxwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwxwxwwwxwwwwwwwwwwwwwwxwxwwwwxxwwwwwwwwwwwwwxwxwwwwwxwxwwwwwwwwwwwwxwwwwwxwxwwwwwwwwwwwwxwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwpwwwxwwwwwwwxwwwwwwpxwwwwwwwwxwwwpwwwwwwwwxwwwxxwwxwpwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxwwwwwwxwwwwwwwwxwwwxxwwwwxwwwwwxwwpwwwwwwwwwwxwwwwwwwwxwwwwwwwwwwwwwwxwwwwxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww( @wwwwwwwwww7wwxwwwwwxwwwwwwwwx7wswwwwwxxwwwwwwxwwwww7wwwww7wwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww8sxxwwwwwwxwwwww7wwswxwwwwwwxwwwww8wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww7xwwxwwwx?w7www?s7xxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww( wwwwwwwxwwwxwwwwwwwwwwwwwwwwwwwwwwwwwsww?wwwwwwwwwwwwwwwwxwwxwwwwwwwwwwwwwwwwpuzzles-r9872/icons/slant.ico0000644000175300017530000006117612161170337015371 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $fhiVXYIKKoqrcef{}gijQSTprty{}HIKacdknohjk_bc_abhklUXYXZ[fhiCEFuxy\^_^`bjlnQSU=>@\_`^`bjlnRTU=?@\_`^abjlnRTU=?@\_`^`bjlnRTU=>?\^_^`bjlnQSU@BC`bd^abjmnUWX567PRS]_akmoUWXVXY[]^%&'`bdXZ\BDE\^__bc=?@\_`^`b>@A\_`^`b>@A\_`^`b>@A`bd]`aCEF{~Z\]`bc|rtuqtu,..svwMNOCEEx{|ilmqstEFG[]^\^_]_`lopVXY[]^hkl]_`x{|KMN{~hklUWXBDE}_ab^abDFG|gjkTVW>@A^ab\^`DFG|hjlTVW>@A^`b\_`DFG}gjkUWX?AB^`b\_`HJKtvxikmTVWEFG^ab\^_nqrhklXZ[MNO^`b^abcefuwyilm~KMNSUVNPQy|})**uxy~_ab79:JLNx{|ruvhjk}wz{^`a_bc^`b;<=fhivyzGHJ}^`b\^_8:;DFG|^`b\_`8:;DFG|^`b\_`8:;DFG{~]`a\_`8:;KMNjmnWYZ`cd=?@qtuhklY[\&''|( @ ]_`z}~{~_bcmpq^abdgh}wz|Y[]`bduxzZ\]acduxyY[]`bd|x{}\^_cfgqtuTVWHIJWY[{~~Z]^z}~Z\]z}~}Z]^{~}egi~]_`]_`~z}~npqruv}|wz{acdjmny|~]_a_acfijy|}~Z\^dfgjlnx{|Y\]hjk]`a]``123UWX_acjmnx{|dgh[]^bde_acZ\]ade`cd[]^ceflopfijknox{|NPP(  (0` gijUWXGHJoqracdz|~ilmRTU~lop^ab\_`VXY[]^CEFvyzNPQ9;<>@A567+,,=?@qtuJLM|uwy79:&''              !"#$  %&#'(( (" #)$*+$  ,% -./0 # (12## .-%34 ++ 56 .-,34$ 76!0-,'( 56!0-,3(76!(!0-,3  16! -%8'0,9/0 : 01)( .-;6 (..;6! ..79  0-44764 .+  #19+"0 $*  -)&$6<#<:<=+#+31 "   %& <4*+>#>+1  40>$90 4.& + ,!9 /064.+"$$( ?@! + $%41)+-.+(1@!47A..44 1)A%47-.(1) A% 7..#(?B  %'19!4-. * # %  /?$--!B((( %)(6? ('3#6 " (:  2)-C? 6*" < )". -- 5"  ##)! -.5++ ( 1@!-.C( 1@!-0C(1"!-.5 ?,#/-;+ 96 %* +  90  $ D@      ((  4 ( @]_`y|~~bdenpq^abdghvy{Z\^ruvTVWHIJWY[|jlnfij123UWXlopNPP                   ! "# $ %  &# % '($ %&$#$"( &)    * + ' $' %&,$ %&$ %&&&&   -" &   !    , ".!   &  -($&/  #0   1  &&'$2/&%  "1 &% & ! 3'2 '  !    ,- 4    5', 1'&6 - &% ''    $&%'&%' 7 2 '  &- &8  '  (               !"   #!$%&'( )*+, &-.* -%#/  & +01(  +23'4  -5 %   6-7*8  (0`wwwwwxwwwwwwwwwwwwwwxwwxwwxxwwwwwwwwwwwwwxwxwwwwwwwwwwwwwwwwxwwpwpwwwwwwwwwwwwwwwxwxxwwxxxwxwwwwwwpwwwxxwwpwxwwwwwwwwwxwwxwwwwwwwwwxwxwxwwpxwwwwwwwwwwwwxwwwwwwwwwwwwwwxwwwwwwpwwwwwwwwwwwxwwwwwwwwwxwwwwwwwwwwwpxwwwpwwwxwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwpwwpwwwxwwwwwxwwwwxwwwwwwwwxxwwwwwwwwwwwwwwxwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwxwwxwwwwwwwwwwxwwwxwwxwwxwwwwwwwwxwwwwxwxwwwwwwwxwwxpwwwpwwwwwwxxwwxwxwwwxxwwxwxwwxwwwwwwxwpwwwxwwxpwwwwwwwwxwwwxwxwwwxwwxwwxwxwwwwwwwwwwxwwpxwwwwxwwwwxwwxwwwwwwwwwwwwwwwpwwwwwwpwwwwwwwxxwwxxwwwwxwxwwxwwwwxwwxwwwwpwxwwwwwxwxpxxwwwwwwxwwxwxxwwpwwxwwwxwwxwwwwxwwxwwxxxwwpwwwwwwxwxwwwwwwwxwwwwwwwwpwxwxwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwpwwwxwwwwwwwwwwwwpwwwwwwwxwwwwwwwwwxwwwpwwwwwwwwxwxwwwxwwwwwwwwwwwwwwwxxxxwwwwwwxwwwxxwwxpxwwwpwwwxwwwwwwwwwwwwwwwwwwww( @wwwwwwwwwwwwwwwwwwwwwwxwwx8wwwwwwwwwxwxwww7wwwxwxwxwuww7wx8wwsw7wwwxwxwwwsww7wwwwwwwww8wswwwwwwwwxwwwwwxwxwwwwwwwx7wwww8wwwwswwwuwwwwwwwww7wwwxwwwxwwwwwww7wwwwxwwxxwxwwwwxqww8wxxwwwwwwxwwwxwwwwwwxwwsxwwwww7w7wwww7wuww7wwwwwwwwwwwxwwwwxwwww7wwxwwxwwwwwxwxxwwww7wwswx7wwwuwwwwwwwwwwwwwwwwww8wwwwwwwwwwww7wwwwwwwwwwwww( wwwwwwwwxxwwwxwwwwwwwxwwwxwwwwwwwwwwxwwwxwww7wwxwxwwwwwwwwwxwxwwwxxxwwwwwwwwwwwwwwwwwwwwpuzzles-r9872/icons/solo.ico0000644000175300017530000006117612161170341015217 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $  VXY FHJMŦBDDRTTNPP gjk ors$$%Ȯ^a "AC9;>?@ʲy}qu?@AlnoY[\cefhk[]ŧhklxz| || JLL()*"##kmn()*mpq !!QSTFHI+,,KMMHJJ || svwgk\_IKXZ[hl46>@9;ǭ| ^a[]^ŧIL&'ȯ/1+-eiħIK8:žǭ"#̺>?@122]``VY46nq  ( @   lnompgjXZ[SUVIKL˵FHrvkmn]_`=>?˷13}nqrZ\]'((|BD456} oqrBDD567pst ˸z~gij^abor_cBDʴsuv lposFHMPOQLNN_b<>jmQT45iklJLLpstʳ@Bqtͽ  (  εbdeɱhk_bbprsͺη<>Y[\TW}Þǧ(0`  VXYIKILŧBDDRTTNPP |gjknqr"##ȯfj#$AC9;?@Aʲy}quY[\cef\^xz||JLL)*+jmnFHIsvwhl^a46>@ /1+-̺>?@122]``VYnq                  !  "#$ % & '()* +, -./   0123    - 45&  67  8  9(4:  ;<=)>  8"4   .?@9  31A!  +B    C --       &  &  D8EEFD 8G(8558HE  -8C8    5 5 D             IJ/          KLJ       M./#          NJ        $4IG         O5             & 5 5  FCCF 58EHF55FHE855OEEF8 D    C    -P   %QRO  PS  >TUV  O?  TWX7  0Y(ZU  O4?  [!3V3 \20 KG  ]^_T  *]V!  F(9& W'0  &\2` Fabc   dWe                 ( @kmnmpgjXZ[SUVIKLʴFHrv]_`=>?˸13}nqrZ\]'((|BD456BDDpstz~gij^abossuvFHMPLNN_b<>jmQT45ʳ@Bqtͽ      !"#$% &'()*+,- ./012+3*456%2789:;*<=>?@ABC5+2DEF7%G=4 2%2HIJ*% **@KJJK   L  L  L%%M%% N5%%%%%5O %% P5++5QL %*2* 22R>S %%&TG5 2+TUVK %2K WXY 2*4@K%*22% 22*5++5 Z%%LN5%%%%%ZNL%%L  L L   $7[Y[\TW}Þǧ        !" #$ % &'()  )  *  + , & % -  $%%* .$.. $ /0)1 )01 &2$%34&56 7189:0%;<$ =>?(@ABC%D%CEFGH(?C " C1"7I7(0`wwwwwxwwwwwwxwwwwwwpGwwwwwwwwwwwwwxwwwwwwwpwwwwwwwwwwwwwwxwwwwwwwpwwwwwwxwwxwwwwwwxxwwpwwxwwwww*wwxwwwwpwwwwwwwwwwrwxwwwxwwqwwwwwwwwxxwwww(wwpwwwpwxwwxwwxwwwwwpwwwqwwwwwxwwwswwpwwp$wwwwwrwwxwwpGwwpwwwwwwwwwwwwwwwwwwwwwpwwwwwwxwwwwwwxwwwwwwpwwwwwwwwwwwwwwxwwwwwwwpwwwwwwwwwwwwwxwwwwwwwpxwxxwxxxwxwxwxwxwxwwxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwpwwwwwxwwwwwwwxwwwwwwpwwxXwwwwwwwwxwwwwwwwpwwp0wwwwwwwwwwwwwwwpwwwwwwwwwwwxwwwwwwwpwwxwxwwwwwwwxwwwwwwwpwwpwwwwwwwwwwwwwwwwwpwwwxwwwwwwxwwwwwwpwwxwwwwwwwwwxwwwwwwwpwwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwxwwwwwwwpwwwwwwxwwwwwwwxwwwwwwwwwwwwwwwwwwwwxwwwwwwwpxxxxxwxwxwxwwwxwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwxwwwwwwwxwwwwwwwpwwwwwwwwwwwwwwwwwwwwpwwwwwwwRWwxwwwr(wwpww"wxwwwwxww(wwpwrwwww0wwxwww'wpwwrrwwwwwp'wwww""wwpwwx*wwwxwwwww(wwpwwwwxwwwwxwwwr&wwpwwwwwwwwwwwwxwwwwwwwpwwwwwwxwwwwwwwwwwwwwpwwwwwwwwwwwwwxwwwwwwwpwwwwwwwwwwwwwwwwwwwwpwwwwwxwwwwwwwxwwwwwwp( @wwwwwwwwwwwwp/wwpxwxwwwpwwwwwwwpWwww{8w7p'wwwhwpWswwxwwxp'wwwpwwwpwwxwwwwwwwwwwpwwwwwwwwwwwwpwwwwpwwwwp'xwwwwwwwwwpwwwwp_rwwwwwwpxwwpwwwwwwwwp/wwwwpwwwwwwwwwwwpwwwwxswwwswwwwp/wwwwwwpGwwwwwwp7wwpgjwwwwzzpws7wpwxpgwswwpgwwwwwpwwwwwpwwwp0A  (  wwpwwxw7wxpwwwpwwwwwpwwpswwwwsxwwpwwwwwrwwwwwp/wwprww7w8wwwwppuzzles-r9872/icons/tents.ico0000644000175300017530000006117612161170344015403 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $ƶĸ̯ķzq|zzzzz[yzzzz{s~zb{ade}x`y~ya{euYm\a````aHe`````a]Wl~mzsPϢnyx\}~~~~~~_}~~~~~~\xxxxxzox|Yjٸa`azy|X?Huya~``xacd\^_HkA[gđ}``xBb9PhŒ}``x]``p lfÎ}``xyLh2E3H3G?Xaaay]qZiŒiœhĒiŒhđb‰He`eÆd†d†d†e…aHe```````Hea````bV~|b{}}}}`zmnnnl{``wf|a}{b`xe|^TJݾ^`x`bc[]^e|`~|``xe|`|VL߽~``x[]^e|`~}+w``xe{`~|``xOj\a````aˆHe_`ajʈb`aHe```````Hea````bV~|e|a~yNŧv``xe|`}wVn``xe|`~sLr2Elra`x_bbZ\]e|hŒFb?XhĒ~`xe|hĒIfB\hđ~`xZ\\e|cv re`xdz~}}}}_~Nm:R9R;RIf_~}}}}}~_~~|~vOj]gŇfÅfÅfÅfÅcÈHe_hđkǔsЕkǔhđaHeagĆfÅfÅfÅgĆaHea`b‡jˈabV~|gzhhhhe|cwHsb|fhghf|buJyye} zb}wVn߸`}}`n߸Vxwc_Kݽ_~nHk,Aem~_TU_~mAd,Ilpwvxynpqe{"|hŒD_]KMMez_K޽~hđLjE`hđ|UV|hđD_FgXZ[LNOh~6xeŒy tgÎ~5~gÎt s_sxwxwvxYwKo8T9U9UFhwYvwwxwwvYwFh9U9U8TMpp( @ ô{ԟp|||gӒ}||p¿wmӗw}íƭ³aĉnڙkՕk֖kՕY|l֖kՖkՕmڙaijyÙx_ثMĩfؔq~{|{fˏ|{{~pޜhܖxuwqŜɣHČ({sݡvkӕusȢ{Ǘ)UwziҔtq|ȡ̢' `ֆzkӕur~ȢŢPu(7)9C^nךoߛ|yzzeɍ{zz}oܛlח}z{v_qޠuۣtۢv^bĊqݙpٖqږl֖Y}lؗlחl֖nܚbˆ_oܛk֖m֗gڕzmԙtq* \s֖vtȣkҕnݜ|:wkӓsq|ǡtwxkӕnݜxyIܽgГsq|ǡnԙsߠwNvlҘwtȣ]oޜnڙmڙq`eȌoޜxpޙmٙ[nۚnڙmڙpߝcƋapmڙoښiޗ{eƌyww{iѓl٘|SmybĊxxwzlؗjӔ{wytmԘqߞzdߧkіvsȣkӕvPo  7Mlӗsq|ȡlԖxMl)9p֜tr}ȡjГ|{oܜo,>+'\oӚ||{roݛ}~zƟY}pږsؒrגsݗ\bˆpڞpؙoכlԗWzn֔sٓrؒtݖ`^kזkՒkԓdՑysזR),?rߞw7qpՔ[(.7ps@eӺǢqהbEylaCAhqnԔm6{kݙhH5^lǡjҕ) z@Z'jҘ<ԿwLk śPRSlәbLub߉>XsףnCwiޔ+BͭdvvvwgōlΓUtKfKfjِdtuwwjˑjȑZ{JeLeaڈ(  tznwrֲЯӮqݜym֘tvwЛ޲۫ʿhچ5Nrwu~vҿ[r@Xsx|qߝ~tϽyܣ}xWң@жdԛwwmؘyoͼ~\ux|uܞyqstqݚtymٙypμw{umYҝrw{oޜ|rϽ~/BRrw{qלxqݟY}Eecҋuwk֗zqͻi۪[ڮoJ̏{9̻ xM΅P׆v4߿r+;Pj\Ej Ҽдܷҵ̧˙̭׶ڷβ̝˞(0` ƶķ̯}v}z]wb|acd`euYmHeWl~nsPϢn]{|YmݸX?Huy^aaZ\]FhC]gđ_Da 9ShŒsmhKl3F3H?Z]qcV~|emuhUK޽+OjjˈLçwVnLr2ElsIfqJksЕfH!x_,Aenvxynpq?@ABBCCDEF5G#"H!@IBBBJDKLG#">!&'MJDDDDNO##"PQ*RSTTU##V4LLLLL-#W-#######-####XYZ555[[[\]##Z^___`#QQZ5Gabc_cdeG# ?Z5#^c`a#&fgZ#ebcde'?!W5#h#a#&ijZ#5##k4#####-###l##-#G####-####XY&Z5#5m5##Z5#6nop6#qqZ#Mrstu# ?ZLIDCBBDUL#&fgZ5LvBBBJDEL#'?!Z5ZMJDDDDwO#'xQZ5a4yKKKvG5a5#55"k4OZO-#ZLzLLW-#WOOW#-#Wl#4YZ]]]]{W|\{]]]{W\m'Z5}c_c~W56nop6#`__c`a#6986"Za__cde#wtN5#ebc_cbe#Ny"qZe}c`LEDBCBDUL^c`LDBCDDU$%Z5{cdeZyBBBBBELebcbeLEDJBJD?QZW5JDDDBMOOMBDDDJ1'xq#M4KKKA44AKKKR)&"'****'*@@@*'******'@@)( @³{ԟ¾v{~fӒtkԕw}ĭcÊnۚWz|ě_ثMĩgٕn{eɍqݜjەtvĝɢHČ({rǡ{Ǘ)  Uwz'`ֆPu(7)9C^p֜y[~sݡsף^rڕmԙzt* \r֔z:wtwxIܽMvx]yaSmtPo 7MLk+<k˒+'\R)7q.7eӺbDaCAhqm 6hH5^lzPRSb݉>Xn+BͭdUtKeZ{    !!!"!!!#$%&''()*"""+""""),-./012334335678779:!;< 5! =!>?@ABCDEF =9?:GHHBI!"!=!:J>KLMNO533P4333*"338.QRS5 T)5UU"Q"""*)T*"V1WO!!RXYZ[\]!! !!>^"_5`abcd==>Je"!*`dfgh =>iV!!!RF jk!"!!!l !!!!>m5**5))5n5*m***54o***6W)33*pq)3"3rWV!!5F sq "!# !!> t"luvCDw" =>$xV!lyHCHzO2!!>{|3*2z}~V *_d9+U]]UT)5OVV+"]UUoT""1WOYj5 !]\ >]fbn2"b`*>&"__HCC~VhafdFyCCH." RCCS_ X6CC$i n5l4|6o==n ||W^( tznwrֲӮqݚym֘wvwЛ޲۫ʿhچ5Nrw~v[r@Xqy|qݟϽyܣ}xWң9̻dԛwmؘrμ~\uxҿuܞystxp{umYҝw|r/BRrqלY}Eecҋk֗zqҼi۪[ڮoJ̏{ M΅P׆4߿r+;Pj\Ej д̧ܷ˙̭ڷβ̝˞   !"#$%%&'()*+,-./#012345675*89:;<=>?@ABCDEFEG/H$IJKLMNO6PDQR/=S TUAVWXY5Z58[/\ ]^C_`&*aPDbR/=)cdefCg^FhiR/HS TU4jklmn*nc[o<=paFqrsdgtuvw/##xyz{|}~MO[; *kw # ;;;;(0`wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwxwwwwwwwwxwwwwwwwwwwpwxwwwwwwwxwwwwwwwwwwwxwwwwwwwwwxwwwxwwwwwwpwxwwwwwwwxwwwwwwwwwwwwxwwwwwwwwwwwwxwwwwwwwwvxxxwwwwwxwwwwwwxwwwxwwwxwwwwwwx7wwwwxwwwwwwwwwwwxw{7wwwwxwwwwxwwwwwwwxxw(wwwxwwwwwwwwwwpx""*wwwwwwwwwwwwwwwxwxr""(wwwxwwwwwwxwwwwxr""'wwwwwwwwxwwwwwwxwxx#wwwxwwwwwwwwwwwwwxxxxxwwwwxwxwxwwwxwxwwwxxwwwwxwwww{xwwwwwwwwwwwwwxwwww{xwwwwwwwwwwwwxwxwwww{xwwwwxwwwwwwswwwwwwxwwwwwwwxwwxwxwwwwwwxwwwwxwwwwwwwwwwwwwww{wxwwwwwwwwwxwxwwwwwwwwwwwwwwwwwwwwvwxxxxwxxxwwwwxxxwwxxwwwwwwwwwwwww8wxwwwwwwwwwwwwxwwwwwswwwwwwwwwwwwwxwwwwwwww2xwwwwxwwwxwwtxwwww"""'wwwxwwwwwwwxwxwwww"""wwwwwwwwwwqwwwww""wwwwwwwwwwxwwwww""xwwwwwwwwwwwwxxwxxwxwwwxxwxwwwwwxxxxxwxwwwwxwwwwwxwxwwwwwxwwwwwwx{wwxwxwswxwwwwwxwww2wwwxwwwwxw{w""w{w""(wwwxww{wz"""{w"""'ww7xwxw"""ww""wwxxwwwww""wwwww#"wwwwwxwwwwxxwwwxvxxwwwwxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww( @wwwwwwwwwwwww{xxxwww?xwwxwww?wwwwxwwwwwwxzxwwwsxwxwwxswwww{wxzwww**zxwxw7wx{{{{wszsxz{wws{zzxwwwz{;w{w{wwwxw{8wxz{w7x{sxwzwwwz{zx{zww{{wwxxzzwx{swwx{xwzx{wwxw{z{wxwwxw**z{w{w?{w*wxwwxxzxzzw{wwzwz;z{;wwxwwwzs;({;sw{zs{7xww{xw{wwwzzzz8z87wwwwwwwwww( wwwwwwwww{wwxwwwwzwz{wwwwzwwxswwxw{wxxxzwwzxww{w{w;{7swwwwwwwwwwwwpuzzles-r9872/icons/towers.ico0000644000175300017530000006117612161170346015573 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $oqrqst|}}}}}}}}}}}}}z}~^`a}KMNz}~acdEGHjlmorsilm)**iklHIJ~x{|hjk[]^VXYWYZϿξνWYZDF| |RUWYZŸ~eiWYZ9;Ÿͻ$%|svWYZ̸nrqu^ay1368zWYZx'(VYWZWYZǬ̹ytxvzycfWYZWZ !λ|knPR*+WYZEGloνŨɲWYZ}vmpWYZqt67WYZWYZVXY[^^^aa}~~~~~~~~~~~~~~~nqrruv=>?]_`|ruv !! PRR;<=@AB234MNOuxy_abikl!!"012!""uxycefikllnoZ\]( @ |~`bctwxtwxUWXlopbdez}~uxyx|{vyzSUs:<~vyz~ptjn ʴvyzIK34rvvyz?Az~ad8:w{ģehvyzο<>EGLOw{:;?AǬvyztTWvyzãvyzx{|nqroqr%&&mpqehiMNO89:Y[\ikl789()*svwDEF(  ģŧν.0ƫZ]8:tw|>@̶orsuxygjk(0` }rtu~mpy|}bdeKLMEGHξilm)**[]^WYZͺEG{ QTŸei9; !uyru^a1368'(WZƪcfnr*+loɲv^aaoqr<=>!!" PRR@AB123uxy                  !"#"$%&&&&&&&&&&&&&&&&%"'( ) *%# #+%%%+%%%%%%%%%%%+"   &# ,-    &# ,. /# ,. 011120 /# ,. )345647  /# ,. )8549:; /#  ,. )< =8<)2>4?) )/# ,. 2@@A4BC D48) /#  ,.  E4FGH5I 4I) /# ,. )JH24@K ?C L4M /#  ,., I>24N)  745HO1 &# ,., G3HP 2JQ  /# ,.R )C4S ) /# ,.R ?=C) / ,. )   &  ,. T  % ,.  &  #-,  U  V W(    & U XXXXY  Z[Z[R  R[ ; &\]^; T %W_` a&`bcd ef Ug+*&`  X"\  +d\ T heg '" R"-&  ( ( @rvY[\twxUWXnqrbdez}~uxyx|{οSUt8:~jn ʴx{|IK34?Aadw{ãeh<>EGǬTWsvw%&&ehiMNO789ikl()*DEF       !"#$% &!'(())(((())*+!,-./01)21345555567!89: ;<<<<<<=>?>@ABC>>D< EFGGHIA8 J22K<LMNOHIP8J8.$@ QRSTUH;KJVU#V@ .WXY.;A Z2[\]^_$<Q`#a2 ;KJEbNcT8$@ defa;AJ8Pg[[h$@H2dTij8?H;KJ!?FkSlF@ 8;AJMe2A@????@AJmmn.mA 22 AoZ!?pq(9KoKoKKK9rq!=6H>HHHst5u<v@@@@=uw s  >!((D ox7)((D))(y!>'z62 {| }P/,~w}24>4A.vsr! 2( ģŧν.0ƫZ]8:tw|>@̶orsuxygjk    ! "#$% &'( )*+,- ./0"12(34567 89: ;<=>.?@*A* BC)DEED7))F)GE ?%!H%IEJ%%K?CL 7LL(0`wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwsww'wwwwwwwwwxxxxwwwwwwwwwwwwwxwwwwwwwwwwwxGwwwwwwwwwwWxwwwwwxwwxxxx8Hwwwwwwww%xwwwwwwwwwwwxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwww5wwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwxww"""wwwwwwwwwwwwwwxwwwr"xwwwwwwwwwwxwwwwxww2'wwwww(wwwwwr'wwwwwwx"wwwwwwww""2wwxwwwwwwwwwwwr''wwwxwwwr7wwww(wwwx'r'wwwxwww"wwwwwww"rowwwxwwr"&wwwwwwwwwr'wwwxwwwwwwwwww(wwwx"'wwwwwwwwwwwwww5wwwww27wwwxwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwxwwwwwwxwwwwwwwwwwwwwxCX8x4wwwwwwwwwxwwwwwwwwwwwxxwwxxwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwww0wwwwwwwwwwwpwwwwwwwwx@wwwwwwwwwwwtwwwwwwwwwrwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwwpGwwwwwwwwwuwwwwwwwwwwxwwwwwwwwxpwwwwwwwwwwwwwwwwwww0wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww( @wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwxwxwxwwxwwwxwwxwwwwwwwwxxwwxw{xwww(sx87wwwwwwwwwwwxwww7wwww8ww7wxw*ww8ww'wwwxw*:wxxwg(w7(w8'w*7wxxz'xwwwww7xwwxwwwwwwwwwwwwwwxxwx?wwwxwwwww7wwwww?wwr7wwxrwwwwwwwwwwwwwws?wwxwwwwwwwwwwwwwwwww( wwwwwwwwwwwwxwxwwwxxxwwwwwwxwwwwzwwwwwxw{wwwwwwwwwwwwwwwwwwswwwwxwwwwwpuzzles-r9872/icons/twiddle.ico0000644000175300017530000006117612161170350015677 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $\^_())kmn`cd456qtu+,,fij=?@,-.wz{lop;<=orsuxyKMNLMNacd()*x{|,-.JLM`cc022WZZACCy|}ilmFGH=?@_ab-..\^_VXXsvv^`a345Y[\]_``bccefpstGII577123GHI@BBRTU( @ hkldgh;<=knoehifhi355}befwz{Y[\iklbdejmndfgikl|,--y|}orsilmPRSCEFsuwWYZ(  SUV^`aeghvyz(0` ]_`+,,jmn_ab345qtufijACCvyzlop;<=orsKMNy|}WYYGII=?@RTU                              !     "   #$  %&'  $(( #    )*'$&#"+( &  ,-$    %* & $'   "  &" .+   '/0  & " # " '1, # " 2  '+   " '  '     ' 3/   "+   '  /4   )-  #&  # ##### ############  ########## ( @hkldgh;<=knoehi355}befwz{Y[\iklilm,--y|}suwPRSCEFWYZ        ! "#$%&' ()*+, %-' ./'01'' 234 /50 678/9:;"<=/>93=/?@ A  B( C-2 (D'=$7  /E FGH  /'IJKL8  / M = .= CNO  3P== !K  Q  !RST8   0( <8 ""'UUU///////( SUV^`aeghvyz                  !"# $###  %&'# #"  $ ###  ()! *+  ","-  #  #(0`wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww( @wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwww(?wwwwwwwwwwSwwwwwwwwwswwwwwwwwwwwwwwwwwww8wwwwwwwwwwxCwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww7wwwwwwwwwwwwwxwwwwwwswwwwwwwwwwwwwwwwwwwwwwwwwwwwwww%wwwwwwswwwwwwwwswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww( wwwwwwwwwwwwwwswwwwwwwwwwwwswwwwwwwwwwwww7wwwwwwwww7wwwwwwwwwwwwwwwwpuzzles-r9872/icons/undead.ico0000644000175300017530000006117612161170353015506 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $psch}Xyfjl^^bahg__@ABEFGcef~_qmetj^su]\lnvyVVgg\\ehizkloqkqea`pphhhheejjjiy{/00|etglqm_bbmmaallffeemlno)**_~XaoruQbbii?@122}}egh244addMOPBAKYXc66<__k43;mppegh234`cb}ehi{~x{|x{|twx~799CDELNNTVWTVV244~012SUVsuv./0cef89:ikl()*KMN$%%twx$%%Y[\( @ ŷuxwyvddddff89:SUVxdljii_^klik``ffpstyplqpjddeeggggddHIJwkxskõ`_YYddXXccýnqrecc^bahga`bdeqstUWXUWWwz{UWXhjkUWX:<<po}xwVXY'(( knnmpq/01]_`|)**kno^`aLMNcef455|"#$(  qqĠ˪bc}xz|Ĩb`ÝǃSR\\ǔōȁˣx{|OQQ(0` rdh}\yfjl^^lk__CDEade~_qkerd_su]\lnvyVVhh\\eghtmeaappy{/01z_qmedkkno)**ksuv?@MOPBAKYXc89:__k55<nqrehiy|}TVV./0ikl$%%Y[\              ! "#$$%&'())* +,-./0123!  456789: ;<= >?.@.?0/A!BCDEF9999%GHIJKLM N/>O?PQ2A! CRSTET9S%UVCWX  YZ[,\]^C ST_T`a%bH cd I  eZf0Zg  I#hFTTF:=B ie111j  klhh:m'  noA   Ipqrm  !!  ' C ! ppp st==uvK===wp  CCC   I I =       xyz{| C}  ~WK {sy{ u *d ud  wzy{'  M;d IJ\LM {z  M*dJ\X  K*}|xC  J;dd' K[  J;d B*    JdJC *<  J  C       7  ~~   ~ B I   I u) <} I!L L!MsI *c v<I Xv } (C I IIIIIII   ( @ŷvvyddee9;;TVWydjijý_^klikddnqrpqccJKLwkxskõYYXXec^hgcefqstUWXwz{jmmpo}xwVXY'(( 455]_`|)**^`a       !"#$%&'() *+ ,$,$,-./)0123456 7$89$ %':0;<<=>?@,A,B)0 CCD-E? F??? ??G ????:: 0)))0HIIJ:K/)LM0 NNOPH:Q()QR0STUVUW (&)X/)0YUZ[IS (\%]^)_`aba cFdefX ' )\)^& \g  (0 % %%   )))h hijklFmXn.L)/iGke(hKJ  ( qÝ˪bc}x{|bǃSR\\ǔōˣOQQ    !"#$ %&'()*++    ( ,,-.//0 %1 23456 789:;<= > & h600N; IhR00hX n^(Va(0` $[^gkhkgk[^VXYgju{v{ugjgjkgjkimrimmpq lpplpfijY[\ors345234rtvjnpjnehiqtutwxjnpkn]`atwxiklmqpko466fhijmnfislpilmgju}ogjNPQgjkmp^ailm^abY[\[^gkhkgk[^%%&gju{v{ugj#$$#$$imrim"#$%&&lpplp-//jnpjnz}jnpkn}mqpko|fislp~gju}ogjmp^a( @ ­`cx|imw{suw13y[^|}FHIors|9:; !!345~mqpt !!lop«{|ptOQR %& !"# !!jmnCFadILorDFw35}""#{_ab022Ǹz$$%{[^w{24$%(  KMgkZ\]eh@AB233IK}!""tx˿xz|ʽ.0]`aǺVXosWZ(0` [^gkjnY[\gjs{viklmpq mpofij345234qtutwxlp]`amq466jmn}NPQ^a%&&"#$im-//z}~|                  ! "  #$%$& '()*)*    +,-,.  !(!  /0-1--1-2  34543  67-%08-9:   !5!  +;-7<:-=> ?(!@  A-=-,B CD(D   EF7-GH  (4@  I-J-GH  @ *K5'  +L--7HB   M4N  OGA+    HHP && &&                    +  &  QPP33P   $% " "  RST  '*)*)* @   &UU + V(!W   UT 3!5!3  XY !(! BZ[-\ !5!   /]-^ CD5!   _-]Z `'(4@  ab-^/  *K('@    WMDNc    d@ &ef           eee( @«`c{imw{suw35y[^|}FHIors9:; !!345|mqptlopOQR %& $$%jmnCFadILptDFz|_ab022Ǹw{24$%       !" #$%& '()*+,-./$# # 012345)678 9:$# (;3<=3>?@ )ABCD6 # #E;F3)6GG8H7:$ #(I=J3   * BH7:$ #K(L3M  *NOPQ*6$#6# R=)ST$  ############# $ $ $ # $$ $ $$$U########? $$$$$$ #K V W $ S#XY@Z[ \ ];^#_*`abcdef ghij #klmn,op )$)&qRr?#676nst 6 uvw xnQT\ &yR?67zn`D6 \ {|% #*}/~76 6  ( *lN:X$5T($\f))\\ #6 \ \?)))??S?( KMgkZ\]eh@AB233IK}!""tx˿xz|ʽ.0]`aǺVXosWZ        !"#$% &'()!*+, -     .. /   0 !1 -2)3456789:&;< =>?@)8"AB@=- (0`wwwwwwwwwwwwwwwwwwwwwwwwxxxxxwxwwwwxxxxwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwr((wwwwwwwwwwwwwwwr"""wwwwwwwwwwwwwwwwwz(wwwwwwwwwxwwwwwwx(wwwwwwwwx%wwwwwr(wwwwwwwwxwwwwwwr(wwwwwwwwww wwwwwwwx"wwwwwwwwwwwpwwwwwr(wwwwwwwwwwPwwww*wwwwwwwwwwwPwwwwwr"(wwwwwwwwwpwwwww'wwwwwwwwwwwx(wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxxxxwwwwwxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwxwxxwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpwwwww""(wwwwwwwwwwxwwwwx""wwwwwwwwwwwwwwwwr'wwwwwwwwwwwwwwwwwwr(wwwwwwwwwwwwwwpwwwr(wwwwwwwwwwwwwwwwwrwwwwwwwwwwwwxwwwr(wwwxwwwwwwwwwwwwwwwr(wwwwwwwwwwwwwwwwwwr'wwwwwwwwwwwwpwwwwwr"(wwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwxwwwwwwwxxxxwwwwwxwxxxxw( @wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwws"(wwwwpwwwwwww0wwwwwwwwww0Gwwww2wwwwpu'wwwwwwww7wwwr*wwwwwwwww8wwwwwwv?wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww3wws"ww{wwp?wwwwwwwwwww%wwwwwwwRwwwwwwwwwu7wwwwwwwwwws2wwwwwwx8wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww( wwwwwwwwwwwwxwwwwwwsww7wwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwww?7wwswx?wwwwwwwwwwwwwpuzzles-r9872/icons/unruly.ico0000644000175300017530000006117612161170360015602 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $rssijjmmmllmlmmlllmnn\\]QRRSTTSSTSSTRSSVVWklllmmllmlmmlmmllliiilmmllmlmmllmlmmjjjjkklmmllmlmmmmmjjjnoowww~~~KKK)))1110//000---777}||~~~tttwwwzzzjjjxxxOOO---666555555555===}}}vvvyyy{{{mmmxxxOOO,,,444333333333<<<}}}vvvxxx{{{lmmxxxOOO,,,444444444333<<<}}}uuuxxx{{{llmyyyOOO,,,222111222111<<<~~~wwwyyy|||lmmuuu~~~|||}}}|||MMM,,,222111111///:::xxx{{{{{{{{{{{{zzzqqq{{{{{{{{{zzz|||tttvvv|||{{{{{{{{{zzzjkkEEE???AAAAAAAAA@@@AAA<<<;;;;;;;;;===222~~~rrrzzzxxxxxxyyyxxxoooyyyxxxxxxxxxzzzrrrtttyyyxxxxxxyyywwwjjj===...000000000///444333111333333666'''zzzwwwzzz|||lmm???222444444444333888555111444333777***xxxuuuxxxzzzllm>>>111444333333222777444111444333666)))xxxvvvxxx{{{lmm>>>111444333333222777555222444444777***yyyvvvxxx{{{llm>>>111333333333222666000+++,,,,,,///"""ttt}}}{{{||||||zzzuuuyyy{{{lmmAAA666999888888777===PPPPPPPPPPPPQQQJJJwwwuuuuuuvvvvvvuuuwwwoooqqqwwwuuuvvvvvvtttiii?>>222444333444111===sssyyyyyyyyyyyyxxxxxxzzzxxxzzz~~~lll>>>111444333333111===}}}~~~vvv|||yyy{{{lmm>>>111444333444111===~~~vvv~~~|||xxx{{{lmm>>>111444333444111===~~~uuu~~~{{{xxx{{{llm>>>111444333444111===vvv}}}zzz{{{lmm===...111111111...:::wwwyyyxxxxxxyyywwwrrr~~~}}}}}}}}}}}}{{{tttyyyxxxxxxzzzrrrxxx~~~}}}}}}~~~}||kllddd444>>><<<<<<;;;???CCCBBBBBBBBBDDD999:::<<<<<<<<<===777VVWhhh&&&555333333333555111///222111555%%%000111111111222,++RSSggg'''555333333555888555111444333777***222444444333555...STTggg'''555333333444777444111444333666)))222333333333444...STThhh)))777555555666999444111333333666)))444555555555666000TTUfff)))'''''''''///555///111000444&&&((()))))))))***###PPQlllfffhhhggggggiii```999888888888999222eeedddhhhgggggghhheeedee%%%(((((('''***111555666555555666222&&&))))))***%%%777222666666666888***///222444333444555999444666666777555BBB///444333333666'''000111333333333555888000333333333222@@@///444333333666(((000222444333444555888111333444444222@@@///444333333666'''000111222222222555888000111111111000@@@///333333333555(((.../////////...///888555444444555222AAAGGGCCCEEEDDDEEECCCHHHpppsssrrrrrrrrrpppqqq~~~~~~~~~zzzaaa///999777888444BBB<<<---000///000---:::vvv~~~lll(((555222333///@@@???222555444444222===}}}~~~uuu~~~iii)))555333444000AAA>>>111444333444111===~~~vvv~~~jjj)))555333444000AAA???222444444444111===}}}~~~uuu}}}jjj***666444444000AAA===///222111222///<<<yyylll&&&444111222...???DDD===???>>>???===CCCfffggggggggggggfffaaagggggggggggggggfff[[[999???>>>???===EEErss( @ oppsssrrruvvXYYBCCGGGEEEHHHnoosttrrrrssnnnrrsrssssspqqoopsssrssrssmnn{{{RRR)))222///555zzzzyy~~~|||rssyyy~~~TTT...666444:::yyyxxx|||zzzrss}}}SSS'''000---555zzzzzz~~~|||sssppptttssswwwWWW???DDDBBBFFFuuu|||zzz{{{tttzzzzzz{{{wwwvvv|||zzz|||oopEEE...444222555555444777))){{{}}}}}}}}}vvv||||||}}}yyywww~~~|||~~~pqqEFF///444222666333222666)))yyy}}}{{{sssEFF///444333666555444777***yyy|||zzzrssEFF///444333555...+++///!!!xxxyyyzzzyyyuuu|||zzzrrsGHH333888555<<<[[[\\\\\\WWWwwwuuuyyyyyyvvvtttzzzxxxzyynnnEEF///444111===xxxzzz{{{{{{yyyyyy}}}{{{rssFFF000555111===}}}~~~yyy~~~zzz}}}zzzrrrDDE---333///;;;~~~zzzyyy}}}|||sttIII666;;;888AAA|||~~~~~~~~~uuuyyyyyyyyywwwxxx{{{uuuzzzyyyzzznooppp...;;;888:::;;;:::===111rrrFFF444999433HHHttt%%%555222666222111444'''tttCCC...444...EEEttt***888666999444333666***sssFFF222888222GGGsss)))&&&,,,444111444'''xxx<<<###***###???tttssssssuuuooo:::666888111WWWqqqtttttttttkkl&&&'''+++:::222555666222'''))))))'''444666999***888111444444888444666666666111333666'''888000333444777111222333333222333666(((888...111111777444333333333OOPBBBFFFCCCIIIrrrttttttsssuuuxxx,,,888666444CCC***000,,,999yyy}}}&&&555222222GGG111666333>>>}}}xxx~~~)))777444444BBB+++111---999zyy"""222///...SSTBBBFGGDDDKKKqqqsssrssrrsnnnrssrssrssqqquuu>??GGGEFFEEE(  |}}oooCDDKLL{{{||}|}}~~jjj+**yyy|{{|||~~vwwwwvcddQQQbbb|||}}}yyy}}}yyy||||}};<<###...[[[zzz|||}}}HHI332DDDDDDccc|||yyy|{{||}DDD'''^^^|||zzz~~~zzz}}}LLL333aaa{{{zzz}}}|||yyy{{{222===>>>555+++KLL...333(((?@@SSSooo>>>555TTTZZZxxxyyy YYY111&&&///wwwlllOOOjjjvvvyyyKKK)))uuu99:YYY}}}www}||```vvvVVW???eee{{|eef556xyy(0` ssshhhlmmlll\\]SSTTTUVVWkllllmijjoooyyyKKK)))000,,,777|||uuuxxxzzzOOO///444<<<vvvPPP333{{{+++MMM:::qqqHHH???CCCrrr==='''***"""QQQ?>>0//555ddd;;;DDD>>>%%%111fff```gggBBB@@@(((GGGEEEppprss[[[     !"#$%&'(")*+++,-.///0123++3+433++*56,3+3+ !7#8989:;)**+,<=>>>>?2*333+@*333*52*3+*3 !7#AB9B:;)**+,C0>>D>?+*333+@*33**5E33*++ F7GA 8 :;5*33+,<0>>>>?2333334*333*5E*3+*+ H7I8JBJ'K)L*3+,CMD/D/?+L3*33@L3**,52*****FNO PQPJR4+E+E3S0DDD/T52E2EEUEE2)24422EE6 VWXYXYXY?TT??D+Z[\[]^_)5555 5555E_455@6@ V`a=/===>DDDD>bOcde'ZE,3LL*@L3L3,)2,*L*3 `XD>>DD1>D>D1f'B9Be]53333+4333+35E33333 V`a/DDDD1>DDD1.g&9A9h]5L333+@*333L5E*3333 V`XDDDDD1>D>>1f%B Bd;)L3*33@3333*5E3333+ `aDDDDD>Df00/igjekl_+E22)4*333*5E***33Y>T111av??vYYYYT+;];Z^T?T?1F7$%eed:>DDD>D=D=>(dh]//D0F7r%B AAABB%f>DD>T>D>DTf&B9Bh7>>>>0!7I AAeydAAAb>>D>1D/DD>f,&B99h\DDD>>=!7IB eA BJf11>>T>DDD1f,%BBBd>>>>>/!7I&AAA~ &ff=>=D=>O''cZ..f.MC8!",~[;KnLgT1T>T>sgg3 F7s%Aeebfbfg%%$KD>>>>>>f.f19'h:!7#A8([D1>11f%BBeR=D>D>>T>>11>y8B 8!"GAB9B:[=>>D1b& A h[=DDD>>TzDDDD89A d !7G89A $=>D>>f&BA h[=DDDD>>DD>>D8999 F7I8}BJ&[=>DD1g&AAA%;=DDDD>T=D///8ddh!7uAyQ =DDD>,AyyyB==z===T>>>>DyQQ}L `YW____*++++)[R;z11DY\~Z* `?0zzz=T*LLLL*4*++*++Bhhe$f>DDz}%&'k: `vD{{DD?2*++4*+*+++yA B%f>D>/YPB9 9'`aD>DDDa+*++*+4*3*+*+y A &f>D>/YQBAAA& `aD>>DD?+*++*+43+++++yAB B%M>>>/PJ %, `a=DDD=T*,,,LL),,,*+9&%'%j>DD= dAAA$O`Yaaaa?YOIGGGTva?OI###G_````lllllllllll77"777`"""VVVVVVVVVVVV!FF!H||V!!!!!!V( @nnnsssuuuXYYBBBFGGKKKoppsttooottt|||RRR+++111zyyrssTTT---777666999SSS&&&...qqqWWW???yyyoop444433000,,,###wwwHHH<<<\\\xxxDDD888GGG%%%***kkl'''OOPCCCSST       !"#$%&&& '(")"""**!""+  ,-./0)"*!12345)"*()(""))"("678*!" 9:;24)'"")""!* ""#<=>?@ABC))*)) D EF2G&4GH3%"IJK0)** *)*DD( * FLH&44&4%#M-IN""!")""" ""#2G&44&4%"OPQN"""!)""" )"""<FL&442R;S#QOJT) T"""*)"""U&54VWBXYZ=[\]^# )))) &&&C####X)* )J/_)!"*)""! 24LV ***)"""*NQ./])"" )(("<  `24&5*"(()"!""8a/b\)""* #"' 45c)***)))d08Z)*" )))  Zefgh8iK<2V3VC5C&PiN0jHcHjFb/IJPI7MS4&4&&Gk_lI-`;G2F6K/N87PI%3454&4%6P-JHc&UFmaK/N/Mano%kp4&4kXna_/VSpSC F Z8hNq"#!#<54c&B "#"<r,aQM>::po#naMP5&44&s%%stauv,Q-OwH35%!aP7ic&GHc4444d/./\\/-Oh&H4:#M-INcLHG4&&&&@alx.NKy&&4%"/8Kw3;&&3GHHGzKKx{<T"!!"q>g0TRc4G|.KYF}%LR5"!!")*** eOQ#k4&&ga/F&4&C*"**)"""*i7Pa!%44Gy7.a~%&25*'"")""""eb/u#S&22>I-M~`<<<<[,vC=A,      N FFFFF( |||oooCDDKKKyyy~~jjj)))}}}wwweefQQQbbb;<<###...[[[HHI332DDD}||'''^^^OOO222???555&&&?@@VVWlllYYYwwv vww99:```{{|          !"#$%& '() *+,- .)/0 12- 34 5, 675894::;9&3<&7=>"4?@ABC6DEFG:;EHIJGKL MH%84?">NOF3JPN ND6CQAHRI.DSTN=KCE:U6K; VC(0`wwwwwwwwwwwwwwwwwwwxwxxwwwxwwwwxxwwwxxwwwxxwwwxwwxxwwwwxxwwwxwwwpwwxwwwxwwwpwwwxwwwxxxxxwwwxxwwwxwwwxxwwwxpwwwwxwwwxpwwwxxwwxwwwwwwpwwwwwwwwwwwpxwwwwwwwwwwpxwwwwwwwwwwpxwwwwwwwwwpwwxwwwwwwwwpxwwwwwwwwxpxwwwxxwwwpwwwwwwwwwwpwwwwwxwwwpwwwwwwwxwwwpwwwwwwxwwwpwwwwwwwwwxwwwwwwxwwpwwwwwxxwwwxwwwwwwxwwwwxwwxwwwwxwwwxwwwwwwxwwxwwxxwwwwpwwwxwwwxxxxxwwwwwwwwwwxwwwwwwwwwwww( @wwwwwwwwwwxxS4w$xwwC%ww0Xw1FuHCHr@047`gwpwx`awq%awxxt`wsaqfwwxv`wxxwwwws$aa7wxPwpQ%4gRxwwwt!BC4wxPww$aWxwwwwu`wxCwwB'ww4wxwaw6wwwGwAa8wwwwP'wa@xxw0(Wxwf18wwwt1wwtCwsB@ax0wqfwwwppHwwxcx(wwwwwwwxwwwwwww( wwwwwwxx%q!CxCwxvwwwx(!wWw7wXxT1w!`wx4wtwx8wwwwwwwwpuzzles-r9872/icons/untangle.ico0000644000175300017530000006117612161170363016064 0ustar simonsimon 00 %  >& h600N; IhR00hX n^(Va(0` $EFjkef 89==]^MNqr{~~##11lmklvyz++@A%&EEIJde ( @ AB./ jklmab^_xyDEvwefHH !EFwyrs(  89ʃef˦34ɼopmnâȯɛbc(0` EFjkef89==]^MNqr{~~##11lmklvyz++@A%&EEIJde    !"#$%&'()* +,$"%-(./01/.23(456(-78 $%9-8(#"':"((-8(#- :2:"(;-:&%" "(-8""33#23:3#3$-&&<3-#3=>"%:";("?@A-#(;B CD6;E1FG$;6&$:"':"(#3":H;##='I38H(8;"(2((-(;"(&(3(";#'8";)"98""JKLMN CO.98PQR 9-&4OS 9(";%3 (";#.%;"(H8?FT37:H)U QVW& '9MX."&:( @AB./ jklmab^_xyDEvwefHH !EFwyrs    !" #$%&'()**   +,-./0  12345 16 + 7 4833*9:; " 4<3=>9 ?>*@*A . BC=>9 ?..D?BC2E0 :6 AFG6" HIJ KLD )AMNOP FQ #RSTHU? 47VW26X =1   ]^<9A =XAE>Y-_ Q<\37 +Z.) `a.Q  06PbcQ dAeZfg3\4 hij+93 4E" kl6)mnW 3o )^pqrhW ?"Z kstuvw\-.A xy9A3" z4( 89ʃefɛ˦34ɼopmnâȯbc   !"#$% &'#()%%*+,'-%%%%./##0%1%&%1%(23(%04567 289:0;<= >%%?@% @ %(0`wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxGwwwwwwwwwwwwwwwwwwwwwxGwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwxxwwwwwwwwwwwwwwwwwwtȇwxwwwwwwwwwwwwwwwwwwLwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwtwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwxxwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwxwwwwwwwwxwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwxwwwwwwwxxwwwwwwwwwwwwwxxwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwLwwwwwwwwwwwwwwwwwwwwxHwwwwwwwwwwwwwwwwwwwwxGwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwxwwwwwxwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwxxwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwLwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxxwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwxwwwwwxwwwwwwwwwwwwwxwwwwwwwxwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwtGwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwww( @wwwwwwwwwwwLwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwxwwwwwwwwwww{wwxwwwwwwwww7wwwwwwwwwwwwwwww|wwwwwwwXwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwuwwwwwwwwwwtGwwwwwwwwwwwwwwwwwwwwww( wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxwwwwwwwwwwwwwpuzzles-r9872/icons/blackbox.rc0000644000175300017530000000005712161170363015655 0ustar simonsimon#include "puzzles.rc2" 200 ICON "blackbox.ico" puzzles-r9872/icons/bridges.rc0000644000175300017530000000005612161170363015506 0ustar simonsimon#include "puzzles.rc2" 200 ICON "bridges.ico" puzzles-r9872/icons/cube.rc0000644000175300017530000000005312161170363015002 0ustar simonsimon#include "puzzles.rc2" 200 ICON "cube.ico" puzzles-r9872/icons/dominosa.rc0000644000175300017530000000005712161170363015701 0ustar simonsimon#include "puzzles.rc2" 200 ICON "dominosa.ico" puzzles-r9872/icons/fifteen.rc0000644000175300017530000000005612161170363015507 0ustar simonsimon#include "puzzles.rc2" 200 ICON "fifteen.ico" puzzles-r9872/icons/filling.rc0000644000175300017530000000005612161170363015513 0ustar simonsimon#include "puzzles.rc2" 200 ICON "filling.ico" puzzles-r9872/icons/flip.rc0000644000175300017530000000005312161170363015016 0ustar simonsimon#include "puzzles.rc2" 200 ICON "flip.ico" puzzles-r9872/icons/galaxies.rc0000644000175300017530000000005712161170363015665 0ustar simonsimon#include "puzzles.rc2" 200 ICON "galaxies.ico" puzzles-r9872/icons/guess.rc0000644000175300017530000000005412161170363015213 0ustar simonsimon#include "puzzles.rc2" 200 ICON "guess.ico" puzzles-r9872/icons/inertia.rc0000644000175300017530000000005612161170363015522 0ustar simonsimon#include "puzzles.rc2" 200 ICON "inertia.ico" puzzles-r9872/icons/keen.rc0000644000175300017530000000005312161170363015006 0ustar simonsimon#include "puzzles.rc2" 200 ICON "keen.ico" puzzles-r9872/icons/lightup.rc0000644000175300017530000000005612161170363015543 0ustar simonsimon#include "puzzles.rc2" 200 ICON "lightup.ico" puzzles-r9872/icons/loopy.rc0000644000175300017530000000005412161170363015227 0ustar simonsimon#include "puzzles.rc2" 200 ICON "loopy.ico" puzzles-r9872/icons/magnets.rc0000644000175300017530000000005612161170363015525 0ustar simonsimon#include "puzzles.rc2" 200 ICON "magnets.ico" puzzles-r9872/icons/map.rc0000644000175300017530000000005212161170363014640 0ustar simonsimon#include "puzzles.rc2" 200 ICON "map.ico" puzzles-r9872/icons/mines.rc0000644000175300017530000000005412161170363015200 0ustar simonsimon#include "puzzles.rc2" 200 ICON "mines.ico" puzzles-r9872/icons/net.rc0000644000175300017530000000005212161170363014651 0ustar simonsimon#include "puzzles.rc2" 200 ICON "net.ico" puzzles-r9872/icons/netslide.rc0000644000175300017530000000005712161170363015677 0ustar simonsimon#include "puzzles.rc2" 200 ICON "netslide.ico" puzzles-r9872/icons/pattern.rc0000644000175300017530000000005612161170363015544 0ustar simonsimon#include "puzzles.rc2" 200 ICON "pattern.ico" puzzles-r9872/icons/pearl.rc0000644000175300017530000000005412161170363015170 0ustar simonsimon#include "puzzles.rc2" 200 ICON "pearl.ico" puzzles-r9872/icons/pegs.rc0000644000175300017530000000005312161170363015022 0ustar simonsimon#include "puzzles.rc2" 200 ICON "pegs.ico" puzzles-r9872/icons/range.rc0000644000175300017530000000005412161170363015161 0ustar simonsimon#include "puzzles.rc2" 200 ICON "range.ico" puzzles-r9872/icons/rect.rc0000644000175300017530000000005312161170363015021 0ustar simonsimon#include "puzzles.rc2" 200 ICON "rect.ico" puzzles-r9872/icons/samegame.rc0000644000175300017530000000005712161170363015647 0ustar simonsimon#include "puzzles.rc2" 200 ICON "samegame.ico" puzzles-r9872/icons/signpost.rc0000644000175300017530000000005712161170363015736 0ustar simonsimon#include "puzzles.rc2" 200 ICON "signpost.ico" puzzles-r9872/icons/singles.rc0000644000175300017530000000005612161170363015533 0ustar simonsimon#include "puzzles.rc2" 200 ICON "singles.ico" puzzles-r9872/icons/sixteen.rc0000644000175300017530000000005612161170363015546 0ustar simonsimon#include "puzzles.rc2" 200 ICON "sixteen.ico" puzzles-r9872/icons/slant.rc0000644000175300017530000000005412161170363015206 0ustar simonsimon#include "puzzles.rc2" 200 ICON "slant.ico" puzzles-r9872/icons/solo.rc0000644000175300017530000000005312161170363015040 0ustar simonsimon#include "puzzles.rc2" 200 ICON "solo.ico" puzzles-r9872/icons/tents.rc0000644000175300017530000000005412161170363015222 0ustar simonsimon#include "puzzles.rc2" 200 ICON "tents.ico" puzzles-r9872/icons/towers.rc0000644000175300017530000000005512161170363015411 0ustar simonsimon#include "puzzles.rc2" 200 ICON "towers.ico" puzzles-r9872/icons/twiddle.rc0000644000175300017530000000005612161170363015523 0ustar simonsimon#include "puzzles.rc2" 200 ICON "twiddle.ico" puzzles-r9872/icons/undead.rc0000644000175300017530000000005512161170363015326 0ustar simonsimon#include "puzzles.rc2" 200 ICON "undead.ico" puzzles-r9872/icons/unequal.rc0000644000175300017530000000005612161170363015541 0ustar simonsimon#include "puzzles.rc2" 200 ICON "unequal.ico" puzzles-r9872/icons/unruly.rc0000644000175300017530000000005512161170363015424 0ustar simonsimon#include "puzzles.rc2" 200 ICON "unruly.ico" puzzles-r9872/icons/untangle.rc0000644000175300017530000000005712161170363015705 0ustar simonsimon#include "puzzles.rc2" 200 ICON "untangle.ico" puzzles-r9872/icons/blackbox-icon.c0000644000175300017530000003757612161170363016441 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 112 2", " c #000000", ". c #070707", "X c #191818", "o c #1C1C1B", "O c #2D2D2C", "+ c #3C3C3B", "@ c #5B5A58", "# c #5D5D5B", "$ c #5E5D5B", "% c #5F5E5C", "& c #61605E", "* c #62615F", "= c #636260", "- c #646361", "; c #656462", ": c #656463", "> c #666563", ", c #666664", "< c #676664", "1 c #686765", "2 c #696866", "3 c #6A6967", "4 c #6B6A68", "5 c #6D6C6A", "6 c #6E6E6B", "7 c #706F6D", "8 c #71706D", "9 c #71706E", "0 c #72716F", "q c #73726F", "w c #737270", "e c #747370", "r c #747371", "t c #757371", "y c #757472", "u c #767572", "i c #767573", "p c #777673", "a c #777674", "s c #787774", "d c #797875", "f c #7A7976", "g c #7A7977", "h c #7C7B78", "j c #7C7B79", "k c #7D7C7A", "l c #7F7E7B", "z c #807F7C", "x c #81807D", "c c #82817E", "v c #82817F", "b c #83827F", "n c #848380", "m c #868582", "M c #878683", "N c #888784", "B c #8B8A86", "V c #8B8A87", "C c #93928E", "Z c #A19F9B", "A c #A9A8A4", "S c #ABAAA6", "D c #ACAAA6", "F c #ADACA8", "G c #AEACA8", "H c #AFAEAA", "J c #B0AEAA", "K c #B0AFAB", "L c #B1AFAB", "P c #B1B0AC", "I c #B2B1AC", "U c #B2B1AD", "Y c #B3B1AD", "T c #B4B2AE", "R c #B4B3AE", "E c #B5B4AF", "W c #B6B4B0", "Q c #BDBBB7", "! c #C3C2BD", "~ c #C7C5C0", "^ c #C9C7C3", "/ c #CBC9C4", "( c #CDCBC6", ") c #CECCC7", "_ c #CFCDC8", "` c #CFCDC9", "' c #D0CEC9", "] c #D1CFCA", "[ c #D1CFCB", "{ c #D1D0CB", "} c #D2D0CB", "| c #D2D1CC", " . c #D3D1CC", ".. c #D4D2CD", "X. c #D4D3CD", "o. c #D5D3CE", "O. c #D5D4CE", "+. c #D6D4CF", "@. c #D7D5CF", "#. c #D7D5D0", "$. c #D7D6D0", "%. c #D8D6D0", "&. c #D8D6D1", "*. c #D9D7D1", "=. c #DAD8D3", "-. c #DBD9D3", ";. c #DBD9D4", ":. c #DCDAD4", ">. c #DCDAD5", ",. c #DDDAD5", "<. c #DDDBD5", "1. c #DDDBD6", /* pixels */ "o.o.o. .{ { ] .{ ] .] [ ] { o.", "@.o.( *.Y w u z c u s M g w c { ", "o.;.8 Y Y % = 8 w = < g 1 - u ] ", "#.@.W / Y < 8 u a 2 4 k 5 1 s ] ", "#. .;.<.Y 5 @ c N g h V k g M ] ", "#.o.G ~ A . O g < 2 k 4 < s [ ", "#.*.V Y Z X 8 < < g 1 = u ] ", "#. . .;.Y + o % M w a M s w n [ ", "#.o.] #.G u u c k 8 w M w 8 l .", "#.#. .<.G & 2 w a 1 2 h 4 > a [ ", "#.@. .<.A % & 6 7 & - a - % 8 { ", "@.` ( ` ! G G K K G G Y G K Y .", "o.o.o.o.=.<.;.#.#.;.;.#. .Q <. .", "o.o.o.o.@. .@.{ { @. . ./ C #. .", "o.o.o.o.@.` { ( ` ' ` ( ` / ` .", "o.o.o.o. .] ] ] { { .` . . .o." }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 168 2", " c #000000", ". c #010101", "X c #030302", "o c #030303", "O c #040404", "+ c #050505", "@ c #1B1B1B", "# c #1E1E1E", "$ c #212120", "% c #333231", "& c #343332", "* c #393937", "= c #3E3E3C", "- c #444442", "; c #494947", ": c #4B4A49", "> c #565553", ", c #575755", "< c #5C5C59", "1 c #5D5C5A", "2 c #5E5D5B", "3 c #5E5D5C", "4 c #5F5E5C", "5 c #5F5E5D", "6 c #605F5D", "7 c #605F5E", "8 c #61605E", "9 c #61605F", "0 c #62615F", "q c #636260", "w c #636360", "e c #636261", "r c #646360", "t c #646361", "y c #656462", "u c #666563", "i c #666564", "p c #676664", "a c #676665", "s c #6A6967", "d c #6B6A68", "f c #6C6B68", "g c #6C6B69", "h c #6C6C69", "j c #6D6C6A", "k c #6F6F6C", "l c #706F6D", "z c #737270", "x c #767573", "c c #777673", "v c #777674", "b c #787774", "n c #787775", "m c #797875", "M c #797876", "N c #7A7976", "B c #7B7A77", "V c #7B7A78", "C c #7C7B78", "Z c #7C7B79", "A c #7D7C79", "S c #7D7C7A", "D c #7E7D7A", "F c #7F7E7B", "G c #807F7C", "H c #81807D", "J c #82817E", "K c #82817F", "L c #83827F", "P c #848380", "I c #848381", "U c #858481", "Y c #868482", "T c #868582", "R c #878683", "E c #888784", "W c #898784", "Q c #898885", "! c #8A8985", "~ c #8A8986", "^ c #8B8986", "/ c #8B8A87", "( c #8C8B88", ") c #8E8D8A", "_ c #8F8E8A", "` c #908F8B", "' c #908F8C", "] c #91908C", "[ c #91908D", "{ c #92908D", "} c #92918D", "| c #92918E", " . c #94928F", ".. c #94938F", "X. c #949390", "o. c #959491", "O. c #969591", "+. c #979692", "@. c #999895", "#. c #9A9995", "$. c #9A9996", "%. c #9B9A96", "&. c #9B9A97", "*. c #9D9C98", "=. c #9E9D99", "-. c #B3B1AD", ";. c #B4B2AE", ":. c #B5B3AF", ">. c #C2C0BB", ",. c #C5C3BE", "<. c #C6C4BF", "1. c #C6C4C0", "2. c #C7C6C1", "3. c #C8C6C1", "4. c #C8C7C2", "5. c #C9C7C2", "6. c #C9C7C3", "7. c #C9C8C3", "8. c #CAC8C3", "9. c #CAC8C4", "0. c #CAC9C4", "q. c #CBC9C4", "w. c #CCCAC5", "e. c #CCCAC6", "r. c #CDCBC6", "t. c #CDCCC7", "y. c #CECCC7", "u. c #CECDC8", "i. c #CFCDC8", "p. c #CFCDC9", "a. c #CFCEC9", "s. c #D0CEC9", "d. c #D0CFCA", "f. c #D1CFCA", "g. c #D2D0CB", "h. c #D2D1CB", "j. c #D2D1CC", "k. c #D3D1CC", "l. c #D4D2CD", "z. c #D4D3CD", "x. c #D5D3CE", "c. c #D6D3CE", "v. c #D6D4CF", "b. c #D7D4CF", "n. c #D6D5CF", "m. c #D6D5D0", "M. c #D7D5D0", "N. c #D7D6D0", "B. c #D8D6D1", "V. c #D9D6D1", "C. c #D8D7D1", "Z. c #D9D7D1", "A. c #D9D7D2", "S. c #D9D8D2", "D. c #DAD8D2", "F. c #DAD8D3", "G. c #DBD8D3", "H. c #DAD9D3", "J. c #DBD9D4", "K. c #DCDAD5", "L. c #DDDBD6", "P. c #DEDCD6", "I. c #DEDCD7", "U. c #DFDCD7", "Y. c #DFDDD8", "T. c #E0DED9", "R. c #E1DFD9", "E. c #E2E0DA", /* pixels */ "x.x.x.x.v.v.v.v.m.m.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.", "x.x.x.g.k.g.k.k.f.x.x.x.x.x.x.x.x.x.x.x.x.x.x.k.x.x.x.x.x.x.x.x.", "x.A.x.f.g.x.f.f.k.X.x Z M V n E ' b C N V v / _ v Z N F c ^ x.x.", "k.A.N.x.x.q.A.x.F.R 6 p i p 9 n L 8 a y p q N G 8 p y a 8 c x.x.", "k.A.x.Y.} > +.L.m.E r j s h y C E y f d f p Z Y p f d f y F x.x.", "x.D.k.R.E : F R.m.E r f s d p N U r d d d i Z Y r d d d p N x.x.", "k.D.x.J.=.:.+.x.N.^ r d f f y F E y f f f p S Y p d h h p N x.x.", "x.N.A.x.A.A.D.x.F.R 5 p q r 8 v J 6 p r i 6 n G 5 p y y 8 c x.x.", "k.A.k.u.r.r.r.r.f.@.G ^ ( ( U ] +.D U P Y G ' +.G Y P P G _ x.m.", "x.A.k.u.f.f.f.f.g.+.N , * - h ( } n F V Z N / ' v F Z F N ^ k.x.", "x.A.A.x.x.A.v.x.D.^ # y R 5 p p p 9 V G 5 p p p q n x.x.", "x.A.x.L.' :.^ u.U.l + & / y d d d p Z P p d d d p V x.x.", "k.A.k.R.J 6 r x.Y.2 $ P y d d d p Z P r d d d y N v.x.", "x.A.x.L.=.,.&.u.U.k O + + % / y d d f p D Y p f d h p Z x.x.", "k.N.A.x.A.A.A.v.D.^ @ q U 5 p r p 5 n G 6 p y p 8 c x.x.", "x.A.g.r.y.y.r.r.u.&.J < = : z +.+.J R U U P } +.J U U U J ' x.x.", "x.A.x.f.f.f.f.s.k.X.c P J P V E ' v V V V v ^ ( N Z V F c ^ k.v.", "x.A.x.x.x.x.x.x.A.U 5 p r y 8 v P 6 p p p 8 N J 8 p y p 8 b k.v.", "k.A.M.x.x.x.x.x.A.^ p d d d p V U p d d d p Z P y h d d a N v.v.", "x.A.m.x.x.x.x.x.N.E q d d d p V U y d d d y F P y d d d y N v.k.", "k.A.v.x.x.x.x.x.A.^ p f d f p V U p d d h p F Y p h d h a Z v.v.", "x.M.A.x.x.A.M.x.A.U 5 p r r 9 v J 5 y q y 6 n G 5 t t t 8 c v.k.", "x.A.f.3.9.9.r.9.t.*.U ^ E ^ U X.&.U ^ ^ ^ U +.&.U ^ ^ ^ R X.k.v.", "x.v.f.r.f.t.t.t.f.N.N.N.A.A.D.g.u.A.A.N.N.A.k.u.D.N.J.N.D.k.g.v.", "x.x.x.A.x.b.A.x.A.v.x.k.x.k.x.f.t.x.k.x.x.x.f.t.N.k.3.x.x.f.k.m.", "x.x.x.x.x.x.x.x.A.A.x.x.x.x.x.f.s.x.x.x.x.x.f.t.N.t.5 >.A.f.x.x.", "x.x.x.x.x.x.x.k.A.x.x.x.x.x.x.f.r.x.x.x.x.x.f.r.k.Y.Y ;.L.f.f.m.", "x.x.x.x.x.x.x.x.A.A.f.x.x.x.x.f.r.M.x.x.x.x.f.r.N.t.q X.L.f.x.x.", "x.x.x.x.x.x.x.k.A.A.x.A.A.A.A.f.f.A.x.N.A.A.f.f.A.b.T.G.x.k.k.m.", "x.x.x.x.x.x.x.x.A.s.3.9.3.3.9.3.,.r.3.9.3.r.1.,.q.9.3.3.q.3.x.x.", "x.x.x.x.x.x.x.x.x.f.t.f.f.r.s.u.f.s.f.t.f.r.f.f.i.i.f.r.r.f.x.x.", "x.x.x.x.x.x.x.x.x.M.M.x.b.m.b.x.x.b.b.M.b.x.x.b.b.x.M.x.M.x.x.x." }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 233 2", " c #000000", ". c #010101", "X c #020201", "o c #020202", "O c #030202", "+ c #030303", "@ c #040404", "# c #060606", "$ c #080808", "% c #161615", "& c #1B1B1A", "* c #1E1D1D", "= c #232322", "- c #323130", "; c #353534", ": c #363634", "> c #363635", ", c #3E3E3C", "< c #424240", "1 c #474745", "2 c #484745", "3 c #484746", "4 c #484846", "5 c #4F4E4C", "6 c #545452", "7 c #565654", "8 c #575654", "9 c #595856", "0 c #5A5A58", "q c #5B5A58", "w c #5C5B59", "e c #5D5C5A", "r c #5D5C5B", "t c #5E5D5B", "y c #5F5E5B", "u c #5E5D5C", "i c #5F5E5C", "p c #5F5E5D", "a c #605F5D", "s c #605F5E", "d c #60605D", "f c #61605E", "g c #61615E", "h c #61605F", "j c #62615E", "k c #62615F", "l c #636260", "z c #636261", "x c #646361", "c c #646362", "v c #656462", "b c #656562", "n c #656463", "m c #666563", "M c #676663", "N c #666564", "B c #676664", "V c #686765", "C c #686766", "Z c #696865", "A c #696866", "S c #6A6967", "D c #6B6A67", "F c #6B6A68", "G c #6C6B68", "H c #6C6B69", "J c #6D6B69", "K c #6D6C69", "L c #6D6C6A", "P c #6F6E6C", "I c #706F6C", "U c #706F6D", "Y c #71706D", "T c #71706E", "R c #72716E", "E c #72716F", "W c #73726F", "Q c #747371", "! c #757472", "~ c #767572", "^ c #767573", "/ c #777573", "( c #777673", ") c #787674", "_ c #787774", "` c #787775", "' c #797876", "] c #7C7B78", "[ c #7C7B79", "{ c #7D7C79", "} c #81807D", "| c #82817E", " . c #848380", ".. c #858481", "X. c #858581", "o. c #868582", "O. c #878683", "+. c #888784", "@. c #898885", "#. c #8A8985", "$. c #8A8986", "%. c #8B8A86", "&. c #8B8A87", "*. c #8C8A87", "=. c #8C8B87", "-. c #8C8B88", ";. c #8D8B88", ":. c #8D8C88", ">. c #8D8C89", ",. c #8E8D8A", "<. c #8F8E8B", "1. c #908E8B", "2. c #908F8B", "3. c #908F8C", "4. c #918F8C", "5. c #91908C", "6. c #91908D", "7. c #92918D", "8. c #92918E", "9. c #93928F", "0. c #959490", "q. c #969491", "w. c #969591", "e. c #979692", "r. c #979693", "t. c #999894", "y. c #999895", "u. c #9A9995", "i. c #9A9996", "p. c #9B9A97", "a. c #9C9A97", "s. c #9D9B98", "d. c #9E9D9A", "f. c #9F9E9A", "g. c #A09E9A", "h. c #A09F9B", "j. c #A19F9C", "k. c #A1A09C", "l. c #A3A19D", "z. c #A3A29E", "x. c #A4A29E", "c. c #A5A4A0", "v. c #A6A5A0", "b. c #A6A5A1", "n. c #A7A6A2", "m. c #A8A7A3", "M. c #A9A8A4", "N. c #ACAAA6", "B. c #ACABA7", "V. c #ADABA7", "C. c #ADACA8", "Z. c #AEADA9", "A. c #AFAEAA", "S. c #B3B2AE", "D. c #B5B4AF", "F. c #BBB9B5", "G. c #BFBDB9", "H. c #C0BFBA", "J. c #C1C0BB", "K. c #C2C0BB", "L. c #C3C1BC", "P. c #C3C1BD", "I. c #C3C2BD", "U. c #C4C2BD", "Y. c #C5C3BE", "T. c #C6C5C0", "R. c #C7C6C1", "E. c #C8C6C1", "W. c #C8C7C2", "Q. c #C9C7C2", "!. c #C9C8C3", "~. c #CAC8C3", "^. c #CBC9C4", "/. c #CCCAC5", "(. c #CCCAC6", "). c #CCCBC6", "_. c #CDCBC6", "`. c #CDCCC7", "'. c #CECCC7", "]. c #CECCC8", "[. c #CFCDC8", "{. c #CFCEC8", "}. c #CFCEC9", "|. c #D0CEC9", " X c #D0CFC9", ".X c #D0CECA", "XX c #D0CFCA", "oX c #D1CFCA", "OX c #D1CFCB", "+X c #D1D0CA", "@X c #D1D0CB", "#X c #D2D0CB", "$X c #D2D1CC", "%X c #D3D1CC", "&X c #D3D2CC", "*X c #D3D2CD", "=X c #D4D1CD", "-X c #D4D2CD", ";X c #D4D3CD", ":X c #D5D2CE", ">X c #D4D3CE", ",X c #D5D3CE", " - 2 U ~ 5.9.T _ ~ ~ _ ~ _ T 9.5.T / / ~ / ` ` U 0.7X,X,X,X", ",X,X,XuX,X,X @ % $.&.l F F F F F F x -.+.B L F F F F L x -.7X,X,X,X", ",X,X#XuX3X3X,X,X4X,X,X,X6X,X| l > * 9 $...r m l B l n B r $...r B x x n x B r +.uX,X,X,X", ",X,X,XuX_.Q.Q.Q.Q.Q.Q.Q.Q.!.c.r.k.9.~ Q +.d.p.c.c.p.p.y.p.y.p.p.r.m.m.w.y.p.p.y.p.y.r.M.7X,X,X,X", ",X,X,XuX&X|.&X|.&X|.&X|.OX&X-.U Q _ ] { ' ~ P 9.9.P ~ ~ ~ Q ~ _ U 9.,.U _ Q Q _ ~ ~ P 9.7X&X,X,X", ",X,X,XuX3X,X6X6X,X6X6X,X6X,X .l A B m m m B j ....j A B B B B B s &...s V B B B B A s $.tX,X,X,X", ",X,X,XuX,X,X,X,X,X,X,X,X,X,X..m L A A F A P m $.$.m A F F C F F x -.+.x F F V V A L x &.7X,X,X,X", ",X,X#XuX,X,X,X,X,X,X,X,X,X&X..x F F F A A A l $.$.m F A F F F F l &.+.x F F F F A F x &.7X,X,X,X", ",X,X,XuX,X,X,X,X,X,X,X,X3X;X..m F F A F F F m $.$.l F F V V F F l &.+.x F F A F F F x $.7X,X,X,X", ",X,X&XuX,X,X,X,X,X,X,X,X&X;X..x F A F A A F l $.$.l F A F F F F l &.+.x F A F A A L x $.tX#X,X,X", ",X,X&XuX,X,X,X,X,X,X,X,X3X&X..m F A A F A F m $.$.l F F F F V F x &.+.x F A A F A F x -.7X,X,X,X", ",X,X,XuX,X&X,X,X,X,X,X&X3X&X..m P F F F A P x $.$.B F F F F F L x -.+.x L L F F F P x -.tX,X,X,X", ",X,X,XrX7X6X6X7X3X7X7X3X7X3X| t m x x x x m t ....t x x x x x x r +...r m x x x x B r +.7X,X,X,X", ",X,X,XuX~.H.P.P.P.P.P.P.P.P.B.k.x.k.k.x.c.k.j.C.Z.k.c.k.c.k.k.c.k.Z.Z.j.x.x.k.c.k.k.j.A.7X,X,X,X", ",X,X,X,X|.|.|.|.|.|.|.|.}.7X7XtX7XtX7XtXrX7XuX|.|.rX7XrX7XqXrX7XeX}.|.uX7X7X7X7XuX7XrXOX,X,X,X,X", ",X,X,X,X,X6X,X,X3X3X3X3X,XuX,X&X&X,X,X,X&X,X,X_._.6X&X,X,X&X,X&X3X_._.,X,X,XuXrX&X,X,X}.OX,X,X,X", ",X,X,X,X,X,X,X,X,X,X,X,X,XuX,X,X,X,X,X,X,X,X7X_._.6X,X,X,X,X,X,X6X_.`.3X;X6XV.D.7X,X c #83827E", ", c #83827F", "< c #848380", "1 c #858481", "2 c #868481", "3 c #868581", "4 c #868582", "5 c #878683", "6 c #888783", "7 c #888784", "8 c #898784", "9 c #8B8A87", "0 c #8E8D89", "q c #8F8E8B", "w c #908F8B", "e c #908F8C", "r c #918F8C", "t c #91908C", "y c #91908D", "u c #92918D", "i c #92918E", "p c #93928E", "a c #93928F", "s c #94928F", "d c #94938F", "f c #959490", "g c #979693", "h c #989693", "j c #989793", "k c #989794", "l c #999894", "z c #9A9995", "x c #9B9995", "c c #9A9896", "v c #9B9A96", "b c #9C9B97", "n c #9D9C99", "m c #9E9D9B", "M c #9E9E9D", "N c #A09F9B", "B c #A1A09C", "V c #A3A19D", "C c #A0A09F", "Z c #A6A4A0", "A c #A6A5A1", "S c #A7A5A1", "D c #A5A5A3", "F c #A6A6A5", "G c #A8A6A2", "H c #AAA9A5", "J c #ABA9A5", "K c #ABAAA6", "L c #ACABA7", "P c #ABABA8", "I c #B0AEAA", "U c #B3B1AC", "Y c #B3B1AD", "T c #B4B2AE", "R c #B5B3AF", "E c #B2B1B0", "W c #B7B5B1", "Q c #B7B6B1", "! c #B7B6B2", "~ c #B5B5B4", "^ c #B8B6B1", "/ c #B9B7B3", "( c #B8B7B5", ") c #BAB8B4", "_ c #BAB9B4", "` c #BBB9B4", "' c #B8B8B6", "] c #BAB9B6", "[ c #BCBAB6", "{ c #BDBBB6", "} c #BCBAB7", "| c #BEBCB7", " . c #BEBCB8", ".. c #BFBDB9", "X. c #BEBEBD", "o. c #C1BFBB", "O. c #C0BFBC", "+. c #C1C0BB", "@. c #C2C0BC", "#. c #C2C1BC", "$. c #C3C1BD", "%. c #C3C2BD", "&. c #C4C2BD", "*. c #C4C2BE", "=. c #C5C3BE", "-. c #C5C3BF", ";. c #C5C4BF", ":. c #C0C0C0", ">. c #C5C4C0", ",. c #C6C5C0", "<. c #C9C7C2", "1. c #CAC8C3", "2. c #CAC8C4", "3. c #CBCAC5", "4. c #CDCBC6", "5. c #CDCBC7", "6. c #CDCCC7", "7. c #CECCC7", "8. c #D2D0CB", "9. c #D2D0CC", "0. c #D2D1CC", "q. c #D3D1CC", "w. c #D3D2CD", "e. c #D4D2CD", "r. c #D5D3CE", "t. c #D5D4CF", "y. c #D6D4CF", "u. c #D7D5D0", "i. c #D9D7D2", "p. c #DAD8D3", "a. c #DAD8D4", "s. c #DBD9D4", "d. c #DCDAD5", "f. c #DDDBD6", "g. c #DEDCD6", "h. c #DEDCD7", "j. c #DFDDD8", "k. c #E0DDD8", "l. c #E0DED8", "z. c #E1DED9", "x. c #E1DFD9", "c. c #E0DEDA", "v. c #E1DFDA", "b. c #E2E0DA", "n. c #E4E2DC", "m. c #E4E2DD", "M. c #E5E3DD", "N. c #E5E3DE", "B. c #E6E4DF", "V. c #E8E6E0", "C. c #EFEFEE", /* pixels */ "r.r.r.r.r.r.r.r.7.{ 9.2.9.5.5.i.", "r.r.r.r.r.r.q.r.5.; >.< 5.&.3 r.", "r.r.r.r.r.i.p.8.a.C L W ( X.F U ", "r.r.r.r.d.#.o.d.i.k &.6 { 7.< 3.", "r.u.q.g.x @ * 9 l.y b.K ; = ( d.", "#.W j.o.; T ^ 6 W b q.V.K ` n.q.", "..I l.` < ` m r ` M j.j.K W d.r.", "y.d.p.n.s : 9 , M + x n.K W h.q.", "r.&.&.&.1.C V x x ` y G U E g.r.", "7.& . o X & & C.3 :.x E ` d.q.", "r.+.+.#.#.o.1.y g 0.l A g x b.r.", "y.n.N.N.n.n.N.N.C y A 8 d y y d.", "#.& < , , , , , q y O K O.A M W ", ";.6 s s s s s y q g # m y.;.y | ", "y.l.b.l.l.l.l.l.l.g.N.q e q b j.", "r.8.8.0.8.0.q.8.0.q.q.d.` ..j.q." }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 76 1", " c #1F1E1E", ". c #201F1E", "X c #20201F", "o c #252424", "O c #2D2D2B", "+ c #30302F", "@ c #343433", "# c #383837", "$ c #3C3B3A", "% c #40403F", "& c #444442", "* c #484746", "= c #494846", "- c #4D4D4B", "; c #504F4D", ": c #52514F", "> c #545452", ", c #585755", "< c #5A5957", "1 c #5F5E5C", "2 c #605F5D", "3 c #62615F", "4 c #636363", "5 c #686765", "6 c #6A6966", "7 c #6D6C6A", "8 c #706F6C", "9 c #72716E", "0 c #757471", "q c #7A7977", "w c #7E7D7A", "e c #807F7C", "r c #81807D", "t c #838381", "y c #898784", "u c #8A8986", "i c #8D8B88", "p c #92918E", "a c #969591", "s c #989794", "d c #9C9A97", "f c #9F9E9A", "g c #A09F9B", "h c #A2A19D", "j c #A5A5A3", "k c #A8A7A3", "l c #ACAAA6", "z c #AEADA9", "x c #B3B1AD", "c c #B7B5B1", "v c #B9B7B3", "b c #BBBAB5", "n c #BFBEB9", "m c #C1BFBA", "M c #C5C3BE", "N c #C6C5C2", "B c #C9C7C3", "V c #CDCBC6", "C c #CECDC9", "Z c #D0CEC9", "A c #D5D3CE", "S c #D7D5D0", "D c #D9D7D2", "F c #DCDAD5", "G c #DFDDD8", "H c #E1DFDA", "J c #E4E2DD", "K c #E4E3E2", "L c #EEECE6", "P c #EEEDEA", "I c #F0EEE8", "U c #F5F3EE", "Y c #F6F5F1", "T c #F8F6F0", "R c #FCFAF5", "E c #FFFFFD", /* pixels */ "AAAAAAAASAAAAAASADDFDAAFFASFSAAA", "AASAASAAAAASSAAASmmmmAFMvAVcADAA", "ASAAAASASAAAASASZm==vGdOmERiOBSA", "AAAAASAAAAAAAAAAZRreEVONJ9vEq>JA", "AASASAASAAAAAAASCUeeRp-EJZ%ERk$ME&hE3hLAAAS", "SZq0000000000q:%EJA8zE&hE1gLZAAA", "AAhfffggggggggj+CPqaENOVE4jIVAAA", "ASLLULLLLILLILRaoDEESopLA%9GHAAA", "AABBBVBBBBBBBBMFuo$$oyKYn#FA", "Fn=piiiiiiiiiiiiiiuys;4ERj&GE@mD", "DM#3<1111111111111115@,ECn1AR@ND", "DMzMnnMnnnnnmmmnnmnnmnOFLybEp*JZ", "ASJJJJJJJJJJJJJJKJJJHPioBERpXnDZ", "AAZAAZZAZAAAAZAAZAZAACJp@@+&vGAS", "AASAAAAAASAAAAASSAAASAAHFnNJFZSA", "ASAAASAAAAAAAAAAAAAAAAAZAFDZASAA" }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 84 1", " c #030303", ". c #080807", "X c #0C0B0B", "o c #121212", "O c #181817", "+ c #1E1E1D", "@ c #201F1E", "# c #20201F", "$ c #232322", "% c #292827", "& c #2B2B2A", "* c #302F2E", "= c #31302F", "- c #343433", "; c #383736", ": c #3A3937", "> c #3C3B3A", ", c #41403F", "< c #434241", "1 c #484745", "2 c #494846", "3 c #4D4D4B", "4 c #51504E", "5 c #555553", "6 c #585755", "7 c #5A5957", "8 c #5E5D5B", "9 c #605F5D", "0 c #60605E", "q c #646361", "w c #696866", "e c #6E6D6B", "r c #71716D", "t c #737372", "y c #787774", "u c #7A7976", "i c #7D7C79", "p c #807F7C", "a c #82817E", "s c #858481", "d c #898784", "f c #8A8986", "g c #8D8C89", "h c #908F8B", "j c #93928E", "k c #979592", "l c #989796", "z c #9A9996", "x c #9E9D99", "c c #A19F9B", "v c #A3A29E", "b c #A6A5A1", "n c #ABAAA6", "m c #AEADA8", "M c #B1AFAB", "N c #B3B2AD", "B c #B6B5B0", "V c #B8B7B3", "C c #BBB9B5", "Z c #BDBDB9", "A c #C1BFBA", "S c #C3C2BE", "D c #C7C5C1", "F c #C8C7C2", "G c #CCCAC5", "H c #CFCDC8", "J c #D1CFCA", "K c #D5D3CE", "L c #D7D5D0", "P c #D9D7D2", "I c #DCDAD5", "U c #DFDDD8", "Y c #E1DFD9", "T c #E4E2DD", "R c #E5E4E1", "E c #E8E5E0", "W c #EBE9E4", "Q c #EDECE9", "! c #F0EEE9", "~ c #F4F2EC", "^ c #F6F5F2", "/ c #F9F7F1", "( c #FCFAF4", ") c #FFFFFE", /* pixels */ "KKKKKLKKKKKKKKLKKKKKKKKKLKKKKKKKKKKKKKKKKKKKKKKK", "KKKKKKKKKKLKKKKKKKKLKKKKKKJKKJKKKKKJKKKKKKKKLKKK", "KKKKKKKKKKKKKKKKKKKKKLKKKPTTTTYKKKYTIKJITYKKKKKK", "KKKLKLKKKLKKLKKKLKKKKKKKPBdghanKKIzsNIInscIKKKKL", "KKKKKKKLKKLKKKKKKKKKKLKKLAn-OcCKUe sQ)(Tt iYKKKK", "KKLKKKKKKKKKLKKKKKKKKKKKKK)r;)LIb n)UnK))j VIKKK", "KKKKKKKKKKKKKKKLKKLKKKKKKH)e-)HT,1)/q7=S))&6TJLK", "LKKKLKKKKKLKKKKKKKKKKKKLKH)e-)KHXb)/^)qh))f+PKKK", "KKKKKKKKKKKKKKKKKKKKKKKKKH)e-)LG Z)()k2/()b.LKKL", "KKKLKKKLKKKLKKKKKKKKLKKKKG)r-)KKok))eXNQ()i$YKKK", "LKKKKLKKLKKKKKKKKJKJKKKKKH)e-)HT4-)(861B)(@wTJKK", "KKKKKKKKKKKKKKKITTTTYKKKKH)e-)JPV f))))))eXFLKKK", "KKKKKKKLKKKKKKYSu>-5cILKKH)e-)KJUf 8K/^G3 xUKKKK", "KKKKKKKKKKKKKYf.o14= >GLKH)e-)KKKUx+ $+ %mUKKKKK", "KKKLLKKKKKKKYg eL!~YN+&KLG)e-)KKKKULK%>IIIJKKKKK", "KKPSGLKKLKKLSowEKfsKYGXwTG)e-)KKKKKK)2TJPJ;GKPC mP)e-)KKKKKJ)<9)HKKKKKKK", "KKTs9TJKKKKT-5EJIK:TLLD bP)r-)JKKKKJ),9)JKLKKKKK", "KJTfuTKKKLJT5-YKFy@pFYc.AJ)e-)KKLKKJ)<9)HKKKKKLK", "KKYvNIKKKKKYb nRB6q7AW,-UG)e-)JKKKLJ)<9)HKKKKLKK", "KKKKKKKKKKKKU<+FE!E!Ep bIL)r;)UKKKKJ)<9)JKKKKKKK", "KKKKKKKKKKKKLG%XuBAc< pYUx5o.1gIPKKJ)<9)GKKKKKKK", "KKKKKKKJKKKKKLKqo .*vYK4 ,fj4 -FLKJ)<9)JKKKKKKL", "KKKKKKJKKKKKKJKTHnbVUIU6XS))))Y%=LKK),9)HKKLKKKK", "KLJY))))))))))))))))))A Z)NqqV)RXuTH),9)HKKKKKKK", "KKYc$&&%&&&&%&&&%%%%%-o3))jki-~)t$PJ)<8)HKKKLKKL", "KKPApdsssssasssssssssf.g)((q$8))S FK)<9)HKKKKKKK", "KKKJFFFFFFFFFFFFGFFFFHoh)(()T-Q)F SK)<9)HKKKLKKK", "KKYk . .. . q))ie86()gOKK)<8)HKKKKKKK", "KKKY(((((((((((((()(^)n.P)KgxQ)^+qTG)<0)GKLKKKKL", "KKKLYUUUUUUUUUUUUUUUUUT:%W))))(4OGLU)<8)UKKKKKKK", "KKKKKJKKJKJKJJKJKKHHKHKG&XuZSg+oMTHe% .&uLPKKKKK", "KKKKKKKJJKKKKKJKKKKKKKKPKr+ o8STC+ouVBeX%GPKKKL", "KKKIUIIIPIIIIIIIIIIIPIIIIWYGFIWTK$>~))))E%;IKLKK", "KKINT)()))()())()()))()((())))()foQ)s36m)I.fTHKK", "KKTk XXXXXXXXXXXXXXXXXXXXXXX.XXo a)~cvw3))8-UKKK", "KJTs4FNVVVVVVVVVVVVVVVVVVVVVVBVN V))Q6+i))xXLLKK", "KKTd>bjkkkkkkkkkkkkkkkkkkkkkkjzg B)/~)G:))zoLKKK", "KJYko$@@++#+#+##@+#@+@++######+& t)(04:f))5:TKKK", "KKIVW)))))))))))))))))))))))))))k.R)YmG()H kYJKK", "KKKLPKLKKKLKLKKLKLLKKLLKLKLKLKKKI-%Y))))LO1UKKKK", "KKLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKLG=.8xk6 >KLKKKL", "KLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKLKLIs-Xo:hULKKKKK", "KKKKKKKLKKKKKKKKKKKKKKLKKKKKLKKKKKKTYLLTTKKKKKKL", "KKLKKKKKKKKLKKKKKLKKKKKKKKKKKKKKKKKHKKKKKKKKKKKK", "KKKKLKKKKLKKKKKKKKKKKKKKKLKKKKKKKKLKKKKKKKKKLKKK", "KKKKKKKKKKKKKKLKKKKKLKKKKKKKKKKLKKKKKKKKKKKKKKKK" }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/cube-icon.c0000644000175300017530000003431312161170364015555 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 164 2", " c #7E7DBE", ". c #0E0EDE", "X c #2F2FD2", "o c #2222D8", "O c #3534DC", "+ c #3E3DDB", "@ c #0C0CE1", "# c #0D0DE5", "$ c #0000E9", "% c #0000EC", "& c #0000EE", "* c #0000EF", "= c #0909EA", "- c #100FEF", "; c #1717E7", ": c #1919E4", "> c #0000F3", ", c #0303F3", "< c #0101F4", "1 c #0000F5", "2 c #0000F7", "3 c #0303F6", "4 c #0505F5", "5 c #0C0CF1", "6 c #0F0FF0", "7 c #0C0CF2", "8 c #0D0CF2", "9 c #0808F5", "0 c #0909F5", "q c #0101FE", "w c #0000FF", "e c #0101FF", "r c #0202FE", "t c #0404FF", "y c #0707FF", "u c #3838E4", "i c #5A59CA", "p c #5B5BD0", "a c #5C5BD1", "s c #5F5FDB", "d c #5E5DDF", "f c #605FDA", "g c #6B6ACD", "h c #6B6ACE", "j c #6D6CCF", "k c #6766D5", "l c #6362DB", "z c #6766D9", "x c #6363DC", "c c #6968D8", "v c #6A69DA", "b c #7473D6", "n c #7372D9", "m c #7271DA", "M c #BAB8B4", "N c #BDBBB7", "B c #BEBCBE", "V c #BFBDBF", "C c #C2C0BC", "Z c #C3C2BD", "A c #C4C2BD", "S c #C4C3BE", "D c #908FC0", "F c #9C9AC1", "G c #8584D1", "H c #8F8DD1", "J c #B6B4C7", "K c #C5C3C5", "L c #C8C6C2", "P c #C9C7C3", "I c #C9C7C4", "U c #CAC8C3", "Y c #CAC8C4", "T c #CBC9C4", "R c #CBCAC5", "E c #CCCAC5", "W c #CDCBC6", "Q c #CECCC7", "! c #C3C1CD", "~ c #C3C2CD", "^ c #C4C2CC", "/ c #C5C3CC", "( c #C4C2CD", ") c #C5C3CD", "_ c #CBC9C8", "` c #CAC8CB", "' c #CCCAC8", "] c #CECCC8", "[ c #CFCDC8", "{ c #CFCEC9", "} c #CCCBCD", "| c #CCCACE", " . c #CFCDCF", ".. c #D0CEC7", "X. c #D0CECA", "o. c #D0CFCA", "O. c #D1CFCA", "+. c #D1CFCB", "@. c #D1CFCC", "#. c #D0CECD", "$. c #D1CFCE", "%. c #D2D0C4", "&. c #D4D2C4", "*. c #D4D2C5", "=. c #DEDCC3", "-. c #D1D0CB", ";. c #D2D0CA", ":. c #D2D0CB", ">. c #D7D5C9", ",. c #D2D0CC", "<. c #D2D1CC", "1. c #D3D1CC", "2. c #D2D0CD", "3. c #D3D2CD", "4. c #D1D0CF", "5. c #D2D0CF", "6. c #D2D1CF", "7. c #D4D2CD", "8. c #D4D2CE", "9. c #D5D3CE", "0. c #D6D4CE", "q. c #D7D5CF", "w. c #DDDBCB", "e. c #DBD9CD", "r. c #D7D5D0", "t. c #D9D7D3", "y. c #DAD8D3", "u. c #DBD9D4", "i. c #DAD8D5", "p. c #DAD8D7", "a. c #DCDAD6", "s. c #DDDBD6", "d. c #DEDCD6", "f. c #DFDDD6", "g. c #DEDCD7", "h. c #DFDDD7", "j. c #D9D7DA", "k. c #D9D7DB", "l. c #DAD8D8", "z. c #DBD9D8", "x. c #DDDBDA", "c. c #DFDDD8", "v. c #E0DEC7", "b. c #E0DEC8", "n. c #E1DFC9", "m. c #E0DED9", "M. c #E1DFDA", "N. c #E3E1C6", "B. c #E2E0C9", "V. c #E2E0CA", "C. c #FCFACE", "Z. c #E9E7D7", "A. c #EAE8D0", "S. c #EBE8D1", "D. c #ECEAD2", "F. c #EDEBD2", "G. c #EDEBD3", "H. c #EFECD1", "J. c #EAE8D4", "K. c #EAE8D5", "L. c #EEECD4", "P. c #E2E0D9", "I. c #E7E5D9", "U. c #F2F0D3", /* pixels */ "t.) ~ ) / / ) / { ;.+.{ +.+.+.u.", ") ; 9 5 @ 5 9 : K 0.+.Z 4.0.U ;.", ") 5 w w % w w - .P.s.{ h.m.0.;.", ") 5 w w * r w - } h.y.R u.s.+.4.", "` j v b X * * . B ..I N T W A ;.", ";.n.J.C.z w w - | s.s.{ m.m.0.;.", "+.4.j.J.s w w 5 4.I.;.Z +...W 0.", "+.P #.v.p 1 1 # J ) V 0.0.P M 4.", "+.R r.V.a 1 , % = 1 G Z.j.#.I 0.", "+.0.l.S.l w y , 4 w H C.S.N.%.0.", "+.+.p.A.f w t > 1 w o + u O F e.", "+.Z W %.D v n h h n k i x d >.", "+.4.h.t.&.S.L.n.V.L.S.=.S.U.w.4.", "+.0.m.h.W p.x.#.4.x.l.R x.x.4.;.", ";.I ;.+.A +.;.P U 0.+.Z ;.;.P ;.", "p.;.0.;.4.;.0.;.4.;.4.;.0.0.;.s." }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 66 1", " c #1212BA", ". c #565592", "X c #0C0CCC", "o c #1010CF", "O c #0101D4", "+ c #0D0DD0", "@ c #0101DB", "# c #1010D0", "$ c #1515DB", "% c #2020CD", "& c #2F2FC8", "* c #2F2FD4", "= c #3232D6", "- c #0000E5", "; c #0E0EE2", ": c #0000EB", "> c #0D0DE8", ", c #1212E3", "< c #0000F1", "1 c #0000FE", "2 c #5958C5", "3 c #A4A39F", "4 c #8382A7", "5 c #918FBF", "6 c #9593B1", "7 c #A7A6AE", "8 c #B0AFAB", "9 c #B3B1AD", "0 c #ABAAB3", "q c #B6B4B1", "w c #B8B7B2", "e c #BCBAB5", "r c #BFBDB9", "t c #C1BFB7", "y c #C1BFBA", "u c #C4C2BE", "i c #C9C7BA", "p c #CAC8B8", "a c #918FC3", "s c #9291C3", "d c #B6B4C1", "f c #B9B7C2", "g c #BDBBC3", "h c #C7C5C1", "j c #C8C7C2", "k c #CCCBC6", "l c #CECDC8", "z c #D0CEC5", "x c #D0CFCA", "c c #D7D5C7", "v c #D3D1CA", "b c #DFDDCA", "n c #D7D5D0", "m c #D9D7D3", "M c #DBD9D4", "N c #DFDDD8", "B c #E0DECC", "V c #E0DED9", "C c #E2E0CC", "Z c #F1EFCE", "A c #E2E0D7", "S c #E9E7D6", "D c #EAE8D7", "F c #E4E2DB", "G c #E9E7DD", "H c #EFECDA", /* pixels */ "MMFFAAAFAFAFAFFFFFNFNVFNNFNFNNMM", "NmhgggggggggfhgguihhhhhhhhhhjjmN", "Ag@:::::O@:::::+eljkklqeljjklqlN", "Ag:11111-:11111,lFNNNFjlFNNNFhlN", "Fg:11111-:11111;kNmMmFukVMmmNylN", "Fh:11111-:11111,lNmNmVukAmNNFhlN", "Ag-11111-:11111;kNmmmVukNmmmNulN", "Fg:11111-:11111;vFNVFFjvFFNFFhlN", "Ag+>;;;>XO:---:Xwjuhuk9qjuuuk9vN", "Fuelkjjl0@<::::+rkkkklqrlkkjl9kF", "FjlFFNAGg:11111,xFVVNFhxFNNNFhkN", "NhjNmmmAf:11111;kNmmmNukNmmmVulN", "FukNmmmFf:11111;kNmMMFklFNNVFulN", "FhkNMmmFf:11111;kNmNkr89uruqmhkF", "NhlFFNNFg:11111;cFAGujmvvxm99ylN", "Fuqhhuhk7O:---:X0gdf7mNNNMNuv3lN", "Vhelkkkv0@<::: c #0000C6", ", c #0808C1", "< c #0000CD", "1 c #0F0FCD", "2 c #0505D1", "3 c #0000D8", "4 c #0505D9", "5 c #0404DA", "6 c #0505DA", "7 c #0505DB", "8 c #0000DC", "9 c #0101DC", "0 c #0000DD", "q c #0101DD", "w c #0202DD", "e c #0303DD", "r c #0000DE", "t c #0000DF", "y c #0101DF", "u c #0202DE", "i c #0505DC", "p c #0808D8", "a c #0909D8", "s c #0808D9", "d c #0808DA", "f c #0808DF", "g c #1414D0", "h c #1414D1", "j c #1414D2", "k c #1515D3", "l c #0000E0", "z c #0000E1", "x c #0000E3", "c c #0505E1", "v c #0606E0", "b c #0707E0", "n c #0505E2", "m c #0404E3", "M c #0000E4", "N c #0000E5", "B c #0202E5", "V c #0000E7", "C c #0808E0", "Z c #0808E1", "A c #0000E8", "S c #0000E9", "D c #0000EA", "F c #0202EA", "G c #0000EF", "H c #0101F4", "J c #0000F5", "K c #0000F8", "L c #0101F8", "P c #0000FC", "I c #0101FC", "U c #0000FD", "Y c #0101FD", "T c #0202FC", "R c #0000FE", "E c #0101FE", "W c #0000FF", "Q c #0101FF", "! c #0202FF", "~ c #8C8A88", "^ c #898896", "/ c #959490", "( c #A2A19D", ") c #A2A09E", "_ c #A3A19E", "` c #A5A39F", "' c #9D9CA0", "] c #A19FA0", "[ c #A09FA4", "{ c #A5A3A0", "} c #A4A3A1", "| c #A5A3A1", " . c #A4A2A3", ".. c #A4A3A3", "X. c #A5A4A0", "o. c #A6A5A1", "O. c #A7A6A2", "+. c #A7A6A4", "@. c #A8A6A2", "#. c #A8A7A2", "$. c #A8A7A3", "%. c #ABA9A3", "&. c #AAA8A4", "*. c #ABA9A5", "=. c #AFADA9", "-. c #B5B3AF", ";. c #A3A2B5", ":. c #A4A2B4", ">. c #A4A3B6", ",. c #A6A5BE", "<. c #A9A7BC", "1. c #B5B3B0", "2. c #B5B4B0", "3. c #B6B4B0", "4. c #B6B5B0", "5. c #B7B5B1", "6. c #B9B7B3", "7. c #B9B8B3", "8. c #BAB8B4", "9. c #BAB9B5", "0. c #BBB9B5", "q. c #BCBAB6", "w. c #BCBBB6", "e. c #BDBBB7", "r. c #BEBCB7", "t. c #B9B7B8", "y. c #B8B7B9", "u. c #BAB8B9", "i. c #BDBBB8", "p. c #BDBBB9", "a. c #BDBBBA", "s. c #BDBCB8", "d. c #BEBCB8", "f. c #BEBDB8", "g. c #BFBDB8", "h. c #BFBDB9", "j. c #BEBCBA", "k. c #BFBDBC", "l. c #BFBDBE", "z. c #BFBDBF", "x. c #C0BFB6", "c. c #C1BFB9", "v. c #C0BEBA", "b. c #C0BFBA", "n. c #C1BFBA", "m. c #C0BFBB", "M. c #C1BFBB", "N. c #C2C0AF", "B. c #C2C0BB", "V. c #C4C2BB", "C. c #C2C0BC", "Z. c #C2C1BC", "A. c #C3C1BC", "S. c #C3C1BD", "D. c #C3C2BD", "F. c #C3C1BF", "G. c #C4C2BC", "H. c #C4C2BD", "J. c #C4C2BE", "K. c #C4C3BE", "L. c #C5C3BE", "P. c #C4C2BF", "I. c #C6C4BC", "U. c #C6C4BD", "Y. c #C7C5BE", "T. c #C6C4BF", "R. c #CCCABD", "E. c #CECCBE", "W. c #C3C1C0", "Q. c #C4C2C0", "!. c #C6C4C0", "~. c #C7C5C1", "^. c #C7C6C1", "/. c #C8C6C1", "(. c #C9C7C1", "). c #C8C6C2", "_. c #C8C7C2", "`. c #C9C7C3", "'. c #C8C6C4", "]. c #C8C7C5", "[. c #C9C8C3", "{. c #CAC8C3", "}. c #CAC8C4", "|. c #CCCAC5", " X c #CECCC7", ".X c #CECDC8", "XX c #CFCDC8", "oX c #D0CEC0", "OX c #D1CFCA", "+X c #D3D1C1", "@X c #D2D1C2", "#X c #DEDCC7", "$X c #D5D3CE", "%X c #D8D6D2", "&X c #D9D7D2", "*X c #D9D7D3", "=X c #D9D7D4", "-X c #DAD8D3", ";X c #DAD8D4", ":X c #DBD9D4", ">X c #DBD9D5", ",X c #DCDAD5", " = V y y z y y y V = +./.m.m.S.M.M.m.!.O.*./.m.S.S.B.S.j.!.+.S.fX:X:X", ",X:XrXH.] z.t.u.t.u.u.t.z. .o V 8 y y 8 y 8 z * .H.i.m.j.m.j.r.H.O.+.S.r.r.j.j.j.j.S.O.S.rX:X,X", ",X:XfXS.H.BXrXaXaXaXaXrXBX/.7 W W W W W W W W y Q.BXqXrXrXrXrXqXCXH.!.cXrXrXrXfXrXrXxXS.S.fX:X,X", ",X:XqXH.e.wX-X-X:X=X:X%XqXm.7 W T W W T W T W 8 j.rX%X:X:X:X:X-XrXr.B.3X:X:X:X:X:X:XrXu.K.qX:X,X", ",X:XqXH.e.wX:X,X,X,X,X:XwXV.i W T W W W W W W 8 j.aX:X,X,X,X,X:XrXj.S.rX:X:X:X:X:X:XrXr.S.rX:X,X", ",X:XqXV.e.wX:X,X,X,X,X:XwXn.i W W W W W W T W y j.rX:X,X,X,X:X%X3Xj.b.rX:X:X:X:X-X-XfXj.S.fX:X,X", ",X:XqXV.v.wX:X,X,X,X,X,XqXV.i W W W W W W W W y j.aX:X,X,X,X3XrXBXU.!.BXrXfXfXfXfX:XrXr.K.rX:X,X", ",X:XrXH.e.wX-X,X,X,X,X,XwXn.i W T W W W W W W y j.rX:X:X:X3XOX-.j.) ` j.3.q.7.q.3.$XjXw.K.rX:X,X", ",X:XfXH.e.qX-X:X,X-X,X%XqXm.7 W T W T W W T W i i.qX=X:X%XqX1.3.{.[.{.Y.[.!.U. X) / rXB.S.rX:X,X", ",X:XqXS.n.VXqXwXqXqXqXqXcX/.7 W W W W W W W W y Q.BXqXfXrXBXr. XjX3XrXrXqXqXqXjXj.b.=.-.!.qX:X,X", ",X:XrXH.o.H.v.m.x.v.m.m.Y.%.o V 8 y y y y 8 z = ..W.i.j.i.W.) {.3X:X:X:X-X-X:XfX-. X-X~ S.fX:X:X", ",X:XrXS.O.!.j.j.m.m.m.m.H.&.; V 8 y y y y 8 V o > V 8 y 7 z $ +X3X:X.>.;.;.;.<.^ R.mXq.S.rX:X c #545352", ", c #595857", "< c #5A5857", "1 c #5C5A59", "2 c #5C5B59", "3 c #5C5B5A", "4 c #5D5C5A", "5 c #61605E", "6 c #62615F", "7 c #686765", "8 c #696866", "9 c #6D6B6A", "0 c #6E6C6A", "q c #706F6D", "w c #73726F", "e c #767472", "r c #797775", "t c #797875", "y c #7B7976", "u c #7B7A77", "i c #7D7B78", "p c #7D7B79", "a c #7D7B7A", "s c #7D7C79", "d c #7E7D7A", "f c #7E7D7B", "g c #7F7E7B", "h c #807F7C", "j c #817F7D", "k c #858482", "l c #8A8986", "z c #8C8A87", "x c #8D8B88", "c c #8D8B89", "v c #8F8D8A", "b c #908E8B", "n c #918F8C", "m c #92908D", "M c #92918D", "N c #94928F", "B c #969491", "V c #979592", "C c #989693", "Z c #999794", "A c #9A9895", "S c #9A9896", "D c #9B9996", "F c #9B9A97", "G c #9C9A97", "H c #9C9A98", "J c #9C9B98", "K c #9D9B98", "L c #9D9B99", "P c #9D9C98", "I c #9E9C98", "U c #9F9E9A", "Y c #A19F9C", "T c #A4A29E", "R c #A9A7A3", "E c #A9A7A4", "W c #ACAAA6", "Q c #BEBCB8", "! c #C0BEBA", "~ c #C4C2BD", "^ c #C4C3C1", "/ c #C6C5C3", "( c #CBC9C5", ") c #CDCAC5", "_ c #CFCDCB", "` c #CFCECC", "' c #D2D0CB", "] c #D3D1CC", "[ c #D3D1CD", "{ c #D1D0CF", "} c #D5D3CE", "| c #D6D4CE", " . c #D6D4CF", ".. c #D5D4D0", "X. c #D7D6D6", "o. c #D8D6D1", "O. c #D8D6D2", "+. c #D9D7D2", "@. c #DAD8D3", "#. c #DAD8D5", "$. c #DAD9D5", "%. c #DAD8D6", "&. c #DCD9D4", "*. c #DCDAD4", "=. c #DCDAD5", "-. c #DCDBD7", ";. c #DEDCD7", ":. c #D9D9D8", ">. c #DAD9D8", ",. c #DFDDD8", "<. c #DDDDDB", "1. c #E1DFDA", "2. c #E1DFDC", "3. c #E4E1DC", "4. c #E4E2DD", "5. c #E6E4DF", "6. c #E3E2E0", "7. c #E4E3E1", "8. c #E7E5E1", "9. c #E7E6E3", "0. c #E6E5E5", "q. c #E7E6E4", "w. c #E6E6E5", "e. c #E9E7E2", "r. c #EBE9E3", "t. c #EAE9E8", "y. c #EBEAE8", "u. c #EDECEA", "i. c #EEEDEA", "p. c #EDEDED", "a. c #EFEEEC", "s. c #EEEEED", "d. c #F0EFEE", "f. c #F0EFEF", "g. c #F2F1EF", "h. c #F2F2EF", "j. c #F3F2F0", "k. c #F2F2F1", "l. c #F3F3F3", "z. c #F6F5F3", "x. c #F7F7F7", "c. c #F9F8F7", "v. c #F8F8F8", "b. c #F9F8F8", "n. c #F9F9F9", "m. c #FAF9F9", "M. c #FAFAF9", "N. c #FBFBF9", "B. c #FAFAFA", "V. c #FAFAFB", "C. c #FBFBFB", "Z. c #FCFCFB", "A. c #FCFCFC", "S. c #FFFFFF", /* pixels */ "m x x x z n Z Z T U B H F A H B ", "x : * - # f 1.' R 9 _ S.v.C.f.A ", "x * - 2 X h o.q & 7 j.S.0.A.C.A ", "x - 2 e @ l U ; 2 W j.C.^ j.v.G ", "x # X # . w 6 , G @.y.S.C.S.x.F ", "n f f y r k R @.r.~ ' d.j.j.` B ", "A ,.5.i.C.-.3.! f < ~ -.W 8 9 H ", "Z [ >.X.s.| Q 6 * f r.H 2 @ E T ", "B %.s.{ %.;.h * 6 ! -.4 : 9 | A ", "B t.C.e.e.) < h Q 3.Y : m ) | V ", "v a r a u v ( -.o.} / 9.z.i.6.A ", "z $ X % u e.%.j.g.0.S.C.S.C.H ", "x - 2 e $ a 9.{ >.2.0.A.~ j.n.F ", "x * = 2 X y x.s.,.o.y.S.0.A.C.F ", "x : * - $ f y.%...[ 7.A.v.C.s.F ", "n x x x l v V V A Z Z H F F A B " }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 64 1", " c #3C3B3B", ". c #403F3E", "X c #41403F", "o c #444342", "O c #484746", "+ c #494847", "@ c #4C4B4A", "# c #514F4E", "$ c #52514F", "% c #555453", "& c #585755", "* c #595856", "= c #5D5C5A", "- c #605E5D", "; c #656462", ": c #686665", "> c #6A6967", ", c #6D6B69", "< c #72716E", "1 c #757371", "2 c #7A7876", "3 c #7D7B78", "4 c #83817E", "5 c #868481", "6 c #8B8987", "7 c #8D8B88", "8 c #908E8B", "9 c #93918E", "0 c #959390", "q c #999794", "w c #9A9896", "e c #9D9B98", "r c #A19F9B", "t c #A3A29E", "y c #A7A5A3", "u c #A9A7A4", "i c #ABA9A6", "p c #AFAEAA", "a c #B0AFAB", "s c #B2B0AD", "d c #B6B5B1", "f c #B8B6B2", "g c #BAB8B4", "h c #C1BFBB", "j c #C2C0BC", "k c #C6C4C1", "l c #CCCAC5", "z c #CECDCA", "x c #D0CECA", "c c #D4D2CD", "v c #D6D4D0", "b c #D8D7D2", "n c #DCDAD5", "m c #DEDDDB", "M c #E1DFDA", "N c #E4E2DD", "B c #E5E4E2", "V c #E9E7E4", "C c #E9E9E7", "Z c #EBEBEB", "A c #F0EFEF", "S c #F1F0EF", "D c #F3F3F3", "F c #FDFDFD", /* pixels */ "99000000990999997997988888788899", "99667776787qq9qq9qtt0eeeeeewee99", "07#@@@@@@@XpNcbnNns*+XsN2%45%,ncFFCz7SFFFe8", "07@O@=,=@@Xgj+%>>otNcFFFmzDFFFe8", "07@@O@%@O+.g9.o+% c #696765", ", c #6A6967", "< c #6C6B69", "1 c #716E6C", "2 c #71706E", "3 c #767572", "4 c #7E7C7A", "5 c #868582", "6 c #898784", "7 c #8B8986", "8 c #8D8B89", "9 c #908E8B", "0 c #93918E", "q c #959390", "w c #999793", "e c #9B9996", "r c #9F9D9A", "t c #A09E9B", "y c #A3A19E", "u c #A6A4A1", "i c #A8A6A2", "p c #ABA9A5", "a c #AEACA9", "s c #B4B2AE", "d c #B6B4B0", "f c #BCBAB6", "g c #BDBCB8", "h c #C1BFBB", "j c #C4C2BE", "k c #C5C3C1", "l c #C8C6C2", "z c #CBC9C6", "x c #CECCC9", "c c #D1CFCB", "v c #D4D2CE", "b c #D6D4D1", "n c #D9D7D2", "m c #DCDAD5", "M c #DDDCDA", "N c #E0DED9", "B c #E4E2DD", "V c #E3E3E2", "C c #E8E7E7", "Z c #E9E9E6", "A c #ECEBEB", "S c #F0EFEF", "D c #F0F0EF", "F c #F4F4F4", "G c #F8F7F7", "H c #FEFEFE", /* pixels */ "000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000q00000q0000", "0000qq0qqqq00qqq00080990999080099998999999990000", "00097777777777777weeeeeeweweye0trrtrtrrrrrrt0000", "00q7@@@@@@@@@@@@osBnnnnnnmBba<0HHHHHHHHHHGHGr9q0", "00q7@O+OO+++o++@osBmmmNBBzw:X#gHHHHHHHHHHHHHr900", "00q7@+++++oo@+++osBnnBms4$XoX5nGHHHHHHHHHHHHr900", "0007@O+++#=$+++@osBnNy>$@+@@ogvGHHHHFCGHHHHHr900", "0007@O+@o=&=o++@XsBmjXO==$oooOdBne900", "000wmxAHFmFnHVcMnjB6.@*-*-XyBmjml@@$;%-.>mmne900", "008envHHHZNZGxmMvxv&X@@;*X;cmBjB0 @o==$ouBmne900", "000exVHHHHZnzbmmvcyX#XX+1ynBnBjc-+@o.=8jNnmme900", "000ezGHFNxjnmmnmvj1.X*6hNBNnmmcyXXO c #151514", ", c #151515", "< c #161615", "1 c #181818", "2 c #191919", "3 c #1A1A1A", "4 c #1B1B1A", "5 c #1A1A1B", "6 c #1C1C1C", "7 c #1D1D1C", "8 c #1F1F1E", "9 c #20201F", "0 c #212121", "q c #222121", "w c #222221", "e c #232222", "r c #232322", "t c #252424", "y c #242425", "u c #262524", "i c #262625", "p c #282827", "a c #2D2C2B", "s c #323231", "d c #313132", "f c #333232", "g c #323233", "h c #333333", "j c #343332", "k c #343433", "l c #353533", "z c #393837", "x c #3B3A39", "c c #414142", "v c #434444", "b c #474644", "n c #484846", "m c #4B4B4C", "M c #525150", "N c #595856", "B c #5B5B5C", "V c #656462", "C c #696969", "Z c #6B6A68", "A c #6D6C6A", "S c #6C6D6D", "D c #6D6D6E", "F c #747371", "G c #767572", "H c #7E7D7A", "J c #8A8885", "K c #8A8985", "L c #8E8D8A", "P c #91908D", "I c #92908D", "U c #92918D", "Y c #9B9A96", "T c #9D9C98", "R c #9F9D99", "E c #A09F9B", "W c #A1A09C", "Q c #A2A19D", "! c #A3A29E", "~ c #A4A29E", "^ c #A4A39F", "/ c #A5A4A0", "( c #A6A4A1", ") c #A8A7A3", "_ c #A9A7A3", "` c #A9A7A4", "' c #ABAAA6", "] c #ACAAA6", "[ c #ADACA8", "{ c #AEACA8", "} c #B0AEAA", "| c #B0AFAB", " . c #B6B4B0", ".. c #BAB9B4", "X. c #BBBAB5", "o. c #BCBAB6", "O. c #BCBBB6", "+. c #BEBCB8", "@. c #C1BFBB", "#. c #C2C0BC", "$. c #C3C1BC", "%. c #C3C1BD", "&. c #C4C2BE", "*. c #C4C3BE", "=. c #C5C3BF", "-. c #C7C5C1", ";. c #C9C7C2", ":. c #C9C7C3", ">. c #CAC8C4", ",. c #CBC9C4", "<. c #CBC9C5", "1. c #CCCAC5", "2. c #CCCAC6", "3. c #CDCBC6", "4. c #CDCBC7", "5. c #CECCC8", "6. c #CFCCC8", "7. c #CFCDC8", "8. c #D0CEC9", "9. c #D0CECA", "0. c #D1CFCA", "q. c #D2D0CB", "w. c #D2D0CC", "e. c #D3D1CC", "r. c #D3D2CD", "t. c #D4D2CD", "y. c #D4D2CE", "u. c #D5D3CE", "i. c #D5D4CF", "p. c #D6D4D0", "a. c #D7D5D0", "s. c #D8D6D1", "d. c #D9D7D2", "f. c #D9D7D3", "g. c #DAD8D3", "h. c #DAD8D4", "j. c #DBD9D4", "k. c #DCDAD5", "l. c #DDDBD6", "z. c #DEDCD6", "x. c #DEDCD7", "c. c #DFDDD8", "v. c #E0DDD8", "b. c #E0DED9", "n. c #E1DFDA", "m. c #E2E0DB", "M. c #E3E1DC", "N. c #E4E2DD", "B. c #E5E3DD", "V. c #E5E3DE", "C. c #E6E4DE", "Z. c #E6E4DF", "A. c #E7E4DF", "S. c #E7E5E0", "D. c #E8E6E0", "F. c #E8E6E1", "G. c #E9E7E2", "H. c #EBE9E4", /* pixels */ "l.x.a.g.g.l.a.x.b.s.g.j.g.a.l.x.", "B.N 8 i t 0 6 M Z 4 p 0 i t z t.", "Z.a d 0 * Z d x m c o v : 1.", "Z.l * 1 @ 1 k n $ 1 @ j r 1.", "c.+./ _ ` _ ~ X.&.~ ' ~ V b G l.", "j.Z.$.w.H.M.X.m.S.&.<.1.@ k , 1.", "j.b.I } B.;.F w.Z.W ~ @.& D 8 ;.", "l.j.l.j.j.c.c.j.l.s.B.O.. * <.", "j.l.j.l.l.j.s.l.j.s.M.O.o * 1.", "j.c.I | B.9.H t.S.W W $., D 1 <.", "l.F.@.w.B.t.{ f.m.O.-.$.o t 1 1.", "a.{ ` ` 7.7.s.9.7.w.t.7.Y J E l.", "4.$ , @ I F. .j.x.-.-.b.B.w.G.l.", "7.O B h J l.A t.S.T ] B.a.L t.l.", "4.d r 0 ( B.a.s.x.9.a.l.l.5.j.l.", "x.j.s.g.c.l.l.l.g.b.x.l.j.c.l.l." }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 61 1", " c #010101", ". c #0C0C0C", "X c #121212", "o c #1C1C1C", "O c #2D2D2C", "+ c #31302F", "@ c #333231", "# c #3C3B3A", "$ c #41413F", "% c #454443", "& c #4A4947", "* c #4C4C4B", "= c #51514F", "- c #565554", "; c #595856", ": c #5D5C5B", "> c #62615F", ", c #656463", "< c #696866", "1 c #6D6C6B", "2 c #72716F", "3 c #747372", "4 c #787774", "5 c #7A7976", "6 c #7C7C7A", "7 c #807F7C", "8 c #82817E", "9 c #858482", "0 c #888784", "q c #8A8986", "w c #8D8D8D", "e c #908E8B", "r c #949392", "t c #9B9996", "y c #9C9C9A", "u c #A3A29E", "i c #A4A3A0", "p c #A9A7A4", "a c #AAA8A4", "s c #ABABAA", "d c #B7B5B1", "f c #B8B6B2", "g c #BDBCB7", "h c #BFBEB9", "j c #C1BFBB", "k c #C3C1BD", "l c #C7C5C0", "z c #C9C7C2", "x c #CDCBC6", "c c #CECCC8", "v c #D1CFCA", "b c #D4D2CD", "n c #D7D5D0", "m c #D9D7D2", "M c #DCDAD5", "N c #DFDDD8", "B c #E0DED9", "V c #E4E2DD", "C c #E7E5E0", "Z c #E9E7E1", "A c #ECEAE4", /* pixels */ "MMMmMmMmmmMmMmMMMMMmMMmmMmmMmMMM", "MMVVVVVVVVVVVVVVNVVVVVVVVVVVVVMM", "MVu++O+@@@@@@@O0x#+++@@@@@O@O,NM", "nA> . . #t . .cB", "mA> Xro . 37 &t Xy: 3- XxN", "mA> ,O +s9 $y +r4. @; XcB", "nA, Xw- X=2 $t .:: .. <0 .xB", "mA< $u XvN", "MNl656555555764dM956665600qq9sBM", "MMBAAAAAAAAAAAAVMAAAAAAV1-;;-8NM", "MMMbMvMmmmmmMmmMMMmvmnVi .cN", "MMMVk,tVMMmA86VmMNv,8VmMBv14MVa Xi; .xB", "mBBVru8BMMBBe;VmMMBy=bAa X63..cB", "mBmVj>tVmMBk68BmMBc87mZa .:9..cN", "BmmnBvMmmBBBcVVBMBBvMBVp .cB", "mBVAAAZAVbxxcxxbmxxvxxvb:%%*%3NM", "Bn775664jmkkjkjxbjkjkkkBAAZZAZMM", "Bx. =ABVVAVMMBVVVBBMmMBBmmMM", "BxX ,- =VbVt0VmMNc6tBMMMV9iVMMM", "Bx. Oa, *ABk=4VMMMBa6VMMmVptAMMM", "BxX @ww *VBg1:MMMBv#tVMMMV9,NMMM", "Bc. . &AmBVMNMMMmkxMMMMMkkmNMM", "Bv#O@@@OrVmMMNMMMMMVBMMMMMVVMMMM", "MMVVVVVVVMMMMMMMMMMMMMMMMMMMMNMM", "MMMmmMmmMMMMMMMNMMMMMNMMMMMMMMMM" }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 74 1", " c #010101", ". c #080807", "X c #0A0A0A", "o c #10100F", "O c #141313", "+ c #1B1B1B", "@ c #201F1F", "# c #20201F", "$ c #232322", "% c #2B2B2B", "& c #31312F", "* c #343433", "= c #383837", "- c #3C3B3B", "; c #41403F", ": c #444442", "> c #484746", ", c #494847", "< c #4C4C4A", "1 c #504F4D", "2 c #51504E", "3 c #555453", "4 c #5A5957", "5 c #5C5C5B", "6 c #605F5D", "7 c #646361", "8 c #686765", "9 c #6A6966", "0 c #6C6C6A", "q c #706F6C", "w c #747472", "e c #787774", "r c #7B7A77", "t c #7B7A79", "y c #81807D", "u c #838383", "i c #888784", "p c #8A8986", "a c #8D8C8A", "s c #908F8B", "d c #93928E", "f c #939392", "g c #999794", "h c #9C9A97", "j c #9C9B9A", "k c #A3A19E", "l c #A4A3A2", "z c #A8A7A3", "x c #ABAAA6", "c c #ACACAA", "v c #B0AFAB", "b c #B4B3AE", "n c #B4B4B2", "m c #B9B7B3", "M c #BCBAB6", "N c #BEBCB9", "B c #C1BFBA", "V c #C4C2BE", "C c #C6C5C2", "Z c #C8C6C2", "A c #CAC8C4", "S c #CDCCC9", "D c #D1CFCA", "F c #D4D2CD", "G c #D7D5D0", "H c #D9D7D2", "J c #DCDAD5", "K c #DFDDD8", "L c #E1DFDA", "P c #E3E1DC", "I c #E7E5E1", "U c #E8E6E1", "Y c #ECEAE4", "T c #F6F4EF", /* pixels */ "JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJKJ", "JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ", "JJJJHJJJJJJJJJJJJJJJJJJHJJJJJJJJJJJJJJJJJJJJJJJJ", "JJJJPPJKKJJJJKJJJKJKKJPLJPJKJJKJJJJJKJKJJKKPJJJJ", "JJHPm&+$$$$$$$$$$$$$$@*MK3+$$$$$$$$$$$$$$$@pPJJJ", "JJHY8 . . wB $KJJJ", "JJHY7 . <* . 3$ wN O4$ X @3. $JJJJ", "JJHY6 .ji >I3 wN jl: ;C$ $JJJJ", "JJHY7 . *u +j05 wM +Nee p% $KJJJ", "JJHY6 7cO *nMN wM OfpeX $JJJJ", "JKHY7 . wM $KJJJ", "JJHPzO.XoXXXX.XXX.XXX.OcH=.XXoXX..X.....XX 0PJJJ", "JJJJPFZAAAAAAAAAAAAAAAGLKJCAAACAZAAFGHHGGGJYJJJJ", "JJJJJJLLKKKKLKLKLLLKKLKJJJKLKKKLLLId:>>,,>>lPJJJ", "JJJJJJJHLIHJJJJJKHJLKHHJJJJJLIKJHJSO %KJJJ", "JJJJJJJPVmPLJJJJJHPmSJJJJJJJMzDLJLAX =$ $JJJJ", "JJJJJHPm12kPJJJJHInosYHJJJLD813LHLA. 8c5 $JJJJ", "JJJJJHTrkV9YHJJJJD7rdYHJJJHPB<3PGLAX Nfw $KJJJ", "JJJJJHPdryrYHJJHUz*>:JJJJJLFbx>LJLA. j*c+ $JJJJ", "JJLJJJJJe7FLJJJJJHSssPJJJJLF04kPHLAX &k3 $JJJJ", "JJJJJJJJPYPHJJJJJJPPPJJJJJHLPPPLJLAX $KJJJ", "JJJJJJJJHHHJJJJJJJJJJJJJJJJJHGJLHLAX $JJJJ", "JJJJJJJJJJJJJJJJJJJJJJJJLJJJJLJHJLA. $KJJJ", "JJJJJJJJJJJJJJJJJHJLJJJJHJJJJHLHJLA. $JJJJ", "JJJJJJJJJPHJJJJJJLLPHJJJJJJHLLJJJLA. #JLJJ", "JJJJJJJPFDPHJJJJJLVVPJLJJJJLDVLJJLAX o$O $JJJJ", "JJJJJHPV2 c #B1AFAB", ", c #B3B2AD", "< c #B7B5B1", "1 c #B9B7B3", "2 c #BAB8B4", "3 c #BCBAB5", "4 c #BEBDB8", "5 c #C0BEBA", "6 c #C1BFBA", "7 c #C2C0BB", "8 c #C6C4BF", "9 c #C8C6C2", "0 c #C8C7C2", "q c #C9C7C2", "w c #C9C7C3", "e c #C9C8C3", "r c #CAC8C3", "t c #CAC9C4", "y c #CBCAC5", "u c #CCCAC5", "i c #CCCAC6", "p c #CCCBC6", "a c #CDCBC6", "s c #CDCBC7", "d c #CECCC7", "f c #CECCC8", "g c #CFCDC8", "h c #CFCDC9", "j c #CFCEC9", "k c #D0CEC9", "l c #D0CECA", "z c #D0CFCA", "x c #D1CFCA", "c c #D1CFCB", "v c #D1D0CB", "b c #D2D0CB", "n c #D3D1CC", "m c #D3D1CD", "M c #D4D2CD", "N c #D5D3CE", "B c #D5D3CF", "V c #D5D4CF", "C c #D6D4CF", "Z c #D6D5D0", "A c #D7D5D0", "S c #D8D6D1", "D c #D9D7D1", "F c #D9D7D2", "G c #DAD8D3", "H c #DAD9D3", "J c #DBD9D3", "K c #DAD9D4", "L c #DBD9D4", "P c #DCDAD4", "I c #DCDAD5", "U c #DDDBD6", "Y c #DEDCD6", "T c #DEDCD7", "R c #DFDCD7", "E c #DFDDD8", "W c #E0DED8", "Q c #E0DED9", "! c #E1DED9", "~ c #E1DFDA", "^ c #E4E2DD", /* pixels */ "NNNNNNNNSISISISC", "kpCNNNNC^TIWIQSk", "gyANNNNSWkHNSNyd", "kpANNNNSTA:p=Npk", "kyNNbnbCIL$+ wkg", "gkTIIIIQQS:<2kpk", "kHTLLSISINILISyk", "kSNCNISgZkkkkkpk", "kCI;1O8NGGSSSLck", "bNQ&%.,SZNSSLNpk", "kAS515NkSS:*+Gpf", "bASSIISkNI#o.ppk", "bgy9wwwwJS256Apk", "g0nkkNkSQcJGSSwk", "knQEQIWQWkcckNdc", "CSLLSLSISSSGSSGA" }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 166 2", " c #31302F", ". c #323130", "X c #393837", "o c #3A3938", "O c #3D3C3B", "+ c #3E3D3C", "@ c #41403F", "# c #4A4947", "$ c #4C4B49", "% c #51504F", "& c #555452", "* c #565654", "= c #585856", "- c #5A5957", "; c #5D5C5A", ": c #61615E", "> c #676664", ", c #686764", "< c #6C6B69", "1 c #6E6D6A", "2 c #6E6D6B", "3 c #747370", "4 c #757471", "5 c #777673", "6 c #7B7A77", "7 c #83817E", "8 c #868581", "9 c #888784", "0 c #898784", "q c #8C8B88", "w c #8E8D8A", "e c #8F8D8A", "r c #92908D", "t c #94938F", "y c #979592", "u c #9F9D99", "i c #9F9D9A", "p c #9F9E9A", "a c #A3A19E", "s c #A3A29E", "d c #A4A39F", "f c #A7A5A1", "g c #A8A6A2", "h c #A8A6A3", "j c #A9A7A3", "k c #A9A8A4", "l c #AAA8A4", "z c #ACAAA6", "x c #AEADA8", "c c #B1AFAB", "v c #B1B0AC", "b c #B3B1AD", "n c #B3B2AD", "m c #B5B3AF", "M c #B6B4B0", "N c #B6B5B0", "B c #B7B5B1", "V c #BAB8B4", "C c #BAB9B4", "Z c #BBB9B4", "A c #BBBAB5", "S c #BCBAB6", "D c #BDBBB7", "F c #BDBCB7", "G c #BEBCB7", "H c #BEBCB8", "J c #BEBDB8", "K c #BFBDB9", "L c #BFBEB9", "P c #C0BEBA", "I c #C0BFBA", "U c #C1BFBB", "Y c #C2C0BC", "T c #C2C1BC", "R c #C3C1BC", "E c #C4C2BD", "W c #C4C3BE", "Q c #C5C3BF", "! c #C5C4BF", "~ c #C6C4BF", "^ c #C6C5C0", "/ c #C7C5C0", "( c #C7C6C1", ") c #C8C6C1", "_ c #C8C6C2", "` c #C8C7C2", "' c #CAC8C4", "] c #CAC9C4", "[ c #CBC9C4", "{ c #CBC9C5", "} c #CCCAC5", "| c #CCCAC6", " . c #CCCBC6", ".. c #CECCC7", "X. c #CECCC8", "o. c #CECDC8", "O. c #CFCDC8", "+. c #CFCDC9", "@. c #CFCEC9", "#. c #D0CEC9", "$. c #D0CFCA", "%. c #D1CFCA", "&. c #D1CFCB", "*. c #D1D0CB", "=. c #D2D0CB", "-. c #D1D1CC", ";. c #D2D1CC", ":. c #D3D1CC", ">. c #D3D1CD", ",. c #D3D2CD", "<. c #D4D1CD", "1. c #D4D2CD", "2. c #D4D3CE", "3. c #D5D3CE", "4. c #D6D4CF", "5. c #D7D4CF", "6. c #D7D5CF", "7. c #D7D5D0", "8. c #D7D6D1", "9. c #D8D5D0", "0. c #D8D6D0", "q. c #D8D6D1", "w. c #D9D7D1", "e. c #D8D7D2", "r. c #D9D7D2", "t. c #DAD7D2", "y. c #DAD8D2", "u. c #DAD8D3", "i. c #DBD8D3", "p. c #DBD9D3", "a. c #DBD9D4", "s. c #DCDAD5", "d. c #DDDBD5", "f. c #DDDBD6", "g. c #DDDCD6", "h. c #DEDCD6", "j. c #DEDCD7", "k. c #DFDDD7", "l. c #DFDDD8", "z. c #DFDED8", "x. c #E0DED8", "c. c #E0DED9", "v. c #E1DFDA", "b. c #E2E0DB", "n. c #E3E1DB", "m. c #E3E1DC", "M. c #E4E2DC", "N. c #E4E2DD", "B. c #E5E3DD", "V. c #E6E4DE", "C. c #E6E4DF", "Z. c #E7E5DF", "A. c #E9E6E1", "S. c #E9E7E1", "D. c #E9E7E2", "F. c #EAE8E2", "G. c #EAE8E3", "H. c #EBE9E4", "J. c #ECEAE4", "K. c #ECEAE5", "L. c #EDEBE5", "P. c #EDEBE6", "I. c #EEECE6", "U. c #EEECE7", "Y. c #EFEDE8", "T. c #F2EFEA", /* pixels */ "3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.,.&.3.,.&.&.3.&.&.3.&.3.3.3.", "3.3.3.4.3.3.3.3.3.3.3.3.3.3.3.3.0.i.0.f.0.f.f.a.a.f.a.a.f.i.3.3.", "4.#.I 4.,.3.3.3.3.3.3.3.3.3.3.#.L.L.G.H.A.G.G.A.G.G.G.L.L.f.&.7.", "4.#.K 9.3.3.3.3.3.3.3.3.3.3.3.3.L.3.#.3.3.&.,.3.&.3.&.&.3.S X.7.", "4.#.I 4.,.3.3.3.3.3.3.3.3.3.3.,.L.0.3.7.3.3.3.3.3.3.3.3.0.I X.9.", "9...K 9.3.3.3.3.3.3.3.3.3.3.3.3.L.9.3.3.3.3.3.3.3.3.3.3.0.K X.7.", "4.#.K 4.=.3.3.3.3.3.3.3.3.3.3.,.H.0.3.3.7.3.3.3.0.4.4.,.4.K #.3.", "9...I 4.3.3.3.3.3.3.3.3.3.3.3.3.L.3.&.f.f @ R A.2 % x.=.9.K #.9.", "4.#.L 4.,.4.3.3.3.3.3.3.3.3.3.,.L.0.3.&.f., ( x ; 2 f.=.0.K ..9.", "9...L y.,.4.4.4.4.4.4.4.3.7.7.3.H.3.3.3.3.& c < # B 0.4.I #.4.", "4.@.F 4.=.=.=.=.&.#.=.=.&.&.&.#.G.0.3.f.h & 6 #.m 7 ..3.4.Y ..9.", "4...W B.b.M.M.M.M.M.M.M.M.M.N.M.U.7.3.3.0.N.l.8.f.v.8.3.0.F #.4.", "8.} -.P.b.b.b.M.M.M.N.N.b.N.N.a.v.0.3.3.3.#.,.3.3.=.4.,.4.L ..9.", "8.} =.j.@.=.=.=.&.&.&.#.&.#.3.U f.f.3.7.3.8.7.3.4.4.4.4.0.Y #.4.", "4.} =.b.=.4.4.=.3.,.,.3.3.3.0.( f.7.X.@.@.#.#.#...#.@.@.=.A ..9.", "0.} =.b.=.4.4.j.8.7.l.f.3.3.0.( 8.#.{ } } X.} } } } } } ..` =.4.", "0.} ,.v.#.7.&.c } &.z B 3.3.0./ l.A.M.M.N.N.N.N.N.N.N.N.A.,.#.4.", "8.} #.v.#.0.} + y U * . j f.7.R f.7.&.&.=.#.=.=.=.=.#.#.3.Z ..8.", "7.} #.v.3.#.A.u y T.j # a j.7./ f.f.3.7.3.3.4.3.3.,.3.3.0.I #.4.", "7.} ,.v.#.8.3.= : c q - i v.0.R f.0.,.3.0.f.3.y.v.f.3.3.8.I ..9.", "7.} &.v.#.0.{ e r K e a 0.3.0./ f.0.,.8.{ c 4.R d v 3.3.3.I } 0.", "7.} &.v.,.3.8.v.b.y.M.l.=.3.0./ j.a.&.f.j .., O y f.,.0.I #.9.", "7.} ,.v.&.3.3.#.#.3.#.,.3.3.0.! j.y.,.@.L.5 } e 4 3 ..4.4.K } 0.", "7.} ,.v.3.0.0.8.0.3.8.8.8.8.a.) j.y.,.0.S o q h 0 $ ( 0.0.K #.9.", "7.} ,.a.( } } { } { { } } { } K f.a.3.0.B 0 i V 8 Z i.3.3.K ..9.", "8.X.R { I U R U R U U R R I R I N.0.3.,.0.N.v.a.N.a.=.3.0.I #.4.", "3.@.H 0.0.0.8.0.0.0.0.8.8.8.8.8.L.0.3.3.3.,.&.3.=.3.3.3.3.I ..9.", "4.#.I 0.3.3.3.3.3.3.3.3.3.3.3.3.L.a.7.0.0.8.8.8.0.0.8.8.y.I #.4.", "3.#.S 3.&.,.&.3.&.3.&.3.&.3.3.&.A.{ ) ) ) { ( { ( { ) ) } B X.9.", "0.@.} L.G.H.G.A.G.G.H.G.A.G.A.L.L.8.8.0.0.0.0.0.8.0.0.0.0.0.3.3.", "3.3.8.a.0.f.a.a.f.a.a.a.f.a.f.0.a.l.j.j.f.f.f.f.j.j.f.f.j.f.7.3.", "3.3.3.3.3.&.,.,.&.3.&.3.&.,.&.,.3.&.3.&.3.&.3.3.&.,.3.&.3.&.3.3." }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 233 2", " c #070606", ". c #0F0F0E", "X c #161615", "o c #181817", "O c #191918", "+ c #1A1919", "@ c #21201F", "# c #232322", "$ c #242423", "% c #262625", "& c #282727", "* c #2A2A29", "= c #2C2B2A", "- c #2D2D2C", "; c #2E2E2D", ": c #30302F", "> c #353534", ", c #363534", "< c #363635", "1 c #373735", "2 c #383736", "3 c #383836", "4 c #3B3B39", "5 c #3D3D3B", "6 c #3E3E3C", "7 c #3F3E3D", "8 c #3F3F3D", "9 c #403F3E", "0 c #434241", "q c #444342", "w c #454443", "e c #464544", "r c #474644", "t c #474645", "y c #484745", "u c #4B4A48", "i c #535250", "p c #545351", "a c #585755", "s c #595856", "d c #595957", "f c #5A5957", "g c #5D5C5A", "h c #5E5D5B", "j c #62615F", "k c #686765", "l c #6A6966", "z c #6C6B68", "x c #6D6C69", "c c #6F6D6B", "v c #71706D", "b c #72716E", "n c #73726F", "m c #747370", "M c #757371", "N c #757471", "B c #777673", "V c #777674", "C c #7B7A77", "Z c #7D7C79", "A c #858481", "S c #868481", "D c #878582", "F c #878683", "G c #898884", "H c #8C8A87", "J c #908E8B", "K c #92908D", "L c #92918D", "P c #969591", "I c #989693", "U c #9D9B98", "Y c #9E9C99", "T c #9F9E9A", "R c #A19F9C", "E c #A2A19D", "W c #A3A19D", "Q c #A3A29E", "! c #A9A7A3", "~ c #A9A8A4", "^ c #ACABA7", "/ c #ADACA8", "( c #AEACA8", ") c #B0AFAB", "_ c #B1AFAB", "` c #B1B0AC", "' c #B2B0AC", "] c #B3B1AD", "[ c #B3B2AD", "{ c #B3B2AE", "} c #B4B3AE", "| c #B4B3AF", " . c #B5B3AF", ".. c #B5B4AF", "X. c #B5B4B0", "o. c #B6B4B0", "O. c #B6B5B1", "+. c #B7B6B2", "@. c #B8B6B2", "#. c #B8B7B2", "$. c #B8B7B3", "%. c #B9B7B3", "&. c #B9B8B3", "*. c #B9B8B4", "=. c #BAB8B4", "-. c #BAB9B4", ";. c #BBB9B5", ":. c #BBBAB5", ">. c #BCBAB6", ",. c #BCBBB6", "<. c #BCBBB7", "1. c #BDBBB7", "2. c #BDBCB7", "3. c #BEBCB8", "4. c #BEBDB8", "5. c #BFBEB9", "6. c #C0BEB9", "7. c #C0BFBA", "8. c #C1BFBA", "9. c #C1C0BB", "0. c #C1C0BC", "q. c #C3C1BC", "w. c #C3C1BD", "e. c #C3C2BD", "r. c #C4C2BD", "t. c #C4C2BE", "y. c #C4C3BE", "u. c #C5C3BE", "i. c #C5C4BF", "p. c #C6C4C0", "a. c #C7C5C0", "s. c #C7C5C1", "d. c #C8C6C1", "f. c #C9C7C2", "g. c #CBC9C4", "h. c #CBC9C5", "j. c #CDCBC6", "k. c #CDCCC7", "l. c #CFCDC8", "z. c #D0CEC9", "x. c #D0CECA", "c. c #D1CFCA", "v. c #D1CFCB", "b. c #D1D0CB", "n. c #D2D0CB", "m. c #D2D0CC", "M. c #D2D1CC", "N. c #D3D1CC", "B. c #D3D1CD", "V. c #D2D2CD", "C. c #D3D2CD", "Z. c #D3D3CE", "A. c #D4D2CD", "S. c #D4D3CE", "D. c #D5D3CE", "F. c #D6D4CF", "G. c #D7D5CF", "H. c #D7D5D0", "J. c #D8D6D1", "K. c #D9D6D1", "L. c #D9D7D1", "P. c #D9D7D2", "I. c #D9D8D3", "U. c #DAD8D2", "Y. c #DAD8D3", "T. c #DBD9D3", "R. c #DAD9D4", "E. c #DBD9D4", "W. c #DCD9D4", "Q. c #DCDAD4", "!. c #DCDAD5", "~. c #DDDAD5", "^. c #DDDBD5", "/. c #DCDBD6", "(. c #DDDBD6", "). c #DEDCD6", "_. c #DEDCD7", "`. c #DFDCD7", "'. c #DFDDD7", "]. c #DFDDD8", "[. c #E0DDD8", "{. c #E0DED8", "}. c #E1DFD9", "|. c #E1DFDA", " X c #E1E0DA", ".X c #E2E0DA", "XX c #E2E0DB", "oX c #E3E1DB", "OX c #E3E1DC", "+X c #E4E1DC", "@X c #E4E2DD", "#X c #E4E3DD", "$X c #E5E3DD", "%X c #E5E3DE", "&X c #E6E4DE", "*X c #E6E4DF", "=X c #E7E5DF", "-X c #E7E5E0", ";X c #E8E6E0", ":X c #E8E6E1", ">X c #E9E6E1", ",X c #E9E7E1", "Xn.D.F.N.X.N.D.D.D.", "D.D.H.e.<.P.D.D.D.D.D.D.D.D.D.D.D.N.D.D.D.D.D.N.pXE.N.D.F.z.>Xc l xXf @ r F ].n.F.N.{ D.D.D.D.", "D.N.P.u.:.E.D.D.F.F.F.F.F.F.F.F.H.H.D.H.D.H.H.H.pXI.D.D.n.'.n O o M h.W F ; <.P.N.F.N.X.N.D.D.D.", "D.D.H.i.<.D.z.n.z.z.z.z.z.z.z.z.z.z.v.z.z.n.z.l.pXE.D.D.D.H.p.e.8.p.!.|.!.z.!.D.D.D.D. .D.D.D.D.", "D.N.Y.e.e.6X-X-X>X-X>X-X>X>X>X*X-X-X-X>X-X-X>X-XjXP.N.D.D.D.P.I.W.P.D.D.D.G.N.D.D.D.z. .N.D.D.D.", "D.N.I.5.N.jX-X6X6X6XqX6X6X6XqXqX6XqX6X6XqX3XeXP.qXE.N.D.D.D.D.N.D.v.D.D.D.D.D.D.D.F.D. .D.D.D.D.", "D.D.I.0.N.-Xl.l.N.z.z.N.z.z.z.z.z.z.v.z.z.z.D.{ &X'.N.D.D.D.D.D.D.D.D.D.D.D.D.D.D.F.N.{ N.D.D.D.", "D.D.P.5.N.6Xb.H.H.H.H.D.D.D.H.D.H.H.H.D.H.D.P.%.>X!.v.D.D.D.D.N.D.D.D.D.D.D.D.D.D.D.z.X.D.D.D.D.", "D.N.P.9.N.6Xb.H.N.D.D.D.D.D.D.D.D.D.D.D.D.N.P.+.-X|.P.I.P.I.I.P.I.I.P.P.P.Y.P.Y.Y.P.H.+.D.D.D.D.", "D.D.P.5.N.6Xl.H.D.D.D.N.D.D.D.D.N.N.D.D.D.D.P.%.%Xp.%.1.1.:.:.:.:.1.:.1.:.:.:.:.:.2.<.( N.D.D.D.", "D.N.P.9.N.6Xz.H.D.D.N.H.F.D.D.H.I.H.D.D.D.N.H.%. XOX!.`.!.`.!.`.`.!.`.!.!.!.!.'.!.!.!.Y.D.D.D.D.", "D.N.P.5.N.6Xn.F.D.D.P.z.l.D.P.z.6.z.W.N.D.D.P.$.6X6XOXOX&XOX&X&X%XoX&X&XoX&XoX&XoX%X%Xi.v.F.D.D.", "D.D.Y.5.N.6Xl.F.v.[.^ # y =X_ 9 u , R }.v.v.P.$.-XP.l.N.N.n.n.n.n.n.n.n.n.n.n.n.n.z.z.' N.D.D.D.", "D.D.P.9.N.3Xn.F.D.F.d.J 9 %Xd.J C ; G oXv.D.P.$.-XE.N.H.N.F.N.F.D.D.D.D.F.D.D.D.F.F.D. .D.D.D.D.", "D.N.P.5.N.3Xn.F.D.D.oX .6 !.|. .a & P }.N.D.P.$.>X'.N.H.F.F.N.F.D.D.D.F.D.D.D.D.F.F.N. .N.D.D.D.", "F.Z.I.7.N.6Xl.H.D.D.!.! 4 X8.$.pXg k =Xz.D.P.$.-X'.N.D.F.F.N.N.D.F.N.N.n.n.D.D.D.F.N. .D.D.D.D.", "F.N.H.7.Z.6Xl.H.v.}.R # + u _ , - q 8.P.D.N.P.+.-X!.N.D.N.F.H. XY.n.Y.oX%X|.n.D.D.F.n. .N.D.D.D.", "D.N.H.0.N.6Xb.H.D.F.z.d.j.d.P.N.i.P.W.D.D.N.P.$.>X'.N.D.D.H.h.T 9.^.=.J H W P.D.D.F.n...D.D.D.D.", "F.N.I.7.N.6Xl.H.D.D.D.P.G.P.D.G.P.D.N.D.H.N.P.%.-XE.N.D.N.].C . N fXf > k N Y.D.D.F.N...N.D.D.D.", "F.N.Y.7.N.3Xn.F.Z.F.D.N.N.N.F.D.D.D.D.D.D.N.P.$.=X!.D.D.D.z.|.n l lXf , c _ Y.N.D.D.N. .N.D.D.D.", "D.N.Y.7.N.3Xb.F.D.D.D.F.F.F.D.D.D.D.D.D.D.D.P.$.>X!.n.D.D.z.yXN M fXU H J * <.W.D.F.n. .D.D.D.D.", "D.D.P.5.F.3Xz.F.N.N.N.N.N.N.N.N.D.N.N.D.N.N.P.+.>X!.D.D.D.D.d.p p k.W T _ = .!.n.F.N. .N.D.D.D.", "D.N.P.9.n.7XF.Y.Y.P.Y.Y.Y.P.Y.Y.P.P.P.P.P.P.!.=.>X!.n.D.n.}.M 3 , Z ! 9 6 J !.N.D.F.N. .D.D.D.D.", "D.N.P.8.N.E.$.2.2.2.2.2.2.2.2.2.2.2.2.2.:.:.8.^ 3X!.n.D.D.D.!.oX%XW.!.%X|.|.D.D.N.F.n.+.N.D.D.D.", "D.D.P.e.0.l.9.9.i.9.i.9.e.e.e.e.u.u.9.u.u.e.u.u.yXE.D.D.D.D.D.z.n.N.D.n.N.z.D.D.F.F.N.{ D.D.D.D.", "D.D.H.u.<.E.H.Y.L.L.L.L.G.Y.Y.G.P.G.G.P.P.P.P.G.fXY.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.F.N...N.D.D.D.", "D.N.P.u.<.H.Z.D.F.n.F.n.D.D.N.D.N.D.D.N.D.D.D.v.pXY.N.D.D.D.D.D.D.D.D.D.D.D.D.D.D.F.N.{ D.D.D.D.", "D.D.H.u.:.P.D.D.F.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.pXW.v.F.D.D.D.D.D.D.G.N.F.F.N.F.D.D.n...D.D.D.D.", "D.v.P.u.:.P.N.D.D.n.D.D.N.D.D.N.v.D.D.D.D.N.D.v.pX!.F.F.F.F.F.F.G.D.G.G.F.F.F.F.F.H.n...D.D.D.D.", "D.D.P.u.:.W.P.F.F.G.G.G.P.G.F.G.P.P.H.P.F.P.F.P.0X8.$.$.$.$.$.$.$.$.O.$.O.$.$.O.+.$...{ D.D.D.D.", "D.D.P.d.D.lXpXpXfXpXfXpXpXfXpXpXpXpXpXpXpXpXpXhXpXqX0X0X0X0XqXqX0X7XwXwXwXwXeXeXeXeXeXeXD.D.D.D.", "D.D.D.D.P.D.D.F.F.F.F.F.D.F.F.F.F.F.F.F.F.F.F.D.H.P.H.P.P.P.G.F.P.G.P.P.P.F.P.P.P.P.P.H.D.D.D.D.", "D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.v.v.D.D.D.N.D.D.N.D.n.D.n.n.n.D.n.D.D.D.D.", "D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.F.F.D.D.D.D.D.D.", "D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D." }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/filling-icon.c0000644000175300017530000003473612161170366016276 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 192 2", " c #656562", ". c #6F6E6C", "X c #71706D", "o c #757572", "O c #787774", "+ c #7D7C79", "@ c #807F7C", "# c #81807D", "$ c #82807E", "% c #82817E", "& c #83817E", "* c #83827F", "= c #858481", "- c #868582", "; c #878683", ": c #8A8986", "> c #8C8B88", ", c #8F8B8B", "< c #8C8C88", "1 c #8D8C89", "2 c #8F8D8A", "3 c #8F8C8B", "4 c #8F8E8B", "5 c #908C8B", "6 c #92918E", "7 c #93918E", "8 c #93928F", "9 c #94908F", "0 c #959491", "q c #969491", "w c #989293", "e c #989593", "r c #989693", "t c #9A9595", "y c #999794", "u c #9C9597", "i c #9C9697", "p c #9A9896", "a c #9B9996", "s c #9C9B97", "d c #9D9B98", "f c #9E9C99", "g c #9E9D99", "h c #A0999B", "j c #A09E9B", "k c #A29D9D", "l c #A19F9C", "z c #8ABE85", "x c #93B58F", "c c #94B98F", "v c #9EBF99", "b c #A1A09C", "n c #A3A19E", "m c #A3A29E", "M c #A4A39F", "N c #A0BA9B", "B c #A4B89F", "V c #A59FA0", "C c #A5A3A0", "Z c #A5A4A0", "A c #A6A5A1", "S c #A7A5A2", "D c #A8A6A2", "F c #A8A7A3", "G c #A9A7A4", "H c #ABA7A6", "J c #AAA9A5", "K c #ABA9A5", "L c #ABAAA6", "P c #ACAAA6", "I c #ACABA7", "U c #ADABA7", "Y c #ADACA8", "T c #AEACA9", "R c #AEADA9", "E c #AFADA9", "W c #B0AEAA", "Q c #B1AFAB", "! c #A9BCA4", "~ c #AABCA5", "^ c #ADBCA7", "/ c #B3B1AD", "( c #B4B3AE", ") c #B4B3AF", "_ c #B5B3AF", "` c #B6B4B0", "' c #B6B4B1", "] c #B7B6B1", "[ c #B7B6B2", "{ c #B8B3B3", "} c #B8B6B2", "| c #B8B7B2", " . c #B8B7B3", ".. c #B9B7B3", "X. c #BAB7B4", "o. c #BBB7B6", "O. c #BCB7B7", "+. c #B9B8B3", "@. c #BAB9B4", "#. c #BBB9B5", "$. c #BCBAB6", "%. c #BDBCB7", "&. c #BEB6B9", "*. c #BDB8B8", "=. c #BEBBB8", "-. c #BEBCB8", ";. c #BEBDB8", ":. c #BFBDB9", ">. c #BFBEB9", ",. c #C0BABA", "<. c #C1BABB", "1. c #C0BCBA", "2. c #C0BEBA", "3. c #C0BFBA", "4. c #C1BFBA", "5. c #C1BFBB", "6. c #C3B9BD", "7. c #C3BCBD", "8. c #98C993", "9. c #A1C19C", "0. c #A5C2A0", "q. c #A9C0A4", "w. c #ACCBA7", "e. c #AEC9A9", "r. c #B2C1AD", "t. c #BEC1B8", "y. c #C1C0BB", "u. c #C2C0BC", "i. c #C2C1BC", "p. c #C4C2BE", "a. c #C5C2BE", "s. c #C4C3BE", "d. c #C5C3BE", "f. c #C5C3BF", "g. c #C5C4BF", "h. c #C6C4BF", "j. c #C6C4C0", "k. c #C7C5C0", "l. c #C8C6C2", "z. c #C9C7C2", "x. c #C7C9C1", "c. c #CBC8C4", "v. c #CBC9C4", "b. c #CCCAC5", "n. c #CCCAC6", "m. c #CCCBC6", "M. c #CDCBC6", "N. c #CDCDC6", "B. c #CECCC7", "V. c #CECEC7", "C. c #CECCC8", "Z. c #CFCCC8", "A. c #CFCDC8", "S. c #D0CDC9", "D. c #D0CEC9", "F. c #D0CFC9", "G. c #D1CFCA", "H. c #D1CFCB", "J. c #CFD1C8", "K. c #CFD2C8", "L. c #D0D0CA", "P. c #D1D0CB", "I. c #D2D0CB", "U. c #D2D0CC", "Y. c #D3D0CC", "T. c #D3D1CC", "R. c #D4D2CD", "E. c #D5D2CE", "W. c #D5D3CE", "Q. c #D5D3CF", "!. c #D4D7CD", "~. c #D6D4CF", "^. c #D6D5CF", "/. c #DBCFD4", "(. c #D6D5D0", "). c #D7D5D0", "_. c #D8D6D1", "`. c #D9D7D2", "'. c #D9D8D2", "]. c #DAD8D3", "[. c #DAD9D3", "{. c #DBD9D4", "}. c #DCD9D5", "|. c #DCDAD5", " X c #DDDBD6", ".X c #E0DED9", "XX c #E2E0DA", "oX c #E2E0DB", "OX c #E3E1DC", "+X c #E4E2DD", "@X c #E8E6E0", "#X c #E9E7E2", /* pixels */ "`.G.R.R.H.R.(.M.:.u.u.a.k.u.A. X", "$.g (.z._ k.M.U y H 6 X p 6 @ |.", "[ _ M _ G. X.XA.!.8.x.l E [ p W.", "[ E .:.c. X.Xc.A.w.t.p E E q W.", "#.y {.c.W a.z.( ,._.X.O q 2 % X", "+.L 2.2.h.`. Xz.C.e.A.k.(.(.a.[.", "[ +.m .W. X+XB.A.z K.I.#X+Xc.|.", "#.; #.L M A.B.$.a.R.h.j ( L A |.", "$.@ m d % T.H.:.k.M.#.+ m g @ |.", " .H A [ s .XOXH.A.; y.C L ( d (.", "#.6 I A : A.A.:.a.#.[ < U D > (.", "$.. u , o q w = : m = y , = X", "#.V v ! <.r.c :.2.2 H h 0.v <.`.", "+.l 9.! &.^ x &.:.q L y ~ ! &.`.", "2.O u 5 2 X.<.s ; g % ; :.{ g `.", "|.G.M.M.A.(.(.T.M.M.C.R.(.(.G.|." }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 107 2", " c #232222", ". c #282827", "X c #353534", "o c #393837", "O c #3A3A39", "+ c #403F3E", "@ c #41403E", "# c #444342", "$ c #484746", "% c #494846", "& c #4D4D4B", "* c #504F4E", "= c #52514F", "- c #545452", "; c #585755", ": c #575855", "> c #595856", ", c #5D5C5A", "< c #605F5D", "1 c #61605F", "2 c #656462", "3 c #686765", "4 c #6A6966", "5 c #6C6B69", "6 c #706F6D", "7 c #72716E", "8 c #767572", "9 c #787775", "0 c #797875", "q c #7D7C79", "w c #807E7C", "e c #41963F", "r c #40983E", "t c #469944", "y c #549D51", "u c #54A251", "i c #5DA55A", "p c #66A463", "a c #69A566", "s c #6BA568", "d c #6EAE6B", "f c #71B06E", "g c #7AAB77", "h c #7DAC79", "j c #7BB377", "k c #82817E", "l c #83B67F", "z c #878683", "x c #888783", "c c #8A8986", "v c #8E8D8A", "b c #908F8C", "n c #93918E", "m c #959491", "M c #989693", "N c #9B9A96", "B c #9E9D99", "V c #A09F9B", "C c #8CAE87", "Z c #85B781", "A c #8DB389", "S c #93B48F", "D c #96B491", "F c #9ABF94", "G c #9FB99A", "H c #A2A19D", "J c #A6A5A1", "K c #A8A6A3", "L c #ACAAA6", "P c #AEADA9", "I c #B0AEAA", "U c #A7BDA2", "Y c #ADBEA7", "T c #AEBAA8", "R c #B3B2AE", "E c #B0BBAB", "W c #B6B5B1", "Q c #B9B6B3", "! c #BBB9B5", "~ c #BEBDB9", "^ c #C1BEBB", "/ c #9EC199", "( c #BBC2B5", ") c #BEC2B9", "_ c #C3C2BD", "` c #C6C5C0", "' c #C9C6C3", "] c #CCCAC5", "[ c #CFCCC9", "{ c #D1CDCB", "} c #CED5C8", "| c #D4D2CD", " . c #D3D8CD", ".. c #D6CDD0", "X. c #D7D5D0", "o. c #D8D6D2", "O. c #D7D9D0", "+. c #DBD9D4", "@. c #DFDDD8", "#. c #E1DEDA", "$. c #E4E2DD", "%. c #E7E5E0", "&. c #E8E5E1", "*. c #ECEAE5", "=. c #F1EEE9", "-. c #F4F2EC", ";. c #FCFAF3", /* pixels */ "+.@.#.+.+.@.+.+.@.@.@.@.+.@.@.#.#.$.$.#.#.#.$.@.@.@.#.#.#.@.+.+.", "@.| ] +.o.o.o.+.| .o.o.o.| +._ _ _ { _ { _ _ { _ { | _ { .+.@.", "$.R - Q L L L R m N R L P L W > & > ; - ; = - > = - < + 7 &.o.", "&.I 4 =.+.&.$.$.^ _ $.@.@.@.$.W o.#.&.$.$.| - [ _ o._ .n 7 *.o.", "$.I 5 $.$.b L *.^ ^ @.o.+.+.$.I { #./ F #.{ - _ ^ w P X.n 6 *.o.", "&.L 4 -.R % M *.W _ $.O.+.+.#.P | +.u f +.{ - _ [ k H o.v 6 *.o.", "&.R 4 -.R 2 q *.^ ^ #.+.+.o.$.P | @.l d .{ = _ _ 1 8 X.b 6 *.o.", "$.L 5 *.$.$.$.&._ _ $.@.@.@.$.R | $.@.O.$.| > .{ ^ ^ @.V 6 *.o.", "&.R 1 O._ _ ] [ L I [ _ _ ] { H _ _ [ _ _ _ @ b n M n N 3 6 *. .", "$.R ; _ R R W ^ V H ^ Q W W ^ b I W ^ W ^ W 1 6 7 7 6 8 1 v &.o.", "$.P 5 -.#.$.$.$._ _ $.$.$.#.$.W @.$.$.@.$.@.^ *.*.*.*.=.o.^ #.@.", "*.P 5 $.$.7 H =.Q _ $.O.+.+.$.P { #.l l #. .L +.o.o.o.@._ W #.o.", "$.I 3 -.H @ c *.! _ $.O.#.o.#.P | .i d _ $.I @.o.@.+.@._ W $.o.", "$.P 4 *._ x n $.W _ #.O.O.o.#.P | #.F l o.o.L +.o.o. .@._ ^ $.o.", "*.P 7 ;.=.;.-.-.] _ *.$.$.#.$.W o.#.$.&.#.#.^ =.*.*.*.=.+.^ $.+.", "$.W O k 7 8 8 w 1 V _ Q ^ W ^ m I ^ R ^ W ^ 1 8 8 w 8 w 3 n $.o.", "$.R # m c b x m & x | _ ` _ ] V ^ _ _ { _ _ @ k v n v m < 5 *.o.", "%.P 2 $.] Q [ $.q M =.#.#.#.$.R X.$.#._ $.| > o.[ ^ _ #.V 7 *.o.", "%.I , | Q , Q X.6 n &.o.+.+.#.I | #.1 w *.{ ; _ _ 1 H .n 6 *.o.", "%.I , | ' 4 J +.7 n *.o.+.+.#.I | | 4 1 _ | - _ | w v +.n 6 *.o.", "%.I , X.Q 7 B o.7 n &.o.+.+.#.I { $.P V @._ = _ ^ w c X.v 6 *.o.", "%.P 2 +.] o.[ +.8 N -.#.$.#.&.W +.$.*.*.*.+.- { _ X.| X.M 6 *.o.", "$.Q . : * * = > X @ < > > > , $ - , - = > - = ; * % ; o < &.o.", "%.P : ~ P Q W R B H R I Q I R b P I W ^ W H @ ^ I ^ ^ W H H $.+.", "%.P 1 +.{ G T ..P R | ] S _ { V _ { _ m { ^ = .._ C U | W L $.o.", "%.I , X.E t C ..H P { D e Y { B ^ ] 8 = _ W * _ _ p y { R L $.o.", "%.I , +.S t s ..K P { g r A { B ^ ^ 1 @ W ^ * ] _ l p { W L $.+.", "%.I 2 X.] _ ) { P I ] ] ( _ ] H ^ ] { W ] ^ = ] _ G ! _ W P $.o.", "%.I - ~ P R I Q M K _ ^ ^ ^ _ m L W P W W H & _ ^ _ ^ _ L V $.o.", "$.~ o > : - - , + w H H V V K ; % ; - = ; & % H V V V H w 4 $.+.", "+.+.@.$.#.#.#.#.#.+.#.#.#.#.#.+.#.$.$.$.#.#.#.#.#.#.#.#.#.o.#.#.", "+.+.+.+.+.+.+.+.+.+.o.+.+.o.o.#.+.o.+.+.#.o.o.o.o.#.+.+.o.#.+.o." }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 124 2", " c #0C0C0C", ". c #141413", "X c #181817", "o c #1E1E1D", "O c #232322", "+ c #292827", "@ c #2C2B2A", "# c #302F2E", "$ c #30302F", "% c #333331", "& c #383837", "* c #3E3D3C", "= c #403F3E", "- c #464544", "; c #484746", ": c #494847", "> c #4B4A49", ", c #504F4E", "< c #51514F", "1 c #555453", "2 c #5A5957", "3 c #5C5B59", "4 c #605F5D", "5 c #62615E", "6 c #656462", "7 c #686765", "8 c #6A6966", "9 c #6D6C6A", "0 c #706F6D", "q c #72706E", "w c #767572", "e c #7D7C7A", "r c #817F7D", "t c #258E24", "y c #2D922B", "u c #379235", "i c #399537", "p c #3F983D", "a c #419D3F", "s c #489E46", "d c #4F9D4D", "f c #559F53", "g c #4DA14B", "h c #55A253", "j c #58A055", "k c #5BA359", "l c #5EA85B", "z c #60A25D", "x c #61A95E", "c c #65A462", "v c #68A565", "b c #6BA668", "n c #6FA86C", "m c #71AC6E", "M c #75A972", "N c #7AAB76", "B c #7EB57A", "V c #82817E", "C c #81B57D", "Z c #858481", "A c #898784", "S c #8B8A87", "D c #8E8C8A", "F c #908F8C", "G c #93918E", "H c #969491", "J c #999794", "K c #9B9A97", "L c #9E9C99", "P c #A09F9B", "I c #87B083", "U c #88B084", "Y c #88B984", "T c #8CB188", "R c #8EBB8A", "E c #95B291", "W c #9CB697", "Q c #99BF94", "! c #9EB799", "~ c #A3A29E", "^ c #A3B99E", "/ c #A6A4A1", "( c #A8A7A3", ") c #ABAAA6", "_ c #AFADA9", "` c #B0AFAB", "' c #AABAA4", "] c #AEBCA9", "[ c #B3B1AD", "{ c #B7B5B1", "} c #B9B7B3", "| c #BAB9B5", " . c #BFBDB9", ".. c #C0BEBA", "X. c #A1C39C", "o. c #A5C4A0", "O. c #ACC7A7", "+. c #AFC7A9", "@. c #BAC0B4", "#. c #BFC1B9", "$. c #C4C2BE", "%. c #C7C4C1", "&. c #C9C6C3", "*. c #CCCAC5", "=. c #CEC6C8", "-. c #CECCC8", ";. c #D1C7CB", ":. c #D1CECA", ">. c #CCD3C5", ",. c #D4D2CD", "<. c #D7D5D0", "1. c #D9D6D2", "2. c #DCDAD5", "3. c #DFDDD8", "4. c #E1DEDA", "5. c #E4E2DD", "6. c #E7E5E0", "7. c #E9E6E2", "8. c #EDEBE6", "9. c #EFEDE8", "0. c #F0EEE8", "q. c #F5F3EE", "w. c #F9F6F1", "e. c #FAF8F2", /* pixels */ "2.2.2.3.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.", "2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.4.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.3.2.2.2.2.", "2.2.2.2.5.1.2.1.1.2.1.2.2.2.2.2.2.1.1.1.1.2.1.4.4.2.2.1.2.1.2.2.2.4.2.2.2.2.2.2.2.2.2.2.2.2.2.2.", "2.2.3.1.&.5.5.5.5.5.5.5.5.4.2.5.5.5.5.5.5.5.5.*.1.5.5.4.5.4.5.5.2.*.5.5.6.5.5.5.5.5.4.2.4.2.2.2.", "2.3.5.| O L L L P K L K ~ V V ~ K L P K K K ~ + o # # # @ @ # # + % % % % % % % & o q 7.1.2.2.", "2.1.5.| @ ,.,.-.>.*.-.-.1._ ~ 2.-.-.>.-.-.-.1.A ~ ` _ ` ) _ _ { F O P K K K J K J ) ; 6 9.1.2.2.", "2.1.5.| @ 5.3.3.4.8.5.2.5.| [ 5.2.5.2.5.4.4.5.~ 1.8.4.7.0.8.5.9.#.@ ,.:.-.2.1.:.*.5.6 7 0.1.2.2.", "3.2.5.| @ 5.1.1.4.e | 4.5.| ` 5.1.2.2.2.2.1.5.P *.4.4.>.C O.2.5.{ @ %.$.%.V Z *.#.<.3 6 0.1.2.2.", "2.2.5.| @ 4.1.7.J @ ~ 7.5.[ [ 5.2.2.2.3.2.1.5.P =.4.5.l a X.4.5.| + %.%.&.w < :.$.2.5 5 0.<.2.2.", "2.1.5.| @ 3.5.| ; q V 5.5.{ [ 5.1.2.2.2.2.<.5.K *.4.1.s R x 1.5.{ + *.$.*.%.5 1.@.1.5 8 0.1.2.3.", "2.3.5.| @ 3.5...9 = 2 5.5.{ [ 5.2.2.2.2.2.2.5.P -.2.4.C m x 2.5.| @ &.$.&.9 + K &.<.5 5 0.<.2.2.", "2.1.5.| @ 4.1.4.9.,.,.3.5.{ [ 5.1.2.2.2.2.1.5.P =.5.2.1.X.,.4.5.[ @ &.$.$.K G ( $.,.5 5 0.1.2.2.", "2.1.5.| # 5.3.2.1.5.3.1.5.| [ 5.3.5.4.4.5.1.5.~ -.5.4.4.5.5.2.5.| @ ,.-.,.1.1.,.*.5.5 6 0.1.2.2.", "3.2.5.| @ ,.,.>.:.>.,.-.1._ ( 1.-.>.:.-.:.-.2.H $.,.-.-.:.:.>.2._ O ~ P L L L L L [ > 6 9.<.2.2.", "2.2.5.| o P L K L K K K ~ V e ~ K K K K P K ~ q G P K K K K P P D O % $ $ $ $ $ $ % o 9 8.<.2.2.", "2.1.5.| # 5.5.5.5.4.5.3.8.| { 8.5.5.5.5.5.5.5.~ ,.5.5.4.5.5.5.5.1.P 7.3.5.5.5.5.5.8.| [ 5.1.2.3.", "2.2.5.| @ 2.1.1.4.5.4.1.5.{ ` 5.,.2.1.2.2.1.5.K $.5.2.4.2.1.2.2.-.K 5.1.2.2.1.2.1.5.[ [ 5.2.2.2.", "2.1.5.| @ 5.2.5.*.% ) 5.4.} ` 5.1.2.2.2.2.1.5.~ $.3.5.X.z X.5.2.,.K 3.1.2.2.2.2.2.5.[ [ 5.2.2.2.", "2.2.5.| @ 5.1.5.r < ~ 8.4.{ ` 5.2.2.2.2.2.1.5.P ,.3.5.g g Q 4.4.,.K 5.3.2.3.1.3.2.5.[ [ 5.2.2.2.", "2.2.5.| @ 2.5.[ & 1 5 5.5.{ [ 5.2.2.2.2.2.<.5.K -.5.<.g X.g ,.4.:.K 5.1.3.1.3.1.2.5.[ [ 5.1.3.2.", "2.2.5.| # 4.2.:.) 8 r 5.5.{ ` 5.1.2.2.2.2.2.5.P *.4.4.X.h C 4.4.,.J 5.2.2.2.3.2.2.5.[ [ 5.1.2.2.", "2.2.5.| @ 1.1.1.5.5.4.1.4.[ _ 5.1.2.2.1.1.1.5.P $.4.1.4.1.5.1.4.,.K 2.,.<.<.<.1.,.5.[ ` 5.2.2.2.", "2.2.5.| $ q.0.0.8.0.8.0.w.$.} 8.5.5.5.5.5.5.5.~ 2.5.5.5.5.5.5.5.2.( q.8.q.8.0.0.8.w.$.| 5.2.2.2.", "2.1.5.| X 8 8 7 7 8 8 7 0 1 r ..` [ { { { [ ..V ~ } [ { ` { [ } / 1 9 8 8 8 8 8 9 9 1 S 5.2.3.2.", "2.2.5.$.. 2 2 4 1 2 2 2 6 + 1 $.` { { ] [ [ { V _ } [ [ ` { [ | J . 3 2 3 2 2 2 2 6 O 5 8.1.2.2.", "2.2.5.| @ 2.1.1.4.4.1.<.8.0 6 e.4.5.5.5.5.5.8.~ 1.5.5.5.0.5.5.9. .% 3.1.2.3.3.<.,.8.9 8 0.1.2.2.", "2.2.5.| + $.$.$.) ) $.| ,.4 4 0.<.1.1.2.1.1.5.J -.4.1.4._ $.2.5.{ + %.#.%.) ) %. .,.3 6 0.1.2.2.", "2.2.5.| @ *.$.$.4 > >.$.1.5 5 0.,.4.2.2.2.2.5.~ :.2.5.V - ~ 5.5.{ + *.%.&.2 > :.#.1.5 6 9.1.2.2.", "2.2.5.| + $.$.-...5 :.$.1.5 5 0.<.4.1.2.2.2.5.K $.5.2.= 5 V 2.5.{ + *.$.*. .4 ,...1.5 6 0.1.2.2.", "2.2.5.| + *.$.*.G = [ $.1.5 5 0.1.2.2.2.2.1.5.~ :.2.5.5 K < ,.5.{ @ *.%.&.F = { $.<.5 6 0.<.2.3.", "2.2.5.| + *.$.$.w 4 H $.,.5 5 0.1.2.2.2.2.1.5.~ -.5.5.$.9 ` 5.5.{ + &.$.%.w 2 G *.<.3 6 9.1.2.2.", "2.2.5...+ *.$.$.:.,.-.$.1.5 5 0.<.2.2.2.2.,.5.~ $.4.2.5.5.5.<.5.{ + *.%.%.:.,.-...<.5 6 9.2.2.2.", "2.2.5.} @ -.*.-.*.&.*.&.4.6 5 e.4.5.5.5.5.4.8.~ ,.5.5.5.3.4.5.8.| + ,.*.=.&.&.=.&.3.6 6 0.1.2.2.", "2.2.4.$. + @ @ @ + @ @ # . X % # $ @ $ $ $ % O @ $ $ $ $ $ @ % + @ + @ @ @ @ + % * 6.2.2.2.", "2.2.5.| O ~ P ~ ~ P P P ( A V ( P P P P L L ~ q G P ~ P L L L / V O / ~ P P L P P / Z H 4.2.2.2.", "2.2.5.| @ :.-.:.-.1.:.=.<.) ~ 1.-.=.;.1.:.-.,.H $.,.-.-.2.1.*.<.) @ ,.-.;.1.1.,.-.<.( ( 6.2.2.2.", "2.2.5.| + $.$.$...b ' &.*.~ P *...=.' b $.$.*.D } &.$.-.V Z &.*.~ + &.%.| N M ' %.*.P / 5.1.2.2.", "2.2.5.| + *.$.;.U t E ;.*.~ ~ =.$.&.f u $.$.-.D | %.-._ % 5 ,.*./ + &.&.@.v y m ;.*.P / 6.2.2.2.", "2.2.5.| + $.=.' a v m ;.-.~ L *.;.M f f ^ &.*.D | %.-.- 8 1 %.-./ + *.$.*.@.k b ;.-.~ / 5.2.2.2.", "2.2.5.| + *.*.] c i d *.*.~ P *.=.T z t U =.*.D | &.$.q 1 + { :.~ + *.&.] b k N ;.*.P / 5.2.2.2.", "2.2.5.| @ $.$.$.;...$.$.*.~ L -.$.=.:.@.*.$.*.D } $.$.,.&.{ &.&./ @ *.%.$.W ! &.$.-.P / 5.2.2.2.", "2.1.5.| @ ,.-.-.=.:.:.-.1.) ~ :.$.$.$.-.&.$.:.G $.,.-.*.-.,.-.1.) + *.*.&.:.:.%.%.:./ ) 5.2.2.2.", "2.2.5.| o ~ L L P K K K ~ V F $.| | | | | } ..e G P L L L L L ~ e + .} } } | | } $.G D 5.1.2.2.", "2.2.4.$.. @ # @ # @ # @ $ . - J S D D D F D G + o $ @ # @ @ @ $ O O G D D D D D S K - & 6.2.2.2.", "2.3.2.1.&.5.3.5.5.5.4.5.5.,.-.5.5.3.3.5.5.5.5.&.2.5.5.4.5.5.3.5.2.&.4.3.4.4.3.5.3.5.,.:.4.2.2.2.", "2.2.2.2.5.2.2.1.1.1.1.2.2.4.4.1.2.2.1.2.1.1.1.5.3.1.2.1.2.1.1.2.2.3.2.2.2.1.1.2.1.1.3.4.2.2.2.2.", "2.2.2.2.2.2.2.2.2.2.2.2.2.1.1.2.2.2.2.2.2.2.2.1.2.3.2.2.2.2.2.2.2.2.2.2.1.3.2.2.3.2.2.2.2.2.3.2.", "2.2.2.3.2.2.2.2.2.2.2.3.2.2.3.2.2.2.2.2.2.2.2.2.2.2.2.2.2.3.2.2.2.2.2.2.3.2.2.2.2.2.2.2.2.2.2.2." }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/galaxies-icon.c0000644000175300017530000002235612161170370016435 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 163 2", " c #7B7A77", ". c #7F7E7C", "X c #858481", "o c #8A8986", "O c #8C8B89", "+ c #8F8E8B", "@ c #8D8D8C", "# c #8F8F8D", "$ c #908F8D", "% c #91908C", "& c #92908D", "* c #91918F", "= c #919190", "- c #949391", "; c #969491", ": c #959594", "> c #979695", ", c #999794", "< c #999894", "1 c #9B9B99", "2 c #9D9B98", "3 c #9C9B99", "4 c #9D9C98", "5 c #9F9E9B", "6 c #A1A09D", "7 c #A2A09D", "8 c #A2A19D", "9 c #A4A29E", "0 c #A4A39F", "q c #A5A39F", "w c #A2A1A0", "e c #A5A3A0", "r c #A4A3A3", "t c #A5A4A0", "y c #A6A4A0", "u c #A6A5A1", "i c #A7A5A1", "p c #A5A4A2", "a c #A7A5A2", "s c #A6A5A3", "d c #A7A6A2", "f c #A7A7A7", "g c #A9A8A4", "h c #ABA9A5", "j c #A8A8A6", "k c #ABAAA6", "l c #ACAAA6", "z c #ABABAB", "x c #AFADAA", "c c #ADACAC", "v c #AEADAD", "b c #AFAEAD", "n c #AFAFAE", "m c #B0AEAA", "M c #B1AFAA", "N c #B1AFAB", "B c #B2B0AC", "V c #B2B1AD", "C c #B3B1AD", "Z c #B4B3AE", "A c #B5B4AF", "S c #B2B2B2", "D c #B5B4B0", "F c #B6B4B0", "G c #B6B5B0", "H c #B7B5B0", "J c #B7B5B1", "K c #B4B4B3", "L c #B8B6B1", "P c #B9B7B2", "I c #B9B7B3", "U c #B8B8B7", "Y c #BCBAB5", "T c #BDBBB6", "R c #BDBCB7", "E c #BCBCBC", "W c #BDBDBD", "Q c #BEBEBE", "! c #BFBFBE", "~ c #BFBFBF", "^ c #C0BEB9", "/ c #C1BFBB", "( c #C4C3BF", ") c #C0C0C0", "_ c #C1C1C1", "` c #C7C6C1", "' c #C7C6C3", "] c #C4C4C4", "[ c #C6C6C6", "{ c #C9C7C3", "} c #CAC9C4", "| c #CBC9C4", " . c #CCCAC5", ".. c #CDCAC5", "X. c #CCCBC6", "o. c #CDCBC6", "O. c #C8C8C8", "+. c #CECDC8", "@. c #CFCDC8", "#. c #CFCDC9", "$. c #CDCDCA", "%. c #D0CEC9", "&. c #D0CECA", "*. c #D1CFCA", "=. c #D1CFCB", "-. c #D2D0CA", ";. c #D2D0CB", ":. c #D3D1CC", ">. c #D3D2CD", ",. c #D4D2CD", "<. c #D5D2CD", "1. c #D5D3CD", "2. c #D4D2CE", "3. c #D5D3CE", "4. c #D6D4CF", "5. c #D1D1D1", "6. c #D3D3D3", "7. c #D6D4D0", "8. c #D7D5D0", "9. c #D7D5D1", "0. c #D7D7D7", "q. c #D8D6D1", "w. c #D8D6D2", "e. c #D9D7D2", "r. c #D9D7D3", "t. c #DAD8D3", "y. c #DBD9D3", "u. c #DBD9D4", "i. c #DCDAD4", "p. c #DCDAD5", "a. c #DDDBD5", "s. c #DDDBD6", "d. c #DFDCD7", "f. c #D9D9DA", "g. c #DFDDD8", "h. c #DDDCDB", "j. c #DFDFDD", "k. c #E0DDD8", "l. c #E0DED8", "z. c #E2E0DA", "x. c #E4E2DD", "c. c #E7E5DF", "v. c #E0E0E0", "b. c #E4E4E5", "n. c #E5E5E5", "m. c #E7E7E7", "M. c #E9E6E1", "N. c #EAEAEA", "B. c #EBEBEB", "V. c #EBECEC", "C. c #ECECEC", "Z. c #EDEDED", "A. c #EEEEEE", "S. c #F1F1F0", "D. c #F2F2F1", "F. c #F2F2F3", "G. c #F4F3F2", "H. c #F5F5F5", "J. c #F8F8F8", "K. c #FBFBFA", "L. c #FCFCFD", "P. c #FEFEFE", "I. c #FFFFFF", /* pixels */ ":.J B B B B B J B m m B m m I :.", "J X k q 8 t l . S O.) Q ] _ @ 3.", "B k M.p.t.p.M.y m.I.K.J.I.I.c 8.", "B q p.*.#.*.u.4 b.I.J.H.n.V.z 8.", "Z 7 u.#.X.#.8.k : y = U B.v.c *.", "B e p.:.@.,.,.,.:.t.J Q F.A.F.8.", "Z y k.:.q.p.p.q.:.s.J Q I.I.F.8.", "B 8 u.*.q 8 q 4 | q.J * Q K N ,.", "B q k.X.1 0.f.f R u.-.R Y L % q.", "Z i t.| w A.J.c ^ ,.,.u.x.h.y t.", "Z 4 j.] > 5.8.r J h.{ P J N O q.", "A , B.$. y i 8 ( V.x : ] Q U ,.", "B y ,.| < l.s.t.-.%.J ) I.I.F.,.", "Z q z.*.% :.-.*.-.h.J ~ I.I.F.3.", "P i q.*.+ % : o { 3.^ @ y 3 k u.", ":.,.,.,.t.3.3.*.-.3.8.8.:.-.X.,." }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 64 1", " c #2E2D2D", ". c #333332", "X c #3E3E3D", "o c #40403F", "O c #4A4A49", "+ c #52514E", "@ c #535352", "# c #5D5C5B", "$ c #61605F", "% c #656563", "& c #686765", "* c #696866", "= c #6C6B68", "- c #706F6C", "; c #73726F", ": c #737372", "> c #787774", ", c #787877", "< c #7E7D7C", "1 c #817F7C", "2 c #83827F", "3 c #858482", "4 c #888683", "5 c #8A8986", "6 c #8D8D8D", "7 c #93918E", "8 c #949391", "9 c #989693", "0 c #9C9A96", "q c #9B9B9B", "w c #A19F9B", "e c #A3A29E", "r c #A6A5A1", "t c #ACAAA6", "y c #AFADAA", "u c #B0AEAA", "i c #B3B1AE", "p c #B5B4B2", "a c #B8B6B2", "s c #BCBAB6", "d c #BDBCBB", "f c #C1BFBA", "g c #C4C2BD", "h c #C6C5C4", "j c #C9C7C2", "k c #CCCAC6", "l c #CECDCA", "z c #D1CFCA", "x c #D4D2CD", "c c #D6D5D1", "v c #D8D6D1", "b c #DDDBD6", "n c #DCDBD9", "m c #E1DFD9", "M c #E5E2DD", "N c #DFDFE0", "B c #E5E4E1", "V c #E8E6E1", "C c #EEECE6", "Z c #ECECEC", "A c #F0EFEE", "S c #F2F1ED", "D c #F3F3F3", "F c #FEFEFE", /* pixels */ "xxmmmmmmmmmMMmMMMmmmnMnMmmmmnbxx", "xxuerrreteeeeeeueeeeeeeeeeeetuxv", "my.-%*==$%**=** >111<<:<<<<5O#bx", "mr-SMMMMmmMMMMM$AFFFFFDFFFFFqwVz", "mt%mkzxxkkzzzzz#ZFFFDDZFDFFF9qBx", "me*Mxxxxzzvxxxv#DFFFFFSFFFFFqeBl", "me*Mxxxxkzxxxxv#ZFFFFDZFFZDFqqVx", "Me%Mxxvvzxvvxvv#DFFFFFDFllhFqeVl", "mt$mgzzzggzzkzx+286663pnnFBnq#nx", "me$mzzzzkzzzzzxe6877q::DlFNcNpcx", "mt=MxvxvzzvvxvvvMMMnCs:FchlFDDcc", "me*Mzxxxzzxxxxxgzxxzbt:FFFFFDZcz", "me*Mzxxvkzvxxxxkxxxxni;FFFFFDZvx", "Me*Mzvxxkzxxvxxkxxxxbu:FFFFFDDvx", "Me=Mzvxvxxvxvxvxxvvxny:FFFFFDDvx", "me$mgzgb5O$$$##+jljjzpo%$$%%+%vx", "Me&MzxzM:yFZBDD#gvxxxxgggggx>0Mz", "Mr&MxxxM-dFhlcF%gvxxxxlvvvxV2rVz", "me*MzxzM-dcDFlD&gvxxxxjvxxxM9mzzzxjxvsgmy:FDFFFDZvx", "Me$MxbvM;9Mzxxxkxxvvby:FFFFFDZvx", "Me=MxxxM;7bzxxzkxxxxbu:FFFDFDZvx", "Me=MxvxM>eSMMMMvvxvxnu:FFFFFDSvx", "Me$bgkkM;@4222<4klkzxiOrqqqq8xbl", "Mt>bkxkxa255455Ojxkzzj624437@rMx", "xxxxvxvxvMMMMMMvxvxvvvmMMMMMnxxx", "xxvxxxvxxxzzzzxxxxxxxxzxzxzxxxcx" }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 78 1", " c #0D0D0D", ". c #111111", "X c #181717", "o c #171818", "O c #1B1A1A", "+ c #232322", "@ c #2E2D2D", "# c #302F2E", "$ c #30302F", "% c #343333", "& c #383736", "* c #383836", "= c #393939", "- c #41413F", "; c #454544", ": c #4B4B4A", "> c #555554", ", c #585754", "< c #595856", "1 c #5A5959", "2 c #61605E", "3 c #636363", "4 c #6B6A67", "5 c #6D6B69", "6 c #706F6C", "7 c #727272", "8 c #787775", "9 c #7B7A77", "0 c #7B7C7C", "q c #807F7B", "w c #83817E", "e c #838384", "r c #888683", "t c #8B8A87", "y c #878788", "u c #8A8A8A", "i c #92908C", "p c #979591", "a c #989793", "s c #9B9A96", "d c #9D9D9B", "f c #A09E9B", "g c #A3A19E", "h c #9FA0A0", "j c #A4A4A2", "k c #A8A7A3", "l c #ADABA7", "z c #AEADA9", "x c #B0AEAA", "c c #B3B2AE", "v c #B4B4B3", "b c #BCBAB6", "n c #BCBBBA", "m c #C0BEBA", "M c #C4C3BE", "N c #C6C5C2", "B c #C8C6C2", "V c #CCCBC6", "C c #C7C7C8", "Z c #CFCDC8", "A c #D1CFCA", "S c #D5D3CE", "D c #D7D5D0", "F c #D9D7D1", "G c #DCDAD5", "H c #DDDCDB", "J c #E1DFD9", "K c #E4E2DC", "L c #DFE0E0", "P c #E5E5E4", "I c #E9E6E1", "U c #E9E8E5", "Y c #EAEAEA", "T c #F1EFE9", "R c #F5F2EC", "E c #F4F4F4", "W c #F9F8F6", "Q c #FEFEFE", /* pixels */ "SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS", "SSSSSSSSSASSSSSASSSSAASSSSSASSSSSSSSSSSSSSSSSSDS", "SSDSGJHJJJJJJJJJJJJGJJJJJJJGJJJJJGGHGGJGGJGGSSDS", "SSSSczzzlllzlzlllzzlzllcllllllzglxllllllllccZDSS", "SSGc #@##@@@##@#@@@#@#@ %===&&&&%%==****&=.@mFSS", "SSJz$KFFGGGFJAAJFGFGFJA@UQQQQQQQWYQQQQQQQQ;nUZSS", "SSJz#FSSSSSSSMVSSSSSASA@PQQQQQQQEUQQQQQQQQ;vUADS", "SSGz@GSSSSSSFVASSSSSSSZ@UQQQQQQQEYQQQQQQQQ;nUASS", "SSGz@GASSSSSSVVSSSSSSSV@UQQQQQQQWYQQQQQQQQ;nUASS", "SSJz@GASSSFSFVAFSSDSSSZ@UQQQQQQQEYQQQQQQQQ;nUSSS", "SSGz@GSSSSSSSVVFSFSSSFZ@UQQQQQQQEPQQQQQQWQ;nISSS", "SAJz@GSSSSSSFVVSSSSSSSV@PQQQQQQQEPQQDvvHQQ;vIASS", "SSGz$GSFGSFSGVAGFSFSSFS@UQQQQQQQWYQHvQWvLQ;nUASS", "SSJx@AVVVVVVVMMVVVVZVVB+377657774hQkEQQYzQ-:AFSS", "SSJz@AVAVVAVAMMVZZZZZZVw55644448=1QzEQQYvQjeCFSS", "SSGz$GSSFSFSGVAGSFSSFSDSIUIIIIKWu1QHxEYzPQYWFSSD", "SSJz@GSSSSSSSVZSSSSSSSSMSSSZSAZK01QQHvnPQQPEGSDS", "SSGz@FSSSSSSFVZFSSSSSSSVSSSSSSSKe1QQQQQQQQUWFSSS", "SSGz#FSSFSSSSZZSFSSSSSSBSSSSSSZKw1QEQQQQQQPQGSSS", "SSJl#GSSSSSSSVZDSSSSSSSMSSSSSSSKw1QQQQQQQQPEGADS", "SSGz@GSSSSSSDVZSSSSSSSSVSSSSSSSKw1QQQQQQQQPWGDSS", "SSJz$FSSSSSFSZAFSFFSSFSVSSSSSSSKw1QQQQQQQQUEGASD", "SSGz@FSSSSSSDVVAAZZZAZZVSSSSSSSKwVgDQQQlBuX3<1,111111MFSS", "SSJl@JmlQQCjR<9WKKKKKKUSFVhEQHfUe1QQQQQQQQYWGSSS", "SAJl#FFcfjjSI<6KASSSSSSMSFndkfZKw c #F10A12", ", c #FE0919", "< c #EC1E17", "1 c #F11310", "2 c #F81918", "3 c #DE3734", "4 c #DD3D3B", "5 c #E62F27", "6 c #F52121", "7 c #F4242D", "8 c #E73326", "9 c #E63330", "0 c #FF7200", "q c #FF7700", "w c #E07A22", "e c #E93943", "r c #967E4E", "t c #9C5A7A", "y c #A3557E", "u c #D64544", "i c #D64E49", "p c #DF594E", "a c #C65755", "s c #C85957", "d c #CF5B58", "f c #D95051", "g c #D75C5B", "h c #D65E5A", "j c #CA6A58", "k c #C96462", "l c #C76B69", "z c #D26064", "x c #D26F6D", "c c #CC7070", "v c #C97E7B", "b c #00FF00", "n c #00FD04", "m c #03EC14", "M c #27DC33", "N c #3CE72C", "B c #6AB856", "V c #73BD63", "C c #4CE543", "Z c #54D468", "A c #7BC275", "S c #EF830C", "D c #DD832F", "F c #D4863E", "G c #FAFA00", "H c #FCF801", "J c #FFFF00", "K c #EDEB12", "L c #EAE61A", "P c #EBE81F", "I c #F1F014", "U c #F1FF14", "Y c #D4D934", "T c #E1DF36", "R c #E3EC32", "E c #B69048", "W c #B88357", "Q c #84827F", "! c #C98250", "~ c #C59B5D", "^ c #D69356", "/ c #C8B54E", "( c #D2807D", ") c #C6A068", "_ c #D2A179", "` c #84C37A", "' c #CDCB40", "] c #DFD542", "[ c #C7C55D", "{ c #D6D559", "} c #C3C372", "| c #E4D770", " . c #7F13AC", ".. c #6F00B5", "X. c #7200B8", "o. c #0E2EFF", "O. c #1642FF", "+. c #1F42FF", "@. c #4C5CDA", "#. c #5B69C9", "$. c #4A60DD", "%. c #6975D2", "&. c #6B70DE", "*. c #7B7DDF", "=. c #861CB3", "-. c #8323AE", ";. c #A75E85", ":. c #8642AE", ">. c #9050A6", ",. c #9C50B6", "<. c #A47BB0", "1. c #A275BF", "2. c #7B87D3", "3. c #868683", "4. c #8E8C88", "5. c #968E8A", "6. c #908C8C", "7. c #999591", "8. c #989693", "9. c #9D9B98", "0. c #A2A19D", "q. c #A4A49D", "w. c #A582AD", "e. c #BC9AAB", "r. c #ABA7A3", "t. c #A8A4A5", "y. c #B1AFAD", "u. c #AFB2A9", "i. c #B3B0AD", "p. c #B5B2AD", "a. c #B9A4BB", "s. c #BCBAB2", "d. c #BAB8B4", "f. c #BFBCB7", "g. c #BEBDB8", "h. c #C88480", "j. c #C68A86", "k. c #CA8E87", "l. c #CE8E8A", "z. c #CA9997", "x. c #C7AD81", "c. c #CAB48E", "v. c #D8B181", "b. c #D7B68F", "n. c #CDAFA8", "m. c #CCB7A4", "M. c #CDB9A3", "N. c #C1B9A8", "B. c #C0BEB9", "V. c #C1BFBB", "C. c #93C890", "Z. c #B4CBB2", "A. c #B9C4BF", "S. c #C8C78E", "D. c #D7D681", "F. c #D7DA81", "G. c #DBD191", "H. c #C2C1BC", "J. c #C5C3BF", "K. c #CAC2BE", "L. c #CAC4BE", "P. c #D2C3B3", "I. c #D2C7B6", "U. c #D4CDBF", "Y. c #CDD5B8", "T. c #8892D4", "R. c #9CA5DD", "E. c #AAABD9", "W. c #ACB3D2", "Q. c #B6B7D0", "!. c #C7C6C3", "~. c #C6C3C7", "^. c #CCC0C2", "/. c #C9C7C2", "(. c #CAC7C2", "). c #CDC6C1", "_. c #C8C7C6", "`. c #CBCAC1", "'. c #CCCBC3", "]. c #CBCDC3", "[. c #CDCBC6", "{. c #CCCAC7", "}. c #CECCC7", "|. c #CECBC8", " X c #C8CCCE", ".X c #CFCDCC", "XX c #D0CEC2", "oX c #CED3CD", "OX c #CFD3CE", "+X c #D5D5C7", "@X c #D3D1CB", "#X c #D3D0CE", "$X c #D4D2CD", "%X c #D5D3CD", "&X c #D5D3CE", "*X c #D6D3CE", "=X c #D6D4CF", "-X c #D7D4CF", ";X c #D8D3CF", ":X c #D6D9CD", ">X c #D7DCCD", ",X c #DFDBCB", " $ a & , 5.A f.tX", "cXb.S v._ w m.f 4 n.8 p oX_.(.qX", ".<.gX$X[.-X", "xXD.H | Z m C.e 8 e.-.,.0.X 7.kX", "3X{ J ] M b B : - ;...=.6. Q VX", "qXXX} U.Z.A 9Xz.j.iXw.a.`.y.H.qX", "*X6X2X6XrX4X-XiXsX-X>X c #E50202", ", c #EB0000", "< c #E30B09", "1 c #F40000", "2 c #FE0202", "3 c #EA1010", "4 c #E51615", "5 c #E50D14", "6 c #CC2323", "7 c #C82F2E", "8 c #D82727", "9 c #CA322D", "0 c #CC3E3C", "q c #CD3534", "w c #D93E30", "e c #DA382A", "r c #E12A3C", "t c #D87314", "y c #E96C00", "u c #F76F00", "i c #EA7807", "p c #F47400", "a c #FF7600", "s c #FE7D02", "d c #F5790F", "f c #E57D10", "g c #CB403F", "h c #D44B3F", "j c #D97D24", "k c #D17B37", "l c #BC5258", "z c #B9615F", "x c #BB5060", "c c #807F7C", "v c #B86764", "b c #BC7976", "n c #B27077", "m c #C14D4A", "M c #C94A49", "N c #C74644", "B c #C7564E", "V c #C45C5A", "C c #C55553", "Z c #C36455", "A c #D05660", "S c #C57978", "D c #C56E66", "F c #01E600", "G c #02FE02", "H c #00F600", "J c #0CEB14", "K c #18E41C", "L c #26D727", "P c #35CD3B", "I c #13D410", "U c #41D731", "Y c #67BF64", "T c #46C744", "R c #4FD341", "E c #56C151", "W c #4ACD5A", "Q c #5AC561", "! c #6FC373", "~ c #FE8203", "^ c #E58114", "/ c #CB833B", "( c #D98B32", ") c #D9DB0A", "_ c #D8D716", "` c #E5E503", "' c #E9EA00", "] c #E9EA06", "[ c #F4F400", "{ c #FEFE02", "} c #E2E717", "| c #CBCA34", " . c #D8D638", ".. c #D5D52A", "X. c #CDE03D", "o. c #BCBC5F", "O. c #84817E", "+. c #AA827E", "@. c #A4847D", "#. c #BE817D", "$. c #BC9C7C", "%. c #B68C63", "&. c #82B179", "*. c #AAA77B", "=. c #BDBA77", "-. c #C38B58", ";. c #C7925F", ":. c #C3877E", ">. c #C29B75", ",. c #C6B563", "<. c #B9C05E", "1. c #C4C346", "2. c #CBDD41", "3. c #C3C055", "4. c #C6D357", "5. c #C0C67E", "6. c #C5C463", "7. c #6E009A", "8. c #720E9A", "9. c #751D9A", "0. c #7B2D9A", "q. c #7202A4", "w. c #7508A6", "e. c #7F15A8", "r. c #7C01B2", "t. c #7E26A0", "y. c #7E46AD", "u. c #6A74BC", "i. c #263FEE", "p. c #233DFC", "a. c #3044D5", "s. c #2640F3", "d. c #2A43FD", "f. c #2742F7", "g. c #364FFF", "h. c #3951FF", "j. c #4B5AC6", "k. c #6C79C6", "l. c #7A79C7", "z. c #8205B4", "x. c #8301B9", "c. c #8026A2", "v. c #87459F", "b. c #884E9B", "n. c #937999", "m. c #BA7583", "M. c #804AAD", "N. c #8655AD", "B. c #936BA9", "V. c #9C7DB0", "C. c #906AA7", "Z. c #7AC986", "A. c #788CC7", "S. c #6883CA", "D. c #868481", "F. c #8B8987", "G. c #94928F", "H. c #999793", "J. c #AB8883", "K. c #BC8A86", "L. c #BC928F", "P. c #AB9A97", "I. c #BF9C97", "U. c #B39998", "Y. c #BCA58D", "T. c #BDBC85", "R. c #A3A29E", "E. c #BDAE9A", "W. c #A9BD9F", "Q. c #BEBD94", "!. c #8F90A6", "~. c #9B9EAD", "^. c #9490AF", "/. c #BE9CA4", "(. c #AE9DB3", "). c #9EA0B9", "_. c #A6A4A0", "`. c #AAA6A3", "'. c #AFACA9", "]. c #ABA9A5", "[. c #B2ADAA", "{. c #BEA8A3", "}. c #B4B2AE", "|. c #BAB7A6", " X c #AAAAB9", ".X c #B8ABB5", "XX c #B8B6B3", "oX c #BCBAB6", "OX c #BFBCBA", "+X c #BAB3B5", "@X c #ABB9B2", "#X c #C08C89", "$X c #C39792", "%X c #C69F92", "&X c #C0A39F", "*X c #CCBB90", "=X c #C2A88A", "-X c #C1ADA9", ";X c #C4B9A7", ":X c #C1AEB3", ">X c #C3B6B2", ",X c #C2BDB5", "XiXCXhXmXuX,XcXhXmXaXoXuXmXhXhXhXmXaXmXhXhX", "hXhXNXb < > l hX9X7 , = I.JX=.) ' 1.fXiX| ' _ Q.mXuXuX9X9XpXmXhX", "hXCX-X> 2 2 1 K.V 2 2 2 8 |.` { { [ T.6.{ { { _ {.S 1 2 2 g sX_ { { ] 5X*X[ { { | zXH.G.[.D.pXmXhX", "hXhXNX>XN w $XNXhXS r A 0XBX1X4.2.3XmXfX6XX.4.eXMXpXpXfXiXhXhXhX", "hXhXlXpXA.S.rXlXMX4XW Z.vXBXpXn x :XCXZX/.l m.bXMXaXpXhXuXMXhXhX", "hXhXdXj.p.d.a.eX2XF G H E cX7 2 2 * >X$X> 2 2 m IXO o # ,XCXfX", "hXBX).s.h.h.d.l.P G G G J J.1 2 2 2 v M 2 2 2 < 9XF.& R.+ fXjXhX", "hXBX Xi.h.h.p.qXR G G G K E., 2 2 2 b C 2 2 2 * 0X+X[.X= 2 > b DXR.R.XXG.fXmXhX", "hXhXmXCX~.!.gXhXVXuXY .XLXvXPXP.J.nXCXNXtXn @XPXhXGXGXBXBXhXhXhX", "hXhXbX$.f i -.mX9X/ d j E.KXK.4 3 m mXiX0 3 ; I.GXO.$ uX'.hXmXhX", "hXMX;Xi ~ ~ p Y.;.a ~ s j [.< 2 2 1 K.D 2 2 2 ; NXO . }.F.pXjXhX", "hXCXY.p ~ ~ s %.-.s s ~ ^ +.1 2 2 2 z N 2 1 2 > ,X9X}.fXXXhXmXhX", "hXMX9Xt s s y -X>.u ~ a / rX- 2 2 , -XS 1 2 2 q NXP.G.'.D.pXMXhX", "mXhXmX;X( ( =XmXbX>.k -.0XCX-Xm 0 #XNXgXS w B 0XNX9XiXfX9XhXhXhX", "hXhXBXpXA.k.wXBXVX|.o.3XcXMXgXb v ,XZXlX.XN.^.lXMXhXpXmXpXjXhXhX", "hXmXdXj.p.d.a.wX7X' { [ 3.bXq 2 2 * 1X(.7.z.q.b.HXO o # oXCXhX", "hXAX).s.g.h.d.k. .{ { { } J.1 2 2 2 Z N.r.z.x.7.uX% @ R.+ pXmXhX", "fXBX Xi.g.g.f.A. .{ { { } P., 2 2 2 D N.r.z.z.e.oXmXCXXX_.fXhXhX", "hXhXjXu.f.d.j.aX;X) { ' =.ZXl 1 2 6 iX+X8.r.q.B.lXOXuX'.G.pXMXhX", "hXhXhXmX^.!.aXlXVXeX*.|.VXjXFXU.+.nXmXBX0Xn.[.FXmXBXBXGXUXMXhXhX", "hXhXmXT.} ] o.cXpXT J L W.KXK.; 4 V mXiXM.e.t.'.GXD.! U.+ aXvXcX", "hXCX,X` { { [ *XQ G G H L :X< 2 2 1 :.C.q.x.r.9.BXO o # eXBXhX", "hXVXT.[ { { { ,.P G G G J @.2 2 2 2 Z y.x.z.z.w. c #BA211E", ", c #BB2D2B", "< c #B63533", "1 c #C40E0D", "2 c #CD0908", "3 c #D40303", "4 c #DB0000", "5 c #D20B0B", "6 c #C71816", "7 c #E30000", "8 c #EC0000", "9 c #F30000", "0 c #FE0202", "q c #C9211E", "w c #C12221", "e c #CA322E", "r c #CB2F35", "t c #BE6A18", "y c #B76F25", "u c #B47536", "i c #BF722A", "p c #D97107", "a c #D06C0A", "s c #E66D00", "d c #F57301", "f c #FD7D01", "g c #FB7600", "h c #ED7000", "j c #CC7925", "k c #BA4847", "l c #BD514F", "z c #AD5755", "x c #BC5652", "c c #B55A58", "v c #AF4745", "b c #BB655E", "n c #B37A42", "m c #807D7A", "M c #AC6A67", "N c #B36A67", "B c #AC7B78", "V c #B47B79", "C c #B37069", "Z c #C14A46", "A c #C0534C", "S c #29B728", "D c #36B134", "F c #43B63E", "G c #02DE02", "H c #07D406", "J c #0AC809", "K c #00EA00", "L c #00E700", "P c #01F401", "I c #01FE01", "U c #28C126", "Y c #35C838", "T c #57BA5E", "R c #54AE52", "E c #6BB466", "W c #6FB16F", "Q c #3FC340", "! c #43C44B", "~ c #BCBC1A", "^ c #BBBB26", "/ c #B5B538", "( c #FF8302", ") c #C3823A", "_ c #DBDB03", "` c #D5D508", "' c #C5C515", "] c #CECC0D", "[ c #E3E300", "{ c #ECEC00", "} c #EAE600", "| c #F3F400", " . c #FEFE02", ".. c #CDCC24", "X. c #C5CA39", "o. c #B58353", "O. c #AB8155", "+. c #AFAE43", "@. c #ADAC4A", "#. c #B5B455", "$. c #B5B553", "%. c #B58B66", "&. c #B89A78", "*. c #AE827F", "=. c #87B07F", "-. c #B3B165", ";. c #B6B674", ":. c #B0AE70", ">. c #C1AE78", ",. c #C39052", "<. c #C3C347", "1. c #67068F", "2. c #6C188E", "3. c #690295", "4. c #6B009C", "5. c #6A0893", "6. c #6C1190", "7. c #7300A4", "8. c #7601AA", "9. c #7B00B1", "0. c #6E00A0", "q. c #4A57B5", "w. c #4955B3", "e. c #5E69B4", "r. c #6E75AB", "t. c #7178AE", "y. c #767DBB", "u. c #6067A9", "i. c #2C3DD8", "p. c #1F39F0", "a. c #253CEC", "s. c #233EFC", "d. c #223BF3", "f. c #3648CA", "g. c #3044CD", "h. c #2541FF", "j. c #2943FC", "k. c #2F4AFF", "l. c #334CFE", "z. c #3951FF", "x. c #3650FF", "c. c #4555CC", "v. c #5B6CC8", "b. c #8203B4", "n. c #8501BC", "m. c #855994", "M. c #8C6797", "N. c #93749C", "B. c #8E7AA8", "V. c #7783BA", "C. c #7F87AB", "Z. c #858481", "A. c #8D8B88", "S. c #918D8B", "D. c #93918E", "F. c #99829D", "G. c #9B9995", "H. c #989793", "J. c #AC8885", "K. c #B48884", "L. c #B48B87", "P. c #AD908D", "I. c #B7948C", "U. c #A29D9C", "Y. c #AD9995", "T. c #B59994", "R. c #B59797", "E. c #AC8C9A", "W. c #89B285", "Q. c #95B397", "!. c #B2B384", "~. c #A3A19D", "^. c #ACA897", "/. c #B4A59D", "(. c #A4B39C", "). c #BAB39A", "_. c #ADAA8A", "`. c #9885A5", "'. c #9997AD", "]. c #979BBA", "[. c #A08DA3", "{. c #A797A5", "}. c #BD9DAA", "|. c #93A1B5", " X c #A3A4A3", ".X c #A9A6A3", "XX c #ACAAA6", "oX c #ABA8AA", "OX c #B4A6A2", "+X c #B5ABA5", "@X c #B1ADAC", "#X c #B4B3AC", "$X c #B9B2AD", "%X c #B9B5A5", "&X c #ABABB1", "*X c #ACBCB4", "=X c #B6B5B3", "-X c #B9B6B3", ";X c #BBBAB4", ":X c #BDBDBA", ">X c #B4B8B4", ",X c #B7ABB0", "XIXXX@XIX-XuXNXkXkXkX", "kXkXkXNXyX6 0 0 0 0 0 e 6X5 0 0 0 0 0 k *X_ . . . . .#.XX} . . . .| -.ZX#XkX-X-XjX-XfXNXkXkXkX", "kXkXkXBX4X5 0 0 0 0 0 w X7 0 0 0 0 0 r (.} . . . . .$._.| . . . . .$.ZXuX.XkXkX XuXBXkXkXkXkX", "kXkXkXkXmX, 0 0 0 0 0 k eX6 0 0 0 0 0 c wX] . . . .| ;.-X_ . . . .{ !.SXU.Z.@X#XZ.~.BXkXkXkXNX", "NXkXkXkXSXR.3 0 0 0 2 +XJXJ.4 0 0 0 6 ;XSX:.} . . .^ rXZX-.{ . . ./ iXBXXXZ.-X-XZ.XXkXkXkXkXkX", "kXkXkXkXkXAXOXl e l @XSXfXAXR.k r c :XVXkXcX_.<.X.-.:XDXkXcX:.<.X.-.rXBXkXkXtXkXkXrXkXkXkXkXkXkX", "kXkXkXkXfXNXKX*X|.:XKXkXkXNXFX*XQ.qXFXkXkXCXAX{.E.wXGXkXfXCXMX{.E.vXGXfXkXCXUXBXCXJXSXkXkXkXkXkX", "kXNXkXkXNXdXu.i.j.i.r.dXVXtXD G P H W bXBX:X< 7 9 2 B VXCX@X> 8 8 1 P.SXCX~.# ;X;X# ~.CXkXkXkXkX", "kXkXkXkXzXr.d.z.l.l.p.C.FXR I I I I L W.SX< 0 0 0 0 4 /.MXw 0 0 0 0 5 $XJXO & & + CXkXkXkXkX", "kXkXkXDX8Xf.x.l.l.l.k.q.).H I I I I I F }.7 0 0 0 0 0 c /.8 0 0 0 0 9 c PXm X ~.U.X m CXkXkXkXkX", "kXkXkXBX7Xg.l.l.l.l.k.c.^.L I I I I I Y E.7 0 0 0 0 0 k P.9 0 0 0 0 0 c HXfX-XBXBX-XkXBXkXkXkXkX", "kXkXkXNXzXq.j.z.l.z.h.e.dXS I I I I I E pX1 0 0 0 0 9 V 4X5 0 0 0 0 8 P.GXG.; @XXX; G.DXkXkXkXNX", "kXkXkXkXCX@Xg.s.j.h.f.-XFXQ.H I I I S 1XGX*.4 0 0 9 < aXGXM 7 0 0 8 v mXBX#XA.;X;XA.@XBXkXkXkXkX", "kXkXkXkXkXBX;Xy.v.C.7XzXkXFX#XT ! W 0XBXkXVXOXc A V sXVXkXVXR.x l K.jXkXkXkXdXNXkXfXkXkXkXkXkXkX", "kXkXkXkXkXBXMX).&.).VXkXkXlXZXOXI.2XAXkXkXSXmXY.P.4XHXkXkXSXhXY.P.qXSXkXkXCXJXCXkXCXBXkXkXkXkXkX", "kXNXkXkXBXrXu s d s n sXHX4Xy h g s O.cXSX%X: 9 9 4 M VXBX/.1 8 9 3 B AXCXH.+ XXfXU.4XlXkXkXkXkX", "kXkXkXkXAXO.g ( ( ( h %.LXu f ( ( ( s &.MX, 0 0 0 0 7 R.hX6 0 0 0 0 3 +XJXO # #X- G.BXkXkXkXkX", "kXkXkXBXqXa ( f ( f ( j &Xp ( f ( f f ) .X7 0 0 0 0 0 x Y.8 0 0 0 0 0 c PXS.+ XuX~.:XlXkXkXkXkX", "kXkXkXBX4Xa ( f ( ( ( j Xp ( f f ( ( ) {.7 0 0 0 0 0 l Y.9 0 0 0 0 0 c HXdX-XkXfX@XuXkXkXkXkXkX", "kXkXkXlXmXn f ( ( ( f o.nXj f ( ( ( g %.nX6 0 0 0 0 8 K.yX5 0 0 0 0 7 Y.HXG.; .XXX; G.BXkXkXkXkX", "kXkXkXkXBX;Xt g f d y 9XSX@Xa g f d u yXGXP.3 0 0 8 v jXSX*.4 0 0 8 c AXBX#XD.7X7XD.-XNXkXkXkXkX", "kXkXkXkXkXVXrX&.,.&.sXBXfXNX:X&.%.T.aXkXkXVX;XV N R.mXlXkXVX#XC N R.mXkXkXNXkXNXkXkXNXNXkXkXkXNX", "kXkXkXkXkXBXdX].V.].xXBXNXDXiX!.;.).cXkXkXSXrXK.V +XVXkXkXCX:X`.B.=XzXNXkXCXCXCXBXBXCXkXkXkXkXkX", "kXkXkXkXBXX;XrXG.;XlXkXkXkXkX", "kXkXkXkXkXNXxX].y.].zXNXkXBXpX).;.2XcXNXfXBXsXP.B $XAXkXkXBX8X`.F.:XzXkXkXkXuXkXkXBXVXkXkXkXkXkX", "kXkXkXkXkXNX0X;.<.;.0XDXkXBX 8 8 3 C VXCXoX2.7.7.1.F.CXBXH.$ #X#X@ G.CXkXkXkXkX", "kXkXkXkXaXBXcX%X!.$XcXBXfXDXbX(.W.#XDXkXgXBXmXT.L.;XVXkXkXCX8X{.[.:XCXkXkXCXJXBXBXJXCXkXkXkXkXkX", "kXkXkXkXkXkXfX;X&X;XfXkXkXkXdX-X,X1XfXkXNXkXuX>X*X7XkXkXkXkXtX>X#X7XkXkXNXkXkXkXkXkXkXkXkXNXkXkX", "kXkXkXNXkXNXkXuXuXuXkXkXkXBXfXuXuXuXkXkXkXkXfXuXtXuXkXkXkXkXfXuXuXaXkXkXkXkXkXkXkXkXkXkXkXkXkXNX", "kXkXkXkXkXkXkXNXBXBXkXkXkXkXkXNXBXBXkXkXkXkXkXBXBXNXkXkXkXkXkXNXBXNXkXkXNXkXkXkXkXkXNXkXkXkXkXkX", "NXkXkXkXkXkXkXkXkXkXNXkXkXkXkXkXkXkXkXkXkXkXkXkXNXkXkXkXkXNXkXkXkXkXkXkXkXkXkXkXkXkXkXkXNXkXkXkX", "kXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXNXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXNXkXkXkXkXkXkXkXkX" }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/inertia-icon.c0000644000175300017530000003413612161170371016273 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 174 2", " c #000000", ". c #050505", "X c #080807", "o c #080808", "O c #0C0C0B", "+ c #0C0C0C", "@ c #131312", "# c #151515", "$ c #1D1D1C", "% c #222221", "& c #232722", "* c #2A2928", "= c #2A2A2A", "- c #353534", "; c #3D3C3B", ": c #504F4D", "> c #706F6C", ", c #71706E", "< c #27BF26", "1 c #36B834", "2 c #16DB16", "3 c #20D61F", "4 c #0AE309", "5 c #00F800", "6 c #00FF00", "7 c #5BA558", "8 c #67A064", "9 c #969491", "0 c #9D9C98", "q c #9E9D99", "w c #9F9D99", "e c #9EB899", "r c #A5A4A0", "t c #A6A7A1", "y c #AAA7A3", "u c #AFACA8", "i c #AFADA9", "p c #B3AEAD", "a c #B1B0AB", "s c #B4B2AE", "d c #B5B3AF", "f c #B4B6AE", "g c #B5B6AF", "h c #B8B3AE", "j c #B7AFB1", "k c #A2B5B2", "l c #A4B6B3", "z c #A1BAB7", "x c #A8BAB7", "c c #A1BAB8", "v c #B6B7B0", "b c #B9B7B3", "n c #BBB2B5", "m c #BAB8B4", "M c #BCBAB6", "N c #BDBBB7", "B c #BBBCB5", "V c #BEB6B8", "C c #B6BDB9", "Z c #B8BDB9", "A c #B8BFBB", "S c #BEBCB8", "D c #BEBDB8", "F c #C4B7BD", "G c #C0BEBA", "H c #C5B9BE", "J c #B9C0BC", "K c #BBC0BC", "L c #BCC1BD", "P c #C1C0BB", "I c #C4C3BE", "U c #C5C3BE", "Y c #C5C4BF", "T c #C8C4BF", "R c #CEBBC7", "E c #89DEDE", "W c #8ADEDE", "Q c #8ADFDE", "! c #8CF4F4", "~ c #8DF4F4", "^ c #8DF5F5", "/ c #C6C5C0", "( c #C7C5C1", ") c #C6C6C1", "_ c #C7C6C2", "` c #C8C4C0", "' c #C8C6C1", "] c #C9C6C1", "[ c #C9C7C2", "{ c #CDC7C2", "} c #CCC1C5", "| c #CAC8C3", " . c #CAC9C4", ".. c #CBC9C4", "X. c #CBC9C5", "o. c #CBCAC5", "O. c #CBCBC5", "+. c #CCCAC5", "@. c #CCCBC5", "#. c #CDCBC6", "$. c #CDCCC7", "%. c #CECCC7", "&. c #CECCC8", "*. c #CECDC8", "=. c #CFCDC8", "-. c #CECDC9", ";. c #CECEC9", ":. c #CFCEC9", ">. c #CECECA", ",. c #D6CBC6", "<. c #D6CCC6", "1. c #D2C5CC", "2. c #D2C7CC", "3. c #D0CEC9", "4. c #D1CCCA", "5. c #D1CFCA", "6. c #D3CFCA", "7. c #D5CFCA", "8. c #D8CFCA", "9. c #D1D0CA", "0. c #D1D0CB", "q. c #D2D0CB", "w. c #D2D1CB", "e. c #D2D1CC", "r. c #D3D1CC", "t. c #D3D2CD", "y. c #D4D1CD", "u. c #D4D2CD", "i. c #D4D3CD", "p. c #D4D3CE", "a. c #D5D3CE", "s. c #D4D4CD", "d. c #D5D4CE", "f. c #D5D4CF", "g. c #D6D4CF", "h. c #D6D5CF", "j. c #D7D5CF", "k. c #D9D2CC", "l. c #DBD2CC", "z. c #DDD3CE", "x. c #DBD4CF", "c. c #D6D5D0", "v. c #D7D5D0", "b. c #D8D5D1", "n. c #D8D6D1", "m. c #D9D7D1", "M. c #DAD7D1", "N. c #D9D7D2", "B. c #DDD6D0", "V. c #DAD8D3", "C. c #DAD9D3", "Z. c #DBD9D3", "A. c #D9D9D4", "S. c #DAD9D4", "D. c #DBDAD5", "F. c #DCDAD5", "G. c #DDDBD6", "H. c #DEDBD6", "J. c #DEDCD7", "K. c #E0D7D9", "L. c #E0DDD8", "P. c #E1DED9", "I. c #E1DFD9", "U. c #E2DFDA", "Y. c #E3DFDA", "T. c #E2E0DB", "R. c #E3E0DB", "E. c #E4E2DC", "W. c #E5E4DF", "Q. c #E6E4DE", "!. c #E6E4DF", "~. c #E9E6E1", "^. c #E8E6E2", "/. c #EAE7E2", /* pixels */ "a.%.N.F.:.:.a.3.e.e.e.c.a.g.c.a.", "q.E.9 , U.:.b ` S G E.~.E./.c.3.", "F.q - X > +.M c.3.m ^.U.F.Y.[ &.", "F.: + o $ b N.c.A.+.U.U.U.U.| :.", "N.g.; @ b a.u 5.` s ~.L.J.U.[ :.", "e.a.[ h A.[ [ { ` X.:.+.U +.G 3.", "a.x.Z x 7.:.z.l A k.D.v % r U.%.", "N.Z Q ! z ,.c ~ Q L y * * . w N.", "A.Z Q ~ z <.z ! Q A u # + q A.", "q.z.C k x.:.z.l A k.D.v & r U.%.", "g.%.q.7.%.+.) { ` X.X.2.R 2.X.q.", "g.g.g.g.g.X.a q.` v } 8 2 7 H g.", "g.a.a.a.g.| g.g.N.3.V 2 6 4 v b.", "s.a.a.a.g.Y M g.3.B F 1 5 < n b.", "a.a.a.a.a.q.N | P U i.j e p 2.q.", "a.a.a.a.a.g.A.g.g.x.i.A.K.Z.g.a." }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 101 2", " c #020202", ". c #0B0B0B", "X c #10100F", "o c #262524", "O c #282727", "+ c #2C2C2B", "@ c #3D3C3B", "# c #484746", "$ c #4A4947", "% c #4B4A48", "& c #52514F", "* c #555553", "= c #585755", "- c #595856", "; c #605F5D", ": c #656462", "> c #6A6967", ", c #6F6E6B", "< c #757471", "1 c #807F7C", "2 c #13BC12", "3 c #1FBC1E", "4 c #22BE21", "5 c #2FB02D", "6 c #3EAA3C", "7 c #0DCA0D", "8 c #00D100", "9 c #0AD209", "0 c #14C413", "q c #00E200", "w c #00F200", "e c #02FE02", "r c #49A746", "t c #4FA34C", "y c #659462", "u c #758771", "i c #768A72", "p c #798A75", "a c #7D8179", "s c #7A9F76", "d c #77A473", "f c #779694", "g c #799492", "h c #7E9C9A", "j c #67B2B2", "k c #68B1B1", "l c #75C3C3", "z c #848480", "x c #8A8286", "c c #8C8A87", "v c #8E8D89", "b c #94888F", "n c #93928E", "m c #979692", "M c #9B9396", "N c #9B9A96", "B c #9C9C99", "V c #A1979C", "C c #9DA09C", "Z c #A3A09D", "A c #A69FA0", "S c #A99BA3", "D c #A7A6A2", "F c #AAA5A3", "G c #ABAAA6", "H c #AEACA9", "J c #B5A5AF", "K c #B1AEAB", "L c #B4B2AE", "P c #BEADB8", "I c #B6B5B1", "U c #B9B7B3", "Y c #BCBAB6", "T c #BFBDB9", "R c #C1BFBB", "E c #BEC1B8", "W c #C4C2BD", "Q c #8AEBEB", "! c #8DF0F0", "~ c #97FBFB", "^ c #9EFFFF", "/ c #A3FFFF", "( c #C7C5C0", ") c #C8C7C2", "_ c #CCCAC5", "` c #CECDC8", "' c #D3CAC5", "] c #D4C4CD", "[ c #D1CFCA", "{ c #D5D3CE", "} c #DBC9D4", "| c #D7D5D0", " . c #D9D6D2", ".. c #DCDAD5", "X. c #DFDDD8", "o. c #E3D0DC", "O. c #E1DDD9", "+. c #E4E2DC", "@. c #EDEBE6", "#. c #F1EEE9", "$. c #F3F1EB", /* pixels */ "{ { | | | { { { .{ .| | | { .{ { .| { { { { { | { { | { { { ", "{ { _ _ ) { ._ ) _ _ _ ) ` [ _ [ [ ) _ ` { [ ` ` ` _ ` [ ` { { ", "{ { [ _ | L 1 { [ [ ) W { ) I { W Y .) { $.#.$.$.#.#.#.$.{ [ | ", "{ { { ...* N +.{ ` { ` c I ..` n H ...$...+.+.+.+.+.+.L { { ", "{ { .._ + > +.{ ) n ) ..{ { O.N L +.@. .O.O...O.....L [ { ", "{ { ` * I o N ` Y U ..{ { { { [ H +.#...O.O.O.O.O.O.L { { ", "[ +.$ . @ . n ....{ { { { { . . .$...+...O.O.O...L { { ", "{ .G @ , W W W .{ { { { { U +.@...O.O.O.O.O...L { { ", "{ { +.Y X # +.{ W n { ..{ { +.F F +.@. .O...O.O.O...L { { ", "{ { [ ..R @ < { ._ { T v W .{ Z C { ..$.+.+.+.+.+.+.+.L { { ", "{ { { _ +.D & .{ { _ ) .R H { T F .` ..+.W W W ) W W W K [ { ", "{ [ ) _ W [ +.W ) _ W W ) ` ` { ` { ) ) W W U T U U T Y T Y { { ", "{ { { { .._ Z O.{ .` ` | { ..F W ..{ { ) .| $.= + +...| ` [ { ", "| { { ..' h k B ..{ _ _ { ..Z k h ' .{ ) { { & @ W .) { { ", "{ { .' g Q / l B O.` _ O.B l / Q g ' .W @.; . ; . @ +.` ` { ", "{ .' f Q ^ ~ / l C { { C l / ~ ^ ! f _ ` $ O m + U { { ", "| | _ f Q ^ ~ / l B { { C l / ~ ^ ! g _ ` $ + U { { ", "{ { .[ g Q / l B O.) ` O.N l / Q g ' | W #.: . @ +.` { { ", "{ { { .' n j C ..{ ` _ { ..Z j h ' .{ W .{ - @ W .) { { ", "{ | | { .._ F ..{ .` ` .{ ..F _ ..{ { W | { @.& O +. .| _ ` { ", "| [ ) ) ( _ { W ) ) W W ) ` ` { ` [ W ) W ) W [ W E ` ) ) W { { ", "{ { { [ { { [ { { { ` ) .T H | W F | { W { [ K } o.K ) .) { { ", "{ | | { .{ { { { { ` { E v W | [ Z N .._ { M a t r u b _ ` ` { ", "{ { { { { { { { { { { W n { ..{ { +.Z G { F p 9 e e q y B ) { { ", "{ { { { { { { { { | { W W | { { { { { Y ) P 5 e e e e 0 F ) [ | ", "| { { { | { { { { | _ [ ..{ { { { { | .._ ] 2 e e e e 8 U | ` | ", "{ { { { { { | { { { { Y Y ..{ { { | ` K ` J 6 e e e e 4 S ( [ | ", "{ { { | | { { { { { _ ) n ) ..{ | X.N L { H z 4 w w 7 p A _ [ { ", "{ { { { { { { { { .` { ) c I ._ n G ..( .A x s d x M { _ [ { ", "| { { | { { { | { | ` _ ..{ Y .W W ..{ ) | .R ..O.W { ._ [ | ", "{ { { { { | { { { { { { { { .{ | | { { { { [ ..[ [ .{ { { { { ", "{ { { { { { { { { { { { { { { { { { { { { | | [ | | { { { | { { " }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 124 2", " c #010101", ". c #0B0B0B", "X c #131312", "o c #191918", "O c #242423", "+ c #2B2B2A", "@ c #302F2E", "# c #363634", "$ c #3D3D3B", "% c #464543", "& c #4E4D4C", "* c #504F4D", "= c #565654", "- c #595856", "; c #5E5D5B", ": c #5C6463", "> c #5D7776", ", c #5C7877", "< c #636260", "1 c #686765", "2 c #696865", "3 c #666D6B", "4 c #6F6E6C", "5 c #706E6C", "6 c #657573", "7 c #6A7674", "8 c #607B79", "9 c #787774", "0 c #7B7A77", "q c #7D7C79", "w c #807F7C", "e c #02AE02", "r c #0DA20D", "t c #06B305", "y c #00BE00", "u c #259524", "i c #3D8C3B", "p c #369135", "a c #369D34", "s c #3B9A39", "d c #21A620", "f c #00C400", "g c #00CF00", "h c #00D000", "j c #00D900", "k c #00E200", "l c #00EE00", "z c #01FE01", "x c #468C44", "c c #529750", "v c #70896C", "b c #778B74", "n c #798975", "m c #82817E", "M c #847F80", "N c #5A8383", "B c #5D9695", "V c #5D9998", "C c #619C9C", "Z c #76D3D3", "A c #7AD2D3", "S c #7DDBDC", "D c #868581", "F c #8A8A86", "G c #8E8C89", "H c #908F8C", "J c #899385", "K c #93958E", "L c #91988C", "P c #9C8E97", "I c #9E8F99", "U c #969491", "Y c #9C9693", "T c #9B9A96", "R c #9F9E99", "E c #A2939C", "W c #A29C9D", "Q c #A3A29E", "! c #A79EA1", "~ c #A6A4A0", "^ c #A9A7A4", "/ c #ABAAA5", "( c #AEA0A8", ") c #AEADA9", "_ c #B5ABA7", "` c #B4ADA9", "' c #B3B1AD", "] c #B6A7B0", "[ c #B6B4B0", "{ c #B8B7B2", "} c #BBBAB5", "| c #BFBDB9", " . c #C3BEBA", ".. c #C8BEB9", "X. c #C4C2BD", "o. c #CDBAC6", "O. c #88E7E7", "+. c #8CEBEC", "@. c #93F5F6", "#. c #96FBFB", "$. c #9BFFFF", "%. c #A3FFFF", "&. c #C7C5C1", "*. c #C8C6C2", "=. c #CBC9C5", "-. c #CFCDC8", ";. c #D1CFCA", ":. c #D5D3CE", ">. c #D7D5D0", ",. c #D8D6D1", "<. c #DCDAD5", "1. c #DFDDD8", "2. c #E0DCD7", "3. c #E6D6DF", "4. c #E0DDD9", "5. c #E4E1DC", "6. c #E7E6E0", "7. c #EAE5E1", "8. c #EDEAE5", "9. c #EFEEE8", "0. c #F1EEE9", "q. c #F3F1EB", "w. c #FCFAF4", "e. c #FFFFFC", /* pixels */ ":.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.>.:.:.:.:.>.:.:.:.:.>.:.:.:.:.:.:.:.:.", ":.:.:.:.:.:.:.>.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.>.:.>.:.:.:.", ":.:.:.:.,.,.,.,.,.>.>.>.,.:.,.,.>.,.,.,.,.>.>.>.,.>.,.:.,.:.,.,.,.,.,.,.,.,.,.,.,.>.>.>.:.:.:.:.", ":.:.>.:.*.*.=.*.*.=.-.=.=.=.=.*.*.*.=.*.*.&.=.*.=.*.&.=.*.=.=.*.*.&.*.&.*.*.&.*.*.*.&.=.:.:.:.>.", ":.:.:.:.=.=.=.=.&.X.| X.&.=.=.=. .X.-.*.:.;.*.=.=.-.:.*.-.X.;.q.8.0.0.0.0.0.0.0.0.8.q.-.;.>.:.:.", ":.:.:.:.>.>.:.:.6.$ / 7.:.:.<.=.-.,.4.' q =.<.:.D ~ 1.,.-.<.e.0.0.0.0.0.0.0.0.0.q.5.{ :.:.:.:.", ":.:.:.:.:.:.,.:.F X * } <.:.:.&.=.1.T 0 X.:.:.:.=.D G <.=.<.w.<.<.<.2.<.<.<.<.2.4.X./ >.:.>.:.", ":.:.:.:.;.:.:.$ . T <.:.&.:.[ w 4.<.:.:.:.>.6.G Q :.<.w.<.4.4.4.4.4.4.4.2.4.=.` >.:.:.:.", ">.:.:.:.<.9.< + % X =.7.&.-.q *.>.-.:.>.:.:.:.>.w &.1.w.<.2.4.4.4.2.4.4.<.5.*.) >.:.:.:.", ":.:.:.<.' 9 o / e.. & T &.=.*.>.>.>.:.>.:.:.:.,.*.&.<.w.<.4.4.2.4.4.2.4.4.5.&.) >.>.:.:.", ":.:.;.5.q . . O =.-.,.:.:.:.>.-.:.:.:.:.,.-.<.w.<.2.4.4.2.2.4.4.2.5.&.) >.:.:.:.", ":.:.:.1.R # . O < &.=.>.:.:.>.>.;.:.:.:.:.>.=.1.w.<.4.2.2.4.4.4.2.2.5.&.) >.:.:.>.", ":.:.:.:.<.6.% ' 8.&.=.F ;.>.;.>.>.>.>.;.<.G .1.w.<.2.4.4.4.2.2.4.<.5.&.) >.:.:.:.", ":.:.:.:.:.,.[ o 5 <.:.&.>.W U 6.:.:.:.>.-.6.^ H >.<.w.2.4.2.4.4.4.4.4.4.5.&.) >.:.:.:.", ":.:.:.>.:.:.<.{ & . O F <.:.:.&.=.<.D T <.>.:.;.1.~ 9 >.-.<.w.<.4.2.2.2.<.4.2.2.4.&.) ,.:.:.:.", ":.:.:.:.:.:.:.<.6.+ W 9.;.:.>.*.-.,.>.G 0 =.,.>.D D :.,.=.1.w.4.4.5.5.5.5.5.5.4.7.=.) ,.:.:.:.", ":.:.:.:.;.;.:.;.:.W H X.>.;.;.>.&.=.:.-.<.X.=.:.-. .<.:.-.=.<.6.' [ ' [ ' ' [ [ [ [ ) ) >.:.:.:.", ":.:.>.;.X.X.X.X.X.-.;.X...X.X.X.} | &.X...*.X.X.X.&.X.X.X. .} X.} } } } =.=.} } } } } } :.:.:.:.", ":.:.:.:.>.>.>.>.:.<.<.,.:.,.>.,.=.-.>.>.,.:.,.<.<.:.>.:.>.:.&.,.>.>.<.-.& - >.,.>.>.<.&.:.:.:.:.", ">.:.:.:.:.:.:.;.5.U 3 :.>.:.;.,.&.=.>.:.;.<.X.: ' 1.:.:.>.-.&.>.:.:.5.R . ' 1.:.:.:.X.:.:.:.:.", ":.:.:.:.:.:.:.5.Y C Z 7 ;.,.;.,.&.=.>.:.<...8 O.N _ 1.:.:.-.&.>.:.:.= 1 ,.:.:.X.;.:.:.:.", ":.:.>.:.:.:.4.Y B $.%.A 7 -.,.>.&.-.:.<..., O.%.@.N ` 1.:.-.X.>.5.- 4 5.>.X.:.>.:.:.", ":.:.:.:.:.5.Y B %.$.#.%.A 3 ;.<.&.=.1..., +.$.#.$.#.N ` 1.-.&.-.Q 5.[ X ' -.&.:.:.:.:.", ":.:.:.:.<.Y V %.#.#.#.#.%.O.7 :.*.;.X., +.$.#.#.#.$.#.N ` >.;.% < & ; :.-.:.:.:.", ":.:.>.:.<.T V $.#.#.#.#.%.O.7 ;.*.;.X., +.$.#.#.#.$.#.N _ :.-.% ; :.;.:.:.:.", ":.:.:.:.:.4.Y B %.#.#.%.A 3 ;.<.&.=.1...8 +.%.#.$.#.N _ 5.-.X.:.Q X ` ;.X.:.:.:.:.", ":.:.:.:.:.:.5.Y B $.%.A 7 ;.,.>.*.-.:.<...> +.%.@.N _ 4.:.:.X.<.5.= 4 5.:.X.:.:.:.:.", ":.>.:.:.:.:.:.5.Y C A 7 ;.,.:.,.&.=.:.:.<..., S N _ 5.:.:.-.X.:.:.:.- . 2 <.:.:.X.:.:.:.:.", ":.:.:.:.:.:.:.:.1.T 3 :.>.:.:.>.*.=.:.:.:.<.X.: ' 1.;.:.:.-.X.:.:.:.5.R . ' 5.:.:.:.X.:.:.:.:.", ":.:.:.:.:.:.:.:.>.<.1.>.:.>.:.<.=.-.,.>.>.:.<.<.<.:.>.:.>.:.X.<.:.:.,.=.& - :.:.:.>.,.X.:.:.:.:.", ":.:.:.-.X.X.X.X.X. .X.X.X.X.X.X.} | &.X.X.&.X.X.X.&.X.X.&.| } X.X.X.X.*.;.-.&.X.X.X.X.| :.:.:.:.", ":.:.:.:.:.;.:.:.:.-.:.-.:.:.;.:.X.=.;.:.<.X.=.:.-.| >.:.:.-.X.:.-.<.=.X.:.:.| -.<.=.:.X.:.:.:.:.", ":.:.>.:.>.>.:.>.:.>.>.>.:.>.:.,.*.=.,.,.G q =.,.>.D D -.,.-.&.>.1./ 0 ] o.o.! q { <.:.X.:.:.:.:.", ":.:.:.:.:.:.:.:.:.:.:.:.:.:.:.,.*.=.<.D U <.>.:.>.<.^ 9 :.:.X.1.~ D U i r r x E w ' 1. .:.:.:.:.", ":.:.:.:.:.:.:.:.:.:.:.:.:.:.:.>.&.>.R P 6.-.>.>.>.;.6.^ G ,.&.&.D L e z z z z r R M -.X.:.>.:.:.", ":.>.:.:.:.>.:.:.:.:.>.:.:.:.:.>.*.=.G ;.>.>.>.;.>.>.;.,.G | -.Q E d z z z z z z a E / =.:.:.:.:.", ":.:.:.:.:.:.:.:.:.:.:.:.:.:.:.,.*.=.,.:.:.:.:.>.>.;.>.-.>.-.&.1.J g z z z z z z f W 1.X.;.:.:.:.", ":.:.:.>.:.:.:.>.>.:.:.:.:.:.:.,.&.-.<.:.:.:.:.:.>.>.:.>.<.-.| 7.b k z z z z z z g L 5.| :.:.:.:.", ">.:.:.:.>.:.:.:.:.:.:.:.:.:.:.:.*.*.&.>.:.:.:.:.;.>.>.,.*.*.&.>.U f z z z z z z t ! ;.X.:.:.:.:.", ":.:.:.:.:.:.:.:.:.:.:.:.:.:.:.,.&.;.n =.>.;.:.:.>.;.-.,.q *.-.R I s z z z z z l c P / =.:.:.:.:.", ">.:.:.:.>.:.:.:.:.:.:.:.:.>.:.,.&.;.[ w 1.,.:.:.:.>.5.H Q ,.X.:.m W u k z z j p W D <.X.:.:.:.>.", ":.:.:.:.:.:.:.:.>.:.:.:.:.:.:.,.&.=.4.R 0 &.>.:.:.-.D G <.;.X.<.| 0 I v i i n I 0 *.,.X.;.>.:.:.", ":.:.>.:.:.:.>.:.:.:.:.>.:.:.:.,.&.-.>.2.` q *.>.;.D Q <.>.;.X.>.<.=.D [ 3.3.^ F ;.,.:. .;.>.:.:.", ":.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.=.-.>.;.<.<.>.>.:.,.<.:.>.;.*.,.:.>.<.;.:.:.:.<.,.:.>.&.:.:.:.:.", ":.:.:.:.>.:.:.:.:.>.:.:.:.:.:.:.>.:.:.:.:.:.:.:.:.:.;.>.:.>.:.:.:.:.;.:.>.:.:.;.>.:.:.:.:.:.:.:.", ":.>.:.:.:.:.:.:.:.:.:.:.>.:.:.:.:.:.:.:.:.:.:.>.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.>.:.:.", ":.:.:.:.:.:.>.:.:.:.:.:.:.:.:.>.:.:.>.:.:.:.:.:.:.:.:.:.:.:.:.:.>.:.:.:.:.:.>.:.:.:.>.:.:.:.:.:.", ":.:.:.>.:.:.:.:.:.:.>.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.>.:.:.:.>.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.>." }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/keen-icon.c0000644000175300017530000003377612161170371015573 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 180 2", " c #000000", ". c #010101", "X c #050504", "o c #060606", "O c #070707", "+ c #080808", "@ c #080908", "# c #090909", "$ c #0A0909", "% c #0A0A09", "& c #0A0A0A", "* c #151514", "= c #191918", "- c #1C1C1B", "; c #1D1C1C", ": c #1E1E1D", "> c #1F1E1E", ", c #1F1F1E", "< c #20201F", "1 c #212020", "2 c #212120", "3 c #222221", "4 c #242322", "5 c #252424", "6 c #252524", "7 c #262625", "8 c #272626", "9 c #6B6A68", "0 c #706F6C", "q c #757472", "w c #767573", "e c #7A7976", "r c #7C7B78", "t c #7F7D7B", "y c #289127", "u c #52A24F", "i c #59AA56", "p c #66AB63", "a c #69AB66", "s c #6FAF6C", "d c #77B173", "f c #7CB378", "g c #82B57E", "h c #8B8A87", "j c #8E8C89", "k c #8E8D8A", "l c #8F8E8B", "z c #918F8C", "x c #91918C", "c c #92908D", "v c #92918D", "b c #92918E", "n c #93918E", "m c #93928E", "M c #959490", "N c #969592", "B c #979592", "V c #979692", "C c #989693", "Z c #9A9995", "A c #9B9A96", "S c #A09E9B", "D c #A09F9B", "F c #8FBC8B", "G c #90BA8C", "H c #A1A09C", "J c #A5A39F", "K c #A6A3A1", "L c #A6A4A1", "P c #A8A7A2", "I c #A8A6A3", "U c #A8A7A3", "Y c #A9A7A3", "T c #A9A2A4", "R c #A9A8A3", "E c #AAA9A5", "W c #ABA9A5", "Q c #ABAAA6", "! c #ACABA7", "~ c #AFAEAA", "^ c #B0AFAA", "/ c #B2B0AC", "( c #B2B1AC", ") c #B3B1AD", "_ c #B5B3AF", "` c #B5B4AF", "' c #B6B5B0", "] c #B7B5B1", "[ c #B7B6B2", "{ c #B8B7B2", "} c #BBB9B5", "| c #BCBBB6", " . c #BEBCB8", ".. c #BEBDB8", "X. c #BFBEB9", "o. c #AFC6A9", "O. c #BCCBB6", "+. c #C1C0BB", "@. c #C1C1BB", "#. c #C2C0BC", "$. c #C3C1BD", "%. c #C4C2BD", "&. c #C5C3BF", "*. c #C4CFBE", "=. c #C4D4BE", "-. c #C9C7C2", ";. c #CAC4C4", ":. c #CBC9C5", ">. c #CCCAC6", ",. c #CDCBC6", "<. c #CECCC7", "1. c #CECCC8", "2. c #CFCDC8", "3. c #CFCDC9", "4. c #D1CFCA", "5. c #C7D3C0", "6. c #C6D4C0", "7. c #CAD1C4", "8. c #D2D0CC", "9. c #D3D1CC", "0. c #D3D2CC", "q. c #D4D2CD", "w. c #D5D3CE", "e. c #D5D3CF", "r. c #D5D4CF", "t. c #D6D4CF", "y. c #D3D8CC", "u. c #D7D5D0", "i. c #D8D6D1", "p. c #D9D5D2", "a. c #D9D7D2", "s. c #DAD8D3", "d. c #DAD8D4", "f. c #DBD9D4", "g. c #DBD9D5", "h. c #DCDAD5", "j. c #DDDBD6", "k. c #DBDDD4", "l. c #DEDCD6", "z. c #DEDCD7", "x. c #DFDDD8", "c. c #E2DBDB", "v. c #E0DED9", "b. c #E1DFD9", "n. c #E2DDDB", "m. c #E1DFDA", "M. c #E2DFDA", "N. c #E2E0DA", "B. c #E2E3DA", "V. c #E3E1DC", "C. c #E3E2DC", "Z. c #E4E2DD", "A. c #E5E2DD", "S. c #E4E4DD", "D. c #E7E5DF", "F. c #E7E1E0", "G. c #E7E5E0", "H. c #E8E3E0", "J. c #E9E3E2", "K. c #EAE3E2", "L. c #E8E6E1", "P. c #E9E6E1", "I. c #E9E7E1", "U. c #E9E4E2", "Y. c #E9E7E2", "T. c #ECE6E4", "R. c #EEE5E7", "E. c #EAE8E3", "W. c #EBE9E4", "Q. c #EDEAE6", "!. c #EEECE7", "~. c #EFECE7", "^. c #EFE8E8", "/. c #F0E8E8", "(. c #F0EEE8", "). c #F1ECEA", "_. c #F3EDEB", "`. c #F2EFEA", "'. c #F4F2EC", "]. c #F5F3ED", /* pixels */ "X > 7 3 < < 3 * = 7 7 > 1 < > ", "7 J S 1.q.8.N.z n Z { i.9.j. .# ", "7 I { 4.j.j.L.N z / } y.v.A.%.# ", "> d.V.x.j.i.E.k ` ).6.s f K.#.# ", "3 q.j.f.j.i.L.z / N.c.F p R.+.# ", "3 q.j.d.f.i.L.k ( K.O.y G K.X.# ", "< d.V.v.v.j.!.n X.).S.L.N.~.3.# ", "- | %.+.#.+.<.r q D N N n M t X ", "> -.8.<.<.<.s.h q 9 e n W X.h ", "< s.v.v.z.j.E.C A n K T q.]./ ", "< q.z.s.j.i.L.k ` ).6.i o.T.I ", "< t.z.f.f.s.E.k / L.=.u g ^.R ", "< 9.j.i.s.u.D.k ( K.7.d a /.R ", "3 N.E.D.G.A.].N [ !.k.=.v.).~ ", "= L W W W I _ 0 H :.#.;.@.3.C ", " @ # # # # # X " }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 102 2", " c #010101", ". c #0B0A0A", "X c #1D1C1C", "o c #20201F", "O c #232322", "+ c #282827", "@ c #2E2E2D", "# c #302F2E", "$ c #30302F", "% c #353533", "& c #383736", "* c #383837", "= c #3C3C3A", "- c #403F3E", "; c #027E02", ": c #52514F", "> c #5A5957", ", c #5C5C5A", "< c #686765", "1 c #696866", "2 c #6F6E6B", "3 c #767572", "4 c #7A7876", "5 c #7D7C79", "6 c #807F7C", "7 c #058105", "8 c #0C840C", "9 c #148513", "0 c #148813", "q c #1F8F1E", "w c #229021", "e c #359833", "r c #399937", "t c #3D9B3B", "y c #55A552", "u c #5CA759", "i c #62A85F", "p c #66AB63", "a c #70B06D", "s c #77B273", "d c #7AB476", "f c #7FB47B", "g c #80817C", "h c #81B67D", "j c #80B97C", "k c #868582", "l c #8C8A87", "z c #8E8D8A", "x c #908F8B", "c c #92918E", "v c #949390", "b c #9A9995", "n c #9F9D9A", "m c #A09E9A", "M c #87B782", "N c #8AB986", "B c #8EBC89", "V c #92BC8D", "C c #A4A29E", "Z c #A5A4A0", "A c #A8A7A3", "S c #ABA9A5", "D c #AFAEA9", "F c #B1AFAB", "G c #B3B1AD", "H c #B7B5B1", "J c #B9B7B3", "K c #BCBAB6", "L c #BFBDB9", "P c #C1BFBB", "I c #9AC095", "U c #9FC29A", "Y c #A3C39E", "T c #AAC7A4", "R c #B1C8AC", "E c #C2C0BC", "W c #C5D1BF", "Q c #C7C5C1", "! c #C8C7C2", "~ c #CCCAC5", "^ c #CFC3C9", "/ c #CECCC8", "( c #C7D2C0", ") c #CBD3C4", "_ c #D4D4CD", "` c #D1D8CA", "' c #D7D6D0", "] c #D8D7D2", "[ c #DBDAD4", "{ c #DFD2D8", "} c #DFDDD8", "| c #E2DEDB", " . c #E5E3DD", ".. c #E9DFE1", "X. c #E7E5E0", "o. c #EAE3E3", "O. c #ECEAE4", "+. c #EFEDE8", "@. c #F6E4EE", "#. c #F0EDE8", "$. c #F4F2EC", "%. c #F8F5F0", /* pixels */ " ", " . % % % @ @ @ $ @ @ @ @ @ % . O % % # $ @ @ @ @ @ @ @ @ + ", " % ] b C #.O.X. . . . . . .+.* D J x } } X.X. . . . . .X.! ", " @ X.D , E F ! } ] ] [ ] ] .% A .> S 6 F } ] ] [ [ ] .P ", " % ~ = Z K S Q | ] } [ } [ .% F 1 2 ~ 3 D [ [ [ [ [ ] .P ", " % ' H Q . .| [ [ [ [ [ [ .& A ! L } ~ .......[ [ [ .L ", " @ X. .| [ [ [ [ [ } [ [ ] .& S #. .] o.T t e a [ [ [ .E ", " @ .] [ [ [ [ [ [ [ [ [ [ .& S .] ] | V s Y 8 N o._ .E ", " @ .] [ } [ [ [ [ [ [ [ ] .% S X.[ [ [ | @.| 8 M o.] .P ", " @ .[ [ [ [ [ } [ [ [ } ] X.& S .] [ [ | ) w r | [ ] .P ", " @ .[ [ [ [ [ } [ } [ [ ] X.% S X.] [ | T 7 9 N W } ] .P ", " @ .[ [ } [ [ [ [ [ [ } [ .% S X.[ [ ..V r y r I ..] .E ", " @ .] [ [ [ [ [ [ [ } [ ] X.% S X.[ [ [ | o...o. .[ [ .P ", " @ .' ] [ ] [ [ [ ] ] ] ] .% S .] [ [ ] ] ] ] ] ] ] | L ", " @ O.| . . . . . . . . .| +.* G $.| . . . . .o. . . .+.! ", " O Z n n n n n n n n n m n Z + O = - & - & % % & % % % * $ ", " @ _ / / / / / / / / / / ~ _ % g S 5 Z 5 C G G D S S S H 1 ", " $ .} } } } } } } } | | [ O.& J G > x 5 c E S ' X. . .%.z ", " @ .] [ [ [ [ [ } [ [ [ ] .= n k : 1 5 k g & _ ] ] ] X.k ", " @ .] } [ } ] } ] [ [ } ] .% S | m ! c ~ ^ ^ { | [ ] O.l ", " @ .] [ [ [ } ] } ] } [ [ .& S X. .| .` V j d ) | ] O.k ", " @ .[ } [ [ [ } ] [ [ [ [ .& D X.] ] | R ; i p W | ] O.k ", " @ .[ [ [ [ [ [ } [ } [ ] X.% S X.[ ] .R ; u Y | [ ] O.k . ", " @ .[ [ [ } [ [ ] } ] } [ .% S .] } | ) h s 8 V o._ O.k ", " @ .[ [ [ [ [ [ [ [ [ [ ] .& S X.] } | ) [ | 9 h .._ O.k ", " @ .[ [ [ [ [ [ } [ } } ] X.% S X.] ] ..N w q t _ | ] O.k . ", " @ .[ [ [ [ [ [ [ [ [ [ [ .% S X.] [ [ | ) ) | | ] ] O.k ", " @ | _ ] ] ] [ ] ] ] ] ' ' .% S .] [ ] ] [ [ ] ] [ ' O.k ", " % $.O.O.O.O.O.+.O.O.O.+.X.$.& G +.| . . . . .| . .} $.l ", " X z k k k k k k k k k k k x O b ! L P E E P P L P P P / 3 ", " ", " . . " }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 111 2", " c #010101", ". c #080707", "X c #0F0F0F", "o c #100F0F", "O c #10100F", "+ c #121211", "@ c #1C1C1B", "# c #232322", "$ c #2B2A29", "% c #30302F", "& c #343433", "* c #383736", "= c #3E3D3C", "- c #007600", "; c #007C00", ": c #41403F", "> c #454442", ", c #484746", "< c #494846", "1 c #4D4C4A", "2 c #504F4E", "3 c #545352", "4 c #5E5D5B", "5 c #62615F", "6 c #666663", "7 c #6D6C69", "8 c #73726F", "9 c #767572", "0 c #7A7976", "q c #7E7D7A", "w c #807F7C", "e c #038203", "r c #0B850B", "t c #118611", "y c #108910", "u c #1B8D1B", "i c #20901F", "p c #218C20", "a c #259123", "s c #2C942B", "d c #30962F", "f c #369934", "g c #3E9C3C", "h c #449E42", "j c #4A9F48", "k c #4BA148", "l c #52A34F", "z c #55A552", "x c #64AB61", "c c #6AAD66", "v c #6CAE69", "b c #7FB57B", "n c #82817E", "m c #81B67D", "M c #868582", "N c #8B8A86", "B c #8E8D8A", "V c #93928F", "C c #989693", "Z c #9A9995", "A c #9D9C98", "S c #A09E9B", "D c #84B780", "F c #93BD8E", "G c #97BF92", "H c #A3A29E", "J c #A6A5A1", "K c #ABAAA6", "L c #AFAEAA", "P c #B1AFAB", "I c #B2B0AC", "U c #B7B5B1", "Y c #BBBAB5", "T c #BEBCB8", "R c #C1BFBB", "E c #99C094", "W c #9DC098", "Q c #A3C49E", "! c #A7C5A1", "~ c #AAC6A5", "^ c #ADC7A8", "/ c #B7CBB1", "( c #B9CDB4", ") c #BECEB8", "_ c #BDD0B7", "` c #C4C2BD", "' c #C7C6C1", "] c #CBC9C5", "[ c #D4CECD", "{ c #CCD3C6", "} c #CFD5C9", "| c #D3D4CD", " . c #D6D7D0", ".. c #D9D7D2", "X. c #D7D8D0", "o. c #DCDAD5", "O. c #DFDDD8", "+. c #E2DDDB", "@. c #E3E1DC", "#. c #E7DEE0", "$. c #E9DFE1", "%. c #E7E5E0", "&. c #EAE6E2", "*. c #EEEBE6", "=. c #EFE2E8", "-. c #F1E2E9", ";. c #F1EEE9", ":. c #F4F2EC", ">. c #F9F7F1", ",. c #FDFBF5", "<. c #FFFFFF", /* pixels */ " ", " . ", " ", " . # $ & $ # # # # # # # # # # # # # # # + $ $ & # # # # # # # # # # # # # # # # . ", " # @.R C R @.o.O.+.+.+.+.+.@.O.O.O.O.O.%.+ 0 *.J A o.o.&.@.o.O.O.O.O.O.O.+.O.O.O.@.# ", " # O.A K * ` *.@.@.....o.o.O...o.o.o...@.O 0 | H 0 2 ,.A R *...o.o.o.o.o.o.o...o.O.# ", " # o.>.Y * Y K K P ..O.o.o.o.o.o.o.O...@.+ 9 :.:.6 6 ] 7 M ' O...o.o.o.o.o.o.o.o.O.# ", " # %.V @ L ` V Z A | O.o.o.o.o.o.o...O.@.+ 9 %., , ..K 8 w Y +.o.o.o.o.o.o.o.o.o.O.# ", " # @.7 7 M } *.%.%.o.o.o.o.o.o.o.o.o.o.@.o q } 1 n Z >.B U *.| ......o.o.O.o.o.o.O.# ", " # O.*.*.@.o.....o.o.o.o.o.o.o.o.o.o.o.@.+ 9 ;.*.@.@...@.+.+.=.=.&.$.o.o.o.o.o.o.O.# ", " # +.....o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.@.+ 9 *.[ ....o...O.F k s h F +.o.o.o.o.o.O.# ", " # O.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.@.+ 9 ;...o.o.o.o.+.p i z p - D @...O.o.o.O.# ", " # O.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.@.+ 9 *...o.o.o.o.o./ +.-.{ e g &...o.o.o.O.# ", " # O.o.o.o.o.o.o.o.o.+.o.o.o.o.o.o.o.o.@.+ 9 *...o.o.o.o.o.+...$.~ ; z $...o.o.o.O.# ", " # O.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.@.+ 9 ;...o.o.o.o.o...$.Q t ; ! @.o.o.o.o.O.# ", " # O.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.@.+ 9 *...o.o.o.o.o.&.m e r Q -...o.o.o.o.O.# ", " # O.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.@.+ 9 *...o.o.o...+.x ; e c Q ~ O...o.o.o.+.# ", " # O.o.o.o.o.o.o.o.o.o.o.o.O.o.o.o.o.o.@.+ 9 *...o.o.o.o.+.a e y r e g &...o.o.o.O.# ", " # O.o.o.o.o.o.o.o.o.O.o.o.o.o.o.o.o.o.@.+ 9 *...o.o.o.o.o.| | } | | | o.o.o.o.o.O.# ", " # O.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.@.+ 9 ;...o.o.o.o.o.o.+.O.o.O.O.o.o.o.o.o.O.# ", " # O.o.o.o.o.o.o.o...o.o.o.o.o...o.o.o.@.+ 9 *...o.o.o.o.o...o.o.o.o.o.o.o.o.o.o.O.# ", " # O.....X.............................@.+ 9 *...o.o.o.o.o.o.o.o.o.o.o.o.o.o.o...O.# ", " # *.%.%.%.*.%.&.*.%.*.*.%.&.&.&.*.*.@.:.+ 0 >.o.@.@.@.@.@.@.@.@.+.@.@.@.@.@.@.@.@.# ", " . + B N N N N N N N N N N N N N B N N N N + . + + X + o o + + + + + + + + + + + O @ . ", " @ ` T T T T T T T T T T T T T T T T T ` + = q 9 M 9 0 q q 9 9 9 9 9 9 9 9 9 9 q 3 ", " # %.@.@.@.@.@.@.@.@.@.@.+.@.@.@.@.@.@.&.+ q <.o.3 ' ] 9 H ;.:.:.:.*.*.*.*.*.&.,.K ", " # o.o.o.o.o.o.o.o...o.o.o...o...o.O...+.+ 9 %.B * C 1 K > L w P 0 | ..........@.A ", " # O.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.O.@.X w P 0 1 0 = o.: U J C %.o.o.o.o...%.A . ", " # O.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.@.+ 0 ..J % q n 5 4 | 9 0 8 X.o.o.o.o...%.A ", " # O.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.@.+ 9 ;.&.] .+.L [ O.[ ;.[ +.O.o.o.o...%.A . ", " # +.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.@.+ 9 *.| O.O...@.+.} _ / _ / | O.o.o.X.&.A ", " # O.o.o.o.o.o.o.o.o.o.o.o.o.o.O.o.o.o.@.+ 9 ;...o.o.o...&.b - e e - W #.o.o.o.&.A ", " # O.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.@.+ 9 *...o.o.o...&.m - E X.` } @.o.o.X.@.A . ", " # O.o.o.o.o.o.o.o.o.o.o.O.o.o.o.o.o.o.@.+ 9 *...o.o.o...=.b - j v ^ +.o.o.O...*.A ", " # O.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.@.+ 9 *...o.o.o...=.b u f r ; E #.o.o...*.A . ", " # O.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.@.+ 9 *...o.o.o.o.o. .| -.( ; h &...o. .@.A ", " # O.o.o.o.o.o.o.o.o.o.o.o.O...O.o.o.o.@.+ 9 ;.....o.o.o.o.{ +.-.( ; k $...o...*.A . ", " # O.o.o.o.o.o.O.o.o.o.o.o.o.o.o.o.o.o.@.+ 9 *...o.o.o.o.O.s u s r e ! +.o.o...%.A ", " . # O.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.@.+ 9 *...o.o.O...o.Q x l c ( +.o.o.o...%.A . ", " # O.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.@.+ 9 ;...o.o.o.O.o.+.=.-.=.+.o.o.o.o...%.A ", " # O.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.@.+ 9 *...o.o.o.o.o.o.X.....o.o.o.o.o...%.A . ", " # O...................................+.+ 9 *...o.o.o.o.o.o.o.o.o.o.o.o.o.o...*.A ", " # *.%.*.%.&.&.*.%.*.%.&.*.%.*.%.*.%.*.*.+ 9 *...o.o.o.o.o.o.o.o.o.o.o.o.o.o...%.A . ", " @ S A A A A A A A Z A A A A A A A A A H . 0 :.o.O.O.O.O.O.O.O.O.O.O.O.O.O.+.O.*.A ", " + # # # # # # # # # # # # # # # # # # @ ", " . . . . . ", " ", " " }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/lightup-icon.c0000644000175300017530000003545612161170372016323 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 179 2", " c #000000", ". c #020202", "X c #030303", "o c #040403", "O c #030304", "+ c #020207", "@ c #040405", "# c #080805", "$ c #0D0D0D", "% c #0E0D0D", "& c #10100D", "* c #090915", "= c #0F0F17", "- c #090919", "; c #181817", ": c #242323", "> c #242423", ", c #242324", "< c #272724", "1 c #2E2D2C", "2 c #343433", "3 c #353534", "4 c #434300", "5 c #424201", "6 c #454504", "7 c #65640E", "8 c #646410", "9 c #686815", "0 c #676718", "q c #585731", "w c #5A5A36", "e c #4F4F4B", "r c #4F4F4F", "t c #989713", "y c #989811", "u c #9B9B11", "i c #99981E", "p c #A9A837", "a c #D5D41F", "s c #F5F507", "d c #F8F800", "f c #FBFB00", "g c #FBFB01", "h c #FBFB02", "j c #FAFA03", "k c #FCFC00", "l c #FFFF00", "z c #FEFE01", "x c #FFFE01", "c c #FDFD03", "v c #FFFF02", "b c #FFFF03", "n c #FBFB06", "m c #FCFC04", "M c #FDFD04", "N c #FFFF05", "B c #F4F30F", "V c #F6F60F", "C c #F7F70F", "Z c #FFFF0A", "A c #E1E016", "S c #E9E816", "D c #EFEF14", "F c #EEEE17", "G c #E2E21C", "H c #E7E61F", "J c #EEEE19", "K c #EDEC1A", "L c #EDED1A", "P c #E9E81D", "I c #EBEA1D", "U c #EBEB1D", "Y c #EAEA1E", "T c #EBEA1E", "R c #F1F111", "E c #F2F110", "W c #F2F211", "Q c #F8F810", "! c #D0CF23", "~ c #DEDE28", "^ c #DDDD2A", "/ c #D9D931", "( c #DFDE32", ") c #D6D539", "_ c #D2D13E", "` c #E0DF29", "' c #E3E222", "] c #E5E522", "[ c #E3E324", "{ c #E0E027", "} c #E4E324", "| c #E9E920", " . c #E2E129", ".. c #E1E12A", "X. c #E4E329", "o. c #E6E529", "O. c #EBEB2E", "+. c #E2E231", "@. c #E3E235", "#. c #E2E136", "$. c #E9E93F", "%. c #82807D", "&. c #86867E", "*. c #CECE41", "=. c #CBCA45", "-. c #C6C54B", ";. c #D6D549", ":. c #CFCE50", ">. c #CFCE7F", ",. c #868682", "<. c #858484", "1. c #888682", "2. c #888683", "3. c #898885", "4. c #9C9A87", "5. c #ABA9A5", "6. c #B8B6B2", "7. c #BBB9B4", "8. c #BAB9B5", "9. c #CECC97", "0. c #CDCB9A", "q. c #D0CE97", "w. c #D4D299", "e. c #C4C2BD", "r. c #C5C3BF", "t. c #CBC9BD", "y. c #E2E2A5", "u. c #E4E4BE", "i. c #C7C5C1", "p. c #CAC8C4", "a. c #CECCC8", "s. c #CECDC8", "d. c #CFCDC8", "f. c #CECCCB", "g. c #CFCDCB", "h. c #D0CFCA", "j. c #D1CFCA", "k. c #D0CECC", "l. c #D5D3C3", "z. c #D6D4C1", "x. c #D7D5C2", "c. c #D3D1CC", "v. c #D4D2CD", "b. c #D5D3CE", "n. c #D6D4CF", "m. c #D1CFD9", "M. c #D2CFDB", "N. c #D6D4D0", "B. c #D7D5D0", "V. c #D7D5D3", "C. c #D3D1D7", "Z. c #D6D4D5", "A. c #D8D6D4", "S. c #DBD9D1", "D. c #DBD9D2", "F. c #DAD8D3", "G. c #DCDAD2", "H. c #DBD9D4", "J. c #DCDAD5", "K. c #DDDBD6", "L. c #DEDCD7", "P. c #D2D0D8", "I. c #D2D0DB", "U. c #DFDDD8", "Y. c #E1DFDA", "T. c #E4E4CD", "R. c #EBEBD7", "E. c #E2E0DB", "W. c #E3E1DC", "Q. c #E5E2DD", "!. c #DBD9E0", "~. c #DCDAE0", "^. c #DDDAE0", "/. c #DDDBE1", "(. c #E7E5E0", "). c #EBE9E3", "_. c #EDEDE3", "`. c #ECEAE4", "'. c #FFFFFF", /* pixels */ "F.g.g.f.g.k.g.f.f.f.h.g.g.f.g.H.", "l.X.K Y T _ Y Y K ..) K | D ;.P.", "l.V l l l ^ l l l B ] l M l #.m.", "z.E g j j ^ j l l E } l j l #.m.", "x.C l l l ..l l l B ] l M l #.P.", "t.i u y t p o...[ / *.Y A S :.P.", "6.O + = 0 Z s m D ' G >.^ ( I.", "7. e &.* 7 l g l Q a u.'.R.=.Z.", "8. r ,.- 8 M j l Q ! T.'._.-.V.", "6.@ & 9 M d l R H O.y.$.+.m.", "p.1.3.1.<.4.w.0.9.0.q 6 4 4 w A.", "V.Q.`.(.`.g.U./././., + , H.", "h.E.%.1 E.e.G.G.G.G.< # < F.", "c.J.e.5.Y.r.J.J.L.J.: o > F.", "b.d.J.Y.b.e.b.b.b.b.2 $ ; $ 3 F.", "F.n.b.b.n.V.b.n.n.V.h.d.d.g.h.J." }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 127 2", " c #010101", ". c #080807", "X c #00000E", "o c #080808", "O c #000010", "+ c #131312", "@ c #1E1E1E", "# c #34331C", "$ c #252524", "% c #3E3D23", "& c #31302F", "* c #3B3A2E", "= c #353434", "- c #383735", "; c #3A3937", ": c #38373C", "> c #3C3C3A", ", c #403F3C", "< c #444341", "1 c #494846", "2 c #575654", "3 c #62615F", "4 c #696866", "5 c #797875", "6 c #8F8F1E", "7 c #858523", "8 c #86862C", "9 c #88872E", "0 c #8F8E2B", "q c #919021", "w c #919029", "e c #BDBE2B", "r c #AFAF33", "t c #C9C90C", "y c #D5D500", "u c #DADA00", "i c #DFDF08", "p c #C4C410", "a c #E3E300", "s c #EEEE00", "d c #F3F300", "f c #FEFE01", "g c #F4F40C", "h c #FEFE0D", "j c #ECEB1A", "k c #F3F312", "l c #C1C120", "z c #CBCB24", "x c #CECD2E", "c c #DDDC24", "v c #D7D72C", "b c #D8D72A", "n c #DDDC2B", "m c #CACA33", "M c #D5D436", "N c #D4D338", "B c #E0E037", "V c #86855C", "C c #AAA85A", "Z c #B1AF5F", "A c #BEBD52", "S c #A8A667", "D c #AAAA62", "F c #B1B164", "G c #BEBD63", "H c #B1B06C", "J c #AAA870", "K c #ACAA7B", "L c #C0BF52", "P c #C0BF5F", "I c #C0BF62", "U c #C7C646", "Y c #CFCE41", "T c #C4C34D", "R c #CDCC4C", "E c #C7C650", "W c #C8C750", "Q c #CAC952", "! c #C2C15E", "~ c #CAC95E", "^ c #C3C161", "/ c #C1C069", "( c #838383", ") c #8E8E8E", "_ c #918F8C", "` c #B6B68C", "' c #A4A29F", "] c #B2B291", "[ c #AAAAAA", "{ c #B1AFAB", "} c #B2B2A6", "| c #B1B0AB", " . c #B7B5B1", ".. c #BBBAB3", "X. c #BBBBBB", "o. c #C0BEB9", "O. c #C5C3BE", "+. c #CECBBD", "@. c #C5C4C4", "#. c #C9C7C2", "$. c #CBC9C5", "%. c #C3C1CC", "&. c #C8C6C8", "*. c #CDCCC9", "=. c #D0CEC0", "-. c #D1CFCA", ";. c #D4D2CD", ":. c #C7C7D4", ">. c #CECED5", ",. c #D1CFDC", "<. c #D7D4D4", "1. c #D9D7D2", "2. c #DDDBD5", "3. c #D3D1DC", "4. c #D9D7D9", "5. c #DFDDD9", "6. c #E1DFD9", "7. c #E2E0D7", "8. c #E4E1DC", "9. c #D7D5E2", "0. c #DBD9E4", "q. c #E7E5E0", "w. c #E9E7E1", "e. c #EDEBE5", "r. c #ECECFF", "t. c #F7F7FF", "y. c #FEFEFD", /* pixels */ "2.2.6.7.7.7.7.7.7.7.6.6.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.6.2.2.", "2.<.%.%.%.%.%.%.%.%.%.&.%.%.%.%.%.%.%.%.@.%.%.%.%.%.%.%.%.&.2.2.", "6.#.x c b b b b v c T A c v b b b b b M Z b v b v b b v c H 3.2.", "5.+.g f f f f f f f n N f f f f f f f g T f f f f f f f f ! 3.2.", "2.+.g f f f f f f f n N f f f f f f f g T f f f f f f f f ! 3.2.", "5.+.g f f f f f f f n N f f f f f f f g T f f f f f f f f ! 3.6.", "5.#.d f f f f f f f v N f f f f f f f g T f f f f f f f f ! ,.6.", "6.+.g f f f f f f f n N f f f f f f f g T f f f f f f f f ! >.2.", "5.+.g f f f f f f f n N f f f f f f f g T f f f f f f f f ! ,.6.", "5.#.d f f f f f f f n M f f f f f f f g T f f f f f f f f ! 3.2.", "5.=.k h h h h h h f B Y f k g g g g h j A h k g k k g k f I 3.2.", "q.[ % % % % % % % % * C R T T T T T T A J T T W R R W T U K <.2.", "w.[ X z f f f f f f f g T f f d u y s f f P ,.2.", "8.' . $ . X z f f f f f f f g T f a D >.4.] p f I >.6.", "q.' ( [ ) X z f f f f f f f g E f F t.y.y.y...a ~ ,.6.", "q.[ @ @. X.= X z f f f f f f f g R u *.y.t.y.y.r.l Q >.6.", "q.' @ @. X.= O z f f f f f f f g R y 3.y.y.y.y.r.e W 3.6.", "q.' ( [ ) X z f f f f f f f g E d ` y.y.y.y.>.y ~ 3.2.", "w.[ . $ o O z f f f f f f f g T f t } r.r.:.r f ! 3.2.", "8.' X z f f f f f f f g E f f s z m i f f ! 3.6.", "8.{ & > ; ; > ; ; ; : S ^ ^ ^ ^ ^ I ! / V 7 8 0 q 6 w 9 7 V 4.2.", "6.-.2.8.8.8.8.8.6.q.*.X.9.,.3.,.,.3.3.0.> = 7.2.", "6.$.-.2.1.1.2.<.1.6.O.o.7.2.2.2.5.2.2.8., . . . - 8.2.", "2.$.;.2.2.5.2.2.2.6.O.o.6.2.2.2.2.2.1.8.> = 8.<.", "6.$.-.2.8.2 + 5 q.2.O.o.6.2.2.2.2.2.2.8.> = 8.2.", "6.$.;.2.q.; 3 e.2.@.o.6.1.2.2.2.2.<.8.> = 7.<.", "2.$.-.2.6._ 4 ' 8.6.O.o.6.1.2.2.2.2.1.8., = 8.2.", "6.$.;.2.1.q.e.8.1.7.O.X.6.2.2.1.2.2.2.8.> . = 6.2.", "6.$.;.7.6.2.2.2.6.8.O.O.8.5.5.5.2.5.5.8.> = 6.2.", "6.$.O.$.&.#.#.#.#.$... .-.#.#.#.&.#.#.;.1 . + + + + + + o < 6.2.", "2.2.;.;.;.;.;.<.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.-.;.;.;.<.2.2.", "2.2.5.2.5.5.2.5.5.5.6.5.5.5.2.5.5.5.5.5.5.5.2.5.5.5.5.5.5.5.<.2." }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 138 2", " c #010101", ". c #0C0C0C", "X c #040416", "o c #060618", "O c #141414", "+ c #1D1C1C", "@ c #222222", "# c #282726", "$ c #31312C", "% c #3A3A2D", "& c #3B3B31", "* c #3B3A39", "= c #6B6A34", "- c #706F3B", "; c #727135", ": c #73723B", "> c #79793E", ", c #3E3D40", "< c #5D5C5A", "1 c #6F6E5A", "2 c #716F6D", "3 c #7A7978", "4 c #A8A80D", "5 c #B6B606", "6 c #BABA02", "7 c #B4B40A", "8 c #A1A113", "9 c #ACAC14", "0 c #9E9E27", "q c #9E9E2A", "w c #9E9E32", "e c #CDCD00", "r c #D1D100", "t c #D8D800", "y c #EBEB00", "u c #F5F502", "i c #FEFE01", "p c #F7F70A", "a c #FBFB09", "s c #EAEA12", "d c #F0EF14", "f c #F1F112", "g c #FFFF16", "h c #FDFD18", "j c #DFDE2A", "k c #D1D031", "l c #E0DF29", "z c #ECEC27", "x c #E0E029", "c c #8A8A4E", "v c #929245", "b c #979748", "n c #8A8A57", "m c #969652", "M c #9A9A54", "N c #91915B", "B c #9A9A5F", "V c #BFBE4A", "C c #B3B250", "Z c #B6B55E", "A c #B8B65E", "S c #929269", "D c #81807D", "F c #88867F", "G c #9F9D74", "H c #9F9E79", "J c #A09E75", "K c #A9A867", "L c #A6A46C", "P c #ADAC6A", "I c #B0AF67", "U c #B0AE68", "Y c #B5B462", "T c #B8B661", "R c #BCBA64", "E c #B1B069", "W c #A6A574", "Q c #ADAB72", "! c #A8A67C", "~ c #B0AE71", "^ c #C0BF4A", "/ c #C3C245", "( c #CECD41", ") c #C6C54A", "_ c #CCCB59", "` c #838281", "' c #9E9D85", "] c #969696", "[ c #989793", "{ c #9F9D91", "} c #9E9D9A", "| c #A19F96", " . c #A09E9B", ".. c #A7A58E", "X. c #A8A68E", "o. c #A6A490", "O. c #A9A792", "+. c #ABA992", "@. c #A6A69D", "#. c #A6A6AA", "$. c #ACABA9", "%. c #B0AEAA", "&. c #B4B2AF", "*. c #ACACB5", "=. c #B6B4B1", "-. c #BBB9B5", ";. c #BFBDB9", ":. c #C0BEBA", ">. c #C2C0BC", ",. c #BFBDCC", "<. c #C0BDCC", "1. c #C6C4C2", "2. c #CBC9C5", "3. c #C3C2CC", "4. c #C9C7CC", "5. c #CFCDCA", "6. c #D2CFCB", "7. c #D3D1CC", "8. c #C8C8D4", "9. c #CFCFDF", "0. c #D1CFD6", "q. c #D6D5D0", "w. c #D9D7D2", "e. c #DCDAD5", "r. c #D4D3DB", "t. c #DDDBD9", "y. c #E1DFD7", "u. c #E0DED9", "i. c #E2E0D7", "p. c #E3E1DC", "a. c #D7D7EA", "s. c #E7E5E0", "d. c #E8E6E1", "f. c #EBE9E4", "g. c #E3E3F4", "h. c #EAEAFB", "j. c #F4F4FF", "k. c #FEFEFE", /* pixels */ "e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.", "e.e.e.e.e.e.e.e.e.e.e.e.e.w.e.e.e.w.e.e.e.e.e.e.w.e.e.e.e.w.e.e.e.e.e.e.e.w.e.e.e.w.e.e.e.e.e.e.", "e.e.e.e.i.i.y.i.i.i.i.i.y.i.i.i.u.t.y.y.i.i.i.i.y.i.i.i.i.i.y.y.i.i.i.y.y.i.i.i.i.i.y.t.e.e.e.e.", "e.e.e.w.<.,.<.,.,.,.,.,.,.,.<.,.1.1.,.<.,.,.,.,.,.,.,.<.,.<.1.,.<.,.,.,.,.,.,.,.<.<.,.2.e.e.e.e.", "e.e.i.1.C / V V ^ V V ^ V V V / L K / V V V V ^ ^ V V V / Z G / V V V V V ^ V ^ V ^ C { e.e.e.e.", "e.e.u.1.s i i i i i i i i i i i ) ( i i i i i i i i i i i x Z i i i i i i i i i i i i X.e.e.e.e.", "e.e.t.1.s i a i a i i i i a a i ) ( i a i i a i i i i a i j Z i a i i a i i i a i i u ..t.e.e.e.", "e.e.u.1.s i i i i i i i i i i i ) ( i a i i i i i i i i i x A i i i i i i i i i i i a ..t.e.e.e.", "e.w.t.1.s i i i i i i i i i i i ) ( i i i i i i i i i i i x A i i i i i i i i i i i u ..t.e.e.e.", "e.e.u.1.s i i i i i i i i i a i ) ( i i i i i i i i i i i x A i i i i i i i i i i i a ..r.e.t.e.", "e.e.u.1.s i i i i i i i i i i i ) ( i i i i i i i i i i i x A i i i i i i i i i i i i ..t.e.e.e.", "e.e.u.1.s i i i i i i i i i i i ) ( i i i i i i i i i i i x A i i i i i i i i i i i u ..t.e.e.e.", "e.e.u.1.s i i i i i i i i i a i ) ( i i i i i i i i i i i x A i a i i i i i i i i i i ..t.w.e.e.", "t.w.u.1.s i i i i i i i i i i i ) ( i i i i i i i i i i i x A i i i i i i i i i i i u ..t.e.e.e.", "e.w.u.1.s i i i i i i i i i i i ) ( i a i i i i i i i i i x A i i i i i i i i i i i u O.t.e.e.e.", "e.e.u.1.s i i i i i i i i i i i ) k i i i i i i i i i i i x A i i i i i i i i i i i i ..e.e.e.e.", "e.e.u.2.z g h h g g g g g g h a _ ) p d f f f f f f f f p k E a s f f f f f d d f p s o.r.t.e.e.", "e.e.p.$.$ & & & & & & & & & & % , H E P P P P U P P P P U W ' E P P U P P P P P P P K .e.e.e.e.", "e.w.d. . X k i a i i i i i i i i i x A i i i i i i i i i i i i ..t.e.e.e.", "e.w.d.@. o k i i i i i i i i i i i l A i a i u 5 q 0 7 y a i u ..t.e.e.e.", "e.w.d.} X k i i i i i i i i i i i x A i a t c 3.h.h.4.n r i u ..t.e.e.e.", "e.e.d. . . } 0.$.O X k i i i i i i i i i i i x A i y N j.k.k.k.k.j.S y a ..r.e.e.e.", "e.q.d. . D r. >.} X k i i i i i i i i i i i x Z i 9 r.k.k.k.k.k.k.a.9 i o.e.e.e.e.", "e.e.d.} 1.] 3 a. X k i i i i i i i i i i i x T i b j.k.k.k.k.k.k.k.M u o.t.e.e.e.", "e.e.d. . 3.] 3 t. X k i i i i i i i i i i i x T i m k.k.k.k.k.k.k.k.B u +.t.e.e.e.", "e.q.d.} ` q. >.} X k i i i i i i i i i i i x Z i q g.k.k.k.k.k.k.h.w i ..r.e.e.e.", "e.w.p.} . } r.$.O X k i i i i i i i i i i i x Z i e @.k.k.k.k.k.k.$.e a ..t.e.e.e.", "e.w.f.@. X k i i i i i i i i i i i x A i i 4 #.k.k.k.k.*.8 i u ..t.e.e.e.", "e.w.d. . . X k i i i i i i i i i a i x Z i i i 6 v ' ' v 5 i i u ..t.e.e.e.", "e.e.d. . X k i i i i i i i i i i i x R i a a a i u u i a a i p O.e.e.e.e.", "e.e.s.$.$ * * * * * * * * * * * , H Q Q Q Q Q Q Q Q Q Q P ! 1 ; : : : : > > : : : : = F u.e.e.e.", "e.e.u.2.7.p.u.t.u.u.u.p.u.u.t.s.;.-.r.8.r.0.0.0.0.0.0.0.r.4.+ < d.e.e.e.", "e.e.y.>.2.t.e.e.e.e.e.e.e.e.w.u.&.-.p.e.y.e.y.y.e.e.e.i.y.7.+ . < d.e.e.e.", "e.w.u.1.2.u.e.e.e.7.e.e.e.e.e.i.=.-.p.e.e.e.e.e.e.e.e.e.t.7.+ < d.e.e.e.", "e.e.u.1.2.u.e.e.u.f.f.f.e.e.e.u.=.-.u.e.e.e.e.e.e.e.e.e.t.7.+ < p.e.e.e.", "e.e.p.>.2.t.e.i.:.3 D ` w.t.w.p.&.-.p.e.e.e.e.e.e.e.e.e.e.6.+ < d.q.e.e.", "e.e.i.1.2.u.w.d.[ . 5.y.w.p.=.-.p.e.e.e.e.e.e.e.e.e.e.6.+ < d.q.e.e.", "e.e.p.>.2.u.w.d.[ . 5.y.w.p.=.-.p.w.e.e.e.e.e.e.e.e.y.6.+ < d.e.e.e.", "e.e.y.2.2.u.e.s. .O + # 6.t.w.p.=.-.u.e.e.e.e.e.e.e.e.e.y.6.+ < p.q.e.e.", "e.e.t.>.2.i.e.e.e.e.e.e.e.e.e.p.=.-.p.e.e.e.e.e.e.e.e.e.e.6.+ < f.e.e.e.", "e.e.p.>.2.u.w.e.e.e.e.e.e.e.e.i.=.-.p.e.e.e.e.e.e.e.e.e.e.7.+ < d.w.e.e.", "e.e.y.1.2.u.e.e.w.e.e.e.e.e.e.u.=.-.u.w.e.e.e.w.e.e.e.e.t.6.+ < d.w.e.e.", "e.e.u.1.2.p.e.y.t.y.t.t.y.t.e.s.=.-.p.e.u.e.u.u.e.y.t.t.i.7.+ < s.w.e.e.", "e.e.u.2.=.:.:.:.;.;.;.:.;.;.;.>.%.%.>.;.;.;.:.;.;.;.;.:.:.;.* + # @ @ @ @ @ @ @ # # O 2 s.e.e.e.", "e.e.e.e.w.w.q.w.q.q.w.7.7.q.w.q.e.e.7.w.q.q.q.w.7.q.q.q.w.w.t.t.t.t.y.t.t.y.t.y.y.t.t.t.e.e.e.e.", "e.e.e.e.e.e.e.e.y.e.t.y.t.t.t.e.e.e.y.e.y.t.e.e.t.t.e.y.y.e.e.e.e.e.e.e.e.t.e.e.e.e.t.e.e.e.e.e.", "e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.", "e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.e.t.e." }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/loopy-icon.c0000644000175300017530000004123612161170373016003 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 116 2", " c #5F5F5C", ". c #605F5D", "X c #666563", "o c #676664", "O c #696966", "+ c #71706D", "@ c #73726F", "# c #787774", "$ c #787775", "% c #7F7E7B", "& c #82807D", "* c #82817E", "= c #83827F", "- c #848380", "; c #858481", ": c #868581", "> c #868582", ", c #898784", "< c #898885", "1 c #8A8986", "2 c #8B8A87", "3 c #8E8D8A", "4 c #8F8D8A", "5 c #8F8E8A", "6 c #94928F", "7 c #959490", "8 c #989793", "9 c #9F9D9A", "0 c #9F9E9A", "q c #A19F9C", "w c #A2A19D", "e c #A4A29E", "r c #A6A5A1", "t c #A7A5A2", "y c #A7A6A2", "u c #A9A8A4", "i c #AEACA9", "p c #AEADA9", "a c #AFADA9", "s c #B0AEAA", "d c #B0AFAB", "f c #B1AFAB", "g c #B2B0AC", "h c #B2B1AD", "j c #B4B2AE", "k c #B4B3AF", "l c #B6B4B0", "z c #B6B5B1", "x c #B7B5B1", "c c #B7B6B1", "v c #B7B6B2", "b c #BAB8B4", "n c #BBB9B5", "m c #BCBAB6", "M c #BEBCB8", "N c #BEBDB8", "B c #BFBDB8", "V c #BFBDB9", "C c #C0BEB9", "Z c #C0BEBA", "A c #C1BFBA", "S c #C1BFBB", "D c #C3C1BD", "F c #C4C2BE", "G c #C6C4C0", "H c #C7C5C1", "J c #CAC8C3", "K c #CBC9C4", "L c #CBC9C5", "P c #CBCAC5", "I c #CCCAC5", "U c #CCCBC6", "Y c #CDCBC6", "T c #CECCC7", "R c #CECDC8", "E c #CFCDC8", "W c #D0CEC9", "Q c #D0CFCA", "! c #D1CFCA", "~ c #D2D0CB", "^ c #D2D0CC", "/ c #D3D1CC", "( c #D4D2CD", ") c #D4D2CE", "_ c #D5D3CF", "` c #D6D4CF", "' c #D7D5D0", "] c #D8D6D1", "[ c #D9D7D2", "{ c #D9D7D3", "} c #DAD8D3", "| c #DBD9D4", " . c #DCDAD5", ".. c #DDDBD6", "X. c #DEDCD6", "o. c #DEDCD7", "O. c #DFDDD8", "+. c #E0DDD8", "@. c #E0DED9", "#. c #E1DFDA", "$. c #E2E0DB", "%. c #E3E1DB", "&. c #E3E1DC", "*. c #E4E2DD", "=. c #E5E2DD", "-. c #E5E3DE", ";. c #E6E4DE", ":. c #E6E4DF", ">. c #E7E4DF", ",. c #E7E5E0", "<. c #E9E6E1", "1. c #E9E7E2", "2. c #EBE8E3", "3. c #EBE9E4", "4. c #EFEDE7", "5. c #EFEDE8", /* pixels */ "[ Y .[ o.E ) .} .T K P P K [ ", "Y 7 o.o.&.q l *.O.~ > > > * T ", " .o. .l ) #.#.H D ` = 2.%.*.#.} ", "} } Y + l .o.u * [ % +.} | ' } ", " .*.} p W &.4.Z D :.= $.| .+. .", "W q O.O.&.p > 9 9 2 O #.[ .m T ", ") l o.) ,.4 4 m s p l .` :.e V ", " .#. .| <.8 m 5.%.:.#.o.6 P k V ", "| } O.| 3.8 g *.} } } o.# W j B ", "| ~ / T o.5 g *.} | ~ ~ D } r Z ", "T = 2 2 X l #.} ~ * > * # P ", "P 2 :.l [ #. . .o./ = :.o.#.#.} ", "P - &.o y $.` } | ~ * o.} } [ } ", "J 2 2.d ` >.$.:.:. .> $. . .o. .", "Y @ d g s w r p s e # o.' o.D ~ ", "| G V V V B C Z V B P | | | ~ ` " }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 209 2", " c #0A0A0A", ". c #0C0B0B", "X c #151514", "o c #1C1C1B", "O c #1D1C1C", "+ c #242322", "@ c #252524", "# c #262625", "$ c #2B2B2A", "% c #2C2B2A", "& c #2C2C2B", "* c #2E2E2D", "= c #333231", "- c #373735", "; c #373736", ": c #383836", "> c #383837", ", c #3B3A39", "< c #3B3B39", "1 c #3C3B3A", "2 c #3D3D3B", "3 c #3E3D3C", "4 c #3F3F3D", "5 c #403F3D", "6 c #403F3E", "7 c #40403E", "8 c #424140", "9 c #424240", "0 c #434241", "q c #434341", "w c #474644", "e c #484746", "r c #494947", "t c #4A4A48", "y c #4B4A49", "u c #4B4B49", "i c #4C4B49", "p c #4C4B4A", "a c #4E4D4C", "s c #4F4E4C", "d c #504F4D", "f c #504F4E", "g c #51504F", "h c #535250", "j c #535351", "k c #555452", "l c #565553", "z c #575654", "x c #5F5F5C", "c c #605F5D", "v c #61605E", "b c #636260", "n c #666563", "m c #696866", "M c #6D6C69", "N c #6E6D6A", "B c #6E6D6B", "V c #71706D", "C c #71706E", "Z c #72706E", "A c #72716E", "S c #72716F", "D c #747370", "F c #757472", "G c #767572", "H c #777673", "J c #777674", "K c #787774", "L c #787775", "P c #7C7B78", "I c #7D7C79", "U c #807F7C", "Y c #81807D", "T c #83827F", "R c #878582", "E c #878683", "W c #8A8985", "Q c #8E8D89", "! c #8F8E8A", "~ c #92918D", "^ c #92918E", "/ c #93928E", "( c #94928F", ") c #959390", "_ c #959490", "` c #959491", "' c #969591", "] c #969592", "[ c #989793", "{ c #999894", "} c #9A9895", "| c #9D9B98", " . c #9E9D99", ".. c #A1A09C", "X. c #A2A09D", "o. c #A3A29E", "O. c #A4A29E", "+. c #A6A5A1", "@. c #A8A6A2", "#. c #AEACA8", "$. c #AEACA9", "%. c #AEADA9", "&. c #AFAEAA", "*. c #B0AEAA", "=. c #B0AFAB", "-. c #B2B1AC", ";. c #B2B1AD", ":. c #B3B1AD", ">. c #B4B2AE", ",. c #B9B8B3", "<. c #BBB9B5", "1. c #BBBAB5", "2. c #BBBAB6", "3. c #BCBAB6", "4. c #BDBBB7", "5. c #BDBCB7", "6. c #BEBCB7", "7. c #BFBDB9", "8. c #BFBEB9", "9. c #C0BFBA", "0. c #C1BFBB", "q. c #C3C1BD", "w. c #C4C2BD", "e. c #C4C3BE", "r. c #C7C5C0", "t. c #C7C5C1", "y. c #C8C6C2", "u. c #CAC8C3", "i. c #CDCBC7", "p. c #CECCC8", "a. c #CECDC8", "s. c #CFCDC8", "d. c #CFCDC9", "f. c #D0CEC9", "g. c #D0CECA", "h. c #D1CFCA", "j. c #D2D0CB", "k. c #D3D1CC", "l. c #D3D1CD", "z. c #D4D2CD", "x. c #D4D2CE", "c. c #D5D3CE", "v. c #D6D4CF", "b. c #D6D4D0", "n. c #D6D5D0", "m. c #D7D5D0", "M. c #D7D5D1", "N. c #D8D6D1", "B. c #D8D6D2", "V. c #D9D7D2", "C. c #D9D7D3", "Z. c #DAD8D3", "A. c #DBD9D4", "S. c #DBD9D5", "D. c #DBDAD4", "F. c #DCDAD5", "G. c #DDDBD5", "H. c #DDDBD6", "J. c #DEDCD7", "K. c #DFDDD7", "L. c #DFDDD8", "P. c #E0DED9", "I. c #E1DED9", "U. c #E1DFD9", "Y. c #E1DFDA", "T. c #E2E0DA", "R. c #E2E0DB", "E. c #E3E1DC", "W. c #E4E1DC", "Q. c #E4E2DD", "!. c #E5E2DD", "~. c #E5E3DD", "^. c #E5E3DE", "/. c #E6E3DE", "(. c #E6E4DE", "). c #E6E4DF", "_. c #E7E5DF", "`. c #E7E5E0", "'. c #E8E5E0", "]. c #E8E6E0", "[. c #E8E6E1", "{. c #E9E6E1", "}. c #E9E7E1", "|. c #E9E7E2", " X c #EAE8E2", ".X c #EAE8E3", "XX c #EBE8E3", "oX c #EBE9E3", "OX c #EBE9E4", "+X c #ECE9E4", "@X c #ECEAE4", "#X c #ECEAE5", "$X c #EDEBE5", "%X c #EDEBE6", "&X c #EEEBE6", "*X c #EEECE6", "=X c #EFECE7", "-X c #EFEDE7", ";X c #EFEDE8", ":X c #F1EFE9", ">X c #F4F2EC", ",X c #F5F3EE", " # 4 , , , , 1 , # t.T.", "F.F.! 0.P.A.N.N.N.F.F.^ 3.P.N.N.N.Z.N.{.q F 4.$.&.;.=.$.;.[ k.J.", "F.J.Q.P.F.F.). XJ.F.F.W.P.F.F.{.{.L.N.*Xd .1XE.).).).).`.E.A.A.", "F.F.l.Z.A.E.^ n g.P.F.c.N.P.N.L V g.A.*Xi _ |.m.Z.Z.Z.A.A.c.N.J.", "F.F.v.Z.E.8.N } P {.Z.b.N.P.c.g.m } ). Xi [ OXZ.A.F.F.F.J.m.Z.F.", "F.J.v.Z.Q.,.H >.H #XZ.m.N.F.Q.| v v.Z.*Xi _ OXZ.F.F.F.F.F.m.N.J.", "F.F.m.A.F.P.Y l q.P.F.m.A.L.c.7 k 0.L.#Xa _ OXN.F.F.F.F.L.m.N.L.", "F.F.m.A.F.F.T.P.P.F.F.m.N.N.N.P.m.m.m.{.u _ OXZ.F.F.F.A.F.m.A.F.", "Z.J.N.F.F.J.A.A.F.F.L.c.Q.#X X XOX X).6Xj } OXZ.J.F.F.A.L.m.m.F.", "J.N.o ^ Q.l.m.m.m.m.c.; = w q q q q q u L `.l.m.b.m.m.A.l q.E.", "J.Z.Y 1.L.Z.Z.Z.Z.N.E.; H #.X.O.O.O.O.@.Y ;.T.N.N.N.A.N.).x 4.E.", "F.J.).T.F.F.F.F.F.Z.#Xw #. a i p p p p a % y.P.", "F.P.% H .) ) ^ _ _ [ F ;.E.N.N.N.A.N.).4 b X._ _ [ _ _ [ T g.P.", "Z.{.4 3.X X#X{.P.F.F.F.J.J.N.;Xa X.3X`.#X#X#X X#X).N.F.", "Z.)., $.).g.W I 1.F.N.l.N.L.F.F.A.F.Z.#Xp ) {.m.Z.N.A.N.Z.c.N.J.", "Z.).< $.).m.T * R OXN.b.Z.F.F.F.F.F.Z.#Xp [ OXZ.F.F.F.F.F.N.N.F.", "Z.).1 -.).D.k.V P #XA.m.Z.F.F.F.F.A.N.=Xp _ OXN.F.F.F.F.F.N.N.J.", "Z.)., $. Xi.V v +.Q.A.m.Z.F.F.F.F.F.m.=Xp [ OXA.F.F.F.F.F.m.Z.F.", "Z.).1 $.T.Z.d.f.T.N.N.v.N.N.N.A.Z.Z.b.|.u ` OXN.F.F.F.F.J.m.Z.J.", "N.{.2 1.>X`.OXOX_. X@XF.Q.XX|.{.{.|.`.3Xa [ OXA.J.F.J.F.J.A.Z.J.", "J.N.% l D N N N N N V e c Z B B B B M J + R `.l.m.m.m.b.A.B t.P.", "F.Z.u.0.3.3.5.1.3.3.3.0.4.3.3.3.3.4.1.1.r.d.Z.Z.N.Z.N.A.N.y.N.J.", "F.F.T.Q.Q.Q.Q.Q.Q.Q.W.Q.Q.Q.Q.Q.Q.Q.Q.Q.Y.L.F.F.J.F.F.L.F.P.J.A." }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 238 2", " c #000000", ". c #030202", "X c #030303", "o c #050505", "O c #060606", "+ c #080807", "@ c #080808", "# c #090808", "$ c #090909", "% c #0A0909", "& c #0A0A09", "* c #0B0A0A", "= c #0B0B0B", "- c #0C0B0B", "; c #0C0C0B", ": c #0C0C0C", "> c #0D0C0C", ", c #0D0D0C", "< c #0D0D0D", "1 c #0E0D0D", "2 c #0E0E0D", "3 c #0E0E0E", "4 c #0F0F0E", "5 c #0F0F0F", "6 c #111110", "7 c #111111", "8 c #171716", "9 c #181817", "0 c #232322", "q c #272726", "w c #292928", "e c #2C2B2A", "r c #2D2D2C", "t c #353433", "y c #353534", "u c #363534", "i c #363635", "p c #373635", "a c #383736", "s c #383836", "d c #383837", "f c #393837", "g c #3D3C3B", "h c #3F3F3D", "j c #403F3E", "k c #424240", "l c #434341", "z c #454443", "x c #454543", "c c #474745", "v c #484745", "b c #494947", "n c #4B4A48", "m c #4B4B49", "M c #4C4B49", "N c #4C4B4A", "B c #4D4C4A", "V c #504F4D", "C c #50504E", "Z c #51504E", "A c #555452", "S c #5A5957", "D c #5B5B58", "F c #5C5B59", "G c #5F5E5C", "H c #63625F", "J c #636260", "K c #646361", "L c #696866", "P c #6A6966", "I c #6A6967", "U c #6B6A68", "Y c #6C6B68", "T c #6C6B69", "R c #6D6C69", "E c #6E6D6B", "W c #706F6D", "Q c #71706D", "! c #71706E", "~ c #72716E", "^ c #73726F", "/ c #757471", "( c #757472", ") c #767572", "_ c #7A7976", "` c #7D7B79", "' c #7E7D7A", "] c #7F7E7B", "[ c #81807D", "{ c #82817E", "} c #83827F", "| c #868482", " . c #868582", ".. c #898885", "X. c #8B8A87", "o. c #8C8A87", "O. c #8D8B88", "+. c #8E8C89", "@. c #8E8D8A", "#. c #8F8D8A", "$. c #8F8E8A", "%. c #8F8E8B", "&. c #908E8B", "*. c #918F8C", "=. c #91908C", "-. c #92908D", ";. c #92918E", ":. c #93918E", ">. c #93928F", ",. c #959390", "<. c #959490", "1. c #979592", "2. c #989693", "3. c #989793", "4. c #999794", "5. c #999894", "6. c #9A9995", "7. c #9A9996", "8. c #9B9996", "9. c #9B9A96", "0. c #9D9B98", "q. c #9D9C98", "w. c #9E9C99", "e. c #9E9D99", "r. c #9F9D9A", "t. c #9F9E9A", "y. c #A09E9A", "u. c #A09F9B", "i. c #A1A09C", "p. c #A4A39F", "a. c #A5A4A0", "s. c #A6A4A0", "d. c #A6A4A1", "f. c #A6A5A1", "g. c #A8A6A2", "h. c #A8A7A3", "j. c #AAA9A5", "k. c #B4B2AE", "l. c #B7B6B2", "z. c #B9B7B3", "x. c #BAB8B4", "c. c #BBB9B5", "v. c #C1BFBB", "b. c #C1C0BB", "n. c #C2C0BC", "m. c #C4C2BE", "M. c #C4C3BE", "N. c #C5C3BF", "B. c #C5C4BF", "V. c #C6C4BF", "C. c #C6C5C0", "Z. c #C7C5C0", "A. c #C7C5C1", "S. c #C7C6C1", "D. c #C8C6C1", "F. c #C8C6C2", "G. c #CBC9C4", "H. c #CECCC7", "J. c #CFCEC9", "K. c #D0CEC9", "L. c #D0CECA", "P. c #D0CFCA", "I. c #D1CFCA", "U. c #D1CFCB", "Y. c #D2D0CB", "T. c #D3D1CC", "R. c #D3D1CD", "E. c #D4D2CD", "W. c #D4D2CE", "Q. c #D5D3CE", "!. c #D5D3CF", "~. c #D6D4CF", "^. c #D6D4D0", "/. c #D7D4D0", "(. c #D7D5D0", "). c #D7D5D1", "_. c #D8D6D1", "`. c #D8D6D2", "'. c #D9D7D2", "]. c #D9D7D3", "[. c #DAD8D3", "{. c #DAD8D4", "}. c #DBD9D4", "|. c #DBD9D5", " X c #DCDAD5", ".X c #DDDBD5", "XX c #DDDBD6", "oX c #DEDBD6", "OX c #DEDCD6", "+X c #DEDCD7", "@X c #DFDCD7", "#X c #DFDDD7", "$X c #DFDDD8", "%X c #E0DED9", "&X c #E1DED9", "*X c #E1DFD9", "=X c #E1DEDA", "-X c #E1DFDA", ";X c #E2E0DA", ":X c #E2E0DB", ">X c #E3E1DB", ",X c #E3E1DC", " c #3A0000", ", c #201F1F", "< c #272423", "1 c #252624", "2 c #262625", "3 c #272726", "4 c #212B2A", "5 c #2A2928", "6 c #2C2A2A", "7 c #2C2C2B", "8 c #312D2C", "9 c #223230", "0 c #293937", "q c #363534", "w c #383433", "e c #393937", "r c #3E3E3C", "t c #450000", "y c #4B0000", "u c #490909", "i c #510202", "p c #5E1A19", "a c #770000", "s c #7B0000", "d c #454543", "f c #464442", "g c #4C4C4A", "h c #4D4C4A", "j c #524F4D", "k c #5E5856", "l c #764C4A", "z c #61605E", "x c #65615E", "c c #636462", "v c #737D6F", "b c #6E7E70", "n c #767773", "m c #7D7C79", "M c #800101", "N c #923130", "B c #CD0000", "V c #CC0505", "C c #D20303", "Z c #D70000", "A c #D80000", "S c #DD0000", "D c #DB0404", "F c #DB0606", "G c #CD1010", "H c #CD1514", "J c #CC1B1A", "K c #CA1D1C", "L c #D91110", "P c #E20000", "I c #CD2322", "U c #CB2423", "Y c #CE2727", "T c #CB2827", "R c #CF2C2B", "E c #D52223", "W c #DC2724", "Q c #D82827", "! c #DE2D2B", "~ c #D92E2C", "^ c #DD2E2D", "/ c #CE3334", "( c #CC3736", ") c #D23032", "_ c #DD3432", "` c #886665", "' c #BD7264", "] c #A57D71", "[ c #CF4947", "{ c #CD4A48", "} c #CC4C4A", "| c #DF4240", " . c #D74A48", ".. c #CF514F", "X. c #CD5250", "o. c #CE5E5B", "O. c #D15553", "+. c #D05D5B", "@. c #DA615E", "#. c #D06765", "$. c #D96362", "%. c #D26D6B", "&. c #C2786B", "*. c #CE7C79", "=. c #139411", "-. c #169616", ";. c #189618", ":. c #199719", ">. c #1D971A", ",. c #1F981C", "<. c #1E991E", "1. c #20991E", "2. c #19A21D", "3. c #20A01D", "4. c #309929", "5. c #319B2D", "6. c #359F34", "7. c #18A524", "8. c #19A524", "9. c #22A322", "0. c #20A62B", "q. c #2DA72C", "w. c #23A92E", "e. c #26AA2E", "r. c #2AA732", "t. c #26AD34", "y. c #2FAE3C", "u. c #34AB33", "i. c #34AC33", "p. c #3BA13A", "a. c #3CA139", "s. c #3EAE3C", "d. c #41A23D", "f. c #42A33E", "g. c #34B040", "h. c #36B144", "j. c #43A442", "k. c #46AF44", "l. c #4BBC57", "z. c #798174", "x. c #7A9375", "c. c #799579", "v. c #53C469", "b. c #83997E", "n. c #91947A", "m. c #AC8E71", "M. c #AF8F75", "N. c #B1977B", "B. c #C68071", "V. c #CE827F", "C. c #868581", "Z. c #878D83", "A. c #8F8C88", "S. c #908C88", "D. c #909490", "F. c #989490", "G. c #B7A48D", "H. c #A3B19F", "J. c #A6A5A1", "K. c #A9A6A2", "L. c #A8A7A3", "P. c #A9A8A4", "I. c #ABA8A4", "U. c #ACABA7", "Y. c #A6ADA9", "T. c #AEADA9", "R. c #AFADA9", "E. c #AFAEAA", "W. c #AEAFAB", "Q. c #A8B0AC", "!. c #A9B2AE", "~. c #B3B2AE", "^. c #B4B2AE", "/. c #B7B6B1", "(. c #BAB8B3", "). c #BCBAB6", "_. c #CE9692", "`. c #C99B98", "'. c #C89F9C", "]. c #C7A19D", "[. c #D1A9A4", "{. c #D7AEA5", "}. c #D3AFAB", "|. c #CFBBB6", " X c #CBBDB8", ".X c #D4B4B0", "XX c #D4BAB6", "oX c #B7CAB1", "OX c #B8CAB2", "+X c #BDCCB7", "@X c #C2C2BE", "#X c #C2CEBC", "$X c #D1C1BC", "%X c #D3C1BC", "&X c #D4C2BD", "*X c #C8C6C2", "=X c #C8C7C2", "-X c #CFCAC5", ";X c #CCCBC6", ":X c #CDCBC6", ">X c #CECCC8", ",X c #D1C8C3", "X>X;X9X9X9X5X5X5X9X", "&XI H K L p * $ d =XU.T.T.~.5X", ".XV #.*.D y * w 3 7 ).J.K.K.U.6X", "}.J [.|.W t 0 S.n e (.K.U.K.T.5X", "XXC E ) P i o 7 @XY.!.Q.Q.4X", "9XG.m.M.N.n.c.b.x.H.3X].`.]. X9X", "+Xr.7.7.0.2.q.9.3.l.%.B R B O.jX", "OX1.a.f.1.-.<.j.5.h.+.Y &X..[ gX", "oX:.6.a.:.=.<.f.4.g.o.T $XX.{ gX", "#Xk.i.u.s.e.y.t.w.v.$.A ~ A .gX", "9XZ.v z.b ] &.&.' {.` : u : l kX", "/.O o M S / G ! c O g zX", "T.3 C.A.9 a _ |._.| k d F.x j lX", "^.$ 5 8 @ s D _.} ^ z # q = h zX", "*Xr ; 3 4 N Q ( U @.D.; 3 - m fX", "eXaXaXaXaXrXuX0XuXuXiXaXaXaXaX9X" }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 172 2", " c #010101", ". c #080404", "X c #0C0A0A", "o c #111010", "O c #131A19", "+ c #1B1B1A", "@ c #320201", "# c #252423", "$ c #282726", "% c #292827", "& c #2C2C2B", "* c #322F30", "= c #2F3436", "- c #373536", "; c #393537", ": c #393837", "> c #3C3B39", ", c #7D0000", "< c #5D3436", "1 c #444342", "2 c #484746", "3 c #494846", "4 c #4E4D4B", "5 c #4C5250", "6 c #535250", "7 c #605F5D", "8 c #715250", "9 c #62605E", "0 c #767572", "q c #7D747A", "w c #820202", "e c #9B0201", "r c #A21A19", "t c #C70302", "y c #CB0101", "u c #CC0D0D", "i c #D50101", "p c #DA0101", "a c #CD1413", "s c #CC1B1A", "d c #D61A19", "f c #CE2524", "g c #CD2D2C", "h c #CE3433", "j c #CE3C3A", "k c #D23334", "l c #D93436", "z c #D43D3C", "x c #817C7D", "c c #CF4543", "v c #CF4D4B", "b c #D44341", "n c #D34846", "m c #CF504E", "M c #CF5250", "N c #D35452", "B c #D05D5A", "V c #D06260", "C c #D06F6D", "Z c #CE7572", "A c #C6747A", "S c #D17774", "D c #DE7177", "F c #D17875", "G c #DD747A", "H c #E2747A", "J c #008E00", "K c #069105", "L c #0A9107", "P c #0C930B", "I c #10950F", "U c #139713", "Y c #169816", "T c #1B9618", "R c #1B991B", "E c #219A1F", "W c #239C22", "Q c #2B9E2A", "! c #2FA02E", "~ c #30A02F", "^ c #32A031", "/ c #38A237", "( c #3CA43B", ") c #42A541", "_ c #43AB46", "` c #4DAC4E", "' c #57AC56", "] c #59AD57", "[ c #5DAE5B", "{ c #66B164", "} c #6BB268", "| c #D1817E", " . c #8E8C89", ".. c #908E8A", "X. c #93928E", "o. c #9D9B97", "O. c #BB918E", "+. c #87BF87", "@. c #A7A39F", "#. c #A7A6A2", "$. c #A8A7A3", "%. c #AAA9A5", "&. c #AFAEAA", "*. c #B0AEAA", "=. c #B4B3AE", "-. c #BBBAB5", ";. c #BEBCB8", ":. c #D28481", ">. c #D28986", ",. c #D58D8A", "<. c #D3918E", "1. c #D29A96", "2. c #D99B9C", "3. c #D3A09C", "4. c #D4A6A2", "5. c #D4AAA6", "6. c #D3AEAA", "7. c #D3B2AE", "8. c #CCBCB7", "9. c #D4B6B2", "0. c #D4BAB5", "q. c #D4BDB8", "w. c #94CF9F", "e. c #96CFA1", "r. c #9ACFA1", "t. c #96D0A1", "y. c #98D0A2", "u. c #ACC7A7", "i. c #A6CFA1", "p. c #AEC7A8", "a. c #A7D0A2", "s. c #AAD1A5", "d. c #BFC4BF", "f. c #BECCB8", "g. c #B5D3B8", "h. c #C3C1BD", "j. c #D4C3BF", "k. c #BFC7C2", "l. c #BFC8C3", "z. c #C1C9C3", "x. c #CBCFC4", "c. c #C2CDC8", "v. c #CBCFCA", "b. c #D5C7C3", "n. c #DEC1C2", "m. c #D5CBC6", "M. c #D3CFCA", "N. c #C5D7C7", "B. c #CDD7C7", "V. c #C6D7C8", "C. c #CFD7C8", "Z. c #C5D8C8", "A. c #C9D8CB", "S. c #D4D2CD", "D. c #DFCDD0", "F. c #D5D6D1", "G. c #DCD6D4", "H. c #D5DAD5", "J. c #DDDBD5", "K. c #DFD6D8", "L. c #D5DED8", "P. c #DEDDD8", "I. c #E0DED9", "U. c #D6E1DC", "Y. c #DCE1DD", "T. c #E4E2DC", "R. c #D6EAE4", "E. c #D6F3EE", "W. c #D7F7F2", "Q. c #F2EFEA", "!. c #F5F2ED", "~. c #F8F6F0", /* pixels */ "S.S.S.S.S.S.S.F.F.S.F.S.S.S.S.F.S.F.S.S.S.S.S.S.S.S.S.S.S.S.S.F.", "S.F.H.S.S.S.S.S.S.m.S.S.S.S.v.S.S.m.S.J.S.S.F.S.F.S.S.S.S.S.S.S.", "F.m.n a s a a d s d r O + + + + + + + .Y.-.*.*.*.&.&.&.&.=.S.S.", "L.4.y y y u a y y i e > G.$.$.%.%.%.%.%.$.*.S.F.", "L.4.y y y >.b.y t i e > G.$.%.%.%.%.%.%.%.&.S.S.", "L.4.y f z 3.b.c g i e : 1 1 1 3 . : J.$.%.%.%.%.%.%.%.&.S.S.", "L.5.y B W.S.M.E.1.i e ;.Q.T.T.!.& & P.$.%.%.%.%.%.%.%.*.S.S.", "L.4.y a s 1.b.f s p e + $ # # & : J.$.%.%.%.%.%.%.%.*.S.S.", "L.4.y y y | 9.y y i e . . : G.%.%.%.%.&.%.%.%.&.S.F.", "H.9.y y y y y y t i e 4 I.$.@.#.@.@.@.#.#.%.S.S.", "S.F.2.G G G D G G G A q q q q q q q x h.F.v.c.c.N.c.c.c.z.h.S.S.", "S.G.g.w.y.t.t.t.y.t.t.a.i.a.a.a.a.a.a.S.G.0.,.,.<.<.,.,.6.G.S.S.", "G.f.R K L P P L K P P P K K P P P K J { I.s y y y y y y y 4.H.S.", "K.u.P ( ~ Y R W ) R R R R ) R R U ( W _ n.t y y z F u y y <.L.S.", "K.u.U E [ Q Y [ ! Y R R Y ( ' P ( ] P ` n.y y y C R.u y y <.U.S.", "K.u.U Y R ] { W U R Y R R U ~ } ` Y I ` n.y g 6.0.m.7.>.y <.U.m.", "K.u.U Y R ] { W U R Y R R U ^ } ` Y P ` n.y g 7.0.M.9.>.y <.L.S.", "K.p.U W [ Q T [ ! Y R R U ( ' P / ] U ` n.t y y C R.u y y <.U.m.", "K.u.K / Q I Y R ( Y R Y R ( Y R U / R _ n.t y t j S y y y <.U.S.", "F.x.) Y R E W R R R R W E R R W E R T +.n.i p p p p p p p <.L.S.", "S.G.P.B.C.B.B.B.C.C.B.N.Z.Z.Z.Z.N.N.A.Y.8.w w w w w w w , O.P.S.", "F.M.7 * ; - - - ; = < l k k k k k k k 4.z.. X.Y.M.", "P.%. @ i y y y y y y y b k.. . . . . X.I.S.", "I.#. . @ i y y u m.B y y z k.. X.T.M.", "I.$. o % # # % + @ i y s h L.S s y z z. X % # # # # X.T.M.", "I.%. 7 ~.T.T.!.o. @ i y q.R.S.H.E.g k c. > !.T.T.!.-. X.I.C.", "I.%. + 2 1 1 2 & @ i y k m H.:.z y z z. o 2 1 1 1 - X.I.M.", "I.$. . . @ i t y u L.V y y z k.. . X.I.M.", "Y.*. @ i y y y u y y y n c.X . o.P.M.", "S.G.q 4 6 6 6 6 6 5 8 N M M M n M M M 7.P...4 6 6 6 6 4 0 F.F.F.", "S.S.P.T.T.T.T.T.T.T.T.U.U.U.U.R.R.U.U.H.S.P.T.T.T.T.T.T.I.S.M.F.", "S.S.S.M.M.M.v.M.M.v.S.S.m.S.m.S.m.m.S.S.F.M.S.M.v.M.M.M.M.F.F.M." }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 158 2", " c #010101", ". c #0C0C0B", "X c #141414", "o c #181717", "O c #3A0101", "+ c #21201F", "@ c #222221", "# c #363634", "$ c #3D3837", "% c #343C3B", "& c #3D3C3B", "* c #663C3B", "= c #434241", "- c #5A5957", "; c #5C5B59", ": c #605F5D", "> c #646360", ", c #6E6D6A", "< c #73726F", "1 c #757472", "2 c #7B7A77", "3 c #7E7D7A", "4 c #920101", "5 c #AB0101", "6 c #A53C3B", "7 c #B73C3B", "8 c #CB0101", "9 c #CC0B0B", "0 c #D40100", "q c #D80101", "w c #CD100F", "e c #CD1312", "r c #CD1817", "t c #CD1E1D", "y c #CE2423", "u c #CE2827", "i c #CE2D2C", "p c #CE3332", "a c #CB3B37", "s c #CE3D3B", "d c #D63C3B", "f c #CF403F", "g c #807F7C", "h c #CF4341", "j c #CC4945", "k c #CF514F", "l c #CD5755", "z c #CD5856", "x c #CE5B59", "c c #D05553", "v c #D05856", "b c #D0605E", "n c #D06563", "m c #D06866", "M c #D16E6C", "N c #CE7875", "B c #D17371", "V c #D17976", "C c #D17E7B", "Z c #059306", "A c #079308", "S c #0D950D", "D c #0F9610", "F c #129713", "G c #169816", "H c #179918", "J c #1A991A", "K c #21991E", "L c #239A21", "P c #2B9E2A", "I c #2EA02D", "U c #35A234", "Y c #3CA43B", "T c #44A743", "R c #49A747", "E c #4CA94A", "W c #54AB53", "Q c #5EAE5C", "! c #69AF63", "~ c #65B062", "^ c #69B267", "/ c #6EB36B", "( c #73B26D", ") c #75B672", "_ c #79B776", "` c #7EB77A", "' c #7FB87B", "] c #D2827F", "[ c #8F8E8A", "{ c #91908C", "} c #989793", "| c #9B9896", " . c #9D9C98", ".. c #AC9896", "X. c #A09D9B", "o. c #8BBC87", "O. c #A2A19D", "+. c #A7A6A2", "@. c #A8A7A3", "#. c #AAA9A5", "$. c #B0AFAA", "%. c #B3B1AD", "&. c #B6B5B1", "*. c #BBBAB5", "=. c #D28884", "-. c #D28D8A", ";. c #C99793", ":. c #C59896", ">. c #D39793", ",. c #D59896", "<. c #D99896", "1. c #D59B99", "2. c #D2A19D", "3. c #D3A7A3", "4. c #D3ABA7", "5. c #D3AEA9", "6. c #D4B1AD", "7. c #D4B7B3", "8. c #D4BAB5", "9. c #98C094", "0. c #B7CAB1", "q. c #BECCB8", "w. c #C5C3BF", "e. c #C3CCBB", "r. c #D4C3BE", "t. c #C8C6C2", "y. c #CCCBC6", "u. c #CECDC8", "i. c #D4C6C1", "p. c #D6C9C5", "a. c #D4CEC8", "s. c #D5D2CD", "d. c #D9D0CE", "f. c #CBDCD0", "g. c #D6D6D0", "h. c #DAD6D2", "j. c #D6DBD3", "k. c #DCDBD5", "l. c #D5DED9", "z. c #DFDDD8", "x. c #E2DFDA", "c. c #D6E0DA", "v. c #DAE0DB", "b. c #E2E0DA", "n. c #D6EAE5", "m. c #D6EFE9", "M. c #D7F6F0", "N. c #D7FBF4", "B. c #D7FFFE", "V. c #E8E6E1", "C. c #E2EBE4", "Z. c #ECEAE5", "A. c #E4E4E9", "S. c #F1EFE9", "D. c #E3F0EB", "F. c #F4F2EC", "G. c #FFFCF6", "H. c #FFFFFF", /* pixels */ "s.s.s.s.g.s.s.g.g.s.s.g.s.s.s.g.s.s.g.s.s.s.s.s.s.s.s.g.s.g.s.s.s.s.s.s.s.s.s.s.g.s.s.g.s.s.s.s.", "s.s.s.g.s.s.s.s.s.g.s.s.s.s.s.s.s.s.s.s.s.g.g.s.s.g.s.s.s.s.s.s.s.s.g.s.s.g.s.s.s.s.s.s.s.s.g.s.", "s.s.s.s.s.s.s.a.s.a.a.s.a.s.a.s.a.s.a.s.s.a.s.s.a.s.u.s.a.s.g.s.s.s.s.s.s.s.s.s.s.s.s.s.h.s.s.s.", "g.s.s.s.j.c.c.v.c.c.v.l.v.c.c.c.v.b.x.x.b.b.b.b.b.b.b.b.v.s.s.g.g.h.g.j.h.s.h.s.s.k.s.k.s.s.s.s.", "s.s.s.g.1.h s s s s s s s a s d 6 % & & & & & & & & & & , s.g.a.*.&.&.&.&.&.&.&.%.&.&.%.s.s.s.s.", "g.g.g.i.r 8 8 8 8 8 8 8 8 8 8 q 4 2 V.*.+.@.@.@.@.+.+.@.@.#.@.@.u.s.s.j.", "s.a.j.6.8 8 8 8 8 y y 9 8 8 8 q 4 . . ; V.$.@.#.#.#.#.#.#.#.#.#.#.#.u.s.s.s.", "g.s.l.6.0 8 8 8 w p.l.u 8 8 8 q 4 ; V.$.@.#.#.#.#.#.#.#.#.#.#.#.u.h.s.s.", "s.s.l.5.8 8 8 8 9 i.j.t 8 8 8 q 4 ; V.%.+.#.#.#.#.#.#.#.#.#.#.@.u.h.s.s.", "s.a.g.7.8 0 h C V a.g.=.V l 8 q 4 > 2 1 1 1 1 3 # ; V.%.@.#.#.#.#.#.#.#.#.#.#.#.u.h.s.s.", "g.d.l.5.8 8 -.B.n.g.a.n.B.5.8 0 4 u.G.F.F.F.S.H., ; V.%.@.#.#.#.#.#.#.#.#.#.#.@.u.s.j.s.", "s.a.l.6.8 8 h C V p.g.=.N l 8 q 4 > 2 1 1 2 1 3 # ; V.%.@.#.#.#.#.#.#.@.#.#.#.@.a.s.s.s.", "g.a.g.7.8 8 8 8 9 i.g.y 8 8 8 q 4 . ; V.%.@.#.#.#.#.#.#.#.#.@.#.@.u.s.s.s.", "g.a.l.6.8 8 8 8 w p.l.y 8 8 8 q 4 . - V.%.@.#.#.#.#.#.@.#.#.#.#.#.u.h.s.s.", "g.s.l.5.8 8 8 8 8 y u 9 8 8 8 0 4 ; V.%.@.#.#.#.#.#.#.#.#.#.#.#.u.s.g.s.", "g.a.s.g.h 8 8 8 8 8 8 8 8 8 8 0 4 . +.z.w.@.@.@.@.@.@.+.+.@.@.@.@.u.h.s.s.", "d.s.s.j.p.2.<.2.<.<.2.<.>.<.<.2.:.| | | | | | | | | .} *.k.a.s.u.y.y.y.t.t.y.y.t.y.t.t.s.s.s.s.", "s.s.s.d.k.f.f.f.f.f.f.f.f.f.f.f.f.k.j.j.j.c.j.j.j.j.j.j.k.d.s.j.g.p.p.p.p.a.p.p.p.a.j.h.s.s.g.s.", "s.s.s.h.' K L L K K K L K K L L L K K K L J L L K K K K R e.k.5.u 9 9 9 9 e 9 9 9 9 x g.s.s.s.s.", "s.s.d.e.J G D G G J G G G F G G G G G G D G H G G G G G Z ( C.f 8 8 8 8 8 8 8 8 8 8 8 1.l.d.s.s.", "s.s.k.0.H H T L G K J G J T J J J J G J R H G J J D I Y Z ^ V.a 8 8 8 8 p m p 8 8 8 8 >.l.s.s.g.", "s.s.h.0.H F U ) K D G G / T G J J J J G W ~ D J F I ) I Z / A.a 8 8 8 8 C B.C 8 9 8 8 >.c.a.s.s.", "s.s.k.0.H J D U ) J D ( T S J J G J G J S W ~ S I ) I F S ^ A.a 8 8 8 8 B m.B 8 8 8 8 >.c.a.s.s.", "s.s.h.0.H J K D U ^ ^ T S J J J J J J J J D E ^ ^ P F K A ^ A.a 8 t 7.4.r.j.r.4.7.t 8 >.c.s.s.g.", "s.s.h.0.J J J J D _ 9.F G J J J H H J J J F P o.~ A J J D ~ V.a 8 u n.n.l.s.l.n.n.u 8 >.v.a.s.g.", "s.s.h.0.G J G G ( Y I / K F J J J H J J F P _ K E ~ F J S ^ A.a 8 e s i -.n.-.i s 9 8 >.c.s.s.s.", "s.s.k.0.G J J / T D F U ) L G J H J J G P _ P F F W ~ J S ! A.a 8 8 8 8 N N.V 8 8 8 8 >.c.s.s.s.", "s.s.h.0.G F W R S J K S Y Q F J J J J J ! P F K J D W R Z ^ A.a 8 8 8 8 n p.n 8 9 8 8 >.c.a.s.s.", "s.s.h.e.J F J F J G G J F G G G G G G G H F H G G H F J Z ( A.a 8 8 8 8 8 8 8 8 8 8 8 >.c.s.s.s.", "s.s.s.h.` L J L K K K K L L K K L L L L L L L L L L L K R e.c.d 0 0 0 0 0 0 0 0 q 0 0 >.c.a.s.s.", "s.s.s.s.k.k.k.j.j.j.j.j.j.j.j.j.j.f.f.f.f.f.f.f.f.f.f.f.j.g.c.7 5 5 5 5 5 5 5 5 5 5 5 :.c.s.s.s.", "s.s.s.j.u.X.} | | | | | | | | } ..<.,.<.,.,.,.,.,.,.,.,.7.g.z.& } b.s.s.s.", "s.s.s.s.= O q 8 8 8 8 8 8 8 8 8 8 9 2.C.& } x.u.s.s.", "s.s.z.%. O q 8 8 8 8 8 8 8 8 8 8 8 l D.$ } b.s.s.s.", "s.s.z.%. O q 8 8 8 8 i 3.C 8 8 9 8 l D.$ } b.s.s.s.", "s.s.k.%. O q 8 8 8 8 f M.8.8 8 8 8 l D.$ | b.s.s.s.", "s.s.k.%. X @ + + + + @ o O q 8 8 t r l c.6.t y e 8 l D.$ @ + @ o + @ @ . } b.u.s.s.", "s.s.z.%. 3 Z.g.k.k.h.V. . O q 8 8 7.c.s.s.s.g.n.b 8 x D.$ @ z.k.k.z.k.k.z.@ } b.s.s.s.", "s.a.k.%. < k.w.t.t.w.s.[ O q 8 8 3.a.p.s.s.r.s.l 8 l D.& @ y.t.t.y.t.t.y.@ | z.s.s.s.", "s.s.k.%. . . . . . . . O q 8 8 9 8 h c.5.9 9 9 8 l D.& . . . . . . . . } b.s.g.s.", "s.s.k.%. O q 8 8 8 8 f N.8.8 8 8 8 l D.$ } b.a.s.s.", "s.s.k.%. O q 8 8 9 8 y =.m 8 9 8 8 l D.$ . . } b.s.s.s.", "s.s.g.w.X O q 8 8 8 8 8 8 8 8 8 8 8 N C.; $.k.s.s.s.", "s.s.s.k.O.= & & & & & & & & & % * d a d s s s s s s d a M s.h.y.; & & & & & % & & & { z.s.s.g.s.", "s.s.g.s.k.b.b.b.b.b.x.x.b.b.b.b.z.c.c.c.c.c.v.l.c.v.c.c.l.g.s.h.b.z.b.b.z.b.b.z.b.b.z.s.s.s.g.s.", "s.s.s.s.s.s.s.u.s.u.s.s.s.s.a.s.s.s.s.a.s.a.s.s.s.a.a.s.s.s.s.s.s.s.u.s.s.a.s.s.s.u.s.s.s.s.s.s.", "s.s.s.s.s.s.s.s.s.s.s.g.g.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.g.s.s.g.s.g.s.s.g.", "g.s.s.g.g.s.s.s.s.s.s.s.s.s.s.s.s.h.s.s.s.s.s.s.g.s.g.s.s.g.s.s.g.s.s.g.g.s.s.s.s.s.s.s.s.s.s.s." }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/map-icon.c0000644000175300017530000003621512161170374015420 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 224 2", " c #7D7055", ". c #876B51", "X c #876D53", "o c #866B57", "O c #866F55", "+ c #886A51", "@ c #886F56", "# c #896F57", "$ c #8B6E57", "% c #876F58", "& c #8B6D58", "* c #8A6D59", "= c #837153", "- c #817355", "; c #897055", ": c #887056", "> c #897257", ", c #8B7059", "< c #8B735A", "1 c #8A735B", "2 c #8D7258", "3 c #8C7359", "4 c #8C7458", "5 c #8D755A", "6 c #8D765A", "7 c #84795B", "8 c #847A5D", "9 c #977C5E", "0 c #AC765B", "q c #8B7A60", "w c #8E7860", "e c #8C7B62", "r c #937562", "t c #927A61", "y c #917962", "u c #927862", "i c #917B63", "p c #A67961", "a c #A77A61", "s c #A37F60", "d c #A77C60", "f c #A97A60", "g c #AD7964", "h c #B17B62", "j c #B67E63", "k c #B37F67", "l c #B47F66", "z c #B47F6F", "x c #79875F", "c c #788B5E", "v c #7E885D", "b c #7D8E5F", "n c #7C8F62", "m c #769061", "M c #789062", "N c #7B9262", "B c #789461", "V c #7E9966", "C c #799C64", "Z c #7E9B6F", "A c #88805A", "S c #8E805C", "D c #8E825D", "F c #90835A", "G c #88945E", "H c #90945F", "J c #98925A", "K c #A18659", "L c #B1865F", "P c #A7975D", "I c #A9905B", "U c #AF975E", "Y c #B7955E", "T c #BC9A5C", "R c #BD985F", "E c #BEA25A", "W c #BEA75D", "Q c #898063", "! c #898665", "~ c #828964", "^ c #8C8962", "/ c #918260", "( c #809565", ") c #889360", "_ c #819C65", "` c #879C65", "' c #839469", "] c #809868", "[ c #829B69", "{ c #82986A", "} c #949462", "| c #A78063", " . c #A88164", ".. c #A88E65", "X. c #A4836D", "o. c #AB8369", "O. c #B18363", "+. c #B08164", "@. c #B28167", "#. c #B18266", "$. c #B48E66", "%. c #B2876C", "&. c #B98268", "*. c #AA9169", "=. c #B39060", "-. c #B99163", ";. c #B49E66", ":. c #BD9D60", ">. c #B4946A", ",. c #B48C78", "<. c #A0A068", "1. c #BFB064", "2. c #C2A95E", "3. c #C1A663", "4. c #C1A064", "5. c #C7AF60", "6. c #C9AF63", "7. c #C3AC6C", "8. c #CAB262", "9. c #CEB562", "0. c #C8B065", "q. c #CBB364", "w. c #CAB365", "e. c #CCB264", "r. c #CDB366", "t. c #CDB466", "y. c #CFBA66", "u. c #C9B16A", "i. c #C9B26C", "p. c #C9B26D", "a. c #CBB36C", "s. c #C8B26E", "d. c #CAB46E", "f. c #CCB66E", "g. c #CDB86F", "h. c #D0B565", "j. c #D0B766", "k. c #D5BA68", "l. c #C6B273", "z. c #CBB475", "x. c #CBB779", "c. c #D1BD70", "v. c #B79D8D", "b. c #AFA293", "n. c #B1A799", "m. c #B0A59C", "M. c #B3A99C", "N. c #B3AC9F", "B. c #ABB49D", "V. c #C7BF98", "C. c #C3B4A8", "Z. c #C0B9AF", "A. c #C1BAB1", "S. c #CAC3B3", "D. c #C5C1BB", "F. c #C8C1B8", "G. c #C8C0B9", "H. c #C8C4BC", "J. c #C9C8C1", "K. c #CDC9C1", "L. c #CCC9C6", "P. c #CECDC8", "I. c #CFCDC8", "U. c #CFCEC8", "Y. c #D3CCC9", "T. c #D1CEC8", "R. c #D1CFC9", "E. c #D2CEC8", "W. c #D1CFCA", "Q. c #D1CFCB", "!. c #D5CFCB", "~. c #D3D0C7", "^. c #D1D1C9", "/. c #D3D0CB", "(. c #D2D1CB", "). c #D1D3CA", "_. c #D3D2CB", "`. c #D5D1C8", "'. c #D6D2C9", "]. c #D4D0CA", "[. c #D5D0CB", "{. c #D4D1CB", "}. c #D4D3CB", "|. c #D6D3CA", " X c #D6D3CB", ".X c #D7D3CB", "XX c #D1D4CA", "oX c #D3D1CD", "OX c #D4D0CC", "+X c #D5D0CC", "@X c #D4D1CC", "#X c #D6D1CC", "$X c #D4D2CC", "%X c #D4D3CC", "&X c #D6D3CC", "*X c #D4D3CE", "=X c #D5D3CE", "-X c #D6D4CC", ";X c #D7D4CC", ":X c #D5D4CE", ">X c #D8D1CB", ",X c #D9D3CC", "XX.;.c.>.z *./ o.%.7.d.s.f.z.:X", "rXQ.$X>X=X=X>X>X=X>X.X;X{.|.|.wX" }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 144 2", " c #7E6C51", ". c #7C7555", "X c #7C7957", "o c #7B7758", "O c #7C7D59", "+ c #84694F", "@ c #856D54", "# c #8A6E55", "$ c #836D5A", "% c #8D6F5A", "& c #926D55", "* c #926F58", "= c #847255", "- c #8A7256", "; c #8F7855", ": c #86745C", "> c #8C745A", ", c #927956", "< c #9D7C56", "1 c #91735B", "2 c #9B735B", "3 c #947E5E", "4 c #9B7C5F", "5 c #A2745D", "6 c #A9755C", "7 c #A47C5D", "8 c #AB7A5F", "9 c #B0785C", "0 c #9F7E60", "q c #907C68", "w c #A47C62", "e c #AC7C63", "r c #A97D68", "t c #B17660", "y c #B47E66", "u c #B57F68", "i c #76865C", "p c #79855C", "a c #758C5D", "s c #7C8B5E", "d c #7B915F", "f c #7B8E60", "g c #749762", "h c #7C9464", "j c #7D9A66", "k c #7C9368", "l c #7D9E69", "z c #7DA069", "x c #8B8256", "c c #84845D", "v c #88835C", "b c #838D5C", "n c #8C945E", "m c #8E995F", "M c #92965D", "N c #92985F", "B c #AD825F", "V c #A08858", "C c #AB8B59", "Z c #B08D5E", "A c #A69157", "S c #A39E5E", "D c #A89F5F", "F c #B4945E", "G c #B29C5D", "H c #BA9E5D", "J c #B3A35F", "K c #BAA15D", "L c #809765", "P c #819A67", "I c #8D9960", "U c #81976A", "Y c #829B69", "T c #919B61", "R c #8E8671", "E c #9C8E7E", "W c #859870", "Q c #899976", "! c #AD8260", "~ c #AE826B", "^ c #B48066", "/ c #B88166", "( c #B28B61", ") c #B68268", "_ c #BC8169", "` c #A79261", "' c #B69E64", "] c #BA9860", "[ c #A48674", "{ c #AEA165", "} c #B4A560", "| c #BCA363", " . c #BFAA60", ".. c #B4A36C", "X. c #BDA96C", "o. c #C4AB5D", "O. c #C1A661", "+. c #C4AC65", "@. c #C8AE63", "#. c #C4AD6A", "$. c #C7B164", "%. c #CCB365", "&. c #C6B16B", "*. c #CDB569", "=. c #CFB868", "-. c #D2B666", ";. c #D3BA67", ":. c #D0B568", ">. c #D2BA69", ",. c #A39585", "<. c #A79C8F", "1. c #B69786", "2. c #A69C90", "3. c #A89D90", "4. c #ACA194", "5. c #B0A599", "6. c #B1A89D", "7. c #B6AEA5", "8. c #BEB6A3", "9. c #B6B3AA", "0. c #BEB4AB", "q. c #B9BDAF", "w. c #C2B488", "e. c #C4B7AD", "r. c #C1BCB3", "t. c #CABCB2", "y. c #CCC5B3", "u. c #CFC8B4", "i. c #C5C2BC", "p. c #C7C5C1", "a. c #CAC6C0", "s. c #CDCBC6", "d. c #CFCDC9", "f. c #D2CEC6", "g. c #D1CFCA", "h. c #D3D1CC", "j. c #D6D5D2", "k. c #D9D7D1", "l. c #DCDAD5", "z. c #DEDDD8", "x. c #E0DDD9", "c. c #E2E1DD", "v. c #E4E4E2", "b. c #E9EAE7", /* pixels */ "l.l.z.x.x.z.z.x.x.x.x.z.z.x.z.z.z.l.z.z.x.z.x.z.z.z.x.z.z.x.z.k.", "l.k.f.s.s.f.f.s.s.s.s.f.s.s.d.h.h.h.g.f.f.f.f.f.f.h.f.h.f.f.k.z.", "x.d.k h k j j j j h . % > @ E l.g.j.0.5 ' $.#.+.+.+.+.+.@.X.h.z.", "x.s.h P L L j O X X = > > + 6.c.l.c.t.6 O.-.%.%.%.%.:.%.%.&.h.z.", "x.s.Q U h Y : > 1 1 > > @ 3.z.l.k.v.e.6 | =.%.%.%.*.*.$.%.#.h.z.", "z.h.h.s.Y a = @ @ # + <.c.l.l.x.i.~ y ! K $.$.@.$.-.%.%.&.h.z.", "z.h.h.c.h.q.9.r.q.r.7.i.z.l.l.k.c.1.9 ) ) e B B ! B K :.%.+.h.z.", "l.h.h.z.h.g.k.c.c.c.d.h.s.p.d.z.z.[ y ^ ) ^ ^ ) y u e K ;.&.h.z.", "z.g.h.g.d.l.l.k.l.k.l.j.a.d.a.s.,.+ 2 ) ^ y ^ ^ ^ ^ ) e H | h.z.", "z.h.g.d.l.l.l.l.l.l.h.s.s.j.4.@ - - 2 ) ^ ! ^ y ^ ^ y ) & : h.z.", "z.h.g.d.l.l.l.l.l.l.s.l.k.j.0.+ > @ y / ) y / ) ) w c e 2 > h.z.", "l.h.g.g.l.l.l.l.l.k.d.z.h.p.b.6.# - 1 5 4 0 4 ^ u 4 g 3 - : h.z.", "z.h.g.d.l.l.l.l.l.l.s.j.w.X.h.s.$ > > @ i l a 6 w b j j O > h.z.", "l.h.g.g.l.l.l.l.l.s.j.y.o.%.&...a . * @ h p @ # O l L Y O > h.z.", "z.g.i.p.s.h.x.h.s.h.v.y.%.*.-.o.v X X h Y s X X f Y P j o > h.z.", "z.h.h.z.k.s.g.i.d.s.d.8.K o.O.G 9 c z L L L j l h f h O - : h.z.", "x.g.g.x.x.l.h.k.i.$ > - # - - @ y w h g j L L p @ = @ - > : h.x.", "l.h.h.x.k.k.x.x.j.q - > - > > - 2 _ 7 S n j h @ 1 > > > > > h.x.", "x.h.g.x.l.l.l.k.c.g.> - > > - 1 # 2 7 -.@.M b ; = = - > > > h.z.", "l.h.g.x.k.k.l.k.l.6.- > > - > > > = ; $.%.-.@.%.m i - > > > h.z.", "x.h.k.v.c.c.b.7.> @ > - > - > - > - , %.%.%.%.:.T p - > > > h.x.", "x.f.2.4.4.4.5.R . X . > > > > > - # , $.%.%.%.%.T p - > > > h.x.", "x.s.@ + @ + + . z P h . = = - @ * u B %.%.$.%.:.I i # 1 @ : h.x.", "x.s.> > > > 1 = s P P L f G @.o.Z y y F %.=.%.:.} h o @ a h h.z.", "x.s.% - > - > > # s L P j S -.-.$.B u e ! F %.%.-.} s f j U h.x.", "x.s.# ; ; % > # e e s P L h D :.%.( y y e F %.%.$.-.} h j U h.x.", "x.f.e F =.V @ # / ) e s j j N %.$.( e F $.$.%.%.%.$.-.J d k h.x.", "z.f.r F >.%.K F e y y e s l O - $.( e K >.%.%.%.*.%.*.:.o.{ h.l.", "x.g.e < A %.-.;.K e ) / 6 M x @ C B y 8 O.-.%.%.%.-.%.%.-.&.h.z.", "x.g.r > : &.#.&.| r ~ ~ r | ` $ r ~ ^ r ' &.#.#.#.#.#.#.#.X.h.z.", "l.l.h.f.h.h.h.h.h.g.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.l.l.", "l.l.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.x.l.x.l.z.x.l.l.l.l." }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 98 2", " c #7B684F", ". c #7E6A51", "X c #767554", "o c #787755", "O c #757855", "+ c #89664F", "@ c #83694E", "# c #846D54", "$ c #8B6C55", "% c #846F59", "& c #877056", "* c #897157", "= c #86715A", "- c #8C735A", "; c #867C59", ": c #887A59", "> c #90765C", ", c #9D735B", "< c #A2755C", "1 c #AC775C", "2 c #A6785F", "3 c #AA7A5F", "4 c #A27761", "5 c #A47961", "6 c #AB7B63", "7 c #A57D68", "8 c #AC7F68", "9 c #B27E65", "0 c #74865A", "q c #758A5C", "w c #788E5F", "e c #77915D", "r c #7A905F", "t c #778A61", "y c #7A8E61", "u c #769564", "i c #7B9363", "p c #7D9965", "a c #7F9568", "s c #9B8555", "d c #8F9159", "f c #92925A", "g c #A8865A", "h c #A9895A", "j c #AE9759", "k c #AE9A59", "l c #B29B5B", "z c #B7A15B", "x c #BDA55F", "c c #BEA85F", "v c #819A66", "b c #809769", "n c #839D69", "m c #AE8069", "M c #B48166", "N c #B78268", "B c #B98469", "V c #B39E61", "C c #B5A165", "Z c #BDA660", "A c #BFAA65", "S c #C1A95F", "D c #C9AE5D", "F c #C0A760", "G c #C1AA63", "H c #C9AF61", "J c #C3AC68", "K c #CDB366", "L c #CFB668", "P c #D1B666", "I c #D2B867", "U c #D0B768", "Y c #D3B969", "T c #9B8F80", "R c #9E9285", "E c #A19689", "W c #A3988C", "Q c #B09A8D", "! c #99A38B", "~ c #B9AD87", "^ c #BAAF8B", "/ c #BEBBB5", "( c #C0BDB7", ") c #C1BFBA", "_ c #C3C2BD", "` c #C7C6C1", "' c #CAC7C1", "] c #CECCC7", "[ c #CFCDC9", "{ c #D0CDC7", "} c #D0CEC9", "| c #D3D1CC", " . c #D7D5D0", ".. c #D9D7D2", "X. c #DCDAD5", "o. c #DFDDD8", "O. c #E0DED9", "+. c #E3E1DC", /* pixels */ "X.X.o.X.X.X.X.X.X.X.o.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.", "X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X...X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.o.", "X.X.X.X.o.o.o.o.o.o.o.O.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.O.o.o.o.o.o.o.o.o.o.o.o.o.o.o.X.X.X.X.", "X.X.X.X.] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] } [ } } } } ] ] { ] { { ] [ ] { { { { { ] ] X.X.X.X.", "X.X.O.] i a i i a i i a a a a y . - = - = = / | ] ] } ( 5 2 V J A A A A A A A Z G A G C } o.X.X.", "X.X.O.] a v v b v v v p r i i 0 # - - - - # ' +.X.X.+.{ 8 6 S I K K L L L L L L L L U J { o.X.X.", "X.X.o.] a v b v v v p O . # # # - - - - @ T X.X.X.X.o.' 6 6 c L K K K K K K K K K K K A { o.X.X.", "X.X.o.] y i i n v v O * - - - - - - - # R o.o.X.X.X.O.[ 6 3 S U H K H K K K K K K K L Z { o.X.X.", "X.X.o.] / ] ! e n r # - - - - - - - @ T o.X.X.X.X.X.o.Q 6 9 h K I U U I U U K K K K L A ] o.X.X.", "X.X.O.] | +.o.! u q . = & * & & * . R X.X...X.X.X.o.Q 1 M N 6 h S c c Z c c K L K L L G ] o.X.X.", "X.X.O.] ] X.X.o.] _ / ' _ ' ' / ( ] o.+.o.o.X.X.o.' 6 N 9 M N 8 3 3 2 2 6 2 h L K K L Z { O.X.X.", "X.X.O.} ] O.X. .| ` | +.o.+.+.| ' | [ ] [ [ X.X.o.[ 6 M M M 9 M M M M M M M 6 h K K K A { o.X.X.", "X.X.o.] ] O. ._ ] ] ..X.X.X.X...] ] | _ _ ` | +.+.R + M M M 9 M M M M M M M M 8 g K I J ] o.X.X.", "X.X.o.} ] o._ .O.o.X.X.X.X.X.X.+.X.| _ | | / | R @ * $ M M M M M M M M M M 9 M 6 g c C ] o.X.X.", "X.X.O.[ } | ] O.X.X.X.X.X.X.X.X...) } _ } ] = * # > * $ 6 M M 8 6 M M M M 9 M M N < & [ o.X.X.", "X.X.O.} } | ] X.X.X.X.X.X.X.o.X._ ..+.[ } ] # > - * $ M M 9 M M M M M 9 M M 2 6 M 2 & - [ X.X.X.", "X.X.o.} [ | ] o.X.X.X.X.X.X.o.[ ] O.X.] [ o.R # - % $ 9 N M M N M M 9 M M 2 0 0 9 6 # - ] o.X.X.", "X.X.o.[ } | ] o.X.X.o.X.X.X.o.[ [ X.O.[ [ O.o.T $ - # $ 6 2 2 3 6 2 9 M M 2 i i , , # = ] o.X.X.", "X.X.o.[ [ | ' O.X.X.X.X.X.X.o.[ [ o.o.~ ~ X.+.| - - - - $ 0 y i 0 6 N 9 ; p v y 0 # = ] o.X.X.", "X.X.o.[ [ | ] o.X.X.X.X.X.X.O.] [ X.^ H D ^ | ( . # - - > # i n i 0 , 3 ; p v p n i # - [ o...X.", "X.X.O.} | ..} +.X.X.X.X.o.+..._ X.[ c L K H G l q q # > $ X p w . & # . r b p v b w # - [ o.X.X.", "X.X.o.[ _ _ _ [ ..X.X.X.[ ] ) | o.] A L K K U Z w q # X p n w . & & . i v v p n w # - [ o.X.X.", "X.X.O.} _ } ] ] _ | +.} ` | X.+.+.| J I L K U F < , 0 r a v v p w w w r v v v n p X $ = ] o.X.X.", "X.X.o.} [ O.o.o.X._ } _ _ | ' ' [ / l c c x F l 6 3 i v v v v v n v n v w w w r X - - - ] o.X.X.", "X.X.o.} ] o.X.X.o...] ] | / % & & & $ # # * & @ 2 M ; i v v v v v v p X # # # # $ - - = ] o.X.X.", "X.X.o.} ] o.X.X.X.X.o.o.+.] # - - - - - - - - # 2 B 9 ; r r p v p p X $ - - - - - - - - ] o.X.X.", "X.X.O.} ] o...X.X.X.X.X.X.o.R @ - - - - - - - * $ 9 B 3 l S f u n r # > - - - - - - - = ] O...X.", "X.X.o.} ] o.X.X.X.X.X.X.X.X.+.W & - - - - - - - $ $ 6 , S Y K f r 0 # & # # - - - - - - ] o.X.X.", "X.X.o.} ] o.X.X.X.X.X.X.O.o.+.E $ - - - - - - - - - # . S K K K c x Z k q 0 # - - - * - ] o.X.X.", "X.X.o.} ] X.X.X.X.X.X.X._ ] R @ - - - - - * * - - - > $ c P H K L K I Z i i # - - - - = ] o.X.X.", "o.X.o.} [ +.o.o.+.o.+.| - * & > - - - - - - - - - - - = Z L K K K K L x r r # - - - - - ] O.X.X.", "X.X.o.[ / ] ' ' ' _ ] / . & & # & - * - - - - - - $ # @ Z K K K K K U c i r # - - - - = { X.X.X.", "X.X.o.] % # = # - % $ . 0 i w u o * > - - - - - & $ 1 < c U K K K K K Z i w # - - - - - [ o.X.X.", "X.X.o.] - - - - - - - # r n v v p O # # # & & # $ M B 3 S U H K K H L Z i y # - - - & . ] o.X.X.", "X.X.o.] = - - - - - - - X i v v v p r q k G S c h 6 M 6 g K U U K K K K f i o = $ O i t ] o.X.X.", "X.X.o.] - - - - - - - - - X i v v v v i S Y L U L h 9 M 6 g c c J K K K H f p X X p n a ] o.X.X.", "X.X.o.] - - - - - - - - * $ : i v v v i d K K K U S 3 M M 9 6 < Z L K J L K f i v v a a ] o.X.X.", "X.X.o.] # # # # * - - * $ 9 9 : p v a v u d H K K c 6 M M 6 3 < F L K K K K K f p v p i ] o.X.X.", "X.X.X.] 5 < j H s # - & 1 N N 9 : i v v b i c I U Z 3 M 6 h S S K K K K K H K K f u n p ] O.X.X.", "X.X.o.] 8 8 c U L s # @ < N 9 N 9 : p v v r j x K Z 6 9 h L U L K K K K K K K K K f r w [ X.X.X.", "X.X.O.{ 6 3 F U K L Z G h 6 9 9 M 9 : a n r # # G J 6 9 g K K K K K K K K H K K K K F V [ X.X.X.", "X.X.X.] 8 6 j x H K K U L h 8 N M M 9 : r q # * l l 6 N 6 g K K K K K K K K K K H K U J { O.X.X.", "X.X.o.] 8 6 # @ c U K K I G 3 N M M B 1 z z # # , 3 9 M N 2 G I K K L L K L L P U K P J } X.X.X.", "X.X.O.} 7 < % % l J A A J V < 8 7 6 8 4 Z C # % 4 M 6 6 8 5 V G A A A A A A A A A J G C } O.X.X.", "X.X.X.X.{ ] ] ] { { { { { { ] { ] { ] { ] ] ] ] ] ] { ] ] { ] } ] { { { { { { { { { ] { X.X.X.X.", "X.X.X.X.O.O.X.O.O.X.O.O.O.O.O.X.o.o.O.X.O.O.o.o.o.o.o.O.O.O.O.X.o.o.X.O.X.O.O.O.O.X.O.O.X.X.X.X.", "X.X.X.X.X.X...X.X.X.X...X.X.X.X.X...X.X.X.X.X...X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X...X.X.X.X.X.X.", "X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.O.X.X.X.X." }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/mines-icon.c0000644000175300017530000004541612161170374015761 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 227 2", " c #5F706E", ". c #5E7673", "X c #7C6C6B", "o c #747977", "O c #BB5B58", "+ c #837774", "@ c #F94C4D", "# c #E1514E", "$ c #EC5754", "% c #F35553", "& c #F3615D", "* c #D0716E", "= c #F77876", "- c #F17E7B", "; c #449A42", ": c #489C46", "> c #62A55F", ", c #6FAC69", "< c #73AE71", "1 c #7AB17A", "2 c #80817E", "3 c #95807E", "4 c #83B37E", "5 c #363692", "6 c #45438F", "7 c #484494", "8 c #5B5B9F", "9 c #7271E5", "0 c #7A8B89", "q c #8E9089", "w c #999A94", "e c #B48D89", "r c #A19F9D", "t c #AE9E9B", "y c #BD9C99", "u c #83B580", "i c #8DB389", "p c #97B792", "a c #98B694", "s c #95BB93", "d c #A1BA9C", "f c #A3BC9E", "g c #8480AF", "h c #9FA7A5", "j c #9FB5B3", "k c #B0ACAB", "l c #B9ADA9", "z c #ABBCA6", "x c #B5B2AE", "c c #B2BFAD", "v c #A4A0B4", "b c #A6A5BD", "n c #A7A6BD", "m c #A9A7BF", "M c #AEAAB9", "N c #BCBCB5", "B c #B9BBB8", "V c #FE8481", "C c #E49997", "Z c #F39895", "A c #D8A9A5", "S c #DFAAA7", "D c #C7B0AB", "F c #C5BBB8", "G c #C0BFBB", "H c #C3BDBC", "J c #CFBEBB", "K c #D0BBBA", "L c #EAB7B4", "P c #E8B8B7", "I c #B3C3AE", "U c #B8C4B3", "Y c #B8C2B4", "T c #B8C3B6", "R c #BBC2BF", "E c #C1C5BB", "W c #C3C4BD", "Q c #C7C4BF", "! c #D1C2BF", "~ c #E9C2BE", "^ c #8E8DD7", "/ c #9797D8", "( c #A19FD4", ") c #B8B6C6", "_ c #B8B9C8", "` c #8182E3", "' c #C6BDC8", "] c #BACAC2", "[ c #C1C0C0", "{ c #C5C3C1", "} c #C3C5C1", "| c #C7C5C1", " . c #C6C5C2", ".. c #C8C7C4", "X. c #CCC5C6", "o. c #CCC9C1", "O. c #CAC8C4", "+. c #CAC9C4", "@. c #CFC9C4", "#. c #CDCBC6", "$. c #CCCBC7", "%. c #CBCEC6", "&. c #CDCAC9", "*. c #CECBC8", "=. c #CECCC8", "-. c #CBCACC", ";. c #D0CAC5", ":. c #D2CACB", ">. c #D0CECA", ",. c #D0CFCA", "<. c #D1CFCB", "1. c #DDCBC8", "2. c #DAD5C6", "3. c #D2D0CB", "4. c #D4D2CA", "5. c #D1D1CC", "6. c #D1D0CD", "7. c #D3D0CD", "8. c #D1D2CD", "9. c #D2D1CF", "0. c #D2D3CF", "q. c #D5D1CE", "w. c #D6D1CF", "e. c #D4D2CE", "r. c #D6D3CE", "t. c #D6D3CF", "y. c #D6D4CE", "u. c #DDDBC9", "i. c #D9CDD2", "p. c #DACDD3", "a. c #D4D2D0", "s. c #D4D3D0", "d. c #D4D3D1", "f. c #D7D2D0", "g. c #D1D6D3", "h. c #D5D4D1", "j. c #D7D5D0", "k. c #D6D4D1", "l. c #D5D4D2", "z. c #D6D5D3", "x. c #D6D6D2", "c. c #D7D6D3", "v. c #D8D3D1", "b. c #DAD3D3", "n. c #D8D6D1", "m. c #D8D6D2", "M. c #DBD4D4", "N. c #DDD4D6", "B. c #DAD8D3", "V. c #DBDAD3", "C. c #DAD8D4", "Z. c #D9D8D6", "A. c #D9D9D7", "S. c #DAD8D6", "D. c #DBD8D7", "F. c #DAD9D7", "G. c #D9DAD7", "H. c #DCDAD5", "J. c #DFD9D6", "K. c #DDDBD6", "L. c #DCDBD7", "P. c #DEDCD7", "I. c #DBDAD8", "U. c #DCDAD8", "Y. c #DCDBD9", "T. c #DEDCD8", "R. c #DFDCD9", "E. c #DCDCDA", "W. c #DCDDDA", "Q. c #DDDCDB", "!. c #DEDDDB", "~. c #DFDDDB", "^. c #DFDEDB", "/. c #D9DFDC", "(. c #EDD0CC", "). c #E6DDD4", "_. c #E9DAD7", "`. c #E0DED8", "'. c #E1DED9", "]. c #E0DFDB", "[. c #E1DFDB", "{. c #E3DEDA", "}. c #E3DFDB", "|. c #E4DFDB", " X c #E0DEDC", ".X c #EEDFDF", "XX c #D3E0DD", "oX c #D8E1DF", "OX c #DEE2DE", "+X c #E2E0DB", "@X c #E3E0DB", "#X c #E1E0DC", "$X c #E2E0DC", "%X c #E3E1DC", "&X c #E2E1DD", "*X c #E3E1DD", "=X c #E3E2DD", "-X c #E4E0DC", ";X c #E4E2DD", ":X c #E4E2DE", ">X c #E5E2DE", ",X c #E4E3DE", " p.I : T 2.9 ( fX[.", ",Xg.. 2 H < s p.a , O.o.` / rX[.", "x.l y x G.iX.X} @.X.} 3X2X).0X@X", "<.@.% S sX& - ] M 5 _ 2X@ C XX@X", "9.J $ A gXt X N v 7 ( yX+ + T.@X", "3.F K &...w q k aX4X|.}.j h G.=X", "t.U < I ;.m g wXP = f.`.V L oX|.", "t.R ; z :.8 6 OX~ O B }.* e /.}.", "b.U u %.@.-.n ,X] r A.0 o A.|.", "T.tXpX}.Q.eXrXG.J.1Xl.J.|.Q.Q.}.", ",X@XB.<.x.K.v.#.@XK.&.G.[.y.x.=X", ",XQ.t.+.9.v.9.+.B.t...x.v.#.a.,X", "[.Q.t.a.Q.x.f.G.G.t.f.Q.f.<.5X[.", "K.=X=X=X}.,X=X=X@X,X,X,X5X,X[.G." }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 215 2", " c #0E1111", ". c #151616", "X c #181B1A", "o c #242423", "O c #2D3130", "+ c #373534", "@ c #007900", "# c #00007A", "$ c #494644", "% c #4A4B4A", "& c #5B4E4C", "* c #5C5755", "= c #5C5A58", "- c #7A504E", "; c #6E615F", ": c #6B6A68", "> c #726D6A", ", c #73716F", "< c #757371", "1 c #787774", "2 c #7B7A77", "3 c #7F7E7B", "4 c #FE0000", "5 c #FC0F0F", "6 c #F91515", "7 c #F51F1E", "8 c #F81B1A", "9 c #F8201F", "0 c #F62423", "q c #F12E2C", "w c #EB3C3A", "e c #F33433", "r c #F43836", "t c #F23938", "y c #A55755", "u c #8A6765", "i c #817F7D", "p c #F14543", "a c #EE5351", "s c #EE5855", "d c #EE5F5D", "f c #E7605E", "g c #EC615E", "h c #E86664", "j c #EB6D6A", "k c #E97572", "l c #E97976", "z c #E47D7B", "x c #E87B78", "c c #0E800E", "v c #1E861D", "b c #1D8B1C", "n c #238D22", "m c #288F27", "M c #2A8F29", "N c #2D912C", "B c #349233", "V c #3A9739", "C c #3E983C", "Z c #439A42", "A c #489B46", "S c #4E9E4B", "D c #54A151", "F c #65A762", "G c #69A866", "H c #71A96E", "J c #76AE72", "K c #81B07C", "L c #020281", "P c #0F0F82", "I c #27268E", "U c #3D3C96", "Y c #4B4B9B", "T c #54539E", "R c #5C5CA2", "E c #605FA2", "W c #6362A3", "Q c #6E6DA7", "! c #0C0CFC", "~ c #1717FC", "^ c #2424F6", "/ c #4242F0", "( c #6F6DE6", ") c #7776E2", "_ c #898683", "` c #8B8A87", "' c #8E8C89", "] c #93918E", "[ c #979592", "{ c #999692", "} c #9C9A96", "| c #9E9C98", " . c #A29F9D", ".. c #8EB48A", "X. c #9FA09C", "o. c #96B892", "O. c #9DB997", "+. c #A4A19E", "@. c #A2BD9E", "#. c #8180AF", "$. c #8987B2", "%. c #9896B5", "&. c #9F9EB9", "*. c #A7A4A2", "=. c #A8A6A4", "-. c #ACA9A6", ";. c #AEACA9", ":. c #B0AAA7", ">. c #B1AEAB", ",. c #ACBEA6", "<. c #ADB2AE", "1. c #B3B1AE", "2. c #AAA9BC", "3. c #ACB6B3", "4. c #B5B4B1", "5. c #BAB7B3", "6. c #BBBAB5", "7. c #BDBCB9", "8. c #DE9592", "9. c #DB9D9A", "0. c #E38684", "q. c #E38985", "w. c #C3B2AE", "e. c #D4B2AF", "r. c #DBB3AE", "t. c #C1BEB1", "y. c #C1BEBB", "u. c #D2BAB5", "i. c #E6B2AF", "p. c #E2BAB6", "a. c #E3BCB8", "s. c #E8BCB9", "d. c #ADC3A8", "f. c #B1C3AB", "g. c #B6C5B1", "h. c #BBC4B5", "j. c #BCC9B6", "k. c #BEC9B8", "l. c #C5C3BE", "z. c #C3CBBE", "x. c #DEC0BB", "c. c #9D9BD2", "v. c #9795DA", "b. c #B3B1C0", "n. c #B9B7C3", "m. c #BBB9C3", "M. c #BEBCC8", "N. c #A5A3D2", "B. c #A9A7D8", "V. c #BBB9D0", "C. c #C4BEC2", "Z. c #C6C5C2", "A. c #C9C6C3", "S. c #C6CBC1", "D. c #CCCAC5", "F. c #C4C2CD", "G. c #CBC4CB", "H. c #C6CECA", "J. c #CECCC9", "K. c #D3C6C3", "L. c #D3CAC5", "P. c #D1CDCB", "I. c #CDD2CC", "U. c #DBD8C6", "Y. c #D4D1CD", "T. c #D9D7CE", "R. c #DBD8CC", "E. c #CBD5D1", "W. c #CAD9D4", "Q. c #D5D4D2", "!. c #D9D6D2", "~. c #D3DAD5", "^. c #DCDAD5", "/. c #DED5D8", "(. c #DFDDD8", "). c #E4CAC6", "_. c #E3CCC8", "`. c #E1DFC5", "'. c #E7D2CD", "]. c #E3D5D1", "[. c #E0D8D3", "{. c #E1D7DB", "}. c #E0DED9", "|. c #D7E2DC", " X c #DFE1DD", ".X c #EAE8C4", "XX c #E3E1DD", "oX c #E8E1DD", "OX c #F2EDDF", "+X c #F2DBEB", "@X c #DEE7E2", "#X c #D7E9E3", "$X c #DAE9E4", "%X c #DEEFE9", "&X c #DDF0EB", "*X c #E4E4E2", "=X c #E9E6E4", "-X c #E6E8E5", ";X c #EAE9E6", ":X c #E5EBE8", ">X c #ECECEA", ",X c #F0E7EA", "X!.l.D.D.A.Z.A.A.A.A.D.Z.Z.S.Z.A.z.A.A.A.D.XX(.^.", "!.=X9X2X7X>X;X=X-.l.A.6.A.D.Z.1.A.Z.h.D.A.l.4.A.A.C.Z.Z.P.wX(.!.", "!.=X-Xr.x q p.H.{ !.F V Z z.Y.7.J.D V D Y.J.7.T.( ~ B.U.!.wX(.!.", "!.=X>Xk 5 4 a.I.} P.f./.v ../.7.S.h.D.c g.P.5.Y.F.^ v.R.Q.wX(.^.", "!.-X;X|.3Xy u.~.} P./.F N D.P.6.Y.Y.Z A !.D.7.Y..X^ N.`.Q.wX(.!.", "!.=X>XP.;.O 1 D.} /.S @ C h.Q.5.J.M @ D D.J.7.Y.) ! / M.!.wX(.!.", "!.=X:X{ * * = [ } J.,.,.O.k.J.7.Y.d.f.@.J.J.5.Y.N.N.c.M.T.wX(.^.", "(.!.*.X.3.<.:.} 4.>X,X,X9X,X-X5.7.C.C.A.y.5.!.-XOX3X3X;X=XwX(.^.", "XXl.l.L.9.e.E.L.;X>X1X5X'.=XJ.=.J.L.A.2.D.J.9X:X5X*X].-Xl.9X(.^.", "XXl.H.q.h 0 0.W.=X}.0.r 9 R.5.+.!.T.Y L n.T.9Xx.j 6 g #X-.9X(.^.", "}.A.A.Y.q.0 8.W.=XoXk 8 6 ^.7.+.^.E Q I b.T.9Xu.a 4 d #X>.9X}.!.", "}.l.Z.x.9.9 z W.=X=X&X4X- XXl.+.J.I Y L E !..9X}.!.", "oXl.H.0.a t 9.I.=X,X5.< . 3 6.+.Y.J.m.I 2.!.9X^.*.% o ;.4.9X}.^.", "}.D.A.Y.u.D.~.D.=XP._ : , , { +.Y.P.Y.A.J.Y.>X>.< : > 2 *.0X(.!.", "}.l.1.y.G.y.5.7.5.*.-.4.t.4.+.Y.4X;X4X7XX_.s 4 e :X>.*Xp.p 4 g #X-.9X(.^.", "(.A.l.!.+XB H {.5.R.R W U #.R.>X].q.e w @X>.*X_.x 0 d #X>.9X(.^.", "(.A.A.j.m n z.Y.6.Y.R Y P U P.>X X7X~.& >X4. X(.qX4.; 4X;.9X}.!.", "}.Z.P.J b Z o./.5.J.T.R.T &.R.>XoX[ $ . i >.*X(._ + X [ 4.9X(.^.", "}.A.Z.!.{.Q.P.Y.h.J.Y.P.!.Y.P.;X7.` ` ] ` .(.>._ ' ] ' -.0X(.!.", "!.XX9XX9X-X:X*X-X>X-X>X^.:X*X>X;X;X;X^.0X(.^.", "!.=X>X!.^.^.(.Z.Z.>XQ.(.^.}.7.Q.-X(.}.(.XX5.*XXX(.(.(.(.4.9X}.!.", "!.=X-X^.^.^.}.l.Z.;X!.^.^.XX6.Q.*X!.^.!.(.1.*X(.^.^.^.^.1.9X(.^.", "!.-X>X!.^.^.oXl.A.>X!.^.^.XX6.Q.*X!.(.^.}.1.*X}.^.^.^.^.1.9X(.^.", "!.=X;XR.(.!.}.l.Z.;XQ.^.!.XX6.Q.XX^.!.!.}.4.XX(.^.^.^.^.<.9X(.^.", "!.=X>X^.^.^.XXl.Z.>X!.^.^.XX5.!.=X!.(.(.}.1.XX}.^.^.^.(.>.9X(.^.", "!.-XJ.-.1.1.1.*.D.Z.;.1.>.4.*.Q.7.;.1.1.>.-.~.4.;.4.1.;.;.wX(.!.", "^.oX0X9X9X9X9X0XwX9X9X0X9X9X9XwX9X9X9X9X9X0XwX9X0X9X9X9XwXwX(.^.", "^.^.XX(.(.(.(.(.(.(.(.(.}.(.(.(.(.(.(.(.(.(.(.(.(.(.(.(.(.(.^.^.", "^.^.!.^.!.(.!.^.!.^.^.^.^.^.!.^.^.^.^.^.^.^.^.!.^.^.^.^.!.^.^.^." }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 256 2", " c #020101", ". c #0C0C0C", "X c #110F0F", "o c #141212", "O c #181918", "+ c #262625", "@ c #282727", "# c #282827", "$ c #2C2B2B", "% c #323130", "& c #343A38", "* c #3C3E3D", "= c #4F2928", "- c #007500", "; c #007B00", ": c #000076", "> c #01017C", ", c #0B0B7F", "< c #424240", "1 c #4B4947", "2 c #4F4F4E", "3 c #565553", "4 c #5B5A57", "5 c #615F5D", "6 c #61605E", "7 c #646361", "8 c #6D6C6A", "9 c #777673", "0 c #777F7C", "q c #D50E0D", "w c #D31D1C", "e c #FF0101", "r c #FD0C0C", "t c #FA1C1C", "y c #FA1616", "u c #FB2121", "i c #F92B2B", "p c #F43130", "a c #F43836", "s c #AA4E4C", "d c #B65351", "f c #807F7D", "g c #EE4D4B", "h c #F24443", "j c #F24F4D", "k c #EB5957", "l c #EC5C5A", "z c #E25B59", "x c #F05855", "c c #F15E5C", "v c #F25452", "b c #E9615F", "n c #EC6967", "m c #E37F7C", "M c #E97471", "N c #9E2120", "B c #038003", "V c #0B830B", "C c #138612", "Z c #178916", "A c #198918", "S c #268E24", "D c #2C902B", "F c #359433", "G c #3C963A", "H c #40983E", "J c #459A43", "K c #4A9B47", "L c #4C9D4A", "P c #509E4E", "I c #539F50", "U c #55A052", "Y c #59A156", "T c #5AA258", "R c #66A763", "E c #6AA867", "W c #6DAA6A", "Q c #73AC6F", "! c #77AD74", "~ c #81B07D", "^ c #020280", "/ c #121287", "( c #1A1A87", ") c #181888", "_ c #24238D", "` c #28288E", "' c #2E2D90", "] c #3F3E96", "[ c #333292", "{ c #404097", "} c #454598", "| c #4F4E9C", " . c #54539D", ".. c #5D5CA1", "X. c #6E6DA9", "o. c #6362A3", "O. c #706FA8", "+. c #7675AC", "@. c #7C7BAC", "#. c #0202FE", "$. c #1F1EF7", "%. c #1413FA", "&. c #201FF7", "*. c #2424F6", "=. c #2D2DF3", "-. c #3B3AF1", ";. c #3434F3", ":. c #4241EF", ">. c #4847EE", ",. c #4C4BED", "<. c #807FDF", "1. c #898885", "2. c #8E8C89", "3. c #918F8B", "4. c #94918D", "5. c #98918E", "6. c #969491", "7. c #999693", "8. c #9B9A96", "9. c #9E9C99", "0. c #A09E9B", "q. c #85B281", "w. c #89B385", "e. c #8FB68B", "r. c #92B78D", "t. c #9ABA96", "y. c #9CBA98", "u. c #A3A19D", "i. c #A3BD9F", "p. c #8281AD", "a. c #8987B2", "s. c #8B89B1", "d. c #9694B5", "f. c #9897B7", "g. c #A6A4A1", "h. c #A8A6A3", "j. c #ABA9A6", "k. c #AEADAA", "l. c #B8A9A5", "z. c #B1AEAB", "x. c #A7BFA2", "c. c #B2B1AE", "v. c #ABA9BD", "b. c #A5A4BC", "n. c #B6B4B1", "m. c #B9B7B3", "M. c #BCB9B5", "N. c #BFBDB9", "B. c #C39390", "V. c #D99693", "C. c #E48582", "Z. c #E88885", "A. c #E48C89", "S. c #E88F8C", "D. c #EB8582", "F. c #E79692", "G. c #E59E9B", "H. c #E1A39F", "J. c #DAA8A5", "K. c #D9AEAA", "L. c #C0BCB6", "P. c #C1BEBC", "I. c #DDB5B0", "U. c #D6BAB6", "Y. c #DFB8B4", "T. c #D4BDB9", "R. c #E0A8A4", "E. c #E2AEAA", "W. c #E1B0AC", "Q. c #E0BEB9", "!. c #E2BAB5", "~. c #A5C1A0", "^. c #ABC3A6", "/. c #AEC1A9", "(. c #B1C3AB", "). c #B7C6B2", "_. c #B8C5B3", "`. c #C5C2BE", "'. c #C2C9BC", "]. c #C4C2B2", "[. c #D3C1BD", "{. c #8785DD", "}. c #B6B5C2", "|. c #BCBAC3", " X c #B9B7CF", ".X c #AEADD6", "XX c #AAA8DA", "oX c #B7B5D7", "OX c #BDBBD0", "+X c #C0BEC4", "@X c #C6C4C1", "#X c #CAC7C3", "$X c #C5CBC0", "%X c #CCCBC6", "&X c #C4C2CC", "*X c #CECCC9", "=X c #DCC5C0", "-X c #DFC9C3", ";X c #D1CECB", ":X c #DCCEC9", ">X c #C5D2CD", ",X c #D5D3C6", ".,.;..X,XsXUXyXeXeXeX", "eXeXqXlXPX`.9.4.9.0.8.6.g.2.@X,XqXqXrXrX;X%X%Xc.1XeXvXaXvXvX0X0X#Xc.;X%XX%X%X$XNXUXGXSXSXLXIXGXPX0Xu.$X@X@X@X,X,X@X@X%XUXJXGXSXJXIXJXPXNX;XUXsXeXeXeX", "eXeXsX%XM.wXK.n l A.;X1X*XNXAX0XnXtXI.C.1XyXu.9.qX;X1X1X+.X.*X1X0XPX0XfXfX:XC.Y.gXM.k.UXyXeXeXeX", "eXeXsX%Xm.tXl g k e l 7X#XNXJX-XD.h r t eXlXg.9.1X*XpX .> , |.,X0XPXeXG.c u e H.ZXP.c.UXyXeXeXeX", "eXeXsX%XM.0X*X*XU.y b tX%XNXLXR.i e e u yXsXg.9.1XpX@.[ @.> *X1X0XUXqXx r e e V.ZXM.n.UXyXeXeXeX", "eXeXyX%XM.1X9Xg e r J.5X%XBXGXwXgX!.z w eXsXg.9.eXv.> f.+.> b.*X0XPXyXnX-XS.q V.ZXM.z.UXyXqXeXeX", "eXeXyX%XM.1X[.9X1Xr h tX#XBXJX2XeXFX>X+ BXsXu.8.pXd./ ` / ^ _ @XqXPXeXqXyXUX* 9.KXM.n.UXyXeXyXeX", "eXeXeX%Xm.tXk p a e m 9X%XNXGX1X1Xc.5 . 6.pXg.9.0X,X;XpX@.: @X1X0XPXrXeXP.3.X 3 ,XN.c.UXsXqXeXeX", "eXeXlX%XM.0XT.C.m K.5X*X*XNXKXeX8 % @ $ + f j.9.0X;X;X1X}.a.*X1X0XLXCX9.= # $ @ 2 k.m.UXyXeXeXeX", "eXeXsX%XM.0X,X9XdX0X;X1X%XNX0X9.u.j.j.j.j.u.3.8.;X%X%X*X,XeX*X%X0XAXh.8.j.k.h.j.g.2.n.UXyXeXeXeX", "eXeXeX;Xg.n.k.z.z.z.c.c.c.c.g.9.0.0.8.8.9.0.9.qXPXJXJXJXGXSXJXJXSXAXgXlXfXbXyXyXlXlXnXUXsXeXeXeX", "eXeXsX%Xm.1XqXqXqXqX1X;X0Xn.#XqX1X1XpXeX0X0X;XJXAXlXBXZXDXCXBXNXn.PXCXBXCXHXHXCXAXqX@XUXlXeXeXeX", "eXeXsX%Xm.eXe.F S I @X;X1Xm.@X;X;X2X .( f.3X#XJXNXfXfXY.l M gX;X9.JXeXbXqXR.h R.mXm.k.UXyXeXeXeX", "eXeXsX%Xm.aXP Q e.; I rX1Xm.@X,X3Xo.( > p.eX%XJXCXS.i r e c MX,X0.PXqXn y e e G.ZXL.c.UXyXeXeXeX", "eXeXsX%XM.1X%XaXvXA J aX1Xn.`.eXs.' |.> d.kX#XJXNXH.v t e c MX;X0.PXpXZ.h r e G.ZXL.c.UXyXeXeXeX", "eXeXsX%XM.1XqX;XI - e.qX1Xn.@X*X, } O.> { |.;XJXBXgXmXpXd s NX1X9.PXyXmXfXuXN B.ZXM.c.UXsXeXeXeX", "eXeXsX%Xn.qX/.D ; R 1X*X0Xm.@X*X..| { > _ v.1XJXBXqXlXUX0 2 UX1X9.PXeXeXlXUX& 8.UXM.c.UXeXeXeXeX", "eXeXsX%Xm.aXJ ; C Z Y qX1Xm.@X1X3XkX;X> f.kX#XJXBX1X9.4 o . 9 @Xu.PXlX%X2.1 # 8.M.c.UXyXeXeXeX", "eXeXgX*Xm.1X'.'.'._.'.;X1Xn.`.1X*X*X*X+X@X*X%XJXCXn.5 3 5 6 3 9.u.GXlX7.3 3 7 4 7 g.n.UXyXeXeXeX", "eXeXsX;XP.qX0XqXqXeXqX1XeXN.#XqX0X0X0XeXqX0X0XHXn.0.c.n.c.c.c.u.h.NX9.h.c.c.c.c.c.8.P.UXlXeXeXeX", "eXeX2XsXUXPXPXPXPXPXLXUXPXVXUXPXPXPXPXPXPXUXAXAXKXJXJXJXJXGXKXKXNXLXPXPXJXKXJXJXJXJXNXUXyXeXeXeX", "eXeXeXlXUXeXeXeXyXyXeXlXM.N.PX0XyXeXyXeXeXlXg.yXAXeXyXeXeXsXgXeX0.LXyXeXeXeXyXeXlX`.c.UXyXeXeXeX", "eXeXeXlXPXeXeXeXeXeXqXlXn.N.PXqXeXeXeXeXeXlXu.yXCXqXeXqXeXqXpX1X9.PXyXeXeXeXeX0XgXM.c.UXyXeXeXeX", "eXeXeXgXPXeXeXeXeXeXeXlXn.N.LXqXeXeXeXeXeXyXu.cXCXeXeXeXeXeXlX1X0.PXyXeXeXeXeXeXlXN.n.UXyXeXeXeX", "eXeXqXlXPXeXeXeXeXeXeXlXn.N.PX1XyXeXeXeXeXlX9.gXAX1XeXeXeXeXeX1X9.PXyXqXeXeXeXeXsXN.c.UXyXeXeXeX", "eXeXeXlXPXeXeXeXeXeXeXlXn.N.PXeXeXeXeXyXwXsXu.lXAX2XeXeXeXeXeX1X9.PXyXeXeXeXeXeXlXM.c.UXyXeXeXeX", "eXeXeXlXPXeXeXeXeXeXeXlXc.N.PX1XeXeXeXeXqXsX9.lXCX2XeXeXeXeXeXeX0.PXeXqXeXeXeXqXlXL.c.UXyXeXeXeX", "eXeXeXsXPXsXlXlXlXlXsXBXM.N.PXyXsXlXsXlXlXBXg.yXSXyXlXlXlXlXlXeX9.PXlXsXsXlXlXsXCX`.n.UXyXeXeXeX", "eXeXqXlXPXN.N.N.L.M.N.`.0.N.AXm.N.N.P.M.M.`.6.lXeXm.N.N.M.N.P.m.6.PX`.N.M.P.N.M.`.u.z.UXyXeXeXeX", "eXeXeXNXNXk.n.z.c.c.n.n.k.eX0Xj.n.n.c.c.c.n.c.NXN.k.n.c.c.c.n.z.+XNXn.c.c.c.c.n.c.z.*XUXyXeXeXeX", "eXeXeXsXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXUXsXeXeXeX", "eXeXeXyXsXyXyXyXyXyXsXyXyXyXsXyXyXyXyXeXyXyXyXsXlXyXyXyXyXyXyXeXlXyXyXyXyXyXyXyXyXyXsXsXyXeXeXeX", "eXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeX", "eXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeX", "eXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXyXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXyXeXeXeX" }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/net-icon.c0000644000175300017530000003671612161170375015440 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 182 2", " c #000000", ". c #0E0D0D", "X c #1C2727", "o c #2A3A39", "O c #434443", "+ c #525452", "@ c #5A5957", "# c #5B7E7C", "$ c #5C7F7C", "% c #627573", "& c #757370", "* c #7B7976", "= c #885956", "- c #896B68", "; c #8A6A68", ": c #8A6B68", "> c #8D6D6A", ", c #8D6D6B", "< c #8D6E6B", "1 c #8E6E6B", "2 c #8D6E6C", "3 c #8E6F6C", "4 c #8F6F6C", "5 c #8F6F6D", "6 c #9B6764", "7 c #956B68", "8 c #956E6B", "9 c #8E706E", "0 c #8F7370", "q c #917471", "w c #917572", "e c #5B807E", "r c #5E817F", "t c #5F827F", "y c #81807D", "u c #80827F", "i c #83827F", "p c #2BA3A2", "a c #31BFBE", "s c #3FBCBB", "d c #5A8280", "f c #5D8280", "g c #5F8280", "h c #5D8481", "j c #608280", "k c #608380", "l c #628381", "z c #648684", "x c #6F908D", "c c #669795", "v c #6D9390", "b c #649E9B", "n c #6E9B98", "m c #55A3A1", "M c #67A7A4", "N c #6DA5A2", "B c #74A4A1", "V c #79ACA9", "C c #10DADA", "Z c #11DADA", "A c #26C8C7", "S c #00EFEF", "D c #00FFFF", "F c #9A9995", "G c #9A9A96", "H c #989D99", "J c #9C9D99", "K c #9E9D99", "L c #9F9E9A", "P c #9F9F9B", "I c #A08A86", "U c #A49995", "Y c #AC9894", "T c #A09F9B", "R c #A99E9A", "E c #AB9F9B", "W c #9DA19D", "Q c #9FA39F", "! c #A0A19D", "~ c #A1A19D", "^ c #A3A19D", "/ c #A1A19E", "( c #A2A29E", ") c #A3A29E", "_ c #A5A19D", "` c #A4A29E", "' c #86A5A2", "] c #87A5A2", "[ c #98AAA7", "{ c #9EB6B2", "} c #A1A5A1", "| c #A5A4A0", " . c #A6A4A0", ".. c #A7A4A0", "X. c #A6A5A1", "o. c #A6A6A2", "O. c #A7A6A2", "+. c #A9A5A1", "@. c #A8A6A2", "#. c #A9A6A2", "$. c #ABA6A2", "%. c #ABA7A3", "&. c #AFA6A2", "*. c #A4A8A4", "=. c #A5A9A5", "-. c #A4AAA6", ";. c #A6AAA6", ":. c #A7ABA7", ">. c #A8A9A5", ",. c #AAAAA6", "<. c #AAABA6", "1. c #ABABA7", "2. c #ACA8A4", "3. c #ACABA6", "4. c #ACABA7", "5. c #AEABA7", "6. c #A8ACA8", "7. c #ABACA8", "8. c #B0ABA7", "9. c #ABB0AB", "0. c #B1B0AC", "q. c #B5B1AD", "w. c #AAB4B0", "e. c #ABB4B0", "r. c #B5B4B0", "t. c #CBBCB8", "y. c #C9BEB9", "u. c #CABEBA", "i. c #CCBDB9", "p. c #CCBEB9", "a. c #CCBEBA", "s. c #CDBEBA", "d. c #B1C1BD", "f. c #C9C0BB", "g. c #CEC0BC", "h. c #B6C4C0", "j. c #B9C4C0", "k. c #CAC8C3", "l. c #CBC8C3", "z. c #CCC8C3", "x. c #CCCAC6", "c. c #CDCDC8", "v. c #D1C6C2", "b. c #D3CDC8", "n. c #D0CEC9", "m. c #D3CEC9", "M. c #D1CFCA", "N. c #D2CFCB", "B. c #D6CFCA", "V. c #CDD0CC", "C. c #CFD2CD", "Z. c #D0D0CB", "A. c #D2D0CB", "S. c #D3D0CB", "D. c #D4D0CB", "F. c #D2D0CC", "G. c #D2D1CC", "H. c #D3D1CC", "J. c #D3D1CD", "K. c #D3D2CD", "L. c #D2D3CE", "P. c #D4D1CC", "I. c #D4D1CD", "U. c #D4D2CD", "Y. c #D5D2CD", "T. c #D4D2CE", "R. c #D5D3CE", "E. c #D6D3CE", "W. c #D7D3CE", "Q. c #D2D4CF", "!. c #DAD3CE", "~. c #D6D4D0", "^. c #D4D6D1", "/. c #DBD8D3", "(. c #DBD9D4", "). c #DCDBD6", "_. c #E1E1DC", "`. c #E2E1DC", "'. c #E4E3DD", "]. c #E5E3DE", "[. c #EDE3DE", "{. c #F2E7E2", /* pixels */ "/.D.U.U.G.G.U.G.S.G.G.U.U.G.U.(.", "g.q ;.` O.#.S.D.S.W.5.^ O.| #.U.", "i.1 6.^ H W V.^.^.C.9.+ . O } N.", "i.4 6.d r z ] n c ] v o X x V.", "i.5 ;.l #.2.[.d.w.{.r.* @ & $.D.", "i.: Q $ G J D.e.[ !.#.r.T 0.,.U.", "i.1 *.t ^ O.U.V M U.z.].w c.^.D.", "g.9 ;.t ^ 5.h.S S d.b.`.x N.U.N.", "y.1 6.t ^ 8.d.Z Z j.N.'.i N.U.S.", "i.1 *.r ! #.!.z.z.W.l.`.u c.G.S.", "i.- ( e J G O.3.5.@.W &.I E O.G.", "g.5 6.l ` J W ^ ^ W U b A m R Q.", "i.1 ,.t e e r l g h % p D A ` ^.", "p.0 9.5.6.( *.;.:.;.` B m M ,.^.", "v.= 0 4 9 - 1 9 4 4 - 8 6 7 0 S.", ").v.y.y.i.i.i.i.i.y.i.y.f.y.g./." }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 135 2", " c #010201", ". c #101010", "X c #1C1B1B", "o c #272625", "O c #2F2F2E", "+ c #313130", "@ c #3E3D3C", "# c #6E2524", "$ c #702726", "% c #712827", "& c #722928", "* c #7C2928", "= c #75312F", "- c #1C4242", "; c #285F5E", ": c #236F6F", "> c #2E706F", ", c #2D7271", "< c #2F7877", "1 c #337372", "2 c #3D7473", "3 c #3D7F7D", "4 c #495250", "5 c #535351", "6 c #635E5B", "7 c #456766", "8 c #466866", "9 c #4A7674", "0 c #527E7C", "q c #5B7F7D", "w c #6E6D6A", "e c #71726F", "r c #6C7A77", "t c #3D807F", "y c #43817F", "u c #54817F", "i c #5B817F", "p c #248786", "a c #3F8482", "s c #3F8B89", "d c #3D9492", "f c #02AEAE", "g c #4F8886", "h c #5D8684", "j c #5E8886", "k c #528B89", "l c #598D8B", "z c #5E9E9C", "x c #7A8885", "c c #7F8F8C", "v c #7F918E", "b c #6D9693", "n c #789895", "m c #00CACB", "M c #10C8C8", "N c #02D4D4", "B c #00DBDB", "V c #00E2E2", "C c #00ECEC", "Z c #01F5F6", "A c #03FFFF", "S c #8B8683", "D c #8C8885", "F c #878D8A", "G c #8F8E8A", "H c #938B87", "J c #978C88", "K c #80908D", "L c #8F928E", "P c #94938F", "I c #8C9390", "U c #8B9B98", "Y c #959591", "T c #989693", "R c #959995", "E c #9B9A96", "W c #9D9D99", "Q c #A19794", "! c #A39894", "~ c #A19C98", "^ c #9FA29E", "/ c #A2A29E", "( c #ADA29E", ") c #88A5A2", "_ c #8CAAA6", "` c #9CABA7", "' c #95AEAA", "] c #9CACA8", "[ c #A5A4A1", "{ c #AAA6A1", "} c #A4A9A5", "| c #ABA9A5", " . c #AFAEAA", ".. c #B1A6A1", "X. c #B4A9A4", "o. c #B1AFAB", "O. c #BBAEA9", "+. c #B3B3AF", "@. c #BDB2AD", "#. c #A7B8B4", "$. c #AABAB6", "%. c #AFBEBA", "&. c #B6B5B0", "*. c #BAB6B1", "=. c #B5B9B5", "-. c #BCBBB6", ";. c #BDBDB9", ":. c #C4AEA9", ">. c #C1BFBA", ",. c #CEBEB9", "<. c #B8C0BC", "1. c #C3C2BD", "2. c #CAC3BE", "3. c #C7C6C1", "4. c #CDC6C2", "5. c #CDCAC5", "6. c #CFCDC9", "7. c #D1CECA", "8. c #CFD2CD", "9. c #D3D1CC", "0. c #D7D5D0", "q. c #D9D7D2", "w. c #D7D8D3", "e. c #DCDAD5", "r. c #DFDDD8", "t. c #E5DCD6", "y. c #E8DCD7", "u. c #E0DFDA", "i. c #E9DED9", "p. c #E3E2DC", "a. c #ECE4DE", "s. c #E7E5E0", "d. c #EAE6E1", "f. c #ECEAE5", "g. c #FCEAE4", /* pixels */ "e.e.r.r.r.r.r.r.r.r.r.r.r.r.r.u.u.r.r.r.r.r.r.r.r.r.r.r.r.r.e.e.", "e.e.7.9.9.9.7.9.7.8.9.9.6.6.7.5.5.6.6.6.9.9.9.9.7.9.9.7.9.9.e.e.", "u.4.= R W E E E E E E E 1.1.1.>.;.1.>.3.{ Y E E E E E E W Y 9.e.", "u.4.% / { [ { { { { / { p.u.u.u.u.u.r.s.&.W | . . . .| { W 9.r.", "u.4.% ^ { [ / [ / { ^ / e.e.e.w.e.w.w.r.+.^ P O O + o e .T 9.r.", "u.4.% ^ [ [ { ( ..( ( { a.t.t.t.y.i.y.a.*.( H 6 @.E 7.r.", "u.4.% ^ [ { v , > , > , t t t a g 3 3 a 1 , ; - < 1 9.r.", "u.4.$ ^ [ X.u 2 I v c v $.#.;.b a %.#.%.U F r 4 U x 9.r.", "u.4.% ` [ X.0 u O.| { | a.u.g.' l g.u.d.*.{ P X X X . w @.W 7.r.", "u.4.$ ^ { X.0 0 X.[ / / r.q.a._ k a.q.p.&.W { | | | } { { E 9.r.", "u.4.$ W ~ ..0 0 ..~ ~ / 0.9.t.) g y.6.e.o.Y ~ W ^ { W W W Y 9.r.", "u.4.$ I Y E 9 9 ! Y Y T >.-.3.n 0 2.-.1.| o.&.-.Y 5 ;.+.&.| 7.r.", "u.4.% [ { X.0 0 O.[ [ } u.u.g.] k g.r.p.1.e.p.f. .@ f.r.s.3.6.r.", "p.4.% ^ [ X.0 0 ..[ ~ / p.=.z d p z =.p.;.9.e.p.| @ s.w.r.1.6.r.", "r.4.% ^ [ X.0 0 X.[ [ / d.] V A A V ] d.-.9.e.s.| @ d.q.u.1.7.r.", "u.4.% ^ [ X.0 0 X.[ ^ / a.} V A A V } d.;.9.e.s.| @ d.q.u.;.5.r.", "u.4.% ^ / X.0 0 X.{ / / a.] V A A V ` d.;.9.e.s.| @ s.q.u.>.5.u.", "u.4.% ^ / X.0 0 X./ / / d.} f M M f } d.>.9.e.d.| @ s.q.u.1.7.u.", "u.4.% / / X.0 0 X.[ / / r.9.2.,.,.,.q.u.-.9.e.s.| @ d.w.u.1.6.r.", "u.4.% ^ / X.0 0 X.^ ^ { r.p.p.p.p.p.t.u.;.w.r.s.| @ f.e.p.1.6.r.", "u.4.$ L Y ! 9 9 Q Y P G { / / / [ [ / { Y / [ { Y w { / / E 9.t.", "u.4.& ^ [ X.u 0 X.[ { Y ^ / / / / / ^ [ Y W ( { ..O.{ [ / E 9.r.", "u.4.$ ^ { X.0 0 | ^ [ L ^ [ [ / [ [ ^ { Y ^ R h j j h F | E 9.r.", "u.4.& ^ / X.0 i :.X.X.! X.X.X.X.X.X.X.X.! O.J m A Z C j ..E 9.r.", "u.4.% ^ [ X.u : i 0 0 9 0 0 0 0 0 0 0 0 9 u 8 N A A Z j ..E 9.r.", "u.4.% ^ / { R u 0 0 0 9 0 0 0 0 0 0 0 u 9 u 7 N A A Z j ..E 9.r.", "u.5.$ ^ { / { X.X.X.X.Q X.X.X.X.X.X.X.X.E O.J f B N m q X.E 9.r.", "u.5.% / { [ { [ / { / P / / / { { / / { P / / D S D S E | E 7.r.", "r.5.$ W ^ ^ ^ ^ ^ ^ ` P ^ / ^ / ^ ^ ^ ` I W [ } } } } / ^ R 9.r.", "r.8.* # % % % % % & $ # % % % $ $ $ $ $ # $ $ $ $ $ % % $ = 7.r.", "w.r.8.4.4.4.5.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.4.e.e.", "e.e.r.u.u.u.u.u.u.u.u.u.u.r.u.u.u.u.u.u.p.r.r.p.u.u.u.r.p.u.e.e." }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 169 2", " c #010101", ". c #090909", "X c #272726", "o c #282727", "O c #292827", "+ c #2C2B2B", "@ c #660404", "# c #680404", "$ c #6E0A0A", "% c #711111", "& c #7E1F1E", "* c #004343", "= c #064C4B", "- c #005252", "; c #006464", ": c #0D6767", "> c #006969", ", c #0A6A6A", "< c #1D6E6D", "1 c #1E706F", "2 c #007374", "3 c #007B7B", "4 c #1C7473", "5 c #226969", "6 c #317674", "7 c #3C7876", "8 c #3C7B7A", "9 c #464544", "0 c #494846", "q c #4D4C4A", "w c #445B59", "e c #585A57", "r c #605D5B", "t c #705855", "y c #61605E", "u c #436E6C", "i c #43706F", "p c #467473", "a c #4D7573", "s c #447876", "d c #4B7877", "f c #4D7A78", "g c #5B7270", "h c #656461", "j c #686764", "k c #6D6C6A", "l c #7F6764", "z c #766866", "x c #75716F", "c c #6A7370", "v c #677E7C", "b c #687D7B", "n c #727572", "m c #757875", "M c #787A77", "N c #802C2B", "B c #816765", "V c #827874", "C c #69817E", "Z c #82827E", "A c #90827F", "S c #0D8787", "D c #098D8C", "F c #258281", "G c #298786", "H c #258A89", "J c #229D9C", "K c #1EA09F", "L c #04AEAE", "P c #458483", "I c #468987", "U c #4A8C8A", "Y c #708785", "T c #718C8A", "R c #7F8B88", "E c #00C2C2", "W c #00DCDC", "Q c #00E3E3", "! c #00EAEA", "~ c #02FEFE", "^ c #858581", "/ c #8A8683", "( c #868885", ") c #8C8A86", "_ c #8C8C89", "` c #918E8B", "' c #9D8D89", "] c #8D928E", "[ c #93928E", "{ c #99928E", "} c #8C9793", "| c #8D9894", " . c #969591", ".. c #999793", "X. c #949996", "o. c #9B9995", "O. c #919C98", "+. c #9E9E9A", "@. c #A2928E", "#. c #A9938F", "$. c #A39591", "%. c #A99490", "&. c #A79995", "*. c #AB9995", "=. c #A09F9B", "-. c #AB9E9A", ";. c #B69793", ":. c #B39E9A", ">. c #98A29E", ",. c #A3A29E", "<. c #AEA19C", "1. c #B1A19C", "2. c #9AA5A1", "3. c #9DA8A4", "4. c #A5A5A1", "5. c #A9A6A2", "6. c #A6A9A4", "7. c #ABA9A5", "8. c #ADADA9", "9. c #B3A7A3", "0. c #B3AAA5", "q. c #B8ACA7", "w. c #B0AFAA", "e. c #B9AEA9", "r. c #A0B1AC", "t. c #ABB1AD", "y. c #B2B1AC", "u. c #BEB4AF", "i. c #B6B6B2", "p. c #B9B7B3", "a. c #B6B8B4", "s. c #BAB9B4", "d. c #BEBDB8", "f. c #C4B1AC", "g. c #C2BFBA", "h. c #BFC0BB", "j. c #C3C2BD", "k. c #C9C4BF", "l. c #D4C2BE", "z. c #C7C5C0", "x. c #CAC6C1", "c. c #CDCBC6", "v. c #CFCEC9", "b. c #D7CAC5", "n. c #D1CFCA", "m. c #D3D1CC", "M. c #D7D5D0", "N. c #D9D7D2", "B. c #D2DCD7", "V. c #DCDAD5", "C. c #D1DDD8", "Z. c #DEDDD8", "A. c #EEC9C3", "S. c #E0DBD6", "D. c #EADAD5", "F. c #E1DFD9", "G. c #EEDFD9", "H. c #DFE2DD", "J. c #E3E1DC", "K. c #ECE3DE", "L. c #E7E6E1", "P. c #EAE7E1", "I. c #E6E9E3", "U. c #ECE9E4", "Y. c #EFEDE8", "T. c #F7E7E1", "R. c #F7ECE7", "E. c #F0EEE8", "W. c #F4F2EC", /* pixels */ "V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.", "V.V.V.V.N.V.N.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.N.N.V.V.V.V.N.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.", "V.V.V.F.I.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.H.F.V.V.V.V.", "V.V.Z.x.;.s.s.p.s.p.s.p.i.p.p.s.s.p.p.i.s.p.s.w.y.s.s.s.s.s.s.i.s.p.p.i.p.p.p.s.p.s.i.d.V.V.V.V.", "V.N.I.%.@ } ....................[ [ x.k.k.k.k.h.h.k.k.k.k.k.[ [ ........[ .. ........._ N.V.V.V.", "V.N.I.%.@ 3.7.5.5.5.4.5.4.6.4.7.,.,.J.H.J.H.H.J.J.J.H.J.J.J.,.,.7.5.7.7.7.7.7.7.7.,.7...N.V.V.V.", "V.N.I.#.@ >.5.,.,.5.4.4.4.,.4.4.,.&.J.V.V.V.N.V.N.V.V.V.N.J.,.,.4.=.....X.......,.,.,...m.V.V.V.", "V.N.I.#.@ 2.5.4.4.4.,.,.4.,.,.4.>.,.V.N.M.V.N.V.N.N.N.N.N.V.,.o.8.k . . . . Z 7.,...N.Z.V.V.", "V.N.I.#.# 2.5.,.4.4.5.7.7.7.7.7.5.5.U.J.K.J.J.K.J.J.J.J.J.I.5.,.p.k ^ y.7.o.N.V.V.V.", "V.N.I.#.# >.5.,.,.5.+./ ) / _ _ ^ ( s.y.a.i.a.y.i.i.i.i.p.d.) Z ..e k [ ( Z N.F.V.V.", "V.N.I.#.# 3.5.5.,.<.7 2 > > > > > > > > > > ; 3 3 ; > ; > > > > > * - 2 ; 4 V.V.V.V.", "V.N.I.#.# 3.,.,.4.1.1 u @.` ` [ ) ` k.g.g.j.l.8 8 l.j.j.g.k.` / =.e . x ..` / N.V.V.V.", "V.V.I.#.# 2.5.,.4.<.< f e.6.7.8.,.4.P.J.J.H.T.G P T.H.H.F.K.4.,.i.k / y.5...m.V.V.V.", "V.N.I.#.@ 2.5.4.4.1.< d 9.,.,.,.=.,.Z.N.V.N.G.C C K.m.V.N.F.,.+.7.^ 9 q q q q 0 [ 5.,...N.V.V.V.", "V.N.I.#.@ >.5.,.4.<.1 d 9.,.,.4.+.,.F.V.V.M.K.P G G.N.V.N.H.,.+.4.7.y.y.y.y.y.y.6.,.4...N.V.V.V.", "V.N.I.#.@ 3.7.5.6.1.< f q.4.6.7.,.,.K.F.F.Z.T.P P T.Z.F.J.K.,.,.7.4.4.4.,.,.,.,.5.6.7...N.Z.V.V.", "V.N.L.%.@ } ....X.@.< p &. .....[ ..j.j.g.h.b.8 8 b.d.j.d.z...] [ [ { [ ..[ [ { ..[ .._ N.V.V.V.", "V.N.I.%.# X... .X.' < p &. .....[ [ j.s.d.d.g.s s g.d.d.d.h.+.d.j.g.h.m.y + c.d.j.d.k.,.m.F.V.V.", "V.N.I.%.# 2.9.4.4.9.< d q.4.5.5.,.,.L.J.F.J.R.I I R.J.J.F.J.w.F.J.J.Z.W.j X W.Z.F.H.K.y.m.V.V.V.", "V.N.L.%.@ 2.5.,.4.:.< d 9.4.4.4.=.=.Z.V.N.b.A.s p A.b.V.V.V.8.N.N.V.N.Y.j X I.V.V.V.J.t.m.F.V.V.", "V.N.I.%.@ 2.4.4.4.:.1 d 9.=.4.4.=.,.Z.K.R S J D D K 1 g.J.V.8.N.V.V.N.E.y o U.N.V.V.F.y.m.Z.V.V.", "V.V.I.#.@ 2.4.4.4.-.< d 0.4.4.4.+.,.Z.K.T ! ~ ~ ~ ~ L f.L.V.w.N.V.V.N.E.y o U.V.V.V.F.w.m.V.V.V.", "V.N.I.#.@ 2.4.4.5.1.< d 9.4.4.4.=.,.Z.K.T Q ~ ~ ~ ~ L f.L.V.8.N.Z.V.V.E.j O U.N.V.V.F.y.m.V.V.V.", "V.N.I.#.@ 3.4.4.4.-.1 d 0.=.4.4.=.,.V.K.T ! ~ ~ ~ ~ L f.J.V.w.N.V.V.N.R.y O I.V.V.N.J.=.n.F.V.V.", "V.N.I.#.@ 2.4.4.4.-.1 d 0.4.,.4.+.,.Z.K.T ! ~ ~ ~ ~ L f.L.V.8.N.V.V.N.E.y X U.N.V.V.F.w.m.V.V.V.", "V.N.I.#.@ 2.5.4.6.1.< d 9.4.4.4.=.,.Z.K.T ! ~ ~ ~ ~ L f.J.V.8.N.Z.V.m.W.h X U.N.V.V.F.y.m.F.V.V.", "V.N.I.#.# 3.4.,.4.-.< d 9.4.4.4.=.,.Z.J.X.s U U P I d x.J.V.w.N.V.V.m.Y.h X U.V.V.V.F.y.m.V.V.V.", "V.N.I.%.# 2.5.,.5.-.< d 9.,.4.4.+.,.F.V.F.K.D.D.D.D.G.V.N.V.8.N.V.V.V.Y.h X U.V.V.N.F.w.m.F.V.V.", "V.N.I.#.# 2.7.4.6.9.< d q.4.4.4.,.,.J.F.V.Z.Z.Z.Z.V.V.V.F.F.w.V.J.Z.V.W.j X Y.Z.S.F.K.y.m.F.V.V.", "V.N.I.#.# | =.+.o.$.< p -...+.+.....m.c.c.c.c.c.v.v.v.c.v.n.4.v.c.c.x.H.y X V.c.c.x.m.7.m.V.V.V.", "V.N.I.#.@ } [ [ .' < u ..` .[ [ ^ .[ . . . . . . . . . .^ .[ ..[ X.) Z ..[ [ [ [ ] N.V.V.V.", "V.N.I.#.@ 4.5.5.5.9.< f e.4.5.5.4._ 7.5.5.4.6.6.5.5.5.5.5.5.` 4.5.6.7.6.7.7.6.6.5.6.5...N.Z.V.V.", "V.N.I.#.# >.4.,.4.1.< d 9.,.,.4.,.` 4.4.,.,.,.,.4.,.4.,.,.5.` ,.,.5.*.*.&.&.&.<.4.4.,...m.V.V.V.", "V.N.I.#.# 2.5.,.4.1.< d 9.,.4.4.,._ 4.,.4.4.4.4.4.,.4.4.,.,._ ,.8.M 1 G F 6 G 5 / 7.,...N.Z.V.V.", "V.N.I.#.# 2.5.5.4.<.< f w.4.4.6.4.] 7.4.4.7.4.6.6.4.4.6.6.6.] ,.y.c W ~ ~ ~ ~ E V 8.4...m.Z.V.V.", "V.N.I.#.# 3.,.,.4.<.< a :.&.&.&.&.A &.&.&.&.&.&.&.&.&.&.&.&.A $.1.k W ~ ~ ~ ~ E V 8.4...N.V.V.V.", "V.N.I.#.@ 2.5.,.4.<.6 2 : , , , , , , , , , , , , , , , , , , , , = Q ~ ~ ~ ~ E V 8.,...N.V.V.V.", "V.N.I.#.@ 2.5.4.,.5.[ b C C C C v g C C C C C C C C v Y C C g b T w W ~ ~ ~ ~ L V 8.,...N.V.V.V.", "V.N.I.#.@ 2.5.4.4.,.4.w.0.0.0.0.7.{ w.0.0.0.0.7.0.0.0.0.7.0.{ 7.f.n W ~ ~ ~ ~ E V 8.,...N.V.V.V.", "V.N.I.%.@ 2.5.,.4.4.,.,.,.,.,.,.,.) ,.,.,.,.,.,.,.,.,.,.,.,._ +.8.m 4 H F F H < / 7.4...m.Z.V.V.", "V.N.I.%.@ >.,.,.,.,.,.,.,.,.,.,.>.` ,.,.,.,.,.,.,.,.,.,.,.4.) ,.,.4.*.&.%.*.%.&.,.,.4...N.V.V.V.", "V.N.I.%.@ r.y.8.t.8.t.t.t.8.t.t.8.X.t.8.8.t.t.t.8.t.t.t.8.t.X.8.t.t.t.t.t.t.t.t.t.t.t.2.N.V.V.V.", "V.N.P.%.# z B B l l l l l l l l B t l l l l l l z l l l l l t B l l l l l l l l l B l l V.V.V.V.", "V.V.J.s.& % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % $ & V.V.V.V.", "V.V.V.F.B.B.B.B.B.B.B.B.C.B.B.B.B.B.B.B.B.B.B.B.B.C.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.V.V.V.V.", "V.V.V.V.S.S.F.V.S.V.F.S.V.S.S.Z.S.F.S.S.S.S.S.S.S.S.F.V.S.S.S.S.Z.F.V.Z.S.F.S.S.S.V.F.V.V.V.V.V.", "V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.", "V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V.V." }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/netslide-icon.c0000644000175300017530000003275612161170375016461 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 137 2", " c #89817E", ". c #8A817E", "X c #3737BB", "o c #2827DD", "O c #0000FF", "+ c #2322E2", "@ c #2123E9", "# c #4F51D5", "$ c #5355D2", "% c #7574C7", "& c #7473CB", "* c #878683", "= c #908F8C", "- c #959393", "; c #969491", ": c #969591", "> c #969592", ", c #979592", "< c #9A9995", "1 c #9A9996", "2 c #9B9996", "3 c #999A97", "4 c #9B9A96", "5 c #A19F89", "6 c #A09F9C", "7 c #A19F9C", "8 c #B89F9B", "9 c #B8A19D", "0 c #B9A29E", "q c #BAA39F", "w c #BBA39F", "e c #A8A6A2", "r c #AAA8A4", "t c #ADABA7", "y c #A9ACA9", "u c #AAADA9", "i c #AEACA8", "p c #AFADA9", "a c #AFAEAA", "s c #BBA4A0", "d c #BBA4A1", "f c #BBA5A1", "g c #BCA5A2", "h c #BCA6A2", "j c #BDA6A2", "k c #BCA7A3", "l c #BFA8A5", "z c #B1AEAB", "x c #B0AFAB", "c c #B1AFAB", "v c #B5B6B2", "b c #BFBDB9", "n c #C4B8AE", "m c #C2B6B1", "M c #C3B7B2", "N c #C4B7B4", "B c #C6B9B1", "V c #C7BBB2", "C c #C5B8B4", "Z c #C7BAB6", "A c #C1BFBB", "S c #CABDB9", "D c #C8BEBA", "F c #CCBFBB", "G c #C2C0BC", "H c #C5C3BF", "J c #CDC0BC", "K c #CFC2BD", "L c #C6C5C3", "P c #C8C6C1", "I c #CFC5C1", "U c #CAC8C4", "Y c #CECCC7", "T c #CECDC9", "R c #CDCEC9", "E c #D1C8C3", "W c #D1CFCA", "Q c #D1CFCE", "! c #D9D6C2", "~ c #D2D0CB", "^ c #D2D0CC", "/ c #D3D1CC", "( c #D3D1CF", ") c #D1D3CE", "_ c #D5D3CE", "` c #D5D3CF", "' c #D3D4CF", "] c #D6D4CE", "[ c #D6D4CF", "{ c #DCDAC9", "} c #DAD8CC", "| c #DEDFCF", " . c #D2D3D0", ".. c #D3D5D1", "X. c #D5D5D0", "o. c #D6D4D0", "O. c #D4D6D1", "+. c #D5D6D1", "@. c #D9D5D0", "#. c #D9D7D2", "$. c #DAD7D2", "%. c #D9D7D4", "&. c #D7D8D3", "*. c #D7D9D4", "=. c #DAD8D3", "-. c #D9D9D4", ";. c #DBD9D4", ":. c #DBD9D5", ">. c #DBD9D6", ",. c #DAD8D7", "<. c #DBD9D7", "1. c #DCD9D4", "2. c #DCDAD5", "3. c #DDDBD6", "4. c #DEDBD6", "5. c #DADCD7", "6. c #DBDCD7", "7. c #DEDCD7", "8. c #DFDCD7", "9. c #DDDDD8", "0. c #DFDDD8", "q. c #E1DFCD", "w. c #E0DED9", "e. c #DEE1DC", "r. c #E4E2CB", "t. c #E3E1D4", "y. c #E7E8D6", "u. c #E4E2DC", "i. c #E6E4DF", "p. c #E7E5DF", "a. c #E4E6E1", "s. c #E5E7E1", "d. c #E6E7E1", "f. c #E5E7E2", "g. c #E7E9E3", "h. c #E7E9E4", "j. c #EAE8E2", /* pixels */ "2.2.2.2.2.2.7.[ w.2.:.9.o.w.2.2.", "2.2.2.2.2.7.A 6 P 2.7.H 7 G 7.2.", "2.2.2.2.:.:.r < c 7.2.i < r 7.=.", "2.2.2.2.9.h.) u 6.f.g.=.u ..g.2.", "2.2.=.9.E 8 h 9 q q q q w w k @.", "2.2.2.g.9 d D K m M J Z F =.", "w.A e ) d I e.< g.&.R ' v ' o.:.", "o.7 < u d F =.: w.~ 7.p.p.:.2.2.", "w.L z 5.h J 9.- u.~ i c G 7.:.2.", "1.7.7.a.9 N .- :.U t z * :.2.2.", "2.7.7.f.s N } ! } R 2.j.< =.7.:.", "w.H i :.d B # o & q.L W = Y ~ =.", "' 7 4 u m n + O X 5 Q 2.: =.:.2.", "w.G r o.j B # o % { [ w.: =.7.2.", "2.7.7.g.j F y.r.q.o.' w.< =.2.2.", "2.:.=.=.@.@.<.,.,.2.1.2.' 2.2.2." }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 100 2", " c #525250", ". c #635C5A", "X c #5B5A73", "o c #6E6D6A", "O c #706966", "+ c #707270", "@ c #787774", "# c #7A7976", "$ c #7D7C7A", "% c #8E5553", "& c #8C5957", "* c #8E5B59", "= c #925E5C", "- c #94615F", "; c #857C79", ": c #83827D", "> c #6B6A83", ", c #5150AE", "< c #5858B6", "1 c #5251B9", "2 c #5958B9", "3 c #6463AD", "4 c #6E6DA8", "5 c #7D7CAA", "6 c #6564B6", "7 c #6B6AB6", "8 c #2726C5", "9 c #2A29C6", "0 c #0000EC", "q c #0000F1", "w c #0303FE", "e c #858481", "r c #888784", "t c #8A8986", "y c #8E8D8A", "u c #918E8B", "i c #93928E", "p c #969491", "a c #989793", "s c #9A9994", "d c #9E9C99", "f c #B2928F", "g c #A09F9C", "h c #B39B97", "j c #A3A29E", "k c #A6A5A1", "l c #A8A6A3", "z c #ABA9A6", "x c #AEACA8", "c c #B8AAA6", "v c #B0AFAB", "b c #BBADA9", "n c #B4B2AE", "m c #A8A6BE", "M c #A9A8BF", "N c #B6B5B1", "B c #B9B7B3", "V c #B3B2B8", "C c #B8B7BB", "Z c #BFBDB9", "A c #C3B6B2", "S c #C6B9B5", "D c #C9BCB7", "F c #C3BBB9", "G c #C9BCB8", "H c #C4C2BD", "J c #CBC1BC", "K c #D1C3BF", "L c #C7C5C1", "P c #C9C6C2", "I c #CCCAC5", "U c #CECCC9", "Y c #D4C6C2", "T c #D3CCC7", "R c #D1CFCA", "E c #D3D2CD", "W c #D6D4D0", "Q c #D9D7D2", "! c #D7D8D3", "~ c #DCDAD5", "^ c #DFDDD8", "/ c #E0DED7", "( c #E1DED9", ") c #E4E5CF", "_ c #E2E3D7", "` c #E9E6D7", "' c #EEECD6", "] c #E3E2DC", "[ c #EBE8D8", "{ c #E2E6E1", "} c #E8E6E1", "| c #E6EAE5", " . c #ECEAE5", ".. c #EBEEE9", "X. c #F0EEE9", "o. c #EBF0EA", "O. c #F2F3ED", "+. c #FDFBEE", "@. c #F6F6F1", "#. c #FEFCF3", /* pixels */ "~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ Q ~ ^ ~ ~ ~ ~ ~ ~ ", "~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ Q ] E Q ~ ~ ~ ~ ~ ~ ~ ( Q E ^ ~ ~ ~ ~ ~ ", "~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ] U r d ( ^ ~ ~ ~ ~ ( ~ p y E ( ~ ~ ~ ~ ", "~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ I u x j d ~ ~ ~ ~ ~ W s k x i E ~ ~ ~ ~ ", "~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ] u t N x # n ] Q Q ] l $ v n e s ] ~ ~ ~ ", "~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ Q ] g n g B ] ~ ~ ~ ~ ] c k v k ] W ~ ~ ~ ", "~ ~ ~ ~ ~ ~ ~ ^ ~ ~ ~ ~ ( p d y n ] ~ ~ ~ Q ] z i a g ] ~ ~ ~ ~ ", "~ ~ ~ ~ ~ ~ ~ ~ ~ ~ Q Q ~ F v x U ~ W ~ Q Q / P x x H / W ~ ~ ~ ", "~ ~ ^ ~ ~ ~ ~ ~ ~ ] { { ] | o.o.{ { { { { { { | ..o.| { { { ~ ~ ", "~ ~ ~ ~ ~ ~ ~ Q ] f % - = = * * - = - = * - = = * & - = = - T ^ ", "~ ~ ~ ~ ~ ~ ~ Q { = h J A Y . u K A G c b F A Y ; O Y A S D W ~ ", "~ ~ ~ ^ / ~ ~ Q { - J | / O.+ x o.( { E Q | ] @.s e @.] ] { ^ ~ ", "~ Q ] I u ] ( ~ { - S ] W | o j { ~ / P Z I L W y $ W L I I W ~ ", "~ ] U y t d p Z ..* S { W .o k } W ( I H E T U E T E E T E ~ ~ ", "~ E r n N N d n ..* F ] W .o k } W / I E ] ~ ^ ^ ~ ^ ^ ^ ^ ~ ~ ", "~ ~ d d x d y x ..* D ] W .o l } W ( I U ~ Q Q ! ~ ~ ~ ~ ~ ~ ~ ", "~ ~ ( d # B n T | = D ] W .o k } ~ ( I ~ .. . . .( ~ ~ ~ ~ ~ ~ ", "~ ~ ^ Q v ] ] / ] = S ] W .o k } W ( L r y t t u I ( ~ ~ ~ ~ ~ ", "~ ~ ~ ~ ] ~ Q Q { - G { ~ O.o z | ~ ] I ; e : y ; | Q ~ ~ ~ ~ ", "~ ~ ~ ~ ~ ~ ~ Q { = c E L W o d W I U F ( X.| #.g $ ..Q ~ ~ ~ ~ ", "~ ~ ~ ~ Q ~ ~ ~ { * b W I U U U U U E H I Q E } u $ .W ~ ~ ~ ~ ", "~ ~ ~ ~ ] ~ ~ W { - G { [ ' [ ' ' ] ] T U ] ~ .p $ O.~ / ~ ~ ~ ", "~ ~ ( W k ] ] ~ { = D _ 4 < 2 2 , V ] L H E E _ u # ( T R E ~ ~ ", "~ ( ~ a $ x z I | * D ) 8 w w w 0 m +.E Z U H W t @ W L I P ~ ~ ", "~ W p k v k i v ..* F ) 9 w w w q + s y E ] ~ .p $ O.~ / ( ~ ~ ", "/ W t x N x a v o.* D _ 8 w w w q X : $ T / W .i $ .Q Q ~ ~ ~ ", "~ ( E p e j d H { - D _ 8 w q w q M +.! U ~ ~ .i $ .Q ~ ~ ~ ~ ", "~ Q ( R a } ( / { - A ] 5 6 7 7 6 C ] P T ( W .i $ .Q ~ ~ ~ ~ ", "~ ~ ~ / ( Q Q ~ { = S ] ` [ ' [ ' ( ^ I U ( Q .i $ .Q ~ ~ ~ ~ ", "~ ~ ~ ~ ~ ^ ~ Q { - S ] Q ! Q Q ! Q / I U ^ Q | p : .W ~ ~ ~ ~ ", "~ ~ ~ ~ ~ ~ ~ ~ ^ T W ^ ^ Q ~ ~ ~ ~ ~ Q ~ ~ ~ ^ E T ^ ~ ~ ~ ~ ~ ", "~ ~ ~ ~ ~ ~ ~ ~ ~ ^ ~ ~ ~ ^ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ^ ^ ~ ~ ~ ~ ~ ~ " }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 123 2", " c #3B1514", ". c #2B2B2A", "X c #322F2E", "o c #31302F", "O c #323332", "+ c #74201F", "@ c #4E2424", "# c #613533", "$ c #464543", "% c #4E4E4C", "& c #504F4D", "* c #575641", "= c #50504E", "- c #555452", "; c #5A5957", ": c #565558", "> c #5C5E5C", ", c #7C4B4A", "< c #62615F", "1 c #636260", "2 c #6A6967", "3 c #6D6C69", "4 c #7E7D66", "5 c #71716E", "6 c #767572", "7 c #787774", "8 c #7B7976", "9 c #7D7C79", "0 c #824E4C", "q c #925E5C", "w c #95615E", "e c #966260", "r c #9C6765", "t c #966967", "y c #976A68", "u c #9B6F6C", "i c #9D716E", "p c #817F7D", "a c #9E7371", "s c #9F7875", "d c #A27673", "f c #A57976", "g c #A87C79", "h c #82817E", "j c #101094", "k c #18179B", "l c #1111A8", "z c #1717B6", "x c #1F1FB3", "c c #2A29A4", "v c #2929AD", "b c #2222B4", "n c #2F2FB0", "m c #3130B2", "M c #43438E", "N c #75749C", "B c #7E7D9D", "V c #7675A0", "C c #0000E2", "Z c #0101EC", "A c #0101F3", "S c #0202FE", "D c #858582", "F c #888784", "G c #8A8986", "H c #8E8D8A", "J c #918F8C", "K c #92918E", "L c #84829A", "P c #959491", "I c #989793", "U c #9B9A96", "Y c #9E9D99", "T c #A09F9B", "R c #A2A19E", "E c #8483A1", "W c #9493A1", "Q c #A6A5A1", "! c #A8A7A3", "~ c #ACAAA6", "^ c #AEADA9", "/ c #B0AEAA", "( c #B2B1AD", ") c #B6B5B1", "_ c #B9B7B3", "` c #BBBAB5", "' c #BDBDB9", "] c #C0BFBA", "[ c #C3C1BD", "{ c #CDC6C1", "} c #C2CBC7", "| c #CDCBC6", " . c #CFCDC8", ".. c #D1CEC9", "X. c #DCDAC2", "o. c #D5D2CD", "O. c #D9D2CE", "+. c #DCDACB", "@. c #D7D5D1", "#. c #D9D6D1", "$. c #DCDAD5", "%. c #DFDDD8", "&. c #E0DECE", "*. c #E1DDD6", "=. c #E1DED9", "-. c #EBE7D7", ";. c #E4E2DD", ":. c #EAE6DA", ">. c #ECEADC", ",. c #F5F3DA", "<. c #E7E5E0", "1. c #E9E7E2", "2. c #E3EBE6", "3. c #EEECE6", "4. c #E6EEE8", "5. c #EEECEA", "6. c #F0EEE8", "7. c #E8F1EC", "8. c #F3F1EC", "9. c #EEF7F1", "0. c #F0F9F3", "q. c #F0FFFE", "w. c #FBFFFC", /* pixels */ "$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.%.$.$.$.$.%.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.", "$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.#.$.%.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.", "$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.%.#.#.$.$.$.$.$.$.$.$.$.$.#.%.%.#.$.$.$.$.$.$.$.$.$.", "$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.#.%.o.;.#.%.$.$.$.$.$.$.$.$.#.;.@.#.%.$.$.$.$.$.$.$.$.$.", "$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.#.;.R 3 [ ;.$.$.$.$.$.$.$.$.#.%.@.7 D =.%.$.$.$.$.$.$.$.%.", "$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.;.U F ( 7 ' ;.#.$.$.$.%.$.$.;.o.8 ! U D %.%.$.$.$.$.$.$.$.", "$.$.$.$.$.$.$.$.$.$.%.$.$.$.$.$.$.$.;.R H _ ( ( h [ ;.#.$.$.$.#.%.o.D Q ) ) U H %.%.$.$.$.$.$.$.", "$.$.$.$.%.$.$.$.$.$.$.$.$.$.$.$.#.;.~ ; K ( / ^ 9 < ..%.$.$.$.$.=.6 3 Q / ( T 1 F ;.#.$.$.$.$.$.", "$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.%.o.o.h ~ ) I U $.o.%.$.$.$.$.#.#.( G ) ( D [ #.$.$.$.$.$.$.$.", "$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.=.J / _ U ! 1.$.$.$.$.$.$.$.<.] J _ _ F ..;.$.$.$.$.$.$.$.", "$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.*.D R / J R ;.#.$.$.$.$.$.$.=.] D ^ ~ p .%.$.$.$.$.$.$.$.", "$.$.$.$.$.$.$.$.$.$.$.%.$.$.$.$.$.$.$.=.T G K D ` ;.$.$.$.$.$.$.#.*.| F J H H #.$.$.$.$.$.$.$.$.", "$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.#.#.#.#.#.=.=.=.=.$.#.#.+.#.#.#.#.#.#.*.=.*.=.=.#.#.#.#.$.$.$.$.$.", "$.$.%.$.$.$.$.$.$.$.$.$.$.$.=.2.4.4.4.4.2.2.2.2.4.4.4.2.4.4.4.4.4.4.2.2.2.2.4.2.2.4.4.4.$.$.$.$.", "$.$.$.$.$.$.$.$.$.$.$.$.$.=.{ a u u u i u i g u u u u u u i i u u u u y f d u u u u u i o.*.$.$.", "$.$.$.$.$.$.$.$.$.$.$.$.#.2.s + r q q q e , e q q q q q 0 q w q q q r @ # r q q q q w ..%.$.$.", "$.$.$.$.$.$.$.#.$.$.$.$.#.4.u y q.2.2.2.9.' O 7.4.4.4.4.7.} 2.4.4.4.4.w.> D w.2.4.4.4.4.%.$.$.$.", "$.$.$.$.$.$.$.<.$.$.$.$.#.4.u q 2.O.$.O.;.^ o ;.#.#.#.#.%.) o.$.#.#.o.1.- 8 1.o.#.#.#.#.$.$.$.$.", "$.$.$.$.$.$.;.! O.$.$.$.#.4.u q 4.#.$.#.;.( o ;.$.%.#.#.*.' $.*.$.%.$.8.- 9 7.$.=.$.*.%.$.$.$.$.", "$.$.$.$.#.<.T 1 #.=.=.=.#.4.u q 4.O.%.#.;.^ o ;.#.%.%.#.=.( ( ` ` ` _ [ 7 G [ ` ` ` ` ` #.$.$.$.", "$.$.$.$.<.I H P p H h Y =.1.u q 4.#.$.#.;.( o ;.$.$.$.$.*.` o.#.#.#.#.#.;.%.#.#.#.#.#.#.$.$.$.%.", "$.$.$.%.Y F _ / ^ ^ R H =.4.u q 4.#.%.#.;.( o ;.#.$.$.$.%.` #.=.$.%.$.%.#.$.$.$.$.$.$.%.$.$.$.$.", "$.$.%.o.3 ) ( / ) _ ^ K =.2.u q 4.#.%.#.;.( o ;.$.$.$.#.%.` #.%.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.", "$.$.$.=.[ 7 / ^ I U H G =.4.u q 4.#.$.#.;.( o ;.$.$.%.#.%.` @.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.", "$.$.$.#.<.' D 9 I ! T _ *.4.u q 4.#.$.$.<.^ o ;.$.$.$.$.%._ o.#.#.O.#.#.@.#.%.$.$.$.$.$.%.$.$.$.", "$.$.$.$.#.;.] 1 $.1.<.=.#.4.u q 4.#.$.#.<.^ o ;.#.$.$.$.$.[ 3.7.6.6.6.6.3.;.#.$.$.$.$.$.$.$.$.$.", "$.$.$.$.$.$.=...#.$.#.$.#.4.u q 4.#.$.#.<.^ o ;.$.$.$.$.;.K $ = & & & & - [ =.$.$.$.$.$.$.$.$.$.", "$.$.$.$.$.$.$.%.$.$.$.$.#.4.u q 4.#.$.$.;./ O ;.#.$.$.#.:.R 8 h p p 9 J . h 3.@.$.$.$.$.$.$.$.$.", "$.$.$.$.$.$.$.$.$.$.$.$.#.4.u q 7.$.*.$.1.( o <.%.%.%.%.;.[ 1.3.4.6.1.w.< 8 6.#.$.$.$.$.$.$.$.$.", "$.$.$.$.$.$.$.$.$.$.$.$.#.4.i 0 } _ ` ` ' Q 1 ' _ ` ` ` ` ~ o.#.#.+.o.3.= 8 4.#.$.$.$.$.$.$.$.$.", "$.$.$.$.$.$.$.$.$.$.$.$.#.4.u q 2.@.#.#.@.$.;.@.@.@.#.#.#._ #.%.$.$.#.6.- 8 6.#.$.$.$.$.$.$.$.$.", "$.$.$.$.$.$.$.#.$.$.$.$.#.4.u q 4.#.$.%.%.%.$.%.%.%.%.#.;.' o.%.%.$.#.6.- 8 6.#.$.$.$.$.$.$.$.$.", "$.$.$.$.$.$.%.%.$.$.$.$.@.4.u q 4.#.$.[ &.X.[ &.X.&.+.$.$.` $.=.%.%.$.8.; p 6.$.*.*.%.%.$.#.%.$.", "$.$.$.$.$.=.o.6 #.;.;.%.#.2.i q 2.;.L l b v b v b M &.$.=._ | ......| =.= 8 *.| .....| #.%.$.$.", "$.$.$.$.=.o.D 5 ( ] ` .$.4.u q 2.-.V A S S S S S v X.#.$.( ` [ ' [ ` ..- 5 ..' [ ' ] [ +.%.$.$.", "$.$.$.=.o.9 Q Q D H p F =.2.u q 2.-.V A S S S S S m ,.5.6.} $.=.;.=.%.8.; 8 8.%.%.;.;.%.$.$.$.$.", "$.$.%.@.7 Q ( ( _ _ / J *.2.u q 2.-.N A S S S S S j * : % 8 %.$.#.$.@.3.- 8 6.O.$.$.$.$.$.$.$.$.", "$.$.$.#.F U ) ( ) ( ~ J =.2.i q 4.-.V A S S S S S k 4 9 6 H $.$.$.$.#.6.- 8 7.#.$.$.$.$.$.$.$.$.", "$.$.$.%.%.D U Y p F 8 J =.2.u q 4.-.V A S S S S S m ,.5.8.{ @.$.$.$.@.6.- 9 3.#.$.$.$.$.$.$.$.$.", "$.$.$.$.%.%.H 2 [ o. .#.$.4.u q 4.-.B C A Z Z Z A c +.@.#.` @.$.$.$.@.6.- 8 3.#.$.$.$.$.$.$.$.$.", "$.%.$.$.#.%.%.F o.;.%.%.O.4.i w 2.%.) B E E E E B W *.#.%.` #.#.$.$.O.6.- 8 7.#.$.$.$.$.$.$.$.$.", "$.$.$.$.$.$.$.:.%.#.$.$.#.4.i q 4.@.=.>.>.>.>.1.>.:.$.$.%.` #.%.$.$.#.6.- 8 6.#.$.$.$.$.$.$.$.$.", "$.$.$.$.$.$.$.$.$.$.$.$.@.4.y w 4.#.$.$.$.@.@.@.$.#.$.$.%.` #.%.$.$.#.6.- 8 3.#.$.$.$.$.$.$.$.$.", "$.$.$.$.$.$.$.$.$.$.$.$.#.4.i w 4.@.$.$.$.$.$.$.$.$.$.$.%.` o.%.$.$.#.3.; p 5.@.$.$.$.$.$.$.$.$.", "$.$.$.$.$.$.$.$.$.$.$.$.%.$.o...%.$.$.$.$.$.$.$.$.$.$.$.%.#.$.$.$.$.$.%. .o.$.$.$.$.$.$.$.$.$.$.", "$.$.$.$.$.$.$.$.$.$.$.$.#.%.%.%.$.$.$.$.$.$.$.$.$.$.$.$.#.%.$.$.$.$.$.$.%.%.$.$.$.$.$.$.$.$.$.$.", "$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.", "$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$.$." }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/pattern-icon.c0000644000175300017530000003077012161170376016322 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 126 2", " c #000000", ". c #010102", "X c #030303", "o c #050505", "O c #070707", "+ c #090909", "@ c #0A0A0A", "# c #0D0D0D", "$ c #0E0E0E", "% c #101010", "& c #111111", "* c #121212", "= c #151514", "- c #181817", "; c #1A1A1A", ": c #1C1C1C", "> c #1D1D1D", ", c #242425", "< c #252525", "1 c #252626", "2 c #272626", "3 c #2B2B2A", "4 c #2B2B2B", "5 c #2C2B2A", "6 c #2E2D2C", "7 c #333332", "8 c #333333", "9 c #343434", "0 c #3E3E3E", "q c #424242", "w c #454443", "e c #454545", "r c #4C4C4C", "t c #4D4D4D", "y c #6B6B6B", "u c #6D6D6D", "i c #717171", "p c #727272", "a c #757575", "s c #79797A", "d c #7C7C7B", "f c #7C7C7C", "g c #7E7E7E", "h c #838383", "j c #848483", "k c #848484", "l c #858484", "z c #858585", "x c #8B8B8B", "c c #8F8F8E", "v c #969695", "b c #989898", "n c #9E9E9E", "m c #A1A09D", "M c #A2A19D", "N c #A3A29E", "B c #A2A1A0", "V c #A6A5A2", "C c #AAA8A4", "Z c #AAAAAA", "A c #ACABA8", "S c #AFADA9", "D c #AFADAA", "F c #B0AEAB", "G c #B0AFAC", "H c #B2B0AC", "J c #B2B0AD", "K c #B2B1AD", "L c #B3B1AD", "P c #B4B2AE", "I c #B4B2AF", "U c #B4B3AF", "Y c #B1B1B1", "T c #B7B6B2", "R c #B9B7B3", "E c #B9B7B4", "W c #BAB8B4", "Q c #BAB8B5", "! c #BBB9B5", "~ c #BBB9B6", "^ c #C0BEBA", "/ c #C1C0BB", "( c #C4C2BE", ") c #C8C6C1", "_ c #CBC9C5", "` c #CECCC7", "' c #CECDC8", "] c #D0CEC9", "[ c #D1CFCA", "{ c #D1D0CB", "} c #D2D0CB", "| c #D3D1CC", " . c #D4D2CD", ".. c #D4D2CE", "X. c #D4D3CE", "o. c #D5D3CE", "O. c #D6D4CF", "+. c #D3D3D3", "@. c #D7D5D0", "#. c #D8D6D2", "$. c #D9D7D2", "%. c #DAD7D2", "&. c #DAD8D3", "*. c #DBD9D4", "=. c #DCDAD5", "-. c #DDDBD6", ";. c #DEDBD6", ":. c #DEDCD7", ">. c #DFDDD7", ",. c #DFDDD8", "<. c #E0DED8", "1. c #E0DED9", "2. c #E1DFD9", "3. c #E1DFDA", "4. c #E2E0DB", "5. c #E4E1DC", "6. c #E4E2DD", "7. c #E5E3DD", "8. c #E6E4DF", "9. c #E7E4DF", "0. c #E0E0E0", "q. c #E7E5E0", "w. c #E7E7E6", "e. c #E8E5E0", "r. c #E8E6E1", "t. c #FFFFFF", /* pixels */ "=.=.=.=.*.=.@.@.*.{ -.} =. .$.=.", "=.=.=.=.*.5.~ ) } Y 2.C 2.K ' 2.", "=.=.=.=.*.*.-.-.=.-.=.-.=.=.=.=.", "=.=.=.=.*.5.E / } P &.H 2.K _ 2.", "=.=.=.*.*.2.-.,.2.%.7.*.q.=.2.-.", "=.=.=.2.,.' T W R ! Y P K G W =.", "=.=.&.W 5.A y a i f < @ 4 2.", "=.=.=.^ 2.D s k g x 4 # 6 ,.", "=.=.=.' r.m # ; > : * o * 7 -.", "=.=.=.A 7.m o o o @ 5 ,.", "=.=.=.*.r.m * l n 9 e b v $.", "_ E 2.F r.m @ 0.t.t u t.w. .", "=.$.=.$.7.V < 9 Y +.q t Z B $.", "=.=.=.F 2.G f l i g < @ 3 ,.", "=.=.=.) 4.P g l j c 0 = < ; w -.", "=.=.=.2.=.&.{ .O. .O. . . .@.=." }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 250 2", " c #000000", ". c #010101", "X c #020202", "o c #030303", "O c #030304", "+ c #040404", "@ c #050505", "# c #060606", "$ c #070707", "% c #080808", "& c #090909", "* c #090A0A", "= c #0A0A0A", "- c #0B0B0B", "; c #0C0C0C", ": c #0D0D0D", "> c #0E0E0E", ", c #0F0F0F", "< c #101010", "1 c #111111", "2 c #121212", "3 c #161616", "4 c #181818", "5 c #1E1D1D", "6 c #1E1F1F", "7 c #222221", "8 c #222222", "9 c #252524", "0 c #252626", "q c #262626", "w c #272727", "e c #282827", "r c #282828", "t c #292929", "y c #2A2A2A", "u c #2B2B2B", "i c #2C2B2B", "p c #2C2C2C", "a c #2F2F2F", "s c #303030", "d c #313131", "f c #323231", "g c #414141", "h c #444444", "j c #464544", "k c #474645", "l c #474746", "z c #464647", "x c #484846", "c c #494847", "v c #494947", "b c #4A4A49", "n c #4A4A4A", "m c #4B4B4B", "M c #4F4E4D", "N c #4E4E4E", "B c #4F4F4F", "V c #505051", "C c #525252", "Z c #535353", "A c #555555", "S c #565656", "D c #595959", "F c #5B5B5B", "G c #5E5D5C", "H c #5F5F5E", "J c #5F5F5F", "K c #626160", "L c #676868", "P c #6A6A68", "I c #6B6A68", "U c #6B6A69", "Y c #6A6A6A", "T c #6C6C6A", "R c #6C6C6D", "E c #6E6F6F", "W c #70706E", "Q c #71706E", "! c #71706F", "~ c #717172", "^ c #727272", "/ c #727273", "( c #747371", ") c #777573", "_ c #757575", "` c #777775", "' c #767677", "] c #767777", "[ c #777777", "{ c #787775", "} c #797875", "| c #797877", " . c #777778", ".. c #787878", "X. c #797979", "o. c #7A7A7A", "O. c #7B7B7A", "+. c #7D7D7B", "@. c #7F7D7A", "#. c #7C7C7C", "$. c #7D7D7C", "%. c #7D7D7D", "&. c #7F7F7D", "*. c #7E7E7E", "=. c #7F7E7E", "-. c #7E7E7F", ";. c #7F7F7F", ":. c #807E7B", ">. c #807F7E", ",. c #81807E", "<. c #81807F", "1. c #81817F", "2. c #7F7F80", "3. c #808080", "4. c #808081", "5. c #818181", "6. c #828282", "7. c #838382", "8. c #838383", "9. c #848380", "0. c #848383", "q. c #848484", "w. c #858585", "e. c #878787", "r. c #888784", "t. c #888786", "y. c #8A8885", "u. c #888887", "i. c #8A8987", "p. c #888888", "a. c #898989", "s. c #8A8A8A", "d. c #8E8D8A", "f. c #8E8D8B", "g. c #8F8E8B", "h. c #8C8C8C", "j. c #8F8E8C", "k. c #8F8F8F", "l. c #908F8C", "z. c #918F8C", "x. c #92908D", "c. c #92918E", "v. c #93928F", "b. c #94938F", "n. c #939290", "m. c #929292", "M. c #939393", "N. c #949390", "B. c #949391", "V. c #9A9995", "C. c #9A9996", "Z. c #9B9A96", "A. c #989898", "S. c #9D9C98", "D. c #9E9C99", "F. c #9E9E9E", "G. c #A09E9B", "H. c #A3A29F", "J. c #A4A39F", "K. c #A5A3A0", "L. c #A6A4A1", "P. c #A8A7A4", "I. c #ACAAA6", "U. c #AFADA9", "Y. c #B0AEAA", "T. c #B0AFAB", "R. c #B2B0AC", "E. c #B4B2AE", "W. c #B5B4B0", "Q. c #B8B6B2", "!. c #B9B7B3", "~. c #BBBAB5", "^. c #C2C0BB", "/. c #C3C2BD", "(. c #C4C2BE", "). c #C5C3BE", "_. c #C7C5C0", "`. c #C7C5C1", "'. c #C9C7C3", "]. c #C9C8C3", "[. c #CAC8C3", "{. c #CAC8C4", "}. c #CBC9C4", "|. c #CBC9C5", " X c #CDCBC7", ".X c #CFCDC8", "XX c #CCCCCC", "oX c #CECECE", "OX c #CFCFCF", "+X c #D0CECA", "@X c #D1CFCB", "#X c #D2D0CB", "$X c #D3D1CC", "%X c #D3D1CD", "&X c #D4D2CD", "*X c #D4D3CE", "=X c #D5D3CE", "-X c #D6D4CE", ";X c #D6D4CF", ":X c #D4D3D2", ">X c #D7D5D0", ",X c #D5D5D3", "X{.wXwXwXwX", "wXwXwXwXwXwXwXwXwXwXwX0X5X0XwX5X5XwXpX6X6X5XtXwX5X5XwXwX5X5XwXwX", "wXwXwXwXwXwXwXwXwXwX0XkXCXnXnXBXnXcXcXnXNXCXnXBXNXNXNXBXCXcXwXwX", "wXwXwXwXwXwXwXwXwX0XpXP.d.B.B.B.n.b.B.B.x.d.j.d.z.d.d.l.i.J.pX6X", "wXwXwXwXwXwX5XwXwX0XjX{ L ^ / R Y ^ R X.j O + = o j CX5X", "wXwXwXwX0XkXx.L.gX6XgX,.#.w.w.#.#.w.5.j.Z O = ; + x CX5X", "wXwXwXwX0XgX!.d.pX6XgX,.' 5.2.X. .2.#.e.V + + = + x CX5X", "wXwXwXwXwXwX*X0XwXwXpX,.#.a.a.5.#.p.q.k.S + = ; $ x NX5X", "wXwXwXwXwXwXnXuXwX6XnXQ 7 r w p p w w r r ; ; ; 2 4 ; 1 N CX5X", "wXwXwXwX0XpXR.'.pX6XnXY + = ; + + - + x CX5X", "wXwXwXwX0XjXS.t.gX5XnXU + = ; O + + 1 = ; + b CX5X", "wXwXwXwXwXpX}.R.tX5XCXY = ; ; O + ; + j CX5X", "wXwXwXwXwXwXtXcXwX6XNXU O ; ; 1 f a p f u ; ; ; , f p f w K cX5X", "wX5XwXwXwXwX6XwXwX6XNXU = + + 1XHXSXHXM. = + OXHXHXHX,X-XwX", "uX@Xb.pX5XnXJ.!.jX>XNXI o 1XHXSXHXn. + OXHXSXHX1X-XtX", "wX*X:.0X6XnXD.J.CX5XNXI + o 1XHXSXHXM. + OXHXSXHX>X-XtX", "wX0X$X0XwXwX-X*XwX6XNXY 1XHXSXHXB. $ + OXHXSXHX1X-XtX", "wXwXuXwXwX0XpXcX0XwXcX{ V G D S e.A.n.F.J + 1 ; , j b N g Q gX6X", "wXwXwXwXwXwX$X^.pX6XpX,.2.a.a.5.' #.X.0.N o + ; x CX6X", "wXwXwXwX0XcXC.) CX5XjX>.' 5.5.X.X.5.2.h.V + = ; O = b NX5X", "wXwXwXwXwXtX'.R.uX6XjX@.' 5.5.#.X.5.#.p.M + - j CX6X", "wXwXwXwXwXwXpXcX0X0XpXt.O.q.5.#.#.0.2.i.H 5 w 7 r p 8 w 4 G nX6X", "wXwXwXwXwXwXwX8XwXwXwX5X-X,X-X-X-X-X-X,X5XwXwXwXwXwXwXwXwXwXwXwX", "wXwXwXwXwXwXwXwXwXwXwXwXtXtXtXtXtXtXtXtXtXwXwXwXwXwXwXwXwXwXwXwX" }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 69 1", " c #010101", ". c #070708", "X c #0C0C0C", "o c #0F0F10", "O c #121212", "+ c #1D1D1D", "@ c #252524", "# c #282827", "$ c #2E2E2E", "% c #363636", "& c #3A3A3A", "* c #444445", "= c #4D4D4D", "- c #535353", "; c #5A5957", ": c #575858", "> c #5B5A59", ", c #62615F", "< c #646464", "1 c #686764", "2 c #696866", "3 c #6C6B6A", "4 c #706F6E", "5 c #71706E", "6 c #747372", "7 c #787774", "8 c #7C7C7C", "9 c #807F7F", "0 c #83817E", "q c #7F7F80", "w c #828282", "e c #898885", "r c #8A8A8A", "t c #908F8C", "y c #92918E", "u c #959492", "i c #989793", "p c #999895", "a c #9D9C99", "s c #A4A29F", "d c #A7A5A2", "f c #A8A7A3", "g c #ABAAA6", "h c #AEADA9", "j c #B0AEAB", "k c #B3B2AD", "l c #B7B5B1", "z c #B8B6B2", "x c #BBB9B5", "c c #BEBCB8", "v c #C1BFBC", "b c #C4C2BE", "n c #C5C4C1", "m c #C9C7C2", "M c #CCCAC5", "N c #CACAC9", "B c #D1CFCB", "V c #D4D2CD", "C c #D5D5D2", "Z c #D9D7D2", "A c #DCDAD5", "S c #DFDDD8", "D c #E0DED9", "F c #E4E2DC", "G c #E7E5E0", "H c #E8E6E1", "J c #EAE8E3", "K c #F4F4F4", "L c #FEFEFE", /* pixels */ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "AAAAAAAAAASAAAAAAAAAZAAAAAAAAAAAAAAAAAAAZAAAAAAA", "AAAAAAAAAAAAAAAAAAAAFFAAAASFAAAAADFDAAADFDAAAAAA", "AAAAASAAAAAAAAAAAASAzvSAASbkZSAAFbvFAADbkZAAAAAA", "SAAAAAAAAAAAAAAAAAASf>SAAAA7aHAAD75JZAAZ5sFAAAAA", "AAAAAAAAAAAAAAAAAAAAf,AAZFg-MSASB5,FAZFs;BDAAAAA", "AAAAAAAAAAAAAAAAAAAAxnSAADxhVSAASVbSAADljVDAAAAA", "AAAAAAAAAAAASAAAAAAAFDAAAAFFAAAAAAFZAAAFFDAAAAAA", "AAAAAAAAAAAAAAAAAAAAZAAAAAAZAAAAAAAAAAAAZAAASAAA", "AAAAAAAAAAAAAAAAAAAADDAAAAASFZAAZDAAAAAZASAAAAAA", "AAAAAAAAAAAAAAASAAZFs0FAASb0kFAAA7MDAADc8cFAAAAA", "AAAASAAAAAAAAAAAAADB31FZAAV2kFZZF5vFAAAA,sGAAAAA", "AAAAAAAAAAAAAAAAAAAAheFAAFi3VSAAZ5kFAASxwbFZAAAA", "AAAAAAAASAAAAAAAAAASFFAAAASDAAAAADAAAAAAFDAAAAAA", "AASAAAAAAAAAAAAAAAAAZAAAAAAAAAAAAAAAAAAAAAAAAAAA", "AAAAAAAAAAAAAAAAAZZAAZZAZAZAZAZAZAZZAZAZZZAZAAAA", "AAAAAAAAAAAAAAAAAFGFGGGGGGGGGGGGFFGFGFFGFFFFAAAA", "AAAASAAAAAAAAAAASgyiuuuuuuuuuuppaaaaaaaaaspkSAAA", "AAAAAAAAAADDAAAZF3:2<<<,><<<<3*.OXoX++XXOO >HAAA", "AAAAAAAAAAMmAAAAD67rweeqqewrwy- XX >HZAA", "AAAAAAAAAD>ySAAAD66wqqq86qqqqw= XO ;HAAA", "AAAAAAAAAAm1zFAAF66wqqq86qqqqr- XX . ;HZAA", "AAASAAAAADzsVSAAD64qqqq76qqqqe- XX ;JZAA", "AAAAAAAAAAFFAAAAF68rrerqqrerwu- . Xo . >JAAA", "AAAAAAAAAAZAAAAAF6%&%%%&&%%&%&%OOOOO++OOOO.,GZAA", "AAAAAASAAAFDAAAZD3 .o O XO . >JZAA", "AAAAAAAAAD8fDAAZF3 XO . O XX ;JAAA", "AAAAAAAAADy7bFAAF6 .o O XX . >HZAA", "AAAAAAAAAAn6bDAZF4 .O . O. XO . ;JAAA", "AAAAAAAAAANASAAAF3 XX O XX ;GZAA", "AAAAAAAAAASAAAAZF6XOXOoO#+++++#oXOOXO#+@++O1GAAA", "AAASFAAAAAFAAAAZF3.X.XX.nLKKKLr XXXXXNLKKKLcZAAA", "AASVbAAAASbBDDAAF4 NLLLLLr CLLLLLnZAAA", "AASV,VDAAFrtFAAZF4 NLLLLLr . CLLLLLnZAAA", "AAAS,mDAAFi0FZAZF4 NLLLLLr CLLLLLnZSAA", "AZDBibDAADjfDAAAF4 NLLLLLr . CLLLLLnVAAA", "AAADFFAAAAFFAAAAF3 NLLLLLy . . CLLLLLnAAAA", "AAAAZAAAAAAZAAAAF6=::::-8uryrp>XOOOO+====-*7FAAA", "AAAAAAAAAADFAAAAD68reerq4q8q8q= .X >HZAA", "AAAAAAAAADB7AAAAS64qq8q76wqqqr- . Xo . . >HZAA", "AADAAAAAZDy=BDAAF66w9ww86wqq8r= XO ;HZAA", "AAAAAAAAAAx4MDZAD66wqqq76w9w8e- XX . >JAAA", "AAAAAAAAAAFFAAAAF46wwqw86w99qr- XX ;HZAA", "AAAAAAAAAAZZAAAAD86888887q888w>+#@@@$$@@@#+3GZAA", "AAAAAAAAAAAAAAAAAZVCVVVVCVVVBBVAAAAAAAAAAAAAAAAA", "AAAAAAAADAAAAAAAASASASASASSADDDDASAAASAAAAAAAAAA", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASA" }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/pearl-icon.c0000644000175300017530000002236412161170377015751 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 169 2", " c #000000", ". c #030303", "X c #060606", "o c #090909", "O c #131312", "+ c #151514", "@ c #151515", "# c #161515", "$ c #161616", "% c #171716", "& c #171717", "* c #181717", "= c #1A1A1A", "- c #1B1A1A", "; c #1D1C1C", ": c #20201F", "> c #242423", ", c #252423", "< c #252524", "1 c #262625", "2 c #272625", "3 c #272626", "4 c #3B3A39", "5 c #3C3B3A", "6 c #3F3F3D", "7 c #424140", "8 c #434341", "9 c #464644", "0 c #484746", "q c #504F4E", "w c #5C5C5A", "e c #5E5D5B", "r c #5C5C5C", "t c #61605E", "y c #60605F", "u c #636260", "i c #616162", "p c #616262", "a c #626262", "s c #636363", "d c #646464", "f c #666666", "g c #676766", "h c #676767", "j c #686767", "k c #6A6966", "l c #686868", "z c #696969", "x c #6B6A68", "c c #6A6A6A", "v c #6B6B6B", "b c #6C6B68", "n c #6C6C6B", "m c #706F6F", "M c #70706F", "N c #73726F", "B c #727171", "V c #727271", "C c #737372", "Z c #737373", "A c #747473", "S c #767573", "D c #777573", "F c #757574", "G c #767575", "H c #787877", "J c #7B7A77", "K c #7C7C7C", "L c #848380", "P c #868582", "I c #8C8B87", "U c #908E8B", "Y c #908F8B", "T c #91908C", "R c #93928E", "E c #94928F", "W c #9C9B97", "Q c #9C9B98", "! c #9E9C99", "~ c #A09E9A", "^ c #A2A19D", "/ c #A6A5A1", "( c #A8A6A3", ") c #ACAAA6", "_ c #ACABA7", "` c #B0AFAA", "' c #B1AFAB", "] c #B0AFAC", "[ c #B1AFAC", "{ c #B2B0AC", "} c #B2B1AD", "| c #B4B3AE", " . c #B6B6B6", ".. c #B8B6B2", "X. c #B8B7B3", "o. c #B9B7B4", "O. c #B9B8B4", "+. c #BAB8B4", "@. c #BBB9B5", "#. c #B8B8B7", "$. c #BBB9B6", "%. c #BBBAB6", "&. c #BCBBB7", "*. c #BDBBB7", "=. c #BDBCB8", "-. c #BEBCB8", ";. c #BEBDB9", ":. c #BEBDBB", ">. c #BFBEBA", ",. c #C0BEBA", "<. c #C0BFBA", "1. c #C1BFBA", "2. c #C1BFBB", "3. c #C1C0BC", "4. c #C3C2BE", "5. c #C4C2BE", "6. c #C4C3BE", "7. c #C5C3BE", "8. c #C5C3BF", "9. c #C6C4BF", "0. c #C6C5C0", "q. c #C7C5C0", "w. c #C7C6C1", "e. c #C8C6C2", "r. c #C9C7C3", "t. c #C9C8C3", "y. c #CAC8C4", "u. c #CBC9C4", "i. c #CBC9C5", "p. c #C9C9C7", "a. c #CCCAC6", "s. c #CDCBC6", "d. c #CDCBC7", "f. c #CECCC7", "g. c #CECDC8", "h. c #CFCDC8", "j. c #D0CEC9", "k. c #D1CFCA", "l. c #D1D0CB", "z. c #D2D0CB", "x. c #D2D0CC", "c. c #D3D1CC", "v. c #D3D1CD", "b. c #D4D2CE", "n. c #D5D3CF", "m. c #D6D4CF", "M. c #D7D5D0", "N. c #D8D6D0", "B. c #D8D6D1", "V. c #D9D7D2", "C. c #DBD9D3", "Z. c #DBD9D4", "A. c #DCDBD6", "S. c #DEDCD7", "D. c #DFDDD8", "F. c #E0DDD8", "G. c #E0DED9", "H. c #E1DFDA", "J. c #E2E0DA", "K. c #E3E1DB", "L. c #E3E1DC", "P. c #E4E2DD", "I. c #E5E3DE", "U. c #E7E4DF", "Y. c #E1E1E0", "T. c #E8E6E1", "R. c #EDEDEE", "E. c #F7F5EF", "W. c #FFFFFF", /* pixels */ "f m G H G C G C C C M C C G S j ", "v B.B.9.B.2.f.C.L.D.v.P.G.v.4.v ", "y b 5 o _ d.B.~ v k u b S u.d.v ", "r 8 - W f.S.q + 9 5 6 X | h.f ", "v U.E , L.>.P.0 M E.v.K.; ' k.l ", "f 4.L , h.} c.7 e k.} =.+ ^ 2.l ", "f m.Y 1 G.4.A.w D F.4.l.+ ` k.v ", "h m.Y 1 G.w.&.R.L.2.i.h.- ' k.f ", "j B.T 2 G.r.i. .#.h.r.l.* { l.v ", "j l.I , B.o.A.8 u D.>.i.- ) i.f ", "f i.P 2 B.&.A.9 y v.X.5.+ / 4.j ", "f M.E : 5.( w.4 k U.w.i.K @.f.v ", "v h.! X + O & . J D.:.:.W.p.>.n ", "v d.C.d.h.>.i.h.M.M.d.h.d @.l.v ", "z ] >.=.:.} &.:.&.o.] @.+ Q o.j ", "f s u u p u s u s u s u h d u f " }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 64 1", " c #000000", ". c #0D0D0C", "X c #141413", "o c #181817", "O c #1D1D1C", "+ c #252423", "@ c #292827", "# c #2C2C2B", "$ c #3C3B3A", "% c #52514F", "& c #555554", "* c #5A5957", "= c #5E5E5E", "- c #605F5D", "; c #62615F", ": c #656565", "> c #686764", ", c #686867", "< c #686868", "1 c #6F7070", "2 c #767575", "3 c #797875", "4 c #7C7B7A", "5 c #858482", "6 c #898885", "7 c #8E8D8A", "8 c #908F8C", "9 c #92918E", "0 c #969592", "q c #999794", "w c #9C9A97", "e c #9E9D99", "r c #A09F9B", "t c #A3A29E", "y c #A6A5A3", "u c #A8A7A3", "i c #ABA9A5", "p c #AEADA9", "a c #B0AEAB", "s c #B3B2AE", "d c #B5B4B1", "f c #B8B7B3", "g c #BBBAB5", "h c #BFBDBA", "j c #C1BFBB", "k c #C3C2BE", "l c #C6C4C1", "z c #C8C6C2", "x c #CCCAC6", "c c #CFCDC8", "v c #D0CECA", "b c #D5D3CE", "n c #D7D5D0", "m c #D8D6D1", "M c #DCDAD5", "N c #DFDDD8", "B c #E0DED9", "V c #E4E2DD", "C c #E6E5E2", "Z c #E9E7E1", "A c #EBE9E4", "S c #EAEBEB", "D c #F6F6F6", "F c #FEFEFE", /* pixels */ ":::=;;;;:;-:=;:;;;;;:;;;;;;;=;::", ",:4444444442444444442344444444<:", ":,zMnnNnbMbfMnnmnnnNfbMnnnnnNz::", ",:lnnngnMbcsnbbbnbnnscbbnnnnnl<:", "::kNg# #jMxsmbbbnnbmdcnbnnnnnl::", "<&O@X %VxsnNiOO+++O++++otNnl:<", "<& $BcdbBy wNnl:<", ",:0p4 0BxsbNu oeyt6wttO eNnl::", ":,cBA& *VbxsnMu OMBVjmBB@ eNbk:,", ":,lnV: >AmbfMVi ObnmdvmM@ tVMz,:", ",:djc& *vhgukzq Ohjkugkk+ 7xjd:,", "::dhc& *chgyjz0 Ogjkygjk+ 7zhd,:", ",:zmZ: >ZmbfMVa OMmNfbMM@ rVmz:,", ":,lvV= ;VvcsbM9&*dMbscbb@ eNnl,:", ",:lbV- ;VbcsNahFFyzMdcbm@ eNnl:,", ",:lnV= :VbcsBeSFFzaBscnb@ eNbl,:", ",:zbV- ;VbcdNplFFpkMdcbm@ eNbl::", ":,kbV; ;Vbcdmm72AMngMVp Obbndxmv@ 8Bbk,:", ",:lnV- &hppqsf7 OvbndcmeMDyhMl,,", ":,lbV* obnnsnfhFFSrNk::", ",:lnB, oXXXXXXX.#vnnsnhfFFCyNl::", ",,lmbvvxxclpcxxxcnbndcMtdb0zMl:<", "::jnvbnmnncdmbnnnvbnsxnn+ wNbl::", ":,xBMMMMNNngNMMMMMMBgnMN@ yCMc::", ":,eiiuyuyyt0iiyyyyyi0yyi# 4aye<:", ",:;========:========;===:,===;::", ":<,<,,,,<<::<<<<<<<<:<<<,:<<<<:<" }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 72 1", " c #000000", ". c #080707", "X c #090909", "o c #151514", "O c #181717", "+ c #181817", "@ c #1A1919", "# c #212120", "$ c #282726", "% c #2E2E2E", "& c #363634", "* c #393937", "= c #3C3B39", "- c #464543", "; c #4D4C4C", ": c #50504E", "> c #565554", ", c #595856", "< c #5E5E5E", "1 c #605F5D", "2 c #62615E", "3 c #656566", "4 c #686764", "5 c #6A6967", "6 c #696969", "7 c #706F6C", "8 c #70706F", "9 c #757474", "0 c #7A7976", "q c #7B7A78", "w c #858482", "e c #898784", "r c #8A8986", "t c #8D8C8A", "y c #908F8C", "u c #92918E", "i c #969695", "p c #989793", "a c #9A9997", "s c #9D9C9B", "d c #A09E9B", "f c #A2A19E", "g c #A5A4A1", "h c #A8A7A4", "j c #AAA9A6", "k c #AFADAB", "l c #B0AFAC", "z c #B2B1AD", "x c #B6B5B2", "c c #B9B7B3", "v c #BCBAB6", "b c #BDBCB8", "n c #C2C1BD", "m c #C3C3C4", "M c #C9C7C2", "N c #CCCAC5", "B c #CDCCCA", "V c #D1CFCA", "C c #D4D2CD", "Z c #D7D5D1", "A c #D9D7D1", "S c #DCDAD4", "D c #DFDDD8", "F c #E1DED9", "G c #E5E2DD", "H c #E7E5E0", "J c #E9E6E1", "K c #ECEAE4", "L c #F6F3EE", "P c #F2F2F2", "I c #F8F6F0", "U c #FEFEFE", /* pixels */ "335333333333333333633333363333333333333333333333", "333363363333363333633333636666633336363336363363", "33333<33332333<3333323333<3<<33<323333<33<333333", "363389999999999996999999999999899999999999996633", "336%%>FCCSlbSCSc +VZCCA9363", "3363222;22<4% 4GVCFzbAStVUUUxsSCC9363", "336 c #4544EF", ", c #4A49EE", "< c #5554EB", "1 c #5F5EE9", "2 c #4342F0", "3 c #6867E7", "4 c #6B6AE6", "5 c #6160E8", "6 c #6564E8", "7 c #7574E4", "8 c #807FE2", "9 c #9D9C9F", "0 c #9E9E9E", "q c #A6A5A3", "w c #AAA9A9", "e c #ADABAB", "r c #B1B0AD", "t c #C0BEB7", "y c #C0BEBA", "u c #C2C0BD", "i c #CBC9BD", "p c #CAC8BF", "a c #CECDBE", "s c #8B89DF", "d c #9897DC", "f c #9F9EDC", "g c #ACAACE", "h c #B4B2CE", "j c #BCBBCB", "k c #A5A3D0", "l c #A9A7D0", "z c #A7A5DA", "x c #A7A5DF", "c c #A9A7DB", "v c #B0AEDF", "b c #B3B1D6", "n c #B8B6D4", "m c #BEBCD2", "M c #BEBCD5", "N c #B2B0D9", "B c #B5B3DC", "V c #8280E1", "C c #8482E1", "Z c #8685E0", "A c #8886E1", "S c #8F8DE0", "D c #9291E4", "F c #A9A8E0", "G c #C1BFD3", "H c #C9C8C0", "J c #CECCC3", "K c #CAC8C4", "L c #CBCAC5", "P c #C4C2CA", "I c #CECCC8", "U c #CFCDC8", "Y c #CECCC9", "T c #CFCDC9", "R c #CFCDCA", "E c #CFCDCB", "W c #D0CECA", "Q c #D1CFCA", "! c #D0CECB", "~ c #D1CFCB", "^ c #D1CFCD", "/ c #D2D1C0", "( c #D3D1C7", ") c #D5D3C6", "_ c #D7D5C5", "` c #D9D7C7", "' c #DBD9C3", "] c #DBD9C6", "[ c #D1D0C8", "{ c #D3D1C9", "} c #D2D0CB", "| c #D4D2CA", " . c #D6D4C9", ".. c #D7D5C9", "X. c #D7D5CB", "o. c #D3D1CD", "O. c #D2D0CE", "+. c #D3D1CE", "@. c #D2D0CF", "#. c #D4D2CD", "$. c #D5D3CD", "%. c #D4D2CE", "&. c #D5D3CE", "*. c #D4D2CF", "=. c #D5D3CF", "-. c #D7D5CD", ";. c #D6D4CE", ":. c #D6D4CF", ">. c #D7D4CF", ",. c #D9D7C8", "<. c #D8D7CA", "1. c #D8D6CD", "2. c #D9D7CD", "3. c #D9D7CE", "4. c #D9D7CF", "5. c #DAD8C8", "6. c #DAD8CB", "7. c #DFDDCB", "8. c #DAD8CC", "9. c #DCDACC", "0. c #DEDCCC", "q. c #DFDDCC", "w. c #C2C1D2", "e. c #C3C1D3", "r. c #C5C3D2", "t. c #C7C5D2", "y. c #C8C7D1", "u. c #C9C7D3", "i. c #CAC8D1", "p. c #CCCAD1", "a. c #CDCBD2", "s. c #CECCD0", "d. c #CECDD0", "f. c #CAC8D5", "g. c #CBC9D5", "h. c #CCCAD7", "j. c #CFCDD6", "k. c #D4D2D0", "l. c #D7D5D0", "z. c #D6D4D1", "x. c #D7D5D1", "c. c #D8D6D0", "v. c #D8D6D2", "b. c #D9D7D3", "n. c #D9D7D4", "m. c #DAD8D3", "M. c #DBD9D3", "N. c #DEDCD2", "B. c #DBD9D4", "V. c #DAD8D5", "C. c #E0DECB", "Z. c #E2DFD2", "A. c #E2E0CB", "S. c #E3E0CB", "D. c #E3E1CB", "F. c #E5E2CA", "G. c #E5E3CA", "H. c #E4E2CB", "J. c #E7E5CA", "K. c #E3E1D1", "L. c #E4E2D1", "P. c #E5E3D1", "I. c #E9E7D1", /* pixels */ ":.m.m.m.c.K.K.c.k.:.&.&.&.&.&.&.", "&.B.B.B.Z.B v 9.W l.&.&.&.&.&.&.", "l.L q i M + . k ,.+.&.&.&.&.&.&.", ":.U r J u.@ $ h X.&.&.&.&.&.&.&.", "&.:.B.l.1.a.g.) W :.&.&.&.&.&.&.", ":.} y W l.H p Y W &.&.&.&.&.&.&.", "c.K 0 y B.e w U W +.&.&.^ ^ &.&.", "&.1.' X.k.,.] W 1.I.N.m.K.K.l.&.", "1.a.S w.C.z f 0.j.D h.K.x F 6.} ", "H.6 . , r.O . n 5 . < m . . g ,.", "D.s . 3 +.= # u.8 . 7 a.& * j X.", "^ 7.i.0.7.:.^ 7.1.g.1.C.+.&._ U ", "0.d % 8 1.> - t.8.r } p.; ; P | ", "H.1 . > w.. . c _ 0 a N . . l 6.", "8.w.4 b C.s V 1.:.I &.1.Z A ( } ", "&.1.J.9.+.D.H.&.:.:.&.&.D.D.&.&." }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 83 1", " c #0202FF", ". c #0A0AFD", "X c #1313FB", "o c #1C1CF9", "O c #3E3DEE", "+ c #2424F7", "@ c #302FF4", "# c #3535F2", "$ c #3D3CF1", "% c #4241EE", "& c #4B4AEE", "* c #504FED", "= c #5E5DE6", "- c #5151E9", "; c #5857EB", ": c #5A59EA", "> c #4140F0", ", c #605FE9", "< c #6867E7", "1 c #6464E8", "2 c #6E6DE9", "3 c #706FE5", "4 c #7270E5", "5 c #7C7BE3", "6 c #807FE4", "7 c #A6A5A1", "8 c #A8A7A3", "9 c #AAA9A5", "0 c #AEADA9", "q c #B0AFAB", "w c #B3B2AE", "e c #B6B5B1", "r c #B8B7B2", "t c #BBBAB5", "y c #BDBCB8", "u c #C1BFBB", "i c #C4C2BE", "p c #CDCBBD", "a c #D3D2BC", "s c #8C8BDF", "d c #908FDE", "f c #9492DD", "g c #9D9BDB", "h c #A09FD7", "j c #A09EDA", "k c #A6A4D6", "l c #ACAAD6", "z c #A3A2DA", "x c #A9A7D8", "c c #B0AED7", "v c #B4B3D6", "b c #B8B7D5", "n c #BDBBD3", "m c #B7B5D8", "M c #8382E1", "N c #C6C5C0", "B c #C8C7C2", "V c #CCCAC5", "C c #CECDC9", "Z c #D1CFCB", "A c #D5D3CE", "S c #D8D6CD", "D c #DDDBCC", "F c #C3C1D2", "G c #C8C6D1", "H c #CCCAD0", "J c #D6D4D0", "K c #D8D6D1", "L c #DCDAD6", "P c #DEDCD9", "I c #E0DECB", "U c #E0DED9", "Y c #E3E1CB", "T c #E9E7C9", "R c #EBE9C9", "E c #F1EFC8", "W c #F3F1C7", "Q c #E7E5D7", "! c #E9E7D7", "~ c #ECEAD6", "^ c #E3E1DB", "/ c #E9E7D8", "( c #F1EFD7", /* pixels */ "AAAAAAZAAAAAAZAAAAAAAAASKAAAAAAK", "AALLLLLLLLLKKLPKAAAAAAAAAAAAAAAA", "AK^U^^U^UU^((!^CCAAAAAAAAAAAAAAA", "AAAAZZAZASn=-lDiZAAAAAAAAAAAAAAA", "AAACw0iSSH+ .bVCAAAAAAAAAAAASAA", "AAKt780AIf .. 2aCAAAAAAASAAAAAAK", "AAKu77wADx MaCAAAASKAKAAAAAAA", "AAAAitAAAD1 %JiCAAAAAAAAAAAAAAA", "AAAAKKAAAAIvlSKNCAAAAAAAAAAAASKA", "AAASAAAAAAADDAKiCKAAAAAAAAAAAAAA", "AAAKAZKAAAJZCKKiCAADAAAAAAAAAAAA", "AAACq9iJAAC00NKNAAAAAAAAAAAAAAAA", "AAKt780AAKr790KNCASAAAAAAAAAAAAK", "AAKi77eAAKu77rKiCAAAAAAAAAAAAAAA", "AAAAiyAJAAAiuZKiVAAAAAAAAAAAAAAA", "AAAAJKAAAAAJJAJVLPLLL^LLLLPLLLAA", "AAADYIIAAZDDDDJKP!~~!LUP!~~!UVZA", "AZDv&$fDJDv&$gDZSl%OzSCDk%OkDuZK", "AKH+ gIFo .xYnX .cIv. .npZA", "ADg .. :Ef . 1Ws .. 4W5 .. 6aCA", "ZSv. 5Rl sRg sRg gpZA", "JADM. *AAD4. ;AZD3. =DAD1. c #A6A5A1", ", c #AAA9A5", "< c #AEADA9", "1 c #B0AFAB", "2 c #B3B2AE", "3 c #B6B5B0", "4 c #BDBBB7", "5 c #BEBDBA", "6 c #C1BFBB", "7 c #C3C1BB", "8 c #8887DF", "9 c #8C8ADF", "0 c #908FDE", "q c #9493DD", "w c #9997DC", "e c #9D9BDB", "r c #A09FDA", "t c #BEBCCF", "y c #ADABD7", "u c #A4A2D9", "i c #A8A7D8", "p c #ABA9D8", "a c #B1AFD6", "s c #B2B0D6", "d c #BBB9D4", "f c #8483E1", "g c #8987E0", "h c #8A89E0", "j c #C1BFD3", "k c #C8C6C1", "l c #CCCAC5", "z c #C8C6CD", "x c #CFCDCB", "c c #D0CECD", "v c #D5D3CE", "b c #D8D6CD", "n c #DDDBCC", "m c #C3C2D3", "M c #C9C7D1", "N c #CDCBD1", "B c #D7D5D0", "V c #D8D6D1", "C c #DCDAD5", "Z c #DFDDD8", "A c #E1DECB", "S c #E1DFDA", "D c #E3E1CD", "F c #E6E4E2", "G c #E9E7E1", "H c #EBE9E3", /* pixels */ "vvvvvBvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvbBvvvvvvvvvv", "vvvvvbvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv", "vvvvcvvvvcvvvvxvcvvvvvvvvvvvvvvvvvvvvvvvvvvvBvvv", "vvvvCCCCCCCCCCCACCCCCCCCvvvvvvvvvvvvvvvvvvVvbvvv", "vvvbHGHGGGGGHHHHGHGHGGHvlVvvvvvvvvvBvvvvvvvvvvvv", "vvvvcvcvvvvcccccnvzvnxv5lVbvvvvbvvvvvvvvvvvvvBvv", "vvvvvvvvcvVvvvBne#X+9nv7lvvvvvvvvvvvbBvvvvvbvvvv", "vvvvvbx2,1lVvvnu 9D5lvvvvvvvvvvvvvvvvvvvvvvv", "vvvvvb3>,>1vvcn$ OV7lvvvvvvvvvvvvvvvvBvvvvbv", "vvBbvv<,,,,lBvco .m7lBvvvvvvvBvvvvvvvbvvvvvv", "vvvvvv3>,>,>,>1vV6lBvvvvvvvvvvvvvvvvvvvvvv", "vvvvvv<,,,,lVvvv,,,,,lC6lvvvvvvvvvvvvbvvvvvvvvvv", "vvvvvB3>,>,>1vV6lvvvvvvvvvvvvvvvvvvvvvvv", "vvvvvBv2,2lvvvvBc2,2lvV7lvvvvvBbvvvvvbvvBvvvvvvv", "vvvbvvvvvvbvvvvvvvvvVvV6lvxvvvvvvcvvvxvxvvvvvvvv", "vvvBvvvvvvvvvvvvvvvvvvV7vSZZCZZZZZZZSAZSZZZVvvvv", "vvvvvvBvbBBvvvvvBvnvvvvBGGSGHGGGGGGGFGHGFGHllVvv", "vvvvvvnvjxnvvvvvnvdxnvvvvxnvjxnxcxvcnltcncv5lVvB", "vvvvvnqO.o-nvBvn0@.o;nvvvn8@.o8nBBBn8o.@8nV6lVvv", "vvvBne ;Dxnw fDNnq 8DxA8 9D5lVvv", "vvvcn$ oNvn$ @cvb+ +vvv+ +n5lVvv", "vvvvb@ dnNo .jnxo .mnMX XN7lVvv", "vvvvA& . Ovvn= +vvn$ #vvb# $n5lVvv", "vvvvny. hAxni. qAcnr wAcAe eD5lVvv", "vBvvcni%o#qnvvvni$o#wncvvnr$o#envvcne$o#unB5lVvv", "vvvvvvnnNnnvvvvvnncnAcBvvvnncnnvvBvvnnNnnvV6lVvv", "vvvvvvxvnBcvvvvvNvnvcBBvvvBcvvvvvvvvxvnvcvB5lBvv", "vvvvvBnvdMAvvvvBAcdNABcvvvvBBBvvvvvvDNdcAvB5lVvv", "vvvvcn9@ X-nvBvngo o-nvvvvVvlvvvvvvn;o ofnv5lVvv", "vvvBne -Axnq ;ABvBl<>,>1vvvvO +C5lVvv", "vvBvv@ dnco .jnBx,,,,,cBvMX XN7lVvv", "vvvvD* Ovvn= +vvvv3>,>2vvvn# $C5lVvv", "vvvvns. 9Dcny. eAcvvc3<2cVvBnr uA5lbvv", "vvvvvna&@$wnvvvny=@$envvvvbbvvvvvvvni$@%inB5lVvv", "vvvvvvnnbnAvvvvvnAvnAvvvvvvvvvvvvvvvnAvnncV6lVvv", "vvvvvvvBvvBvvvvvvBvcBvvvvvvvvvvvvvvvBvvvvvvvvvvb", "vvvvvvvvvvvvvvvvvvvvvvvvvvBbvvVvvvvvvvvvvvvvvvvv", "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvBvvvv", "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv" }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/range-icon.c0000644000175300017530000003665612161170400015736 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 138 2", " c #000000", ". c #010101", "X c #020202", "o c #030303", "O c #050504", "+ c #050505", "@ c #060606", "# c #070606", "$ c #070706", "% c #070707", "& c #080808", "* c #090808", "= c #090908", "- c #090909", "; c #0A0909", ": c #0A0A09", "> c #161615", ", c #161616", "< c #1C1C1B", "1 c #1D1D1C", "2 c #1E1D1D", "3 c #1E1E1D", "4 c #1F1F1E", "5 c #201F1F", "6 c #20201F", "7 c #212120", "8 c #222121", "9 c #232222", "0 c #272726", "q c #282726", "w c #282827", "e c #3D3C3B", "r c #40403E", "t c #424240", "y c #464543", "u c #474644", "i c #545351", "p c #6A6966", "a c #6C6B68", "s c #6E6D6B", "d c #777674", "f c #858481", "g c #888784", "h c #8D8C88", "j c #8E8D89", "k c #918F8C", "l c #91908C", "z c #92908D", "x c #93928E", "c c #94928F", "v c #94938F", "b c #949390", "n c #959390", "m c #959490", "M c #969491", "N c #969591", "B c #979592", "V c #979692", "C c #989793", "Z c #999794", "A c #999894", "S c #9A9895", "D c #9A9995", "F c #9B9996", "G c #9E9D99", "H c #9F9D99", "J c #9F9D9A", "K c #9F9E9A", "L c #A09E9A", "P c #A09E9B", "I c #A19F9B", "U c #A1A09C", "Y c #A4A39F", "T c #A5A4A0", "R c #A7A5A2", "E c #AAA8A4", "W c #ADABA7", "Q c #B2B0AC", "! c #B3B2AD", "~ c #B7B5B1", "^ c #B7B6B1", "/ c #B8B6B2", "( c #B9B7B3", ") c #BBB9B4", "_ c #BDBCB7", "` c #BEBCB7", "' c #C0BEB9", "] c #C0BEBA", "[ c #C1BFBA", "{ c #C1BFBB", "} c #C2C0BB", "| c #C2C0BC", " . c #C3C1BD", ".. c #C3C2BD", "X. c #C5C3BF", "o. c #C6C4BF", "O. c #C6C4C0", "+. c #C7C5C0", "@. c #C8C6C1", "#. c #C8C6C2", "$. c #C9C7C2", "%. c #C9C7C3", "&. c #CBC9C4", "*. c #CBC9C5", "=. c #CCCAC5", "-. c #CCCAC6", ";. c #CDCBC6", ":. c #CECCC7", ">. c #CECCC8", ",. c #CFCDC8", "<. c #D0CEC9", "1. c #D1CFCA", "2. c #D2D0CB", "3. c #D3D1CC", "4. c #D4D2CD", "5. c #D5D3CE", "6. c #D6D3CE", "7. c #D6D4CF", "8. c #D7D5D0", "9. c #D8D6D1", "0. c #D9D7D1", "q. c #D9D7D2", "w. c #DAD8D3", "e. c #DBD9D4", "r. c #DCDAD5", "t. c #DDDBD6", "y. c #DEDCD6", "u. c #DEDCD7", "i. c #DFDDD7", "p. c #E0DED8", "a. c #E0DED9", "s. c #E1DED9", "d. c #E1DFD9", "f. c #E1DFDA", "g. c #E2E0DB", "h. c #E7E5DF", "j. c #E8E5E0", "k. c #EAE8E2", /* pixels */ "O 3 3 6 3 > 5 9 9 3 > 5 3 5 < . ", "3 %.1.:.1.k =.| =.| N 5.3.8.~ - ", "3 1.q.7.q.N e.Z d 5.N s./ 7.{ - ", "6 :.7.3.5.v r.n K 5.N s.K =.` - ", "3 1.q.5.7.N 5.{ 8.&.F r.r.y._ - ", "> z N v N p v F A h p F k F f O ", "3 =.q.q.5.n 1.2.7.o.C q.q.r.) - ", "3 5.{ W r.n 3.7.q.$.C s.Y :.{ - ", "3 2.O./ w.n 5.q.e.=.F g.! 5.{ - ", "6 3.r.y.w.v O.%.=._ l :.&.1.Q - ", "O q q w 9 r K N F l s K L K g O ", ". . . . . y j.e.g.:.L p.L q.O.- ", ". . . O . r h.Y ! =.C 5.i T .% ", ". . . o . y k.=.5.1.K s.j E =.% ", ". . . . . e %._ { Q g ./ %.R - ", ". . . . . o - % - - % - - - O . " }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 204 2", " c #000000", ". c #010101", "X c #020202", "o c #030202", "O c #030302", "+ c #030303", "@ c #070706", "# c #0A0A09", "$ c #0F0F0F", "% c #141413", "& c #181817", "* c #1B1B1A", "= c #1F1F1E", "- c #252524", "; c #282726", ": c #282827", "> c #292928", ", c #2A2928", "< c #2B2A29", "1 c #2C2B2A", "2 c #2D2C2B", "3 c #2D2D2B", "4 c #2D2D2C", "5 c #2E2E2D", "6 c #2F2E2D", "7 c #2F2F2D", "8 c #302F2E", "9 c #30302F", "0 c #31302F", "q c #31312F", "w c #323230", "e c #363534", "r c #383736", "t c #393837", "y c #41403F", "u c #474645", "i c #474745", "p c #484745", "a c #4A4A48", "s c #4B4A48", "d c #4B4B49", "f c #4D4C4A", "g c #535250", "h c #555452", "j c #565553", "k c #575755", "l c #585755", "z c #5D5C5A", "x c #5D5D5A", "c c #5E5D5B", "v c #60605D", "b c #61605E", "n c #62615E", "m c #62615F", "M c #656461", "N c #656462", "B c #666562", "V c #6B6A67", "C c #6B6A68", "Z c #6D6C69", "A c #6F6E6B", "S c #73726F", "D c #777673", "F c #7A7976", "G c #7B7A77", "H c #7D7C79", "J c #7E7C79", "K c #7E7D7A", "L c #7F7D7A", "P c #81807D", "I c #82817E", "U c #83827F", "Y c #858480", "T c #868481", "R c #878582", "E c #878683", "W c #888683", "Q c #888784", "! c #8A8986", "~ c #8C8A87", "^ c #8C8B87", "/ c #8F8E8B", "( c #908E8B", ") c #908F8C", "_ c #91908C", "` c #91908D", "' c #92908D", "] c #92918D", "[ c #93918E", "{ c #93928E", "} c #969591", "| c #979692", " . c #989693", ".. c #999794", "X. c #9D9C98", "o. c #9E9D99", "O. c #A4A29F", "+. c #AAA8A4", "@. c #ADABA7", "#. c #ADACA7", "$. c #AFAEA9", "%. c #B0AEAA", "&. c #B1AFAB", "*. c #B4B2AE", "=. c #B7B6B1", "-. c #B8B6B2", ";. c #B9B7B3", ":. c #B9B8B3", ">. c #BAB8B4", ",. c #BAB9B4", "<. c #BBB9B4", "1. c #BBB9B5", "2. c #BFBDB8", "3. c #BFBDB9", "4. c #BFBEB9", "5. c #C0BEB9", "6. c #C0BEBA", "7. c #C0BFBA", "8. c #C1BFBA", "9. c #C1BFBB", "0. c #C1C0BB", "q. c #C2C0BB", "w. c #C2C0BC", "e. c #C4C2BE", "r. c #C5C4BF", "t. c #C6C4BF", "y. c #C7C5C1", "u. c #C7C6C1", "i. c #C8C6C1", "p. c #C9C7C2", "a. c #C9C7C3", "s. c #CAC8C3", "d. c #CDCBC6", "f. c #CDCBC7", "g. c #CDCCC7", "h. c #CECCC8", "j. c #CFCDC8", "k. c #CFCDC9", "l. c #D0CEC9", "z. c #D0CECA", "x. c #D1CFCA", "c. c #D1CFCB", "v. c #D2D0CB", "b. c #D2D0CC", "n. c #D3D1CC", "m. c #D3D1CD", "M. c #D4D2CD", "N. c #D5D3CE", "B. c #D6D4CF", "V. c #D7D5D0", "C. c #D8D5D0", "Z. c #D8D6D0", "A. c #D8D6D1", "S. c #D9D6D1", "D. c #D9D7D2", "F. c #DAD8D2", "G. c #DAD8D3", "H. c #DBD8D3", "J. c #DBD9D3", "K. c #DBD9D4", "L. c #DCD9D4", "P. c #DCDAD4", "I. c #DCDAD5", "U. c #DDDAD5", "Y. c #DDDBD5", "T. c #DDDBD6", "R. c #DEDCD6", "E. c #DEDCD7", "W. c #DFDCD7", "Q. c #DFDDD8", "!. c #E0DED8", "~. c #E0DED9", "^. c #E1DED9", "/. c #E1DFD9", "(. c #E1DFDA", "). c #E2DFDA", "_. c #E2E0DA", "`. c #E2E0DB", "'. c #E3E0DB", "]. c #E3E1DB", "[. c #E3E1DC", "{. c #E4E1DC", "}. c #E4E2DC", "|. c #E4E2DD", " X c #E5E3DD", ".X c #E5E3DE", "XX c #E6E3DE", "oX c #E6E4DE", "OX c #E7E5DF", "+X c #E7E5E0", "@X c #E9E6E1", "#X c #E9E7E1", "$X c #E9E7E2", "%X c #EAE8E3", "&X c #EBE8E3", "*X c #EBE9E4", "=X c #ECEAE4", "-X c #ECEAE5", ";X c #EEECE6", ":X c #F0EEE8", ">X c #F2F0EA", ",X c #F3F0EB", " 4 4 4 4 4 2 4 ; ", " 4 oXY.Y.Y.E.E.Y.-X..Y ;XH.E.!.Y.Y.H.oXM u.`.Y.Y.Y.Y.K.`.e.+ ", " 4 Y.v.M.M.M.M.z.).) K Xz.z.g.B.B.z.Y.c 7.V.v.M.z.M.M.H.1.+ ", " 4 T.M.N.N.N.N.z. X] K [.K.Y k w } E.H.c 0.H.M.S.'.S.z.H.1. ", " 4 T.M.N.N.N.N.M.[.] L Xz.V.i.- *.H.Y.c 0.S.Z.r.~ z.N.K.1.+ ", " 4 Y.M.N.N.N.N.N.).{ K Xh.+XH d oXz.Y.c 0.S.E.@.& y.C.Y.<.+ ", " 4 Y.M.N.N.N.N.c. X{ K XM.M.: O.).z.E.c 0.H.N.B.S.N.M.L.<.+ ", " 2 G.z.c.M.M.M.k.`.] H ).M.e./ N.N.z.L.z 7.S.z.v.M.v.v.S.:. ", " w -X).[. X`.[.).:XX.R v.Y.1. ", " + W ).z.N.B.N.M.z.Y.c 7.S.d.F n *.V.H.1.+ ", " + ~ @XS.Y.Y.K.Y.S. Xn i.).Y.%X=X`.H.`.7. ", " + D i.-.1.<.1.1.=.e.g @.2.-.-.-.:.:.7.O.+ ", " + + + + + + + + + + + + ", " " }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 142 2", " c #000000", ". c #010101", "X c #020202", "o c #040404", "O c #050505", "+ c #060606", "@ c #070707", "# c #080808", "$ c #090908", "% c #0C0C0C", "& c #0E0E0E", "* c #151514", "= c #161615", "- c #1A1A19", "; c #1C1C1B", ": c #1D1C1C", "> c #212120", ", c #222120", "< c #222221", "1 c #232221", "2 c #252423", "3 c #252524", "4 c #282726", "5 c #2C2C2B", "6 c #323130", "7 c #323231", "8 c #373735", "9 c #383736", "0 c #393837", "q c #393937", "w c #393938", "e c #3A3938", "r c #3B3A39", "t c #3E3E3C", "y c #4A4A48", "u c #4D4C4A", "i c #4E4D4B", "p c #4F4E4D", "a c #504F4D", "s c #545351", "d c #555452", "f c #555553", "g c #565654", "h c #575654", "j c #595856", "k c #5A5957", "l c #5B5A58", "z c #5C5B59", "x c #62615E", "c c #666563", "v c #6A6967", "b c #7E7C7A", "n c #807F7C", "m c #82817E", "M c #848380", "N c #868481", "B c #868582", "V c #888783", "C c #888784", "Z c #8A8986", "A c #8C8B87", "S c #8F8E8B", "D c #93918E", "F c #959390", "G c #979692", "H c #989793", "J c #999794", "K c #9A9996", "L c #9D9B98", "P c #A3A19D", "I c #A9A8A4", "U c #AAA9A5", "Y c #AEACA8", "T c #B3B1AD", "R c #B3B2AD", "E c #B4B2AE", "W c #B4B3AE", "Q c #B7B5B1", "! c #BAB8B4", "~ c #BAB9B4", "^ c #BBB9B4", "/ c #BCBAB6", "( c #C4C2BD", ") c #C4C2BE", "_ c #C5C3BF", "` c #CAC8C3", "' c #CBC9C5", "] c #CDCBC7", "[ c #CECCC7", "{ c #CECCC8", "} c #CFCDC9", "| c #CFCEC9", " . c #D0CEC9", ".. c #D0CECA", "X. c #D1CFCA", "o. c #D2D0CB", "O. c #D2D0CC", "+. c #D3D1CC", "@. c #D3D1CD", "#. c #D4D2CD", "$. c #D5D3CE", "%. c #D6D4CE", "&. c #D6D4CF", "*. c #D7D5D0", "=. c #D8D6D1", "-. c #D9D7D1", ";. c #D9D7D2", ":. c #DAD8D3", ">. c #DBD9D3", ",. c #DBD9D4", "<. c #DCDAD4", "1. c #DCDAD5", "2. c #DDDBD6", "3. c #DEDBD6", "4. c #DEDCD7", "5. c #DFDDD7", "6. c #DFDDD8", "7. c #E0DED9", "8. c #E1DED9", "9. c #E1DFDA", "0. c #E2DFDA", "q. c #E2E0DB", "w. c #E3E1DB", "e. c #E4E1DC", "r. c #E4E2DC", "t. c #E4E2DD", "y. c #E5E3DE", "u. c #E6E3DE", "i. c #E6E4DE", "p. c #E8E5E0", "a. c #E8E6E0", "s. c #E8E6E1", "d. c #E9E6E1", "f. c #E9E7E1", "g. c #E9E7E2", "h. c #ECEAE4", "j. c #F7F5EF", "k. c #F8F5EF", "l. c #FAF8F2", "z. c #FBF8F2", "x. c #FEFBF6", "c. c #FEFCF6", /* pixels */ " ", " O o o o o O o o o o o o o o o o o ", " ", " O < < < 2 < < < < < < < 2 = % 3 < < < < < < < < < < 3 % = 2 < < < < < < < < < < 2 O ", " o < 2.=.=.=.*.=.=.*.=.*.&.f.Z p h.+.=.=.=.=.=.=.=.=.#.h.p Z h.#.*.=.=.=.=.=.=.=.=.2.< o ", " < =.#.#.#.#.&.&.&.&.#.o.u.V i f.X.#.#.o.o.o.o.#.#.o.f.i B y.o.&.#.&.#.&.#.#.#.#.=.< ", " < =.#.&.#.&.#.&.#.&.#.o.u.V i f.X.#.*.q.q.q.q.*.#.X.p.i V y.o.&.#.#.&.&.&.#.&.#.=.< ", " o < =.&.#.&.#.&.#.&.#.&.o.u.C i f.} 2.^ 8 0 t 0 ^ ,.} f.i Z y.o.#.&.+.X.o.&.&.#.#.=.< o ", " o < =.&.#.&.#.&.#.&.#.&.o.u.C i f.X.*.} P R h O ( :.X.f.i B u.o.#.+.*.f.2.#.#.&.#.=.2 o ", " < =.#.&.#.&.#.&.#.&.#.o.u.C i f.X.#.#.6.u.3 k y.o.X.f.i C u.o.+.,.( p S 8.#.#.&.=.< ", " o < =.&.#.&.#.&.#.&.#.&.o.u.C i f.X.#.+.6.G R 2.o.X.f.i C u.o.+.2.Q l y.X.#.&.=.< ", " < =.#.&.#.&.#.&.#.&.#.o.u.C i f.+.#.+.t.w w 8.+.&.X.f.i C u.+.#.&.o.R ( *.#.&.#.=.< O ", " < =.#.#.&.#.&.#.&.#.&.o.u.C i f.X.+.6.I J 8.X.#.X.f.i C u.o.#.&.*.6.,.+.#.&.#.=.< ", " o < =.#.&.#.&.#.&.#.&.#.o.u.C i p.X.+.;.^ H #.#.*.*.X.f.i C u.o.&.#.#.+.+.+.&.#.&.=.< ", " o < *.o.o.o.o.o.o.o.o.o.} q.B u u.] X.X.*.6.X.+.X.X.] u.i N q.} o.o.+.o.o.o.o.o.+.&.2 o ", " 2 f.u.u.u.u.u.u.u.u.u.q.j.D s z.9.t.p.u.9.u.p.u.p.8.z.s D j.q.u.u.u.u.u.u.u.u.u.f.3 ", " = C V V C C C C C V V B D h 6 F N C C C C C C C C N F 6 h D B C C C C C C C C C Z = ", " % a i i a i i i i i i i d 6 : d u a u u u u a a u u d : 6 s i i i i i u u p u i p % ", " o 3 h.f.f.f.p.f.f.f.f.f.y.z.F s x.u.f.p.p.f.d.d.d.d.p.x.d F z.p.f.f.p.f.f.f.f.f.f.h.3 o ", " , #.X.X.X.X.X.X.X.X.X.[ 8.N i t.] X.X.X.X.X.X.X.X.] 0.u N 8.] X.o.X.X.X.X.X.X.X.+.< ", " 2 =.#.#.#.#.#.#.*.#.&.o.u.C u f.X.#.*.#.#.X.*.*.*.X.p.u C u.o.#.#.#.*.*.#.#.#.#.=.< ", " o < =.*.*.#.#.*.#.#.&.#.o.u.C p p.X.#.#.*.#.*.*.X.X.X.f.u C u.o.#.&.#.#.#.#.*.#.#.=.< o ", " o < =.#.#.#.#.=.,.*.#.&.o.u.C i f.X.*.*.#.#.*.X.#.*.X.d.p C u.o.#.#.*.2.*.#.#.#.#.=.< o ", " < =.#.*.X.u.v $ ! 2.o.o.u.C i f.X.#.#.#.*.*.*.*.*.X.d.u C u.+.#.,.^ $ v u.X.*.*.=.< ", " < =.*.#.+.w.v $ ^ 2.o.o.i.C u f.X.*.#.#.#.X.*.*.X.X.d.u C u.X.#.,.! $ v u.o.&.#.=.< ", " o < =.#.#.#.&.=.>.&.#.&.o.y.C a p.X.#.#.*.*.X.*.*.X.X.d.p C p.X.#.*.*.,.=.#.#.#.&.=.< o ", " o < =.#.*.#.&.#.&.#.&.#.o.y.C u p.X.#.*.#.#.*.*.*.*.X.f.u C u.X.#.*.#.*.#.#.&.#.&.=.< o ", " < =.#.*.#.#.&.#.&.#.&.o.y.C a f.X.#.#.*.#.*.X.X.*.X.f.p C p.X.#.#.#.#.#.*.#.&.#.=.< ", " o < #.} X.X.o.o.X.o.X.X.] 8.N u 0.] X.X.X.X.X.X.X.X.] p.u N 8.] X.X.X.X.X.X.o.X.X.+.< ", " 3 h.u.y.y.y.y.y.y.u.u.t.j.D d x.p.f.f.p.f.d.d.d.d.0.x.d F z.u.f.f.f.f.f.f.f.f.f.h.3 O ", " % z l z l l l l l l l l x e : d u p i u p p u u u u d : 6 s i i i i i i i i i i p % ", " o e D B C C C C C C C C N F 6 h D N C C C C C C C C B Z = ", " O O O O O O O O O $ x k.q.u.u.y.y.i.i.u.u.8.z.s D z.9.u.u.t.q.q.t.y.y.y.h.< ", " O k q.} o.o.o.o.o.o.o.o.] u.i N 8.} X.+.6.8.u.=.o.o.o.&., ", " O z y.X.#.&.#.o.#.&.#.&.X.p.i C u.+.*.X.L A b Q :.+.#.=.2 O ", " O l y.o.&.#.&.,.2.#.&.#.X.f.i C u.X.*.` # t a J 6.+.&.*.< ", " O l y.o.#.&.&.^ ^ #.#.&.X.f.i C u.X.*.' m 2.8.+.&.&.=.< ", " l y.o.&.#.:.- - :.&.#.X.f.i C u.X.*.` 3 7 @ b 2.+.#.=.< o ", " O l y.X.#.&.=.v v =.#.&.X.f.i C u.+.+.=.=.l.c = 2.#.#.=.< ", " O z y.X.&.#.#.u.u.#.&.#.X.f.i V u.} 6.P B I 5 w 6.+.#.=.< o ", " O l y.X.#.&.#.+.+.#.#.#.X.f.i C u.} ,.Y y 8 h ( :.+.&.*.< ", " O l y.o.&.#.&.#.#.#.*.*.X.f.i V u.+.#.2.t.q.u.=.+.&.#.=.< o ", " O l u.X.#.&.#.&.*.#.#.#.X.p.p V u.+.#.#.+.X.o.+.&.#.#.*.< ", " z f.#.=.=.=.=.=.=.=.=.+.h.a A p.&.=.=.=.*.=.=.*.=.=.2.< o ", " & 3 < < < < < < < < < < 3 % = 2 < < < 2 < < < < < < 2 O ", " ", " o o o o o O o o o O o ", " " }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/rect-icon.c0000644000175300017530000002212012161170400015554 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 155 2", " c #474745", ". c #494847", "X c #535250", "o c #535351", "O c #555452", "+ c #575654", "@ c #595856", "# c #5B5B58", "$ c #5E5D5B", "% c #5F5E5C", "& c #605F5D", "* c #61605E", "= c #62615F", "- c #636260", "; c #646361", ": c #646461", "> c #656462", ", c #656562", "< c #686865", "1 c #696866", "2 c #6A6966", "3 c #6A6967", "4 c #6B6A67", "5 c #6C6B68", "6 c #6C6B69", "7 c #6D6C69", "8 c #6E6D6A", "9 c #6F6E6C", "0 c #6F6F6C", "q c #706F6C", "w c #71706E", "e c #72716E", "r c #747370", "t c #747471", "y c #767572", "u c #777673", "i c #777674", "p c #777774", "a c #787774", "s c #797976", "d c #7A7976", "f c #7D7C79", "g c #7E7D7A", "h c #7F7E7B", "j c #7F7F7B", "k c #82817E", "l c #83827F", "z c #848380", "x c #858481", "c c #868582", "v c #878683", "b c #888784", "n c #8A8985", "m c #8A8986", "M c #8B8A87", "N c #8C8B88", "B c #8D8C89", "V c #8E8D89", "C c #8E8D8A", "Z c #8F8E8A", "A c #8F8E8B", "S c #918F8C", "D c #93928E", "F c #93928F", "G c #94938F", "H c #949490", "J c #959490", "K c #969592", "L c #979693", "P c #989793", "I c #999895", "U c #9A9995", "Y c #9B9996", "T c #9B9A96", "R c #9C9B97", "E c #9D9C98", "W c #9F9D9A", "Q c #9F9E9A", "! c #A09F9B", "~ c #A2A19D", "^ c #A3A29E", "/ c #A4A39F", "( c #A5A4A0", ") c #A6A5A1", "_ c #A7A6A2", "` c #A8A7A3", "' c #A9A8A4", "] c #AAA9A5", "[ c #ACABA7", "{ c #ADACA8", "} c #AFAEA9", "| c #AFAEAA", " . c #B1B0AC", ".. c #B5B4AF", "X. c #BAB9B4", "o. c #BBBAB5", "O. c #BBBAB6", "+. c #BCBBB6", "@. c #BEBCB8", "#. c #BEBDB8", "$. c #BFBDB9", "%. c #BFBEB9", "&. c #C0BEBA", "*. c #C0BFBA", "=. c #C2C0BC", "-. c #C2C1BC", ";. c #C3C1BC", ":. c #C3C1BD", ">. c #C3C2BD", ",. c #C4C2BD", "<. c #C4C2BE", "1. c #C5C3BE", "2. c #C6C4C0", "3. c #C6C5C0", "4. c #C7C5C0", "5. c #C8C6C2", "6. c #C9C7C3", "7. c #CBC9C4", "8. c #CBC9C5", "9. c #CBCAC5", "0. c #CCCAC5", "q. c #CDCBC6", "w. c #CDCBC7", "e. c #CECCC7", "r. c #CECDC8", "t. c #CFCDC8", "y. c #CFCEC9", "u. c #D0CECA", "i. c #D1CFCA", "p. c #D2D0CB", "a. c #D3D1CC", "s. c #D3D1CD", "d. c #D3D2CD", "f. c #D4D2CD", "g. c #D4D2CE", "h. c #D6D4CF", "j. c #D7D5D0", "k. c #D8D6D1", "l. c #D9D7D2", "z. c #DAD8D3", "x. c #DAD8D4", "c. c #DBD9D4", "v. c #DCDAD5", "b. c #DDDBD6", "n. c #DEDCD7", "m. c #DFDDD7", "M. c #DFDDD8", "N. c #E0DED9", "B. c #E1DFDA", "V. c #E5E3DE", "C. c #E6E4DF", "Z. c #E7E5E0", "A. c #E9E7E2", "S. c #EAE8E3", "D. c #F5F3ED", /* pixels */ "z.$.X.X.X.O.X.&.:.$.&.&.&.$.3.c.", "p.C C C x M K 1 @ r 8 3 9 t + 3.", "f.c.S.C.j.Z.D.~ z .._ _ K M p $.", "s.u.b.c.7.j.Z.T k ._ _ C S t $.", "s.1.t.q.$.7.n.S 5 * X , < . 1.", "s.t.c.l.q.t.7.T t G v $ E W < =.", "s.s.V.b.p.7.T W v s M < [ } w $.", "s.7.f.p.3.q.c.K s ~ K * ~ _ 8 $.", "f.3.s.u.=.u.m.G s [ G & ~ / 5 &.", "s.u.V.m.q.z.S.E j .T ; [ [ q &.", "s.p.M.c.p.z.S.Y f } P * _ [ q $.", "s.:.q.p.w < s @ o , O o / ` < &.", "s.p.m.c.w K v W ! _ P ; Y S w $.", "p.f.M.m.t F t [ / } ! < T f t $.", "f.6.p.f.5 f v k k b f @ v M , 1.", "c.f.s.f.7.:.:.:.:.1.1.1.1.*.5.b." }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 71 1", " c #0D0D0C", ". c #151514", "X c #1D1D1C", "o c #262525", "O c #282726", "+ c #282827", "@ c #2C2C2B", "# c #31302F", "$ c #323231", "% c #383736", "& c #3E3E3C", "* c #454543", "= c #4D4D4B", "- c #504F4D", "; c #51514F", ": c #545452", "> c #585755", ", c #585856", "< c #5E5D5B", "1 c #605F5D", "2 c #62615F", "3 c #656462", "4 c #696866", "5 c #6C6C69", "6 c #706F6C", "7 c #72716E", "8 c #757471", "9 c #7A7A76", "0 c #7D7D7A", "q c #807F7C", "w c #81807D", "e c #868582", "r c #8B8A87", "t c #8E8D89", "y c #908F8C", "u c #90908C", "i c #969592", "p c #989793", "a c #9B9A96", "s c #9E9D99", "d c #A09F9B", "f c #A3A29E", "g c #A6A5A1", "h c #A8A7A3", "j c #ABAAA6", "k c #AEADA9", "l c #B0AFAB", "z c #B3B1AD", "x c #B6B5B1", "c c #BBBAB5", "v c #BEBCB8", "b c #C0BEBA", "n c #C3C1BD", "m c #C6C5C0", "M c #C8C6C2", "N c #CCCAC5", "B c #CFCDC9", "V c #D1CFCA", "C c #D4D2CD", "Z c #D7D5D1", "A c #D8D6D2", "S c #DCDAD5", "D c #DFDDD8", "F c #E1DFDA", "G c #E3E1DC", "H c #EEEBE6", "J c #F1EFEA", "K c #F3F1EC", "L c #FAF7F2", "P c #FDFBF5", "I c #FFFFFA", /* pixels */ "SSGFGGGGGGGFGGFFFFFFFFFFFFFFFFSS", "SSMmnmmnnMnMmmnNNMMMMMMMMMMMMBSS", "FB%@$$$$#@$###$XX@++++o+++o+oXBF", "DBnDAASDMnFSAAH:3ldgdgisghjgd+MF", "DBmFSSSFNMFSSAJ:3zfgghpfhe9fg+MF", "DBnFSSSFMMFSSAH:3zfgggidgd*rhOMF", "DBnFSSSDMMFSSAH:3ldgfgidj>=gg+MF", "DBnGSDSGNMFSFSJ:5cjkjlshkyihj+MF", "DBvACCCSnnACCVH:=r0qqw90qeew0oMF", "DVknvvvnllbvvcN=.%@@@# o$@@@@.NF", "DBmGDDDGNNGFGGJ:4ckzzx$exkkkk+MF", "DBnFASSDMMGbrxK:2ze4yl@0jffgf+MF", "DBnFSSSDMMFD6eP:3zf;0x@qkgggf+MF", "DBnFSSSFMMGn6pL:3c1*sk@wkfggf+MF", "DBmGDDSGNNGCNSJ>3zipfz@wkgggg+MF", "DVcVBBBCvvCVCNF-2jdfsg@9gssssoMF", "FVzmnnnmzxmnnvC=2fpppf@9dipai+MG", "DBmGFFFGNNGDGSK>3zfhfz@wzfhhf+MF", "DBnDSSADMMDSSZH:3zfffk@qkffgf+MF", "DBnFSSSFMMDSSZH:3zfhfl@qkffhf+MF", "DBnFSSAFmnDZZCH;3lfffh@wkfggf+MF", "DBmGFFFGCSKHHHI<7nzzzv$wlgggg+MF", "DVxNMMmVf386859@#,;;;:.0faasa+MF", "DBxNmmmA5X>=;==;;;:;;>.wfaaaaoMF", "DBmGFDDJ8,mkkcxgjxxllb$qzsfjg+MG", "DBnDSSAH6=xt<8jiaffffj@qkw:ig+mF", "DBnFADZH7=zf;0kiahgggj@qks&rjoMF", "DBnFASAH6=c5*phiagfgfj#0j0X*::::;oND", "DSVBBBBBBmnnnnnnnnnnnbMmnnnnnNSS", "SSDDDDDDDFGGGGGGGGGGGGGGGGGGGGSS" }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 69 1", " c #020202", ". c #0A0A0A", "X c #121212", "o c #181717", "O c #1A1A1A", "+ c #20201F", "@ c #252524", "# c #2E2E2D", "$ c #31312F", "% c #343332", "& c #3D3C3B", "* c #403F3E", "= c #434341", "- c #484846", "; c #4E4D4B", ": c #504F4D", "> c #50504E", ", c #565553", "< c #5D5D5A", "1 c #61615E", "2 c #666664", "3 c #686765", "4 c #696966", "5 c #6D6C6A", "6 c #71716E", "7 c #767572", "8 c #797876", "9 c #7F7E7B", "0 c #807F7C", "q c #82817E", "w c #858481", "e c #888784", "r c #8B8A86", "t c #8E8D8A", "y c #908F8C", "u c #93928E", "i c #969592", "p c #999794", "a c #9A9A96", "s c #9E9D99", "d c #A09F9B", "f c #A3A29E", "g c #A6A5A1", "h c #A8A7A3", "j c #ABAAA6", "k c #AEADA9", "l c #B0AFAB", "z c #B3B1AD", "x c #B7B5B1", "c c #B8B6B2", "v c #BBB9B5", "b c #BFBEB9", "n c #C0BFBA", "m c #C5C3BE", "M c #C7C5C0", "N c #C9C7C2", "B c #CCCAC5", "V c #CFCDC8", "C c #D0CEC9", "Z c #D4D2CD", "A c #D7D5D0", "S c #D9D7D2", "D c #DCDAD5", "F c #DFDDD8", "G c #E1DED9", "H c #E3E1DC", "J c #E7E5E0", "K c #E9E7E1", "L c #EBE9E4", /* pixels */ "DDDDDDDDDDDDDDDDDDDDFDDDDDDDDDDDDDDDDDDDDDDDDDDD", "DDDDAASSSDSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSDDDD", "DDSGLKLLLLLLLLLLLLLLKKKKKKKKLKKLLLLLLLLLLLLHDDDD", "DDGMtiuuyiyyyuuuuuuuuuippuiiiuiiuiuuuuiuuiufDDDD", "DAKp .................. . ODFDD", "DDHvMGDDDDDAGlVGSDDDSGB.qlgggghhfyhhhfhffz4XFDDD", "DDHvNGDFGDGDGxCGDDDDDHV 0lfggghgfyhfhjjjfz2XDDDD", "DDHvMFDDADDDGlVGDDDDSGB 9kfgggggfthfd6,0fz2XFDDD", "DAHvMGDDDDDDGzCGDDDDSGV 9kffggfgfyhhsa0#hl4XFDDD", "DDHvMGSDDDDDHzCGDDDDDFV 9kfgggggfyffjf&7jl2.FDDD", "DDHvNGDDDDDSHzVGDDDDSGV 0kffgfgffyggf@%efl2XFDDD", "DDHvMGSSSDDSGlVGDSDDSGV 9hdfffffdrffsrrrfk2XFDDD", "DSHvBHGFGGGGHxZHGGFGFHZ rvzzzzzzzazzxcvcln7XFDDD", "DDHvxVBBBBBNVhnVBNmBBVv &,::>>>>>;,>>>>>>,#XDDDD", "DSHvhvxxxxxxvslvxxvxxvh.XOOOOOOOX oOooOOOO.ODDDD", "DDHvBKHHHHHGLxZHHHHHHHZ evkkzkkvt dzlllkkb5XFFDD", "DSHvMFSDSDDAGzVDDDZHGGB 0jdgdhfjq igfffffk2XDDDD", "DDHvMGDDDDDDGzCFFdq5CHB 0kf5,2gkq iggggffl2XFFDD", "DDHvMGDDDDDSHzVFDCj%MHV 0kdgi@skw ijfgggfz2XDDDD", "DDHvMGSDDDDDGxVFDGf%NHB 9kga%7jjq ihfgggfk2XFDDD", "DDHvMGDDDDDSGxVFGsr>>;>>>>>>>>* ufsaasaj1XFDDD", "DDHvBHGGGGGGHo5nzzvxxzzazzzzzxzvu ihgjkgfz3XFFDD", "DDHvmGDDDSDDGo=jl2XFDDD", "DDHvNHDDDDDDGo1zfq+8lffyfffhhfflq ijsi2$hl2XFFDD", "DDGcmGDDDDDDGX1lj,-5pffuffffjffjq ijd5<8hl2XDDDD", "DDHnmGSDSDDDGX1xflzjfjfufjjffffle pjhkkkgz4XFDDD", "DSHvBHGGGHGHHo c #FD0F05", ", c #FF1008", "< c #FF1915", "1 c #C63916", "2 c #FF2204", "3 c #FF220F", "4 c #FF2111", "5 c #FF221B", "6 c #FF2518", "7 c #EB1227", "8 c #FF172A", "9 c #FC2022", "0 c #E03428", "q c #9D4C1C", "w c #B75000", "e c #A96017", "r c #9A4224", "t c #A04C23", "y c #913447", "u c #922B55", "i c #991B6D", "p c #BD1366", "a c #982961", "s c #E21640", "d c #C62040", "f c #078900", "g c #07800D", "h c #0E8609", "j c #1E8608", "k c #049200", "l c #089300", "z c #1D9D04", "x c #15990B", "c c #1A9114", "v c #0E9B23", "b c #0D8936", "n c #03893B", "m c #128132", "M c #108338", "N c #2B8F2B", "B c #3C8E2B", "V c #2A9F38", "C c #31AD2D", "Z c #4F8D1E", "A c #448B2E", "S c #00834F", "D c #0A33A9", "F c #103EA2", "G c #7E1880", "H c #740B9A", "J c #771396", "K c #751799", "L c #41219F", "P c #6A239D", "I c #4B1FAE", "U c #672EBA", "Y c #1D4D8C", "T c #044998", "R c #11439A", "E c #0B49A8", "W c #195CA4", "Q c #0C45B7", "! c #2447B2", "~ c #171CDC", "^ c #300AD9", "/ c #0928C0", "( c #1220DA", ") c #0B3AD4", "_ c #2435D6", "` c #1817E6", "' c #090BF5", "] c #0202FB", "[ c #0304FB", "{ c #0000FD", "} c #0000FF", "| c #0100FF", " . c #0004FF", ".. c #0B04F8", "X. c #0803FF", "o. c #0D04FF", "O. c #000CFF", "+. c #1616F6", "@. c #1D1DF3", "#. c #1F1EF4", "$. c #1B16FF", "%. c #1E1CFC", "&. c #2700FF", "*. c #241EF5", "=. c #002FE8", "-. c #1422FF", ";. c #3A2DE0", ":. c #2E2DF7", ">. c #2323FB", ",. c #2723F8", "<. c #392FF6", "1. c #3537F2", "2. c #3433F5", "3. c #3838F4", "4. c #4915D4", "5. c #5224E6", "6. c #0B40CA", "7. c #861583", "8. c #8234A5", "9. c #8BBB89", "0. c #97BD93", "q. c #90B4A0", "w. c #B2C6A9", "e. c #B9CBB5", "r. c #8483E6", "t. c #9392E4", "y. c #9591E4", "u. c #9893E3", "i. c #BBC3C5", "p. c #C3C8CB", "a. c #C9D3C3", "s. c #D7D3CD", "d. c #D5D3CE", "f. c #D2D4CF", "g. c #D4D5CD", "h. c #D4D5CE", "j. c #D8D2CE", "k. c #CFCDD3", "l. c #D3D2D1", "z. c #D3D2D2", "x. c #D4D2D2", "c. c #D5D3D2", "v. c #D5D3D3", "b. c #D7D5D1", "n. c #D5D5D3", "m. c #D7D5D2", "M. c #D2D1D5", "N. c #D6D4D6", "B. c #D7D5D7", "V. c #D8D6D1", "C. c #D8D6D2", "Z. c #D9D7D2", "A. c #DFD7D3", "S. c #D9D7D7", "D. c #DAD7D6", "F. c #DBD7D6", "G. c #DAD7D7", "H. c #DDD7D4", "J. c #DAD8D0", "K. c #DBDBD1", "L. c #D9D9D3", "P. c #DAD9D2", "I. c #DAD9D3", "U. c #DADAD2", "Y. c #DDD8D2", "T. c #DCD8D3", "R. c #DDD8D3", "E. c #DED8D2", "W. c #DFDED2", "Q. c #DAD9D4", "!. c #DAD8D5", "~. c #DBD8D5", "^. c #DBD9D5", "/. c #DBDAD4", "(. c #D9D8D6", "). c #D9D8D7", "_. c #DCD8D4", "`. c #DCDAD5", "'. c #DDDBD6", "]. c #DEDCD5", "[. c #DEDCD6", "{. c #DFDCD6", "}. c #DEDCD7", "|. c #D9D7D8", " X c #DFD7D9", ".X c #DFDBD8", "XX c #E2DCDD", "oX c #E7DEDF", "OX c #F8F6CF", "+X c #E7E5D3", "@X c #EEE8DB", "#X c #F0EED1", "$X c #E9DFE1", "%X c #EADFE3", /* pixels */ "`.C.C.C.C.C.C.P.C.C.b.M.s.s.g.R.", "C.d.C.C.C.C.C.s.d..Xw.x 0 t V R.", "C.P.].].}.`.`.].XX@Xi.T 7 - v R.", "C.C.`.`.`.`.`.P.e.p.y. .p r A R.", "C.s.].`.`.Q.`.f.z R ...O.J 5 Q.", "C.C.`.`.`.`.].M._ [ ~ % d ; B H.", "C.J.].`.`.Q.XXB.! D & k a y + R.", "C.k.B.Q.`.`.P.a.c f h j =.H 3 D.", "b.@.y.OXv.$Xq. k E 4., > : 9 R.", "b.+.:.r.P.%X0. w I ~ O K # @ `.", "z.@. .2.].oX0.$ s P Q M ) Y Z D.", "z.*. .3.+X#Xu. .X.* m &.b . 8 P.", "z.N F o.1.3.%.' S q 1 n ) # < X", "z.= D .] .^ 7.X u i o ` L e |.", "M.<.$.*.,.-.8.2 < U ;.6 5.W C |.", " X|.|.B.|.|.|.D.P.R.H.Q.|.H.H.H." }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 256 2", " c #007700", ". c #007B01", "X c #2D7D0C", "o c #16782B", "O c #207E37", "+ c #795B16", "@ c #5F6615", "# c #497A13", "$ c #497C24", "% c #1B7644", "& c #1D6B5F", "* c #267D47", "= c #237A4B", "- c #22754A", "; c #185B76", ": c #1C6F6F", "> c #196B65", ", c #267168", "< c #2A7268", "1 c #2C7264", "2 c #2A677E", "3 c #296779", "4 c #3F7876", "5 c #347078", "6 c #54755E", "7 c #58776F", "8 c #FF0000", "9 c #FF0804", "0 c #FF070C", "q c #FB0A0A", "w c #FF1E0A", "e c #FF0E12", "r c #FE0E18", "t c #FF1013", "y c #FF1A13", "u c #F41B1C", "i c #FA1519", "p c #D3290E", "a c #CF2D15", "s c #DF2E1F", "d c #EA2B14", "f c #EF3316", "g c #E03B13", "h c #EA1C2C", "j c #F41920", "k c #E11F37", "l c #CE3722", "z c #D72D3E", "x c #D6372B", "c c #E32D23", "v c #FF2C24", "b c #F52029", "n c #FD2E2E", "m c #F12C2D", "M c #FF3024", "N c #FB322D", "B c #E32334", "V c #E22139", "C c #FB2F31", "Z c #FA3031", "A c #FB393D", "S c #BB421D", "D c #B54724", "F c #B64D25", "G c #BD4E2C", "H c #A05425", "J c #BC4834", "K c #A35F30", "L c #B35A3D", "P c #BE215B", "I c #A51E7A", "U c #B72768", "Y c #B7286B", "T c #BA346F", "R c #B83075", "E c #CF1F4C", "W c #D22249", "Q c #D12653", "! c #E22943", "~ c #906E47", "^ c #837547", "/ c #967F4D", "( c #896E5B", ") c #C64B4B", "_ c #D75A49", "` c #D85948", "' c #C64459", "] c #D74A6A", "[ c #018300", "{ c #028C00", "} c #098C00", "| c #00870D", " . c #0A830B", ".. c #008C0A", "X. c #039102", "o. c #039209", "O. c #0C930B", "+. c #119202", "@. c #139308", "#. c #0F8413", "$. c #108710", "%. c #198116", "&. c #1B851C", "*. c #1C8B1C", "=. c #0C9214", "-. c #009119", ";. c #21910C", ":. c #21871F", ">. c #38831F", ",. c #22921C", "<. c #1A8720", "1. c #1D822F", "2. c #1A9229", "3. c #228A22", "4. c #3A8120", "5. c #308F2B", "6. c #3A8A28", "7. c #22902F", "8. c #33942F", "9. c #319A2F", "0. c #218437", "q. c #2D883F", "w. c #2F9230", "e. c #2F9930", "r. c #329630", "t. c #329E39", "y. c #4E8938", "u. c #508331", "i. c #178D41", "p. c #288449", "a. c #218C42", "s. c #38855A", "d. c #4B824F", "f. c #459B43", "g. c #499A46", "h. c #5C9649", "j. c #4A965B", "k. c #45A142", "l. c #57A655", "z. c #84864C", "x. c #203FB6", "c. c #7F269E", "v. c #7039AA", "b. c #6925B3", "n. c #6332BC", "m. c #234E9F", "M. c #24529C", "N. c #28579D", "B. c #1F6782", "V. c #286783", "C. c #356989", "Z. c #2757A2", "A. c #2C59A3", "S. c #325DA6", "D. c #2548B3", "F. c #284BB7", "G. c #2949B7", "H. c #2955BB", "J. c #3F56B2", "K. c #3D63A7", "L. c #505A99", "P. c #7D488F", "I. c #456E9F", "U. c #46709D", "Y. c #446AA5", "T. c #263BCD", "R. c #2736D2", "E. c #2238D0", "W. c #0101FE", "Q. c #0C04FE", "!. c #010CFF", "~. c #0C0BFF", "^. c #1005FF", "/. c #120DFF", "(. c #1B0BFF", "). c #0314FF", "_. c #0914FF", "`. c #1412FC", "'. c #111DFF", "]. c #1E1DFA", "[. c #191BF4", "{. c #2C1BEB", "}. c #271FFE", "|. c #1A27E2", " X c #1E28F7", ".X c #1D28FF", "XX c #212AE6", "oX c #2221EE", "OX c #3B2AE8", "+X c #2729F6", "@X c #2C2CF7", "#X c #2722FA", "$X c #222BFF", "%X c #2E2DFC", "&X c #2C37FF", "*X c #3131FA", "=X c #3239FF", "-X c #3F39FE", ";X c #3636F4", ":X c #3938E9", ">X c #4B16DA", ",X c #5131D3", ". 8.AXNX", "VXcXcXNXNXNXNXNXNXNXNXNXNXNXNXNXbXNXNXnXSXaXo 0.J 8 t 4.. 9.KXNX", "NXcXcXNXNXNXNXNXNXNXNXNXNXNXNXNXbXNXNXmXGXjXW._.Y 8 r >. 8.AXNX", "VXvXcXNXNXNXNXNXNXNXNXNXNXNXNXNXAXUXAXGXLXjXW.!.U 8 r >.[ 9.AXNX", "VXcXcXNXNXNXNXNXNXNXNXNXNXNXNXNXdXsXfXkXgX7XW.W.qXP R ^ @ z.ZXmX", "VXcXcXNXNXNXNXNXNXNXmXNXNXNXNXcX&. - Q.W.W.W.W.W.W._.V 8 C DXNX", "DXcXcXNXNXNXNXNXNXNXNXNXNXNXNXNX,.{ p.~.W.W.W.W.W.W._.V 8 C VXNX", "VXvXcXNXNXNXNXNXNXNXNXNXNXNXNXMXK.M.G.Q.W.T.3 V.P.I 0X~ + / DXNX", "VXcXcXNXNXNXNXNXNXNXNXNXNXNXNXNX].W.W.W.W.N.} o.F 8 t >.[ 9.AXNX", "NXvXcXNXNXNXNXNXNXNXNXNXNXNXNXMX#XW.~.Q.Q.A.[ | F 8 y 6.[ t.ZXNX", "VXcXcXNXNXNXNXNXNXNXNXNXNXNXNXMXs.% = = = 1.. { L.>X,X) p _ VXNX", "DXcXcXVXNXNXNXNXNXNXNXmXNXNXVXvX*.[ [ [ [ . { D.W._.V 8 Z DXNX", "NXcXzXNXMXNXNXNXNXNXNXNXNXcXNXcX*.. .&.&.*.&.;.J.(.+XV 8 Z DXNX", "VXbX:X~.1XNXmXNXNXNXNXNXIXl.$.,. .[ * }._.wXw u u j j e 8 N DXNX", "VXbXoXW.;XJXKXZXmXNXNXNXIXf. . [ [ = ^.W.c.8 8 8 8 8 8 8 n DXmX", "VXbXoXW.@XlXhXCXNXNXNXnXIXf. .$ # 6 ~.W.v.g l z E W x a ` DXNX", "VXbXoXW.W.W.W.2XJXnXNXNXSXg. :.b 8 Q !.W.A.X.o.H.W.'.i.[ t.ZXNX", "NXbX XW.W.W.W.2XJXmXNXMXSXk.[ ,.u 8 W ).W.M.{ { D.W./.0.[ e.AXNX", "NXbX{.W.W.Q.W.6XJXmXNXNXKXuX; C.8XI eXA.m.U.2 2 Y.m.S.^ + / AXNX", "NXvX|.W.!.W.W.2XGXnXmXmXGX4XW.!.W.W.E.+.{ 2 W.W., { =.s 8 N DXNX", "NXMX{.W.W.W.W.6XFXHXJXHXPX1XW.W.Q.W.R.O.X.V.W.Q.< { =.s 8 Z DXmX", "NXNX7 > V.~.W.@X6X2X5X2X6X].W.`.5 > 7 G S ( : 1 Y.x.H.B 8 Z DXNX", "VXnX3.[ 0.~.W.W.W.W.W.W.W.W.W.[.*.[ $ 0 8 H { } D.W._.k 8 n DXNX", "NXnX5.{ p.`.W.W.~.W.W.W.W.~.!. X7...u.y 9 K -.@.F.W._.! 8 A DXNX", "VXvX c #EF222D", ", c #F62324", "< c #F92121", "1 c #F1252D", "2 c #FF352C", "3 c #EA3A3B", "4 c #FE3434", "5 c #F63E36", "6 c #F83A34", "7 c #F7363A", "8 c #FD3738", "9 c #F53B3C", "0 c #FD3B3B", "q c #EE443B", "w c #F34335", "e c #EF3F43", "r c #F13842", "t c #DC4843", "y c #EE4146", "u c #E74947", "i c #E7464C", "p c #EC4049", "a c #FC524E", "s c #F75655", "d c #FC5051", "f c #F15B57", "g c #F2575C", "h c #038103", "j c #168E0E", "k c #188D0E", "l c #0B8A11", "z c #158C13", "x c #1A8C14", "c c #1E841D", "v c #148E1A", "b c #1C8C1B", "n c #20871A", "m c #208B1C", "M c #2C8A1E", "N c #1F8B23", "B c #1D8A28", "V c #258724", "C c #238D23", "Z c #2D8A23", "A c #258A2C", "S c #328926", "D c #279025", "F c #289127", "G c #2D9C2B", "H c #369D27", "J c #359A2D", "K c #2C9B34", "L c #3D9337", "P c #339A31", "I c #3B9A34", "U c #36933A", "Y c #3D933D", "T c #3B9A3B", "R c #439733", "E c #409933", "W c #47973E", "Q c #4B963D", "! c #419B3F", "~ c #3F9942", "^ c #419743", "/ c #4B9544", "( c #439946", ") c #489D45", "_ c #45934C", "` c #4F934A", "' c #489C48", "] c #559F53", "[ c #4AA84F", "{ c #50A849", "} c #4CA750", "| c #52A752", " . c #5BA555", ".. c #67B167", "X. c #0101FF", "o. c #0A0AFD", "O. c #0E16FF", "+. c #1E1DF7", "@. c #1515FD", "#. c #1916FD", "$. c #171CFF", "%. c #1B1BFC", "&. c #241FF3", "*. c #201FFB", "=. c #1F21F4", "-. c #1E20FB", ";. c #2828E7", ":. c #2627E9", ">. c #2E24EF", ",. c #262BEE", "<. c #292CEC", "1. c #3227ED", "2. c #3228ED", "3. c #3C38E8", "4. c #2325F4", "5. c #2C25F2", "6. c #2629F1", "7. c #2B2CF4", "8. c #2222F9", "9. c #2F37FA", "0. c #3333F5", "q. c #3C3BF5", "w. c #3635FE", "e. c #3936FA", "r. c #3B3BFD", "t. c #473FEC", "y. c #483BED", "u. c #3E40EE", "i. c #3D41F0", "p. c #4446DC", "a. c #4140E3", "s. c #4D41E7", "d. c #454BE5", "f. c #4D49E1", "g. c #4244EB", "h. c #5546E2", "j. c #5555E7", "k. c #595CEF", "l. c #4645F3", "z. c #4C50FD", "x. c #5B56F3", "c. c #5253F9", "v. c #C5C3BF", "b. c #CBCBC4", "n. c #CFCDC8", "m. c #D0CECA", "M. c #D3D1CD", "N. c #DBD9CB", "B. c #CECCD8", "V. c #D0CED7", "C. c #D5D3D6", "Z. c #DAD6D3", "A. c #D2D9D4", "S. c #DCDAD5", "D. c #DFDDD8", "F. c #E0DED4", "G. c #E1DDDA", "H. c #E3E2D6", "J. c #E7E4DB", "K. c #E8E6D8", "L. c #DFE6E1", "P. c #E7E1E0", "I. c #E8E2E2", /* pixels */ "S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.Z.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.", "S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.Z.S.S.S.S.S.S.S.S.", "S.S.S.S.D.D.D.S.S.D.D.S.D.S.D.D.D.S.S.D.D.D.D.D.D.D.S.D.D.D.D.S.Z.S.S.S.G.F.Z.G.Z.G.S.S.S.S.S.S.", "S.S.S.S.M.M.M.M.M.M.M.M.M.M.M.M.M.M.M.M.M.M.M.M.M.M.M.M.M.M.M.M.Z.Z.Z.Z.A.A.A.A.Z.Z.Z.Z.Z.S.S.S.", "S.S.D.M.v.m.n.n.n.n.n.n.n.n.n.m.m.n.n.n.n.n.b.m.M.b.M.b.b.n.m.b.L V N / 3 ; ; t U V c | G.S.S.S.", "S.Z.D.M.m.G.D.D.D.D.D.D.D.D.D.S.D.D.D.H.D.D.D.D.D.D.D.D.D.D.D.S.b . Z % X X : v . T I.S.S.S.", "S.S.D.M.n.D.S.Z.Z.S.S.S.S.Z.Z.S.S.A.S.Z.Z.S.Z.S.Z.S.S.Z.S.Z.D.N.b . Z % X X : b . . T I.Z.S.S.", "S.S.D.M.n.D.Z.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.A.S.Z.J z k R % X X : v h T I.Z.S.S.", "S.S.D.M.m.D.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.u.:.,.f.@ X X : v h . T J.S.S.S.", "S.S.D.M.m.D.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.Z.S.A.#.X.X.>.% X X : v h . T I.Z.S.S.", "S.S.S.M.b.H.Z.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.Z.S.S.S.S.S.A.+.X.X.5.@ X X : v . T I.Z.S.S.", "S.Z.D.M.b.D.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.D.G.G.D.F.F.F.S.%.X.X.2.2 @ o q K v l [ I.Z.S.S.", "S.S.D.M.n.D.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.) F F ' l.5.7.7.X.X.X.o.5.1.&.h.5 : * f L.Z.Z.S.", "S.S.S.M.n.D.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.C C *.X.X.X.X.X.X.X.X.X.X.1.@ X X 8 L.Z.D.Z.", "S.S.D.M.n.D.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.C . C *.X.X.X.X.X.X.X.X.X.X.1.@ X X 0 L.Z.S.S.", "S.S.D.M.n.D.Z.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.D.I x x T *.X.X.o.*.*.#.*.$.-.@.t.2 % O d L.S.S.S.", "S.S.S.M.m.D.Z.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.u.=.6.4.X.X.X.6.T C N / r , - u T C n .J.S.S.S.", "S.S.D.M.m.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.8.X.X.X.X.X.X.6.b . Z % X X : v T I.Z.S.S.", "S.S.D.M.n.D.Z.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.8.X.X.X.X.X.X.6.b h h S % X X : v . T I.Z.S.S.", "S.S.S.M.n.D.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.q.#.8.+.+.8.@.l.b h h Z 8 % @ q P b z | J.Z.S.S.", "S.S.D.M.n.D.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.~ b C C C C C C h h . A q.8.#.h.6 , + s L.Z.S.S.", "S.S.S.M.n.D.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.C h . . h h . h h h A %.X.X.1.@ X X 0 L.Z.S.S.", "S.S.D.M.n.D.Z.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.D . h h . h . h . h A %.X.X.1.@ X X 0 L.Z.S.S.", "S.S.D.M.n.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.Z.S.S.D . h h D N C N m C b / e.*.+.t.@ X X 0 L.Z.S.S.", "S.S.D.N.u.%.+.0.C.S.S.S.S.S.S.S.S.S.S.S.T b D C h h . C q.+.%.g.6 % , = < < < < X X X 0 L.Z.S.S.", "S.S.D.N.;.X.X.@.C.S.S.S.S.S.S.S.S.S.S.D.D . . h h . C 8.X.X.5.% X X X X X X X X X X 0 L.C.S.S.", "S.S.S.N.;.X.X.@.C.D.S.S.S.S.S.S.S.S.S.S.D . h h . h C *.X.X.5.% X X X X X X X X X X 0 L.Z.S.S.", "S.S.S.N.;.X.X.@.V.Z.S.Z.S.S.S.S.S.S.S.S.D h h h C C m ! *.X.X.5.w = , , , , , , , : + f J.Z.S.S.", "S.S.S.N.;.X.X.X.=.8.#.0.C.D.S.S.S.S.S.S.D . h D 9 % % 9 +.X.X.4.I b v ~ e.%.%.u.I b z | J.S.S.S.", "S.S.S.N.;.X.X.X.X.X.X.#.C.S.S.S.S.S.Z.D.F . . C $ X X , -.X.X.6.m . A $.X.X.<.x h T I.S.S.S.", "S.S.S.N.;.X.X.X.X.X.X.#.C.S.S.S.S.S.S.D.C . . C < X X , -.X.X.4.b . . A #.X.X.<.x . T I.Z.S.Z.", "S.S.S.N.;.X.X.X.X.X.X.#.A.S.Z.S.S.S.S.S.^ N N ^ r , , y u.=.=.g.Y N C ( q.4.8.d.E C m .J.Z.S.S.", "S.S.S.N.:.X.X.X.X.X.X.%.C.S.S.S.D.Z.S.S.q.%.*.%.%.-.$.q.I x x T e.%.#.q.I x x W 4 @ O d L.Z.S.S.", "S.S.S.N.;.X.X.X.X.X.X.#.C.Z.S.Z.Z.S.Z.D.8.X.X.X.X.X.X.4.m . A %.X.X.,.x h S # X X 0 L.Z.S.S.", "S.S.S.N.:.X.X.X.X.X.X.#.A.F.S.F.S.F.S.H.6.X.X.X.X.X.X.8.b . A #.X.X.6.x . . S # X X 0 J.S.S.S.", "S.S.S.N.p.4.,.6.o.X.X.#.B.A.C.C.C.C.C.M.8.X.X.o.,.<.=.g.! M M / i.:.4.d.Y A B ` # X X 0 L.Z.S.S.", "S.S.S.Z.Y x x H <.X.X.X.#.#.#.%.#.#.%.@.X.X.X.8.I k z I 4 # # 5 P x k T w.#.@.y.@ X X 0 L.Z.S.S.", "S.S.S.Z.V x <.X.X.X.X.X.X.X.X.X.X.X.X.X.X.8.C . . M = X X , b . . A #.X.X.1.@ X X 0 L.Z.S.S.", "S.S.S.Z.A . . x <.X.X.X.X.X.X.X.X.X.X.X.X.X.X.8.C . C = X X , b . . A *.X.X.2.@ X X 0 L.Z.S.S.", "S.S.S.Z._ V A ~ ,.X.X.X.X.X.X.X.X.X.X.o.2.2.5.s.Q Z Z / p > 1 i ^ S M ` %.X.X.2.w : & f L.Z.S.S.", "S.S.S.N.3.@.#.%.X.X.X.X.X.X.X.X.X.X.X.8.6 o @ @ # # O 7 9.O.O.q.2 # O r $.X.X.,.G l l [ P.Z.S.S.", "S.S.S.N.:.X.X.X.X.X.X.X.X.X.o.X.X.X.X.8., X X X X X X , +.X.X.4.% X X 1 -.X.X.,.x . T I.Z.S.S.", "S.S.S.N.:.X.X.X.X.X.X.X.X.X.X.X.X.X.X.-., X X X X X X , -.X.X.5.% X X 1 $.X.X.,.x T J.S.S.S.", "S.S.S.N.j.w.r.r.i.r.r.r.r.r.r.r.r.r.w.c.s 4 0 0 0 0 4 s c.w.w.x.a 0 4 g z.w.w.k.{ T P ..J.A.S.S.", "S.S.S.S.J.K.K.K.K.K.K.K.K.K.K.K.K.K.K.K.L.L.L.L.L.L.L.L.J.J.K.K.L.P.L.L.K.K.K.K.P.P.I.P.S.S.S.S.", "S.S.S.S.Z.A.Z.Z.Z.Z.Z.Z.Z.Z.Z.A.Z.Z.Z.Z.Z.S.Z.Z.S.Z.Z.Z.Z.Z.S.S.Z.N.C.S.Z.S.A.Z.Z.Z.Z.S.S.S.S.S.", "S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.Z.D.S.S.S.S.S.S.S.S.S.S.S.S.", "S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S." }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/signpost-icon.c0000644000175300017530000002142112161170401016471 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 151 2", " c #644B9C", ". c #6A50A0", "X c #7155AA", "o c #785CB3", "O c #8060C4", "+ c #8464C7", "@ c #866AC1", "# c #886BC4", "$ c #8B6EC5", "% c #8A6CC6", "& c #8B6EC7", "* c #8565C8", "= c #8A69CD", "- c #8A6ACD", "; c #8A68CF", ": c #8F70CF", "> c #9276C9", ", c #9377CA", "< c #9071CD", "1 c #9478CB", "2 c #8B69D1", "3 c #8C6BD0", "4 c #8E6CD3", "5 c #8D6AD4", "6 c #926FDC", "7 c #926DDE", "8 c #926DDF", "9 c #9172D0", "0 c #9373D2", "q c #9374D1", "w c #9474D3", "e c #9576D3", "r c #9373D4", "t c #9576D4", "y c #9575D6", "u c #997DD2", "i c #9370D9", "p c #9471DC", "a c #9571DD", "s c #9470DE", "d c #9570DF", "f c #9878D8", "g c #9571E0", "h c #9773E0", "j c #A4A29E", "k c #A5A39F", "l c #A3A0A0", "z c #A3A0A1", "x c #A5A1A3", "c c #A6A4A0", "v c #A7A5A2", "b c #A7A6A2", "n c #A6A2A4", "m c #A8A6A2", "M c #ABA9A5", "N c #ABA9A6", "B c #ACAAA6", "V c #AEADA9", "C c #AFADA9", "Z c #AFAEAA", "A c #B0AEAB", "S c #B1AFAC", "D c #B1B0AC", "F c #B2B0AC", "G c #B2B0AD", "H c #B2B1AD", "J c #B3B2AC", "K c #B4B3AD", "L c #B3B2B3", "P c #B5B3B0", "I c #B5B4B0", "U c #B7B7B7", "Y c #B8B8B8", "T c #BCBCBC", "R c #BFBFBF", "E c #C1C1C1", "W c #C3C3C3", "Q c #C5C6C6", "! c #CCCDCC", "~ c #CFCFCF", "^ c #D0D0D0", "/ c #D6D7D6", "( c #D9D9D8", ") c #DBDBDB", "_ c #DDDDDC", "` c #DEDCDD", "' c #DFDDDF", "] c #DEDEDE", "[ c #E1E0DF", "{ c #CECEFB", "} c #DEDEFA", "| c #DBDBFC", " . c #E1E0E2", ".. c #E2E2E2", "X. c #E5E4E3", "o. c #E4E2E4", "O. c #E4E4E4", "+. c #E5E5E5", "@. c #E6E5E5", "#. c #E6E6E5", "$. c #E6E5E6", "%. c #E7E7E6", "&. c #E8E8E7", "*. c #E8E7E8", "=. c #E8E7E9", "-. c #E9E9E8", ";. c #EAE8EB", ":. c #EAEAEA", ">. c #EBEBEA", ",. c #ECEBEA", "<. c #ECEBEB", "1. c #EAE9EC", "2. c #ECEAEC", "3. c #ECEBED", "4. c #EDECEC", "5. c #EEEDED", "6. c #EEECEE", "7. c #EDEEEE", "8. c #EFEFEF", "9. c #F0F0EF", "0. c #F1F0EF", "q. c #EFEFF8", "w. c #F0F0F0", "e. c #F1F1F0", "r. c #F2F2F1", "t. c #F2F2F2", "y. c #F3F2F2", "u. c #F3F3F3", "i. c #F4F4F3", "p. c #F5F5F4", "a. c #F5F5F5", "s. c #F6F6F6", "d. c #F7F6F6", "f. c #F7F7F6", "g. c #F3F3FB", "h. c #F5F5FC", "j. c #F9F9F9", "k. c #FBFBFA", "l. c #FBFBFB", "z. c #FCFBFB", "x. c #FCFCFB", "c. c #FCFCFC", "v. c #FDFDFD", "b. c #FFFEFC", "n. c #FCFCFE", "m. c #FDFDFE", "M. c #FEFEFE", "N. c #FEFFFE", "B. c #FFFFFE", "V. c #FEFEFF", "C. c #FFFFFF", /* pixels */ "c J J S D D J V Z I L Z J J Z z ", "J q.} b.n.n.b.:.+.] s.b.n.b.u.c ", "J h.{ b.b.b.b.>.t.^ 8.b.b.b.a.c ", "J g.| b.b.b.b.4.&.O.j.b.n.b.i.M ", "Z n.b.b.b.) :.8.w.b.b.b.j.7.a.c ", "S n.b.b.j.R ~ :.w.b.b.+.Q L ) B ", "J n.b.b.u.Y Q %.t.b.b.w./ E +.M ", "V :.w.:.w.] +. .` 7.*.8.:. .*.c ", "J ..( 7.w.t.s.] > 4 q q : w , z ", "Z n.^ t.b.b.b.1.# O * 3 + 8 y z ", "I +.^ n.b.b.b.:.& ; ; 4 3 s e z ", "J b.b.b.n.s.b.=.q d d a ; * f z ", "Z n.b.b.b.W U ..q i i s o X e j ", "J n.b.b.8.T ! .r 8 s 5 . & z ", "Z t.u.j.&...b.] 1 y q f & @ u z ", "x b b b M B c c z z z j x n z j " }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 60 1", " c #634C95", ". c #674F9A", "X c #684E9F", "o c #6A519D", "O c #6D53A4", "+ c #7256AC", "@ c #7459AD", "# c #795CB4", "$ c #7C5EBA", "% c #7E60BC", "& c #8D74BD", "* c #8263C3", "= c #8B6EC4", "- c #8767CA", "; c #8969CC", ": c #8F71CA", "> c #9277C4", ", c #9072CB", "< c #977CCD", "1 c #8D6BD3", "2 c #906ED7", "3 c #926EDC", "4 c #9576D4", "5 c #9778D6", "6 c #9370DC", "7 c #936EE2", "8 c #9571E1", "9 c #9873E4", "0 c #9F9D99", "q c #A19E9A", "w c #A4A29E", "e c #A6A4A2", "r c #A8A6A2", "t c #ACACAB", "y c #B2B1AE", "u c #B6B5B3", "i c #B8B7B3", "p c #BBB9B4", "a c #BCBCBB", "s c #AFAFF6", "d c #B4B4F7", "f c #BDBDF8", "g c #C3C3C3", "h c #CAC9C6", "j c #CDCDCD", "k c #D0CFCC", "l c #D0D0CC", "z c #D6D5D3", "x c #D8D7D5", "c c #D9D8D5", "v c #DADADA", "b c #C8C8F9", "n c #DEDEFB", "m c #E5E5E4", "M c #E9E8E7", "N c #EBEBEA", "B c #EAEAFD", "V c #F4F4F4", "C c #F4F4FE", "Z c #FEFEFE", /* pixels */ "ww000qq00q0q00qqq000000000000qww", "wriipiiyiuuuiuitypppiuuiiuiuiyww", "0uZCBCZZZZZZZZZzNVmVZZZZZZZZZVww", "0uZBdnZZZZZZZZZzmmmuNZZZZZZZZVww", "0uZZfnZZZZZZZCZzMZzaCZZZZZZZZVww", "qiZZfnZZZZZZZZZzNZViNZZZZZZZZVww", "0uZBdbCZZZZZZZZzmmxaVZZZZZZZZVww", "quZCBCCZZZZZZZZzMZMVZZZZZZZZZVww", "0iZZZZZZZZZmNZZzMZZZZZZZZZCZZVww", "0uZZZZZZZZNeaZZzMZZZZZZZZZaVZVww", "0iZZZZZZZZNtaZZzMZZZZZZNNNuuVVww", "0uZZZZZZVmvyamNzMZZZZZZuttytaNrw", "0uZZZZZZZgtyytmxMZZZZZZgagutjVww", "qiZZZZZZZZhttmZzMZZZZZZZZCyhZVww", "0iZZZZZZZZZzmZZzMZZZZZZZZZvZZVww", "qtzzzlzzzzzxzzxahczzczzzzlczxlww", "qyNmmNNMMMMMMMNh&;,,,=,,,,,,:>qe", "0pZvjjZZZZZZZZZz:9327773336675ww", "0iZVZtNZZZZZZZZz,1%%9-66#;8635ww", "0iZZxiVZZZZZZZZc=$*%*X&9$;8634ww", "0uZlevZZZZZZZZZz:*%&1#16#-6234ww", "0uZxvvVZZZZZZZZz,3263363226334ww", "0uZZZZZZZZZZZZZx:6633336633334ww", "0uZZZZZZZVVZVVZz:66633366OX;74ww", "0uZZZZZZZVauuyZc,84636366O ;85ww", "0iZZZZZZZZMyyuZc,33666324O ;3,ww", "0iZZZZZZZVgtyyZc:333636#....O,eq", "0uZZZZZZZgegBiCc,6346633@ X25ww", "0pZZZZZZZMhZZVZx,73333333+X274ww", "quVVVVVVVVVVVVZk>544554454,64 c #8D6CD3", ", c #8E6ADA", "< c #906ED6", "1 c #926FDB", "2 c #9370DB", "3 c #936EE1", "4 c #946CE8", "5 c #9672E1", "6 c #9975E3", "7 c #9E78EB", "8 c #A19E9A", "9 c #A4A29E", "0 c #978AAA", "q c #9781BF", "w c #A19BA5", "e c #A19AA8", "r c #A5A4A3", "t c #ACAAA6", "y c #ACACAC", "u c #B1AFAC", "i c #B3B1AD", "p c #B3B3B3", "a c #BDBDB7", "s c #BCBCBC", "d c #AEAEF7", "f c #B2B2F7", "g c #BDBDF8", "h c #C4C4C4", "j c #CBCBCB", "k c #D0CFCD", "l c #D1D0CE", "z c #D8DACF", "x c #D4D4D3", "c c #DADBD1", "v c #DCDCDC", "b c #DEE0D6", "n c #C4C4F9", "m c #CACAFA", "M c #D2D2FA", "N c #DFDFFC", "B c #E3E3E3", "V c #EBEBEB", "C c #EAEAFD", "Z c #F3F3F3", "A c #F1F1FE", "S c #F7F8F8", "D c #FFFFFF", /* pixels */ "999999999999999999999999999999999999999999999989", "99999999r9999999r9999999999r9999r9999999r9999999", "999988888888888888888889888888888888888888889999", "998riiuuiiiiiiiiiiiiiiittpuiiuiiiiiiiiiiiiii9999", "998iDDDDDDDDDDDDDDDDDDDllDDDDDDDDDDDDDDDDDDDi899", "998iDDMgnDDDDDDDDDDDDDDlxSjhsxDDDDDDDDDDDDDDi899", "9r8iDDCndSDDDDDDDDDDDDDllDVDvrvDDDDDDDDDDDDDi899", "998iDDDnfDDDDDDDDDDDDDDjlDSVjsZDDDDDDDDDDDDDi8r9", "998iDDDnfDDDDDDDDDDDDDDllDSxphSDDDDDDDDDDDDDi899", "998iDDDnfDDDDDDDDDDDDDDllDDDVrvDDDDDDDDDDDDDi899", "998iDDCgdCSDDDDDDDDDDDDkxSxBhpZDDDDDDDDDDDDDi8r9", "998iDDmMmmADDDDDDDDDDSDllDvjxVDDDDDDDDDDDDDDi899", "998iDDDDDDDDDDDDDDDDDDDllDDDDDDDDDDDDDDDDDDDi899", "9r8iDDDDDDDDDDDDSVVVDDDllDSDDDDDDDDDDDZSDDDDi899", "998iDDDDDDDDDDDDVyyhDSDllDDDDDDDDDDDDDvjDDDDi899", "998iDDDDDDDDDDDDVyyjDDDllDDDDDDDDDDDDDvrxDDDi899", "9r8iDDDDDDDDDDDDByyjDDDllDSDDDDDDDVVBVkpyjDDi899", "998iDDDDDDDDDDSDBpphDSDllDDDDDDDDDhypipppyjDi8r9", "998iDDDDDDDDDZspppppsyBxkDDDDDDDDDhypypppppSp899", "998iDDDDDDDDDDZpupppyxDllDDDDDDDDDvhhjspppZDu899", "998iDDDDDDDDDDDZsypyvDDlkDSDDDDDDDDDDDvysZDDi899", "998iDDDDDDDDDDSDZpyxDSDjkDDDDDDDDDDSSDvyVDDDi899", "998iDDDDDDDDDDDDDZBDDDDkkDDDDDDDDDDDDDVZDDDDi899", "999tllkkklljllllkxxklkxaabccccczccccczbccccct999", "998yklxxxllllllllkkklkxa0===================w999", "998iDDSVZDDDDDDDDDDDDDDc=4355333323333353313w999", "998uDBskphSDDDDSDDDDDSDc=522;<2222<2<2<22222w999", "998uDZSDvyVDDDDDDDDDDDDc=4<>+:2<<22<@;222222w999", "998iDDDDjyVDDDDDDDDDDSDc=,#-+:6-$512#-222221e999", "998iDDDxyhSDDDDDDDDDDDDc<;#7+>-OO#26%;612121w999", "998iDDxyhSDDDDDDDDDDDDDz0,#:O;2=#222@&221211e999", "9r8iDvyphjVDDDDDDDDDDDDc=5>;;,,,>22:;;<22221w999", "998iDSZZZVSDDDDDDDDDDDDc=3222222222252222221e999", "998iDDDDDDDDDDDDDDDDDDDc=3222122221212552122e999", "998iDDDDDDDDDDDDDDDDDDDc=3222222222<2*@@:222w999", "998iDDDDDDDDDDDvvBvvvVDc=22222<222222+. ;222e999", "998iDDDDDDDDDDDvryyprvDz=322222212222#X.:211e999", "9r8iDDDDDDDDDDDDBpppyvDc=3<2222222226#..:622w999", "998iDDDDDDDDDDDDvpppyvDc=322222212;*-+X.%-&,w999", "998iDDDDDDDDDDDxypppyvDz=3222<22<2:X.XXX. #2w999", "9r8iDDDDDDDDDDxypyvByvDz=3212222222o$2221w999", "998iDDDDDDDDDDDDDDDDDSDz=3211111111122<22211w999", "9999iiiiiiiiiiiiiiiiiiitwwwwwwewewweewewwwewr999", "999988888888888888888889999999999999999999999999", "999999r999999r99999999999999r9r9999998r9998r9989", "989999999999999999999999999999899999999999999999" }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/singles-icon.c0000644000175300017530000002271212161170402016274 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 158 2", " c #000000", ". c #020202", "X c #030202", "o c #030303", "O c #040404", "+ c #070706", "@ c #070707", "# c #090808", "$ c #090908", "% c #0A0A09", "& c #1A1A19", "* c #1D1D1D", "= c #212120", "- c #242323", "; c #272726", ": c #282827", "> c #313130", ", c #424240", "< c #454443", "1 c #494847", "2 c #4B4A48", "3 c #535251", "4 c #535351", "5 c #555452", "6 c #565553", "7 c #585856", "8 c #595856", "9 c #5D5C5A", "0 c #5E5D5B", "q c #62615E", "w c #62615F", "e c #63625F", "r c #646361", "t c #676664", "y c #696865", "u c #6B6A68", "i c #6B6B68", "p c #6F6E6B", "a c #71706D", "s c #72706E", "d c #72716E", "f c #72716F", "g c #767572", "h c #777774", "j c #797775", "k c #7A7976", "l c #83827F", "z c #858481", "x c #8B8986", "c c #8B8A86", "v c #8B8A87", "b c #8C8B87", "n c #8C8B88", "m c #8D8C89", "M c #8F8E8B", "N c #908F8B", "B c #908F8C", "V c #918F8C", "C c #91908C", "Z c #92918E", "A c #93918E", "S c #93928F", "D c #94928F", "F c #959490", "G c #969591", "H c #969592", "J c #979592", "K c #989693", "L c #989794", "P c #999794", "I c #999894", "U c #9A9995", "Y c #9B9A96", "T c #9B9A97", "R c #9C9B97", "E c #9C9B98", "W c #9D9B98", "Q c #9D9C98", "! c #9E9C99", "~ c #9E9D99", "^ c #9F9D9A", "/ c #9F9E9A", "( c #A09E9B", ") c #A09F9B", "_ c #A19F9C", "` c #A1A09C", "' c #A2A09D", "] c #A2A19D", "[ c #A3A19D", "{ c #A3A19E", "} c #A3A29E", "| c #A5A4A0", " . c #A6A4A0", ".. c #A6A5A1", "X. c #A7A5A1", "o. c #A6A6A2", "O. c #A7A6A2", "+. c #A8A7A3", "@. c #A9A7A3", "#. c #A8A7A4", "$. c #A9A8A4", "%. c #AAA9A5", "&. c #ABA9A5", "*. c #ABAAA6", "=. c #ACAAA6", "-. c #ACABA7", ";. c #ADACA7", ":. c #ADACA8", ">. c #AEACA8", ",. c #AEADA8", "<. c #AEADA9", "1. c #B0AEAA", "2. c #B0AFAB", "3. c #B2B0AC", "4. c #B3B2AD", "5. c #B4B2AE", "6. c #B4B3AE", "7. c #B5B3AF", "8. c #B5B4B0", "9. c #B6B4B0", "0. c #B6B5B1", "q. c #B7B5B1", "w. c #B8B7B2", "e. c #B9B7B3", "r. c #B9B8B3", "t. c #BBB9B5", "y. c #BCBBB6", "u. c #BEBCB8", "i. c #BEBDB8", "p. c #C0BFBA", "a. c #C2C0BC", "s. c #C3C2BD", "d. c #C4C3BE", "f. c #C6C4C0", "g. c #C7C6C1", "h. c #C8C6C2", "j. c #C8C7C2", "k. c #C9C7C3", "l. c #CBC9C4", "z. c #CBC9C5", "x. c #CCCAC5", "c. c #CDCBC6", "v. c #CECCC7", "b. c #CFCDC8", "n. c #D0CFCA", "m. c #D1CFCA", "M. c #D2D0CB", "N. c #D3D1CC", "B. c #D4D2CD", "V. c #D4D3CE", "C. c #D5D3CE", "Z. c #D6D4CF", "A. c #D7D5D0", "S. c #D8D6D1", "D. c #DBD9D4", "F. c #E3E1DC", "G. c #E6E4DE", "H. c #E9E7E1", /* pixels */ "*.0.Y M 1...M Z M M 1.*.M ~ 0.@.", "0.) z J ~ f @ $ _ D D Z ] 1.", "J _ 1.q x.< $ f Z.t d.J Y ", "c >.7.3 D.< X $ p G.6 7.] Z ", "<.Z +.t.+.9 X $ b ~ ..~ M ;.", "0.t.u r Y 7.u 6 4 g p.+.~ ] u.-.", "<.L <.L Y +.} Q -.Y h.Z.] x.n.+.", "M -.c 1 A.6 M.z K r.) F.f g D...", "Z ) ) q f.0 l.b b 3.+.A., +.N.+.", "w.] 9.a.Y 0.Z ..[ V d.n.f.v.h.*.", "Y 1 = * > _ *.y y <.h.x.M.N.p.-.", "V O t +.l 7.Z u.N.b -.N.+.", "V # @ 2 S.k f u.~ G.e j D...", "V o 7 r.-.Y ~ 3.M.8 w.N.+.", "J : - ; & n Y Z Y J f.v.A.b.d.@.", "*.<.<.<.<.-.<.J L 2.#.*...@.@.*." }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 79 1", " c #010101", ". c #080807", "X c #0B0B0B", "o c #100F0F", "O c #141414", "+ c #1C1C1B", "@ c #232322", "# c #292827", "$ c #2D2D2C", "% c #30302F", "& c #353433", "* c #383736", "= c #393837", "- c #3D3C3B", "; c #41403F", ": c #454443", "> c #4C4C4A", ", c #504F4D", "< c #51514E", "1 c #545452", "2 c #585755", "3 c #595856", "4 c #5D5C5A", "5 c #605F5D", "6 c #62615F", "7 c #656461", "8 c #686765", "9 c #696865", "0 c #6C6B68", "q c #706F6C", "w c #72726F", "e c #767572", "r c #787774", "t c #7A7976", "y c #7E7D7A", "u c #807F7C", "i c #82817E", "p c #848380", "a c #888784", "s c #8B8986", "d c #8E8D89", "f c #908F8B", "g c #92918E", "h c #979692", "j c #989793", "k c #9B9996", "l c #9E9C99", "z c #A09F9B", "x c #A2A19D", "c c #A7A6A2", "v c #A8A7A3", "b c #ABA9A5", "n c #AEADA9", "m c #B0AFAB", "M c #B3B2AE", "N c #B6B5B1", "B c #B8B7B2", "V c #BBBAB6", "C c #BFBDB9", "Z c #C1BFBB", "A c #C4C3BE", "S c #C7C5C1", "D c #C8C7C2", "F c #CCCAC5", "G c #CFCDC9", "H c #D0CFCA", "J c #D4D2CD", "K c #D7D5D0", "L c #D9D6D1", "P c #DBD9D4", "I c #DFDDD8", "U c #E1DFDA", "Y c #E4E2DC", "T c #E7E5E0", "R c #E9E7E1", "E c #ECEAE4", "W c #F1EFE9", "Q c #F4F1EC", "! c #FEFCF6", /* pixels */ "bbcxbMMMcccMMMMMMMMMbcccNNmccvbb", "bbMZMiwdVVndipppppppcMCnuwdCBnbb", "cMYl1ya92CL# hEhC% cx1GSCUb>Nbv", "bM1Jlt$1Qx7& g4HL4 NWz4Nc", "MiwULn#wUJ5@ .6yULB.VPK1xb", "MriYPA%4UL9O .1dIPZ.FLP4jn", "Md9Rhg%1YH3# .0eYGt yJHcRRJi4J$ zA>bREUu7Dcb", "bnFM6242wAA<$;:=-:-&lJn<12>8DVcb", "bmDFa:&,jJVAYn834rDPMAFJBmAJFVvb", "cMJ2tSYC6rFHh>nKFd>NAJJPYRYJPScb", "cZ8gWF;xQ0am1YB:iQN1CJPM5>hPJScb", "nh6YP7 tTS45bUD&>UYriUPMMaXJPAcb", "NetWpy&eYP4=JHE<>RPl8YHPW1+JKSvb", "BtrW91O=JP4;FJY::YPj0YJJ, zPLScn", "nc2PJF:yEN5egWh&&jQ7gPPc+:qFLAcb", "xZi8UYYRFync84HCJJJJJJJJAcb", "nxig8*$-rddNDA4@#yJCmCCCVVVCZNcn", "Mi . &LF4wlj6tPCJJJUUJJLAcb", "Mi =J1lGbxHqeDJJJczJLJAcb", "Mp :ay!6=dAR4lLPx7:-LLAcn", "Ma -5CR0%wAYgqYJGQh.GLAcn", "Mi =5DKJP+0Wz9YGUMX7PKAcb", "Mp :7MIsy+gWirULM.+dJLScb", "Mp . :l7RNsMRF,mLLZhlgHKAcb", "Mp &Yt7GEUm11 c #3C3C3A", ", c #403F3E", "< c #41403F", "1 c #444442", "2 c #494947", "3 c #4D4C4A", "4 c #504F4D", "5 c #51514F", "6 c #555452", "7 c #585755", "8 c #5A5957", "9 c #5D5C5A", "0 c #605F5D", "q c #62615F", "w c #656462", "e c #686765", "r c #696966", "t c #6D6C69", "y c #706F6D", "u c #72716E", "i c #767572", "p c #7A7976", "a c #7D7C79", "s c #807F7C", "d c #83817E", "f c #858481", "g c #888784", "h c #8A8986", "j c #8E8D8A", "k c #908E8B", "l c #93918E", "z c #969591", "x c #989793", "c c #9B9996", "v c #9D9C98", "b c #A09F9C", "n c #A2A19D", "m c #A7A6A2", "M c #A8A7A3", "N c #AAA9A5", "B c #AEADA9", "V c #B0AFAB", "C c #B3B2AD", "Z c #B6B5B1", "A c #B8B7B2", "S c #BBB9B5", "D c #BFBDB9", "F c #C1BFBA", "G c #C4C2BD", "H c #C6C5C0", "J c #C9C7C2", "K c #CCCAC6", "L c #CFCDC8", "P c #D1CFCA", "I c #D4D2CD", "U c #D7D5D0", "Y c #D8D6D1", "T c #DCDAD5", "R c #DFDDD8", "E c #E0DED9", "W c #E4E2DD", "Q c #E7E5E0", "! c #E9E7E1", "~ c #EDEAE5", "^ c #F0EDE8", "/ c #F4F2EC", "( c #FFFCF6", /* pixels */ "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", "NNNNNNNNNmMNNNNNNMMmMmmNMmmmMMNNNNNNMmNNNNNNNNNN", "NNNNmmmmNAAVmnmNNVCCVCCCCVCCCVNmmmmBCACmmMmMNNNN", "NNNNCCAGnaihAGCCVjdgffffgffffhNZCAGnaihAGCCBMNNN", "NNMCIEG7-,1,-aTTG& oAIEG4-<1>-fRYSMNMN", "NNMCWB#tP~^UB,2UG& oCWm#uJ!^Tm:3RSMNNN", "NNMAJ%vWmhvPWW68I$ oSG$n~KvzGWW2wJmBMN", "NNnG9u/j>4.$GUT:l- XG6p!Ti# lEIU;kBMNN", "NNNm-HTKT(w VUWd3: on;JIIRj zWLWp3AMNN", "NMCa1TPTA* wEITC:* #i3RIIWh zEITN-CMNN", "NmAi2WPTPV>.FUUS;& $t4WIIWg lWLYC&BMNN", "NNCh,UUFG^9 VTTm>- +d1TII!j c!PEv;CNNN", "NNmC:B!z-*orUP!r9> oC-STRuO OpTW09AmNN", "NNmGf1WUFZJTPES&C& oGp3!ISAGAFQC=BNNNN", "NNmCR26T!WTT!S$kI& . OCT<9R!WEW~C#cGmNNN", "NNMCTT6-sCSmw$kWG& oCEI4;gASnq%x!SMNNN", "NNNBSSGh5:-&rIUIUSmNNN", "NNmS>NEII= =RK!qej3WIGlo@YIYD%CYITF8Ci pWIUSmBNN", "NMVj,IIW97@$TIRn<3jEII!@+YIIR1gEIIIE~S 7WPUSmNNN", "NmAi1ETn:!o&~LTS-&BRIIU++YIIW6uWPIIUI: dEPUSmNNN", "NMZp1YQi+7..dRIA;-mEIU~@#~UIW6iWIIUP> wEIIUSmBNN", "NNNn:JYDkj.oBIWh2wiWIKfXXfJUI-zRIUP& #smTPUSmNNN", "NNmF6a!IWWyaWIR8q5-tYESDPPr$3w9;=bYHCLKKKKKLKJKKPAmBNN", "NNNbapghw,:2ihspxZCZIC6%-aKJVCBZZCZZZZZZZZZBMNNN", "NMCf . 0UYRa-286>1CWKAYUUUUIIUUUITSMNNN", "NNCg qIR5>CW//Td@cUCTIIITWWUIIIUSmNNN", "NmVf 9W02WIkdic~C$NSIIIUAhhFTIIUSmNNN", "NmCf rV=IRA 59aTWl>GIITF$6$.mTPUSmBNN", "NmCf uruWYS lKWIII=cTIUKS^S 9WIUSmNNN", "NMVf t 9TPW4iWPIIIWd rWIUSmBNN", "NMCf r>MRIYY^3 FTW6uWIIIWj .VTIUSmNNN", "NMCf t5kRIAFI-.GIR1dWIITd OVEIIUSmNNN", "NMCf . tg5WTh=&+jTIG$CUIUG. $&iEPUSmNNN", "NMVf wI=nQIJFPTI!5tHIIUIJJGSJUIUSmBNN", "NmCg . 0EM@nQWERWR71LZUIIIUUUTUIIUAmNNN", "NMCf 0IWM&6xCNs-5UIZYUUUUUUUUUUTSmNNN", "NMBvtttttttttttrkAAGCq>&=1fHSZBSSSSSSSSSSSSVMNNN", "NNNBAZZZZAZAAAZAVMMmMACVVACnmMNMmmmmmmmmmMmMNNNN", "NNNMmmmmmmmmmmmmMNNNNMMNMMNNNNNNNBNNBNNBNNNNNNNN", "NNNNNNNNMNNNNNMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMNNNNNNNNNNNNNNNN" }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/sixteen-icon.c0000644000175300017530000002423412161170403016311 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 77 1", " c #797875", ". c #7B7A77", "X c #7F7E7B", "o c #858481", "O c #8A8986", "+ c #8D8C88", "@ c #969692", "# c #979692", "$ c #979693", "% c #9B9A96", "& c #9C9A97", "* c #9C9B97", "= c #9E9D99", "- c #9E9D9A", "; c #9F9E9A", ": c #A2A09C", "> c #A4A29F", ", c #A6A5A1", "< c #A9A7A3", "1 c #ABA9A5", "2 c #ACAAA6", "3 c #B0AEAA", "4 c #BBB9B5", "5 c #BDBBB7", "6 c #BEBCB7", "7 c #BEBDB8", "8 c #C1BFBA", "9 c #C2C0BB", "0 c #C3C1BD", "q c #C3C2BD", "w c #C4C2BD", "e c #C5C3BF", "r c #C6C4BF", "t c #C7C5C0", "y c #C8C6C1", "u c #C8C6C2", "i c #C9C7C2", "p c #C9C7C3", "a c #CAC8C3", "s c #CAC8C4", "d c #CBC9C4", "f c #CCCAC5", "g c #CCCBC6", "h c #CECCC7", "j c #CFCDC8", "k c #D0CEC9", "l c #D0CECA", "z c #D0CFCA", "x c #D1CFCA", "c c #D1CFCB", "v c #D2D0CB", "b c #D2D1CC", "n c #D3D1CC", "m c #D4D2CD", "M c #D4D3CE", "N c #D5D3CE", "B c #D6D3CE", "V c #D5D4CF", "C c #D6D4CF", "Z c #D6D5D0", "A c #D7D5D0", "S c #D8D6D1", "D c #D9D6D1", "F c #D8D7D1", "G c #D8D7D2", "H c #D9D7D2", "J c #DAD8D2", "K c #DAD8D3", "L c #DAD9D4", "P c #DBD9D4", "I c #DCDAD5", "U c #DCDBD5", "Y c #DDDBD5", "T c #DDDBD6", "R c #DEDCD7", "E c #DFDDD8", "W c #E2E0DB", /* pixels */ "NKKHKKKKKHKCnnnC", "KEIRRAPRRRFHHKCC", "KPieabAdeabCa<0A", "HW1X;AR3o:KC%@;n", "HIt0jnKd4aNAt,9A", "KHNAnjAnKNkAHCCC", "CAAAPRACAHRRNHAC", "CnCntenMnafPd2eH", "mbAhO.aAb+ j;@;b", "mbCm4wbCC4eK0>6A", "mkCmAAbmNCKPKCAC", "NAKAAACKHCCHnnCC", "mAa%rHAd*0KnCCCn", "mH<#,AK2@>CCCnCC", "NAe*9CAe;6CCnCnC", "NNAmANCHbHnCCCnC" }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 172 2", " c #4F4E4C", ". c #605F5D", "X c #62625F", "o c #646361", "O c #686765", "+ c #6C6B68", "@ c #73716F", "# c #72726F", "$ c #737370", "% c #757471", "& c #767573", "* c #777674", "= c #787674", "- c #797875", "; c #7A7976", ": c #7B7B78", "> c #7D7C79", ", c #7F7E7B", "< c #82817E", "1 c #83817E", "2 c #858481", "3 c #878682", "4 c #888683", "5 c #888783", "6 c #888784", "7 c #898885", "8 c #8C8A87", "9 c #8C8B87", "0 c #8D8D89", "q c #8E8D89", "w c #8E8D8A", "e c #91908D", "r c #92918E", "t c #92928E", "y c #94938F", "u c #949390", "i c #959490", "p c #969591", "a c #979693", "s c #979793", "d c #989794", "f c #999794", "g c #999894", "h c #9A9995", "j c #9B9A96", "k c #9B9B97", "l c #9C9B97", "z c #9C9B98", "x c #9C9C98", "c c #9D9C98", "v c #9E9C99", "b c #9F9D9A", "n c #9F9E9A", "m c #A09E9B", "M c #A09F9B", "N c #A1A09C", "B c #A2A09D", "V c #A2A19D", "C c #A3A19E", "Z c #A4A29E", "A c #A4A29F", "S c #A4A39F", "D c #A5A4A0", "F c #A6A4A1", "G c #A6A5A1", "H c #A7A5A1", "J c #A7A5A2", "K c #A6A6A2", "L c #A7A6A2", "P c #A8A7A3", "I c #A9A7A3", "U c #AAA8A4", "Y c #AAA9A5", "T c #ABA9A5", "R c #ABAAA6", "E c #ACAAA6", "W c #ACABA7", "Q c #ADABA7", "! c #ADACA8", "~ c #AFADA9", "^ c #B0AEAA", "/ c #B0AFAA", "( c #B1B0AC", ") c #B2B0AC", "_ c #B3B1AD", "` c #B5B4AF", "' c #B6B4B0", "] c #BAB8B4", "[ c #BBB9B4", "{ c #BFBDB8", "} c #BFBDB9", "| c #C0BEBA", " . c #C4C3BE", ".. c #C6C4BF", "X. c #C6C5C0", "o. c #C7C6C1", "O. c #C8C7C2", "+. c #C9C7C2", "@. c #C9C7C3", "#. c #CAC8C3", "$. c #C9C8C4", "%. c #CAC8C4", "&. c #CAC9C4", "*. c #CBC9C4", "=. c #CBC9C5", "-. c #CBCAC5", ";. c #CCCAC5", ":. c #CCCAC6", ">. c #CCCBC6", ",. c #CDCBC6", "<. c #CDCCC7", "1. c #CECCC7", "2. c #CFCDC8", "3. c #D0CEC9", "4. c #D0CECA", "5. c #D1CFCA", "6. c #D2D0CB", "7. c #D2D0CC", "8. c #D3D0CC", "9. c #D3D1CC", "0. c #D4D1CC", "q. c #D4D1CD", "w. c #D4D2CD", "e. c #D5D2CE", "r. c #D5D3CE", "t. c #D6D3CE", "y. c #D6D4CE", "u. c #D6D4CF", "i. c #D7D5CF", "p. c #D7D5D0", "a. c #D7D6D1", "s. c #D8D6D0", "d. c #D8D6D1", "f. c #D9D6D1", "g. c #D8D6D2", "h. c #D9D7D2", "j. c #D9D8D3", "k. c #DAD8D2", "l. c #DAD8D3", "z. c #DAD9D3", "x. c #DBD9D3", "c. c #DAD9D4", "v. c #DBD9D4", "b. c #DBDAD5", "n. c #DCDAD5", "m. c #DDDAD5", "M. c #DCDBD5", "N. c #DDDBD5", "B. c #DDDBD6", "V. c #DEDBD6", "C. c #DDDCD6", "Z. c #DDDCD7", "A. c #DEDCD6", "S. c #DEDCD7", "D. c #DFDCD7", "F. c #DEDDD7", "G. c #DFDDD7", "H. c #DFDDD8", "J. c #E0DDD8", "K. c #E0DED8", "L. c #E0DED9", "P. c #E1DFD9", "I. c #E1DFDA", "U. c #E2E0DA", "Y. c #E2E0DB", "T. c #E2E1DB", "R. c #E3E1DC", "E. c #E4E3DE", "W. c #E5E3DE", "Q. c #E6E4DF", "!. c #E7E5E0", "~. c #EBE9E3", /* pixels */ "r.r.6.9.9.9.9.9.0.6.r.6.6.r.6.0.6.9.9.9.9.9.9.r.r.r.r.r.r.r.r.r.", "r.u.B.B.B.B.B.B.B.B.B.B.v.B.B.B.A.A.B.B.B.H.f.u.r.r.r.p.r.r.r.r.", "9.B.~.L.L.R.Y.Y.R.Y.R.l.Q.Y.Y.Y.Y.Y.Y.Y.R.v.H.r.r.u.r.9.r.r.r.r.", "9.B.H.3.r.3.9.3.3.3.r.O.B.6.6.6.6.6.3.6.r.X.B.r.9.9.9.v.r.r.r.r.", "9.B.Y.6.r.L.p.B.H.r.u.-.A.u.r.L.p.A.H.r.p.,.B.9.l.H.p.V f.u.r.r.", "9.B.Y.r.r.v ,.' Z l.r.,.H.9.f.M ..T D p.r.-.B.f.} D D & n v.p.r.", "9.B.Y.3.B.> W 3 = [ v.-.B.9.A.3 D X.o #.l.%.v.B.V u V ~ d h p.9.", "9.B.R.3.A.= 4 6 % } v.-.B.9.L.< 7 7 o f.p.-.v.B.V d D ~ z r p.u.", "9.B.Y.9.r.M _ } ^ l.r.,.B.9.p.D E J J 5.p.-.v.v.[ v z : h p.p.r.", "9.B.Y.6.r.L.B.f.B.6.r.-.H.9.r.H.H.S.S.r.p.-.A.9.f.H.p.h r.p.9.r.", "6.B.Y.r.p.9.9.r.9.p.f.-.H.r.r.9.r.r.r.r.p.-.B.r.9.9.9.v.u.9.r.r.", "r.B.v.O.-.-.,.-.,.-.-.O.f.-.,.-.-.-.-.-.,.X.H.r.u.r.u.9.r.r.r.r.", "9.u.A.A.f.R.v.H.B.H.B.L.L.S.p.R.B.B.H.B.B.L.!.9.r.u.r.r.r.r.r.r.", "r.u.r.6.,.A.9.r.9.9.r.5.r.r.-.B.6.r.r.r.9.p.Q.9.r.9.9.v.9.u.r.r.", "r.r.r.r.2.A.9.p.S.p.S.S.r.p.,.H.r.r.B.u.p.H.R.9.f.H.f.~ v.9.r.r.", "r.r.r.r.,.H.9.9.W 1.W / r.r.-.B.r.p.E ,.,.' !.r.X.~ T % D B.r.r.", "9.p.p.9.1.S.9.f.$ ) > f.u.-.H.3.B.= / + O Y.B.V 0 h T p V p.9.", "p.-.-.p.-.S.9.S.- p h . .f.-.B.9.L.3 8 - o X.Y.V x T ) M 9 p.p.", "p.9.9.9.2.S.9.9.r J T T p.r.-.H.r.r.p D } D Q.f.' y p < p 9.p.9.", "r.r.p.9.>.S.9.r.L.A.B.A.9.r.-.H.5.r.L.B.f.R.R.9.l.B.r.0 9.p.9.u.", "r.r.u.r.2.H.9.p.r.r.r.r.r.p.-.H.r.f.9.r.r.f.R.r.p.9.u.v.p.9.u.9.", "r.r.-.-.-.f.-.-.-.-.-.-.-.-.-.p.O.,.-.,.-.2.R.r.u.9.u.9.9.p.9.p.", "r.r.B.B.L.H.v.v.v.B.B.B.A.Z.H.H.l.B.v.B.v.H.A.9.9.p.p.9.p.9.p.9.", "r.r.r.r.r.p.B.B.v.r.r.r.9.u.u.p.B.B.l.r.r.r.r.r.p.9.9.p.9.p.9.p.", "r.r.r.r.f.| M M [ j.9.w.u.9.p.X.M V ' f.9.p.9.p.9.p.9.p.9.p.9.p.", "r.r.r.3.L.P r d v H.9.u.u.9.H.~ w h d A.9.9.p.9.p.9.p.9.p.9.p.9.", "r.r.r.r.p.D V D v p.9.p.w.9.p.~ x W d 9.9.p.9.p.9.p.9.p.9.p.9.p.", "r.r.6.B.V & ~ ^ : h v.9.9.Z.~ $ T ) 2 0 v.9.p.9.p.9.p.9.p.9.p.9.", "r.r.r.r.l.M d z g p.w.w.w.9.v.D u M u 9.p.p.9.p.9.p.9.p.9.p.9.p.", "r.r.r.r.r.l.h r p.p.9.p.p.p.w.A.V 9 w.p.9.9.p.9.p.9.p.9.p.9.p.9.", "r.r.r.r.r.u.f.u.p.9.w.p.w.w.w.w.p.p.p.w.9.p.9.p.9.p.9.p.9.p.9.p.", "r.r.r.r.r.r.r.r.w.p.w.w.w.p.p.w.9.p.w.w.p.9.p.9.p.9.p.9.p.9.p.9." }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 67 1", " c #242423", ". c #2C2C2B", "X c #30302F", "o c #343433", "O c #3F3F3D", "+ c #403F3E", "@ c #41403E", "# c #4A4947", "$ c #504F4D", "% c #575654", "& c #5A5957", "* c #5D5D5A", "= c #62625F", "- c #656462", "; c #686765", ": c #696866", "> c #6D6C6A", ", c #706F6D", "< c #73726F", "1 c #757472", "2 c #787774", "3 c #7A7976", "4 c #7D7C79", "5 c #807F7C", "6 c #82817E", "7 c #868582", "8 c #888784", "9 c #8A8986", "0 c #8E8D8A", "q c #908F8C", "w c #92918E", "e c #969591", "r c #989693", "t c #9A9995", "y c #9E9D9A", "u c #A09E9A", "i c #A3A29E", "p c #A6A5A1", "a c #A9A7A3", "s c #ABAAA6", "d c #AEADA9", "f c #B0AFAB", "g c #B2B1AD", "h c #B6B5B0", "j c #B9B7B3", "k c #BBB9B5", "l c #BDBCB8", "z c #C1BFBA", "x c #C4C3BE", "c c #C7C5C0", "v c #C8C6C2", "b c #CCCAC5", "n c #CECDC8", "m c #D1CFCA", "M c #D5D3CE", "N c #D7D5D0", "B c #D8D6D1", "V c #DDDBD5", "C c #DFDDD8", "Z c #E1DFD9", "A c #E5E3DE", "S c #E7E5E1", "D c #E9E7E1", "F c #EAE8E3", "G c #EFEDE8", "H c #F0EEE9", "J c #F3F1EB", /* pixels */ "MMNMMMNMMMMMNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNMMM", "MMMMMMMMMMMMMMMMMMMMMMMMMNMMNMNMMMMMMMMMNMMMMMMM", "MMNMMMMMMmMMMMMMmMMMMMmMMMmMMMMmMMMMMMMNNMNMMMMM", "MMMMBVVVVVVVVVVVVVVVVVVVVVVVVVVVZNMMNMMMMMMMMNMM", "MMMBJFFFFFFFFFFFFAFGFFFFFFFFFFFFAAMMNMMMMMMMMNNM", "MMMVFnmMMmmmmmmMMxMVmmmMmmMmmmMmxAMMMMMMMMMMMMMM", "MMmVFMMMMMMNNMMMNvNZmMNNNNMMNNMMxAMMMMMNVMMNMMNM", "MMMVFMNMMMMMmMMMNvNZMMMmMMMmMMMMxAMNMMMmiVMMMMMM", "MMMVFmNMVZMNZZMMNvMZMMNZBNZZBMMMxDMMBBVM=uZMMMMM", "MNMVFmMVy&Mb*5VMBxNZmNm=eB:=xNMMxDmZu59696wZMMMM", "MMMVFmMNl#b2q:aZNvNZmNN31Nx>5AmMxAmV0tiisg6rNNMM", "MMMVFMNmF-M>z6tZMxNZMMAf1Hf*vNMMxAmVwihgssf>xNMM", "MMMVFmMVr 1d#*nNBxBZmNn$oa.+gVmMxFMZ06wqis2zVMMM", "MMMVFmMNczzBxmNMBbMZMNmzzxblvNMMxAmVzuir25gVMMMM", "MMMVFmMMNBVMNNMMMbBZmMMBVNNVNMMMxAMMNZAB=hVmMNMN", "MMMVFMMMMMMMMMMMBxMZMMMMMMMMMMMMxAMMMmMMxVMMMMMM", "MMmVFBBBBBBBBBBBBbNZNNNNNNNNNNNNxANNMMMMNMMMMNMM", "MMmZZzccvxvxvxvxbkMmxvvcvvvcvvvczAMNMMMMMMNMMMMM", "BMmZDBBBBBMMBBMBNMFVNNMVNNBNNNNNAAmNMMMNMNMMMMMM", "MMMMBZZmDZZZZZZZZZVZZNNFVZZCZZZVAFmNNMMMMMMMNMMM", "MMMMMmBzZMMMmMmMmMMMMbbZnMMmMmMmVAMNMMMMMMMMMMMN", "MMMBMMBzZBMMMMMMmMMMMbbAmMMMMMMmZFmNMMMMNNMMMMMM", "MMMMMMBcZBMMMZMMZZNMNbnAmNMVVMMVAAmMNZZN>zVmMMMM", "MMMMMMBzZBMMnfmnaubNMnnAmMNljNNaxGmNxshi<5xVMMMM", "BMMMMMBzABMVh.ftX c #9D9B97", ", c #9D9C98", "< c #9E9C98", "1 c #9E9C99", "2 c #9F9D99", "3 c #9F9E9A", "4 c #A09E9A", "5 c #A09E9B", "6 c #A09F9B", "7 c #A19F9B", "8 c #A2A09C", "9 c #A2A09D", "0 c #A2A19D", "q c #A4A29F", "w c #A5A39F", "e c #A5A4A0", "r c #A6A4A0", "t c #A6A5A1", "y c #A7A6A2", "u c #A8A6A2", "i c #A9A7A3", "p c #A9A8A4", "a c #AAA8A4", "s c #AAA9A5", "d c #ABA9A5", "f c #ABAAA6", "g c #ACAAA6", "h c #ACABA7", "j c #ADABA7", "k c #AEACA8", "l c #AEADA9", "z c #B0AFAA", "x c #B1AFAB", "c c #B1B0AB", "v c #B3B1AD", "b c #B4B2AE", "n c #B4B3AF", "m c #B6B4B0", "M c #B7B5B1", "N c #B8B6B2", "B c #BCBAB5", "V c #BCBAB6", "C c #BEBCB7", "Z c #BFBDB8", "A c #C0BEBA", "S c #C1BFBA", "D c #C2C1BC", "F c #C3C1BD", "G c #C4C2BD", "H c #C6C4BF", "J c #C7C5C0", "K c #C8C6C1", "L c #C9C7C2", "P c #C9C8C3", "I c #CAC8C3", "U c #CAC9C4", "Y c #CBCAC5", "T c #CCCAC5", "R c #CCCAC6", "E c #CDCBC6", "W c #CDCBC7", "Q c #CECCC7", "! c #CECCC8", "~ c #CECDC8", "^ c #CFCDC8", "/ c #CFCDC9", "( c #D0CEC9", ") c #D0CECA", "_ c #D1CFCA", "` c #D1CFCB", "' c #D2D0CB", "] c #D2D0CC", "[ c #D3D1CC", "{ c #D4D2CD", "} c #D5D3CE", "| c #D6D4CF", " . c #D7D5D0", ".. c #D8D6D0", "X. c #D8D6D1", "o. c #DAD8D3", "O. c #DBD9D4", "+. c #DCDAD5", "@. c #DDDBD6", "#. c #DEDCD7", "$. c #DFDDD8", "%. c #E0DED9", "&. c #E2E0DA", "*. c #E3E1DB", "=. c #E5E3DE", /* pixels */ "[ ( ( ( ( ( } W ( ( W ( [ Q W } ", "( Q [ ( W } w % y O.H X.S o h } ", "( [ X.[ } y k ( i h %.D ; K ^ .", "( ^ } n z U * ^ p & 4 W 8 i } ", "( ^ ' 1 X ( a z &.$ 8 } @ ; [ ", "( .y h ^ n 4 D 4 B *.a h W K .", "( 1 c K y e X.} .4 4 I h 4 S } ", "D 1 ' p @ K ( K ` S h ' = 4 ( ", "' w a I l [ } Q .( n ; B A m .", "' @.h a @.} } Q .} } .; A o.[ ", "H n %.8 4 X.Q I ( W K W .X I .", "' : N =.w i o.W } [ W o.z w . .", "( ( 8 c ' j a } } .} c w ^ I .", "H 4 Q : ; ' w h } [ n 4 [ # ; } ", "I a ( M ; I .W [ ' Q } Q 4 i [ ", "} ' X.' ^ .[ . .} .{ .' [ ." }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 58 1", " c #333231", ". c #4A4948", "X c #50504E", "o c #575654", "O c #585755", "+ c #5B5957", "@ c #5E5C5A", "# c #605F5D", "$ c #62615E", "% c #656462", "& c #686764", "* c #6A6966", "= c #6E6C6A", "- c #706F6C", "; c #71706E", ": c #767572", "> c #7B7976", ", c #7E7C79", "< c #807F7C", "1 c #82817E", "2 c #868481", "3 c #888784", "4 c #8A8986", "5 c #8D8C89", "6 c #918F8C", "7 c #92918D", "8 c #969491", "9 c #989693", "0 c #9B9A96", "q c #9E9C99", "w c #A09E9A", "e c #A3A29E", "r c #A6A4A0", "t c #A8A7A3", "y c #ABA9A5", "u c #AEADA9", "i c #B0AFAB", "p c #B3B2AE", "a c #B7B5B1", "s c #B9B7B3", "d c #BCBAB5", "f c #BFBDB9", "g c #C1BFBA", "h c #C4C2BE", "j c #C7C5C0", "k c #C9C7C2", "l c #CCCAC5", "z c #CFCDC8", "x c #D0CEC9", "c c #D5D3CE", "v c #D7D5D0", "b c #D8D6D1", "n c #DCDAD5", "m c #DFDDD8", "M c #E1DED9", "N c #E4E2DD", "B c #E7E5E0", "V c #E9E6E1", /* pixels */ "ccbvvvvvnvvvbccbcvvvcncccncccccc", "vxklllllkkkzhcmgzjzllhzzzhclzccc", "bkkxzzxlhzzcds8,phzzkfzlxdsXephb", "vlxbccbcjvcNt-qt*hmcvkccNe;e0pnc", "vlzccccckcNt%zfjd@jmzlcNe&zhhbcc", "vlzvccvbhNr$cMzzNd@jngNe&cmkcvcc", "vlxvcvzee9&cnp8rhNd@hme&cmsqehnc", "vllcccuqO5smft52ujNs$3=cmde,4tzc", "vkhkjfuc$qultc28hulzs pcjut35uhn", "vlxbcNq65esmk3*q7vvcms#hNhy40ucc", "vlzcNt&ptavn=7sp11Nczxd@hNduylcc", "vlzNt$cmkMn*0VkcN,1NckNd@hNzbncc", "bkgr%cngph=qNxkzcN,1lphNd#sdscvc", "bh8:lMd7577VcblxvvN111ehNp>3wuvc", "ns2haktcNccns$fMcclxvvcvclvcnc%ebccc", "clm3:Mczch@dMckzvcccckcvv%tblbcc", "czzN3>NblNh@fNkxvccccjnc%rBlcbcc", "clacN3>hugMh@fclccccclv%rNkpdccc", "ng4ucN343tfNh$ubbvcvch&tBky15uvc", "cc1uskpc1nekcs%hzklkc;0blpr35ujb", "nh3ugcrg#uuclcczzzzxzzclcsu,7ukb", "blepnbhqqqxbvccvvvcvvvcvccurrsbc", "cclbccbxjcvccccccccccvcvcvckkbcv", "ccbcccccncccccccvccvcccccccnbccc" }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 69 1", " c #1D1D1C", ". c #272726", "X c #2C2C2B", "o c #373635", "O c #3A3937", "+ c #3C3B39", "@ c #403F3D", "# c #41403E", "$ c #464543", "% c #4A4847", "& c #4D4C4A", "* c #51504E", "= c #555452", "- c #585755", "; c #595856", ": c #5E5D5B", "> c #605F5C", ", c #62615E", "< c #646361", "1 c #6A6967", "2 c #6D6C69", "3 c #706F6C", "4 c #72716F", "5 c #757471", "6 c #797775", "7 c #7A7976", "8 c #7E7C7A", "9 c #817F7C", "0 c #82817E", "q c #868481", "w c #898784", "e c #8B8986", "r c #8E8C89", "t c #908F8B", "y c #92918D", "u c #969591", "i c #989793", "p c #9B9996", "a c #9E9C99", "s c #A19F9B", "d c #A3A29E", "f c #A6A4A1", "g c #A9A7A3", "h c #ACAAA6", "j c #AEADA9", "k c #B1AFAB", "l c #B4B2AE", "z c #B7B5B1", "x c #B8B6B2", "c c #BCBAB6", "v c #BFBDB8", "b c #C1BFBA", "n c #C3C2BD", "m c #C7C5C1", "M c #C8C6C2", "N c #CCCAC5", "B c #CFCDC8", "V c #D0CFCA", "C c #D5D3CE", "Z c #D7D5D0", "A c #D8D6D1", "S c #DBD9D4", "D c #DFDDD8", "F c #E1DED9", "G c #E5E3DD", "H c #E7E5E0", "J c #E8E6E1", "K c #ECEAE5", "L c #F0EEE8", /* pixels */ "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCZCCCCCCCCCCCZCCC", "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCZCCCCCCCCC", "CCZZSZZZSZAACSSSCSCSCCCDCACACSZSZZZZZZZVZSCZCCCC", "CCZNnMMMMMnnMnnMMMnvCGClCvMMmmMlMMMnMbNFNzSMVZCC", "CCZbvVNNNNNBnnBBBMNyCfw:CuCMBBBlMBNNChln.9GpnSCZ", "CZZNBZZZAAZSVnSCCCGlyy4vwnSCCCSnCZZZCSry2tlsSCCC", "CCZMNZCCCCCCBnCCCCS&3jjd;,GCCCCnCCCVGi@skfiVZCCC", "CCZmNZCCCCCABnCCSS$8JngCG,>GCCCnVZVGi+NSkxSZCCCC", "CCZmNZCCCZCCCMCCA$9JBZNZCG,:GCCnCVGiOBSCVVCCCCCZ", "CCZmNZCCCCCDnlSS$9JVSBjCACG,>GCnBGiONSCSxbSZCCCC", "ZCZMNCCCCCCdaad%0JBSjifulSCG,>GbGi+NSCNisspCZCCC", "CCZMBSCZCSfa51u0AZSxa8>zpnSCG,,Sy+VSCZix18laSCCC", "CCZMmVBNVniDeXyngCVuNZ70CiCNBD,O&bCNCklrt8ffnZCZ", "CCZmvmmnMxiSc20naBmrDi&NNyNmnnS=*SnnNdcry8fjbSCC", "CCZMBSZCZSdl42cpMADkl;&xhvSZZCZC,,JCZViN16NiZCCC", "CCZmNCCCZS&6lxilSVG2=zxf$wJBCCCnF,>GVSxyxztnZCCZ", "CCZmNZCZS$0SgaMZVH2-AxpbV#eGCCAnVG>>GCSNdfCSCCCC", "CCZMNCZS$0GCCBSCH2-FZANASC#eGVCnVCG,>GVZVVZVCCCC", "CCZmNGZ$9JVDBnDG1-FCCCbCVAC#eHSbCSCG>>GFnNFCCZCC", "CCZmpj&9JVZlsph2-FCCCCnCCCSC$0ksacZCG,>ksijSCCCC", "CCSxsy3JVSkawil;SACCZCnACCCSn:r1kicZCF>s8hfhSCCZ", "CCZn$CfMZBuFq:LjxAVCCVbCCCCShnvw:SuZZcjF>qKyNZCC", "CCDk8GpjNlpGM2KkaNbnnbznvnnMynD5iFtbNsfLdqLsbZCC", "CCSj5SdVCCiZ5XxfvACZACnACCCDjc5 sCdZSnsS*$MpBZZC", "CCZBj8:GVScyhktfSCCCCCnCCCCCSpalk,0KBSfikhrzSCCC", "CCZban<>GCZnspxZCCCCCCnZCCCCCCludb$wGBZxisnZCZCC", "CCZMCSG:,GCSCNFCCCCCCCnCCCCCCCDnZFC#rGCFBCSCCCZC", "CCZMNCCG>>GCNnCCCCCCCCnCCCCCCCCnBCSC#wGCnMZCCCCC", "CCZmNZCCG>>GNnZCCCCCCCnCCZCCCCZnVZCZC@rJnNZCCCZC", "CCZmMZCCCG>,DnZCCCZCCCnCCCCCCCCnCCCVZC@rSMZCCCCC", "CCZNZCCZCCG<:ZCCZZZZCCnZZZCCSCSnCZZZCSC$0CCCZCCC", "CCSjwCbnnnnV;:CbnnnnnnxnnnnnnnnzbnnnnbMnXbSCCCCZ", "CCSboeGBVVVCD=:DCBCCBBnCVCCBCCCnBCVCVVG3-VZCCCCC", "ZCZNM$rJVZZZNC<,GCCCCCnCCCCCCCCnCCCCCG2-VNZCCCZC", "CCZmCC#rJBCZNmJ:,GCCCCnCCCCCCCZnCCCCG3*GMNZCCZCC", "CCZmMSC+rJVZNnCG:,GCCCnCCCCCCCZnBCCG3*GZnNZCCCCC", "CCZMCCZZ#rJCCBZCG:,GCBnCZCCCCCCnCVG3=GCZBVZCCCCZ", "CCZnjZZZC+rDlfBZCG>,GCnCCCCCCCCnVG3*FCSVhkZZCCCC", "CCZndumCZC$7ddyxSVG>,GnCCCCCCCCnG2*FCZcyssuMZCCC", "CCSxBZAAAZSCSB1;GSCCic:1cpZCZC", "CCBJ8dhlBxpGc2KjdBnnS=4CmmMMmnC8%CMnBdv0htshbSCC", "CCCC-xfcVnuGp%ZjgCNNMCnBNNNNNNNnCNNNCjzp4 c #121212", ", c #5C5B59", "< c #62625F", "1 c #656462", "2 c #737270", "3 c #3E963C", "4 c #57A254", "5 c #6BAD68", "6 c #82B87D", "7 c #969491", "8 c #9C9B97", "9 c #9D9C98", "0 c #9F9E9A", "q c #85BB81", "w c #A4A29F", "e c #AAA9A5", "r c #B0AEAA", "t c #B1AFAB", "y c #B3B2AD", "u c #B3B2AE", "i c #B4B2AE", "p c #B6B4B0", "a c #B6B5B0", "s c #B6B5B1", "d c #B7B5B1", "f c #B9B8B4", "g c #BAB8B4", "h c #BAB9B4", "j c #BBB9B5", "k c #BEBDB8", "l c #9EC399", "z c #A7C7A1", "x c #B1C9AB", "c c #B5CEAF", "v c #B7CEB1", "b c #BACDB4", "n c #C2C1BC", "m c #C3C2BD", "M c #C4C2BD", "N c #C4C3BE", "B c #C5C3BE", "V c #C5C3BF", "C c #C0D4B9", "Z c #C2D0BC", "A c #C7C5C1", "S c #C7C7C0", "D c #C7C6C1", "F c #C8C6C1", "G c #C8C4C2", "H c #C8C6C2", "J c #C8C7C2", "K c #C9C7C2", "L c #C9C6C3", "P c #C9C7C3", "I c #CAC7C4", "U c #CBC6C5", "Y c #CDC6C6", "T c #C9C8C2", "R c #CAC9C4", "E c #CBC9C4", "W c #CBC9C5", "Q c #CBCAC5", "! c #CCC9C5", "~ c #CCCAC5", "^ c #CCCAC6", "/ c #CDCAC6", "( c #CDCBC6", ") c #CDCAC7", "_ c #CDCBC7", "` c #CECCC7", "' c #CECCC8", "] c #CFCDC8", "[ c #CFCDC9", "{ c #D1CACA", "} c #D1CBCA", "| c #D2CBCB", " . c #D0CCC9", ".. c #D1CFCA", "X. c #D1CFCB", "o. c #D3CBCC", "O. c #D4CBCD", "+. c #D3CFCC", "@. c #D5CFCE", "#. c #C8D6C2", "$. c #CDD3C6", "%. c #D2D0CB", "&. c #D3D1CC", "*. c #D4D3CE", "=. c #D6D9CF", "-. c #D7D5D0", ";. c #D7D5D1", ":. c #D7D6D0", ">. c #D8D6D1", ",. c #D9D7D2", "<. c #D7DAD1", "1. c #D8DAD1", "2. c #D9D8D3", "3. c #DAD8D3", "4. c #DAD9D3", "5. c #DBD9D4", "6. c #DCDAD5", "7. c #DDDAD6", "8. c #DDDBD6", "9. c #DDDCD6", "0. c #DEDCD7", "q. c #DEDED7", "w. c #DFD9D8", "e. c #DFDDD8", "r. c #DFDED8", "t. c #DFDFD8", "y. c #E0DED8", "u. c #E1DFD9", "i. c #E1DCDA", "p. c #E1DFDA", "a. c #E3DFDC", "s. c #E4DEDD", "d. c #E5DFDE", "f. c #E2E0DB", "g. c #E4E3DD", "h. c #E7E4DF", "j. c #E8E3E1", "k. c #E8E6E1", "l. c #EDEBE6", /* pixels */ " O - + - o - - X - O - + - O ", "O g [ o.W s H / &.H s W { &.g O ", "- [ g.l e.W y.A w y.W j.z C @.$ ", "+ { v 3 $.I y.y , 1.V i.4 6 o.X ", "- / q.b 1.H y.k r q.H s.Z #.X.% ", "O d / Y I i m { X.V i H I { s X ", "$ H y.,.5.V -.1.-.-.V 1.,.6.H - ", "$ X.V 2 6.H ,.6.6.,.H w.1.y./ $ ", "$ ` X.< H / ,.6.q.,.A 6.6.y./ - ", "- H ,.H 6.V ,.5.,.,.V 6.,.6.H $ ", "X p [ / H i V H H V i H H / s O ", "- / 6.T 6.T 6.=.1.6.H y.X.q./ - ", "- T ,.1 H T i.x 5 w.m k.7 e &.O ", "$ X.6.8 h.T s.c q s.A l.0 9 ,.$ ", "% g / / / d T V / T s / V / g $ ", " X - - $ $ - - - - X - - - O " }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 217 2", " c #000000", ". c #010101", "X c #0C0C0C", "o c #0D0D0D", "O c #0F0F0F", "+ c #10100F", "@ c #101010", "# c #1B1B1A", "$ c #282827", "% c #363534", "& c #373635", "* c #3F3E3D", "= c #444442", "- c #4C4B49", "; c #4C4C4A", ": c #4E4E4C", "> c #565553", ", c #5B5A58", "< c #5D5C5A", "1 c #605F5D", "2 c #62615E", "3 c #6A6967", "4 c #6C6B69", "5 c #6E6D6B", "6 c #6F6E6C", "7 c #72716E", "8 c #72716F", "9 c #747370", "0 c #767573", "q c #817F7D", "w c #1C8A1B", "e c #339731", "r c #359834", "t c #3E9A3C", "y c #429D40", "u c #449D42", "i c #449E42", "p c #489B46", "a c #489E46", "s c #50A24D", "d c #51A34F", "f c #54A451", "g c #62A85F", "h c #63AA5F", "j c #6AAC67", "k c #6DAF6A", "l c #70AF6C", "z c #70AF6D", "x c #72B06F", "c c #73B06F", "v c #74B171", "b c #76B172", "n c #7EB57A", "m c #81807D", "M c #81B67C", "N c #888683", "B c #8B8A87", "V c #908F8B", "C c #908F8C", "Z c #969591", "A c #969592", "S c #A09F9B", "D c #88B884", "F c #8FBB8A", "G c #92BD8D", "H c #A7A6A2", "J c #ABA9A6", "K c #ACAAA6", "L c #ADACA8", "P c #AFADA9", "I c #B4B3AF", "U c #B5B3AF", "Y c #B5B4B0", "T c #B6B4B0", "R c #B8B7B3", "E c #B9B7B3", "W c #BAB8B4", "Q c #BBB9B5", "! c #BCBAB6", "~ c #BDBBB7", "^ c #BFBEB9", "/ c #9EC199", "( c #A2C19D", ") c #B3CAAD", "_ c #B4CAAE", "` c #B5CBAF", "' c #B7CBB1", "] c #B8CBB2", "[ c #BDCDB7", "{ c #C2C0BC", "} c #C3C1BC", "| c #C3C1BD", " . c #C4C2BE", ".. c #C5C3BE", "X. c #C0CEBA", "o. c #C7C5C0", "O. c #C8C6C1", "+. c #C8C6C2", "@. c #C8C7C2", "#. c #CBCAC5", "$. c #CCCAC5", "%. c #CDCBC7", "&. c #CCCCC5", "*. c #CCCCC6", "=. c #CDCCC7", "-. c #CDCDC7", ";. c #CECCC7", ":. c #CECDC7", ">. c #CECCC8", ",. c #CECDC8", "<. c #CFCDC8", "1. c #CFCDC9", "2. c #CFCEC8", "3. c #CFCEC9", "4. c #D0CEC9", "5. c #D0CECA", "6. c #D1CFCA", "7. c #C8D2C2", "8. c #CBD3C5", "9. c #CCD3C6", "0. c #CCD4C6", "q. c #CED4C7", "w. c #CED4C8", "e. c #D2D0CB", "r. c #D2D0CC", "t. c #D3D1CC", "y. c #D3D1CD", "u. c #D4D2CD", "i. c #D5D3CE", "p. c #D4D7CD", "a. c #D6D4CF", "s. c #D4D6CE", "d. c #D5D7CE", "f. c #D7D5D0", "g. c #D7D7D0", "h. c #D8D6D1", "j. c #D8D6D2", "k. c #D9D7D2", "l. c #D9D7D3", "z. c #D8D9D1", "x. c #D9D8D2", "c. c #DAD8D3", "v. c #DAD9D3", "b. c #DBD9D4", "n. c #DCD9D5", "m. c #DCDAD5", "M. c #DDD9D6", "N. c #DDDAD6", "B. c #DDDBD6", "V. c #DEDAD7", "C. c #DCDCD5", "Z. c #DDDCD6", "A. c #DDDDD6", "S. c #DEDCD6", "D. c #DEDCD7", "F. c #DFDCD7", "G. c #DEDDD7", "H. c #DFDDD7", "J. c #DFDAD8", "K. c #DFDCD8", "L. c #DFDDD8", "P. c #DFDFD8", "I. c #E0DDD9", "U. c #E0DED8", "Y. c #E0DED9", "T. c #E1DED9", "R. c #E0DFD9", "E. c #E1DFD9", "W. c #E1DEDA", "Q. c #E1DFDA", "!. c #E2DFDA", "~. c #E2DEDB", "^. c #E3DFDB", "/. c #E3DCDC", "(. c #E3DDDC", "). c #E3DFDC", "_. c #E4DDDD", "`. c #E6DEDF", "'. c #E2E0DA", "]. c #E2E0DB", "[. c #E3E0DB", "{. c #E3E1DB", "}. c #E3E1DC", "|. c #E4E2DD", " X c #E5E3DD", ".X c #E5E0DE", "XX c #E6E0DF", "oX c #E7E0DF", "OX c #E5E3DE", "+X c #E6E3DE", "@X c #E6E4DE", "#X c #E6E4DF", "$X c #E7E4DF", "%X c #E7DEE0", "&X c #E8DEE1", "*X c #E9DEE1", "=X c #E7E5E0", "-X c #EAE2E3", ";X c #E8E6E1", ":X c #EBE2E4", ">X c #ECE0E4", ",X c #ECE0E5", ".>.<.e.| J a.%.4.<.-.-.-.a.J ..4.<.4.-.3.3.y...O ", " @ a.Y.Z.[.OX[.Z.[.e.E OXU.Y.Y.^.m.Y.OXE 4.OXY.Z.^.L.Z.[.y.@ ", " + 4.Y.m.e.| 4.v.Y.-.U Y.v.m.8.q.^.l.Y.U %.U.l.m.4.l.b.L.4.+ ", " + 4.m.OXH < $ Q ;X4.T Y.m.v.M i G `.[.U 4.m.Z.>.% q 5Xm.4.+ ", " + 4.Y.l.5XQ * Y.Y.4.U Y.v.`.] e F &XY.T 4.Y.l.qXq 7 eXb.4.+ ", " + 4.Y.l.;X- B 8Xm.3.T OXm.a._ a b *XY.T 4.Y.v.;X5 1 $Xm.4.+ ", " + 3.m.c.a.5 a.m.m.>.T Y.m.0.z j X.I.Y.U %.m.Y.O., > ^ OX-.+ ", " O a.Y.Z.U.8XY.Z.OX4.E OXU.Y.-X c #595856", ", c #5C5B59", "< c #60605D", "1 c #666563", "2 c #6B6A67", "3 c #6E6D6A", "4 c #72716E", "5 c #777673", "6 c #7C7A78", "7 c #807F7C", "8 c #068006", "9 c #088207", "0 c #0A840A", "q c #0C880C", "w c #148513", "e c #178A16", "r c #1C8D1B", "t c #249123", "y c #2D952B", "u c #31962F", "i c #369834", "p c #3B9A39", "a c #409C3E", "s c #439E41", "d c #4B9F49", "f c #48A046", "g c #4CA149", "h c #59A656", "j c #5EA95C", "k c #61A95E", "l c #6AAD66", "z c #6CAE68", "x c #71B06E", "c c #75B171", "v c #7DB579", "b c #80B67C", "n c #858481", "m c #888784", "M c #898884", "N c #8D8C89", "B c #908F8B", "V c #92918D", "C c #979692", "Z c #989794", "A c #9C9B97", "S c #A09E9B", "D c #85B781", "F c #87B882", "G c #8EBB89", "H c #90BC8C", "J c #97BF92", "K c #98BF94", "L c #A3A29E", "P c #A7A5A1", "I c #A8A7A3", "U c #ABAAA6", "Y c #AFADA9", "T c #B0AFAB", "R c #B2B1AD", "E c #B7B6B2", "W c #B8B6B2", "Q c #BCBAB6", "! c #9CC197", "~ c #9EC299", "^ c #A0C29B", "/ c #A7C5A2", "( c #ADC7A8", ") c #AFC8AA", "_ c #B2CAAD", "` c #BACCB4", "' c #C6C4BF", "] c #C1CFBB", "[ c #C4D1BE", "{ c #C7C5C0", "} c #C8C6C2", "| c #CCCAC5", " . c #CFCDC9", ".. c #D1CFCA", "X. c #CBD3C4", "o. c #CFD5C9", "O. c #D4D3CD", "+. c #D6D4D0", "@. c #D9D7D2", "#. c #D7D8D0", "$. c #DBD9D4", "%. c #DFDDD8", "&. c #E2DEDA", "*. c #E4E2DD", "=. c #E7DEE0", "-. c #E9DFE2", ";. c #E7E5E0", ":. c #EAE4E3", ">. c #ECEAE5", ",. c #F0E2E8", "<. c #F1EEE9", "1. c #F8F6F0", /* pixels */ " ", " ", " ", " o o o o o o o X o o o o o . o o o o o o o o o o o o . o o o o o o o o o o o o o ", " X ' O...................O.Q S @................... .@.S Q O...................O.{ X ", " o O.&.%.%.%.%.%.%.%.&.%.*.} U ;.%.%.&.%.&.%.%.&.%.%.;.U } *.%.&.%.&.&.&.&.%.%.&.O.o ", " o .%.@.$.@.$.@.@.@.%.@.&.' I *.@.@.$.@.@.@.@.@.@.@.*.I ' &.@.@.%.@.@.@.@.$.@.%. .o ", " o ..%.@.%.%.$.=.&.%.@.%.&.' I *.@.%.@.*.;.*.:.%.%.@.*.I ' &.@.%.@.&.-.&.&.$.$.%...o ", " o ..&.@.%.#.:.H t ` &.%.&.' I -.@.+.=.T $ # < O.%.@.*.U ' &.$.$.&.o.h i x &.@.%...o ", " o ..%.$.@.&.[ r % / &.@.&.' P :.@.@.=.T b Q o n >.#.*.I ' &.$.@.:.d i ^ ) &.@.%...o ", " o ..%.$.$.-.d H t ) :.O.*.' I -.@.$.$.%.1.O.. N >.+.*.I ' &.@.%.@.r u y z &.@.%. .o ", " o ..%.@.&.) w b q k @.$.&.{ I -.@.$.$.*.R O , :.@.@.*.I ' &.@.%.%.e / X.% X.&.%.O.o ", " o O.%.@.&.] z k % d @.%.%.' I *.@.@.*.R o , U *.@.*.U ' &.@.@.&.z i a p %.@.&...o ", " o ..%.%.@.&.,.:.J @.&.@.*.' I :.@.@.*.' M V 5 R *.@.*.I ' *.@.%.%.&.J F O.%.@.&...o ", " o .%.@.@.$.+.@.-.%.@.$.*.' I *.@.@.@.%.>.:.<.*.@.@.*.I ' *.@.@.@.@.-.:.%.@.@.%. .o ", " o +.*.&.*.&.&.&.%.&.&.&.*.} Y ;.&.*.%.*.$.%.%.%.*.%.:.U | *.&.*.*.&.&.%.&.*.&.*.O.o ", " o Q } ' ' ' ' ' ' ' ' ' } R C .' ' ' ' ' ' ' ' ' ' .C T } ' ' ' ' ' ' ' ' ' } Q o ", " . L I I I I I I I I I I Y C 7 T L I I I I I U U I L T 7 C Y L I I I I I I I I I L . ", " o @.:.*.*.*.*.*.*.*.*.*.*. .T <.&.*.*.*.*.*.*.*.*.&.<.T .>.*.*.*.*.*.*.*.*.*.*.$.o ", " o .%.@.$.$.$.@.#.$.$.@.&.' L *.@.$.@.$.@.$.@.$.@.@.*.L ' &.@.$.$.$.$.$.$.@.@.%...o ", " o ..&.@.$.$.*.*.*.&.@.$.&.' I *.$.$.$.$.$.$.$.$.$.@.*.I ' &.@.$.$.@.%.@.%.$.$.%. .o ", " o ..%.@.$.&.' R L ' &.@.*.' I *.$.$.%.%.$.$.$.$.$.@.*.U ' *.$.$.$.%.@.%.@.$.$.&...o ", " o ..%.$.@.>.; O - N *.@.&.' I *.@.$.$.%.$.&.$.$.&.$.*.I ' &.@.@.%.$.$.$.%.%.$.&. .o ", " o ..&.$.@.>.= @ A @.%.$.&.' I *.$.$.$.$.$.$.$.$.$.$.*.I ' *.@.%.@.%.$.$.%.$.$.%. .o ", " o O.%.@.$.*.B 4 + : :.@.*.' I *.$.$.$.$.&.$.$.$.&.@.*.I ' &.@.%.@.%.@.%.@.%.$.%...o ", " o .%.@.%.@.{ 1.3 @ *.@.&.' I *.@.$.$.$.$.$.$.$.$.@.*.I ' *.@.@.%.$.%.@.%.@.$.%...o ", " o O.%.$.%.@.- @ + S *.@.&.' I *.$.$.$.$.$.%.$.$.$.$.*.I ' &.@.$.$.%.@.%.@.%.@.%...o ", " o .&.@.$.&.@.{ @.*.%.@.&.' I *.@.$.&.$.$.$.$.%.$.$.*.I ' *.$.$.%.@.%.@.%.%.$.%...o ", " o .%.@.@.&.@.&.&.+.$.+.=.' L *.@.$.$.$.$.$.$.@.$.@.&.L ' &.@.@.$.$.$.@.$.@.@.%.| o ", " o @.;.*.*.&.-.&.&.-.&.&.,. .Y >.*.*.*.*.*.*.*.*.*.*.<.Y .:.*.*.*.*.*.*.*.*.*.;.@.o ", " . A U I I P P I P P P P Y N b Y L I I I I I I U I L Y 7 C U P I I I I I I I I U S . ", " o Q } ' ' { { { { { { ' } Y C .' ' ' ' ' ' ' ' ' ' .C T | ' { ' ' { ' ' ' ' } Q o ", " o O.*.*.*.&.&.&.&.*.&.&.*.} U >.%.&.&.&.&.*.*.%.&.%.:.U | ;.&.*.%.&.&.*.&.&.&.*.O.o ", " o ..%.@.@.%.O.@.@.O.%.@.&.{ P ;.+.$.$.$.+.+.#.$.$.@.*.P { %.$.$.$.$.#.+.$.$.@.%.o.X ", " o ..%.@.%.@.>.<.>.*.%.%.&.{ P &.$.$.$.&.,.,.&.$.$.@.;.P { *.$.$.$.%.>.>.$.$.@.%. .o ", " o ..&.@.@.@.4 , 1 n %.@.&.{ P ;.$.$.%.X.l j / &.$.$.;.P { %.$.$.%.' 2 6 $.%.$.%.O.o ", " o O.%.%.%.$.n b O & :.$.*.{ P ;.$.@.&._ v c 9 ] &.$.&.P { &.$.@.*.Y * O $.%.$.%...o ", " o ..%.@.@.$.,.@.O L :.#.%.{ I &.$.%.$.=.K s p $.@.$.;.P { &.$.%.+.>.Y o +.$.$.%...o ", " o ..%.@.%.+.,.4 + *.@.$.*.{ P &.$.%.@.-.) l t X.&.$.*.P { &.$.$.@.>.U o >.%.@.%...o ", " o ..%.%.@.&.+.o M :.@.#.*.{ P ;.$.$.&.^ K b 9 [ &.+.;.P { *.@.$.%. .2 X Z +.%.&...o ", " o ..%.$.@.&.W > +.&.@.&.&.{ P *.@.$.&.K d g / &.$.@.*.I ' &.%.@.*.I * : ; .%.%. .o ", " o ..%.@.$.+.&.:.@.@.@.@.&.{ I *.$.$.$.=.:.-.&.@.$.@.*.I ' &.@.%.@.*.>.:.:.%.@.%. .o ", " o O.&.%.%.&.&.@.&.&.&.&.&.} U ;.%.%.%.%.$.&.%.%.%.%.;.U } *.%.%.%.%.$.%.$.%.%.&.O.o ", " X { O..... .............O.Q A $. ..... .............@.S Q O. .......o..... . .O.{ o ", " o o o o o o o o X o o o o . o o o o o o X o o o o o . X o o o o o o o o o o o o ", " ", " ", " " }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/tents-icon.c0000644000175300017530000004121612161170404015767 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 230 2", " c #00B000", ". c #00B100", "X c #02B500", "o c #02B703", "O c #13BE09", "+ c #35BF13", "@ c #3BC62B", "# c #42C72F", "$ c #58C640", "% c #65CF45", "& c #72DA4E", "* c #72C65B", "= c #6AD850", "- c #72D652", "; c #7DD059", ": c #CABD10", "> c #BBCC39", ", c #BFDF34", "< c #D1C819", "1 c #C5C220", "2 c #85CE4D", "3 c #8FCC4A", "4 c #86D750", "5 c #9DD259", "6 c #B6D040", "7 c #A3D257", "8 c #AEDA5B", "9 c #BFE345", "0 c #B5E15C", "q c #BCEB5C", "w c #8BD263", "e c #86DA68", "r c #9BD464", "t c #97D66B", "y c #98D66D", "u c #98D86D", "i c #99D96D", "p c #9CDE6F", "a c #9CD771", "s c #9BD077", "d c #9ADD71", "f c #9CDD71", "g c #9FDD71", "h c #9DDF71", "j c #9EDC75", "k c #AADB69", "l c #A3DC79", "z c #8DE66A", "x c #96E46F", "c c #9EE06D", "v c #9DE071", "b c #9EE372", "n c #9EE273", "m c #9BEB7B", "M c #9FE978", "N c #A0EA6E", "B c #A0EA6F", "V c #A0E373", "C c #A5E771", "Z c #A1E374", "A c #A1E077", "S c #A2E675", "D c #A3E675", "F c #A1EB70", "G c #A2E977", "H c #A4E974", "J c #A5E975", "K c #A6E977", "L c #A5EB76", "P c #A6EA76", "I c #A4EC77", "U c #A7ED77", "Y c #A9EA75", "T c #A8EB77", "R c #A8EF77", "E c #A2E378", "W c #A7E978", "Q c #ADEA7A", "! c #A8EF78", "~ c #A8EE79", "^ c #A9EF79", "/ c #ACEA7E", "( c #A5F172", ") c #A6F174", "_ c #A7F274", "` c #ABFF72", "' c #AAF877", "] c #AEFD76", "[ c #A6F178", "{ c #A8F079", "} c #AAF579", "| c #ACF57B", " . c #ACF47C", ".. c #ADF77C", "X. c #AEF57E", "o. c #AEFA7B", "O. c #B2F072", "+. c #B1F174", "@. c #B2F67D", "#. c #B0FD7B", "$. c #B1FF7A", "%. c #B2F97F", "&. c #B2FD7C", "*. c #B5FF7E", "=. c #A09F9A", "-. c #A5A49F", ";. c #A9A8A3", ":. c #B8B8B2", ">. c #BCBAB6", ",. c #C0BEBA", "<. c #99CB88", "1. c #9ECB8A", "2. c #9DCC8C", "3. c #AED38A", "4. c #ABDB88", "5. c #A7CC92", "6. c #ADCC97", "7. c #B2CE9A", "8. c #AFD092", "9. c #B2D697", "0. c #B2DE92", "q. c #B4D09E", "w. c #B5D29E", "e. c #AAF382", "r. c #ADF081", "t. c #ABFF86", "y. c #B4F580", "u. c #B6F483", "i. c #B3FA81", "p. c #B6FF82", "a. c #B6FF83", "s. c #B9FF82", "d. c #B8FF83", "f. c #BBFF83", "g. c #B8FF84", "h. c #B9FF84", "j. c #B9FF85", "k. c #BDFD87", "l. c #BBCDAB", "z. c #BCCDAA", "x. c #BCCEAB", "c. c #BDCFAB", "v. c #BDCFAC", "b. c #B6D7A1", "n. c #B7DAA2", "m. c #B7DCA3", "M. c #BCD2A9", "N. c #BFD2AC", "B. c #BFCAB1", "V. c #C0D58B", "C. c #C0D2AD", "Z. c #C5CABB", "A. c #C0D5B1", "S. c #CBD1BF", "D. c #CCDABC", "F. c #C5E2AD", "G. c #C8E5AF", "H. c #CAEDAF", "J. c #CAE8B0", "K. c #CDEDB2", "L. c #CAC7C4", "P. c #C9C8C2", "I. c #CAC9C3", "U. c #CACAC3", "Y. c #D0CFCA", "T. c #D0D7C4", "R. c #D1D4C7", "E. c #D1D3C8", "W. c #D2D1CB", "Q. c #D1D5C8", "!. c #D2D5C8", "~. c #D3D6CA", "^. c #D4D2CD", "/. c #D5D3CF", "(. c #D1D7CC", "). c #D5D7CC", "_. c #D6D6CD", "`. c #D6D4CF", "'. c #D6D5CF", "]. c #D7D6CF", "[. c #D7D8CE", "{. c #D7D1D3", "}. c #D7D5D0", "|. c #D8D6D1", " X c #D9D7D2", ".X c #DDD4D6", "XX c #DAD8D3", "oX c #DADAD2", "OX c #DBD9D4", "+X c #DBDAD4", "@X c #DCDAD4", "#X c #DCDAD5", "$X c #DDDBD5", "%X c #DDDAD6", "&X c #DDDBD6", "*X c #DEDAD7", "=X c #DEDBD7", "-X c #DEDCD7", ";X c #DFDCD7", ":X c #DFD7DB", ">X c #DFD7DC", ",X c #DEDAD8", " 1 G 2 4 N.eX:._.", "R.a [ g ; % w S U t Q C M.pX3X3X", "!./ d.u.# . - k.a.P a.o.N.gX;.}.", "R.A o.S c 5 v U } g .( x.pXP. X", "R.j } v V +.d P [ u { F x.pX}.%X", "!./ d.e.0 < Y i.h.W h.*.N.pX-._.", "E.l @.E 7 > r G R u { b x.pX}. X", "5X* o $ e.t.v { ..g ..) v.pXP.+X", ".Xe + & u.h.R %.h.P *.] C.eX;.}.", "E.9.3.3.d ~ y A P s 0.4.B.pX3X+X", "R.Z.kXI.) $.N ' ` 9.wX{.L.3X>.XX", "[.!.9X_.G.K.F.J.K.D.%XXX^.-X>.XX", "$X8XoX5X4X>X4X>X>X,X%X+X%X%X9X-X" }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 183 2", " c #535250", ". c #787774", "X c #00AD00", "o c #02B402", "O c #06BE04", "+ c #0BBA08", "@ c #12BC0C", "# c #439F10", "$ c #48A617", "% c #7BA228", "& c #13C40E", "* c #17C010", "= c #20C417", "- c #27C01C", "; c #2BC11E", ": c #27C819", "> c #29C91A", ", c #37C128", "< c #39C229", "1 c #3CCB2B", "2 c #42D82B", "3 c #5EC435", "4 c #4DD237", "5 c #58DB3E", "6 c #7AAE57", "7 c #7EB15B", "8 c #5AC940", "9 c #5ED743", "0 c #65C64B", "q c #66C84B", "w c #6BCA4C", "e c #68D141", "r c #6CD14D", "t c #77CF55", "y c #6FD350", "u c #74D055", "i c #7BD45A", "p c #75E350", "a c #86921E", "s c #AFA237", "d c #CEA600", "f c #CCAE00", "g c #CCB107", "h c #C9B509", "j c #C9B810", "k c #BFD43C", "l c #CCC71A", "z c #CAC920", "x c #C5CC29", "c c #C5D02E", "v c #CDD32A", "b c #C6D436", "n c #C9DA37", "m c #C4D63A", "M c #C8DF3F", "N c #81B65D", "B c #83BC5E", "V c #B2AC40", "C c #86BE61", "Z c #89BC64", "A c #8EA37D", "S c #92B37A", "D c #8CC448", "F c #A9C44D", "G c #BDDC49", "H c #A6C053", "J c #ABD85F", "K c #81E65C", "L c #BEEB5C", "P c #8AC363", "I c #8DC965", "U c #92CB6B", "Y c #86D660", "T c #89DD62", "R c #92D366", "E c #95D967", "W c #95D46B", "Q c #99D46D", "! c #95DB6A", "~ c #9ADB6E", "^ c #97C77B", "/ c #9BC47C", "( c #94D672", ") c #9CD670", "_ c #95DA72", "` c #9CDD71", "' c #9FD47B", "] c #A7DF64", "[ c #BAD365", "{ c #A3D773", "} c #A1DD73", "| c #9BE36E", " . c #85F161", ".. c #8DF868", "X. c #98FD6C", "o. c #9EE172", "O. c #9AE579", "+. c #9DF771", "@. c #A8E26D", "#. c #BDED62", "$. c #BBF062", "%. c #B6F46D", "&. c #BBF46E", "*. c #A3E474", "=. c #A9E774", "-. c #A6EB76", ";. c #A8E977", ":. c #A7E378", ">. c #A8E47A", ",. c #A3EC7A", "<. c #AAEC7A", "1. c #BAE471", "2. c #A6F374", "3. c #AAF776", "4. c #AAFB74", "5. c #A6F079", "6. c #ABF47B", "7. c #AEFB7B", "8. c #B4FA77", "9. c #BAF876", "0. c #B2FD7E", "q. c #C3DF44", "w. c #C1E24D", "e. c #C2E852", "r. c #878683", "t. c #8E8D8A", "y. c #908F8B", "u. c #94928F", "i. c #989693", "p. c #9D9C98", "a. c #97A988", "s. c #9AAA8A", "d. c #9CBD82", "f. c #9FA794", "g. c #A5A39F", "h. c #AEBE9D", "j. c #ABAAA6", "k. c #AEADA9", "l. c #B0AFAB", "z. c #B6B4B0", "x. c #B9B6B3", "c. c #BCBBB6", "v. c #BEBDB8", "b. c #9DC481", "n. c #A1C784", "m. c #A2C985", "M. c #ADCD8C", "N. c #AEC09E", "B. c #ACE683", "V. c #AFFF89", "C. c #B5FE82", "Z. c #BAFF85", "A. c #B2FF8B", "S. c #BAFC8C", "D. c #BDFE90", "F. c #ADC4A2", "G. c #B3C2A3", "H. c #BEC2B5", "J. c #C1FF8F", "K. c #C5FF94", "L. c #C3C5BC", "P. c #C7C5C1", "I. c #C9C6C2", "U. c #CECAC6", "Y. c #CFCDC8", "T. c #D0C9C7", "R. c #D2CBC9", "E. c #CFD4C5", "W. c #CFDBC1", "Q. c #D2D6C6", "!. c #D2D8C7", "~. c #D4D3CC", "^. c #D3D8C8", "/. c #D7D3D2", "(. c #DAD4D5", "). c #DCDAD5", "_. c #DCD5D8", "`. c #DFDCD9", "'. c #E1DEDA", "]. c #E4E2DD", "[. c #E9DFE6", "{. c #E6E0E1", "}. c #E9E4E3", /* pixels */ ").).).).).).).).).).).).).).).).).).).(.(.).(.).).).).).).).).).", ").).].`.`.`.].`.].`.).`.'.`.'.'.`.].'.'.'.'.`.'.).).).).).).).).", ").(.P.P.P.I.L.P.I.T.T.T.P.P.P.P.P.P.P.L.T.R.R.I.U.'.).).).).).).", "'.Q.Z *.O.` :.I U u 0 0 ! C o.o.O.*.U U i 0 0 T S '.).).'.].).).", "`.!.W S.#.q.D.} T & o o 5 { A.&.q.S.=.! & o o 2 M./.).'.x.k.'.).", "`.Q.W A.x h A.>.3 X o o - Q V.m f 8.<.w o o X & / _.).'.j. /.).", "`.Q.( #.f d q.O.| .# e +.W %.h d b ,.~ ..$ 3 X.n._.).'.j.A /.'.", "'.Q.) e.v x w.` *.K.s 1.Z.( L x c n *.*.K.s [ S.n._._._./.).).).", "`.E.6 _ ( ( _ B P ` ) Q Q 6 W ( _ _ C B W W R E S _.).).).).).).", "'.Q.U 0.7.6.C.~ | 1 ; - K Q 7.7.7.0.*.~ A.7.7.8.b._.).).'.).).).", "'.!.Q Z.C.C.C.:.w X o X 1 ) C.0.C.C.-.| Z.0.Z.0.n.(.).].j.g.].).", "'.!.W 0.0.C.C.:.y = o @ 4 W 0.C.0.C.*.o.C.0.0.0.n._.).'.x.r.].).", ").^.Q Z.C.C.Z.` <.S.a @.S.W C.C.C.Z.;.*.Z.Z.C.0.n._.).'.c.p.).).", "'.Q.P 6.-.-.6.R ~ 7.H @.-.P -.-.-.6.W R 6.-.-.2.S _.).).'.].).).", "`.Q.N ` ~ ~ ` P P ` O.` ~ N ~ ~ ~ ` I C ~ ~ ~ ! S '.).).'.'.).).", "_.^.Q Z.Z.C.Z.} <.S.w.9.Z.W C.Z.Z.Z.:.*.Z.Z.Z.Z.n.'.).'.L.L.'.).", "'.!.W 0.C.7.Z.~ ,.8.f G V.R C.0.0.C.*.o.C.C.0.0.n.(.).].k.i.].).", "`.Q.W A.7.C.C.` ,.m d j 8.R 0.C.0.C.o.o.C.0.C.7.n._.).].l.. '.).", "`.Q.) Z.C.C.Z.} =.v z l L ( Z.C.C.Z.-.*.Z.Z.C.C.n._.).`.~.U.).).", ").~.7 } { ` *.B P ` _ _ W 7 W W W ~ P B ~ W Q E S _.).).).`.).).", "`./.n.p , < 9 ) ` 7.6.6.5.I 6.6.6.7.~ W 0.6.6.3./ ).).).'.'.).).", ").(.m.: X X + Y -.Z.C.C.C.W C.C.C.Z.-.o.Z.C.C.0.m.(.).].l.j.].).", ")._.^ > + o @ t <.C.0.C.C.R C.C.0.C.*.o.C.C.C.0.b._.).'.z.t.].).", ").(.m.Z.D % S.` -.C.C.C.C.R C.C.C.Z.*.o.Z.C.C.0.n._.).].z.t.).).", ").(./ 7.J F 7.E | 0.6.7.6.I 7.6.6.0.` ! 4.3.4.4.b._.).).'.].).).", ").Q.A h.F.F.G.a.P ~ W W W 6 W W W W P s.G.h.h.h.f.).).).).).).).", "'.W.G.[.'.`.}.L.*.Z.C.Z.Z.W Z.Z.C.Z.;.L.}.'.'.[.x.(.`.`.Y.~.'.).", "`.Q.h.).(.).'.H.-.C.7.0.7.R 0.0.0.C.-.H.`.).).`.z./.).].j.i.}.^.", "`.!.G.'.).).`.L.B.D.S.S.S.' D.S.S.D.B.L.'.).).).c.(.).].c.u.'.).", ").).`.).).).).).).)._.)._.).(.).).).).).).).).).).).).`.~.~.).).", ").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).", ").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).)." }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 149 2", " c #4E4E4C", ". c #5D5C5A", "X c #61615E", "o c #646361", "O c #71706E", "+ c #797876", "@ c #A15601", "# c #A55902", "$ c #2C9900", "% c #329600", "& c #3F9000", "* c #00AE00", "= c #03B302", "- c #00B800", "; c #0DB50A", ": c #46D233", "> c #48D233", ", c #53D639", "< c #55D83C", "1 c #5ADA3F", "2 c #659048", "3 c #6A924F", "4 c #6C8A57", "5 c #6D8959", "6 c #718C5D", "7 c #7EBB56", "8 c #758965", "9 c #5DDB43", "0 c #65D541", "q c #6CD745", "w c #61DC44", "e c #68DF46", "r c #66DC49", "t c #6BDC4A", "y c #75D648", "u c #72DD4C", "i c #67E046", "p c #6CE14B", "a c #70E24D", "s c #CEAD00", "d c #CCB302", "f c #C9B90F", "g c #C9BD14", "h c #C7C11A", "j c #C8C21A", "k c #C7C521", "l c #C4CC2B", "z c #C4D236", "x c #84BD5D", "c c #88BF5F", "v c #A5BF48", "b c #8B9C7C", "n c #86BF60", "m c #89BF65", "M c #92AA7E", "N c #95B47D", "B c #85C05D", "V c #A7C34C", "C c #BDDE4B", "Z c #A2CF50", "A c #81E458", "S c #BCE455", "D c #BAEB5F", "F c #87C162", "G c #8AC163", "H c #8BC068", "J c #88CB6A", "K c #91C467", "L c #92C568", "P c #95D073", "I c #B8DD6D", "U c #96F46D", "Y c #99F66E", "T c #9DF571", "R c #9EF973", "E c #B8E06E", "W c #A9F36E", "Q c #A6FC6E", "! c #BAF266", "~ c #B5F36D", "^ c #BAF368", "/ c #BAEB77", "( c #BAEC78", ") c #A3F673", "_ c #AEF575", "` c #A5F873", "' c #ACFC76", "] c #ABF67B", "[ c #A6FC79", "{ c #AEFC7A", "} c #B0F375", "| c #B5F977", " . c #BBF17C", ".. c #B1FC7D", "X. c #B8FF7C", "o. c #8E9D80", "O. c #93948C", "+. c #969592", "@. c #989793", "#. c #9A9995", "$. c #91A084", "%. c #96AC83", "&. c #98AF86", "*. c #9AAF89", "=. c #96B081", "-. c #99B086", ";. c #9BB189", ":. c #9BB98E", ">. c #A3B58E", ",. c #A3A29E", "<. c #A6B491", "1. c #A8B592", "2. c #A6A4A1", "3. c #A8A7A3", "4. c #AAA9A5", "5. c #B1AFAB", "6. c #ABB0A1", "7. c #B7B5B1", "8. c #BDBBB7", "9. c #BFBDB9", "0. c #AFCC97", "q. c #AEFE85", "w. c #B3FF81", "e. c #B2FF89", "r. c #BBFF8A", "t. c #B6C6A6", "y. c #B7C4A9", "u. c #B8C4AB", "i. c #C3C1BD", "p. c #C3EEA5", "a. c #C6EEA8", "s. c #C7C5C1", "d. c #CCCAC4", "f. c #CFCDC8", "g. c #D1CFCA", "h. c #D3D1CC", "j. c #D8DBCE", "k. c #D7D6D1", "l. c #D9D6D2", "z. c #DCDAD5", "x. c #DFD7DB", "c. c #DFDBD9", "v. c #E1DCDD", "b. c #DAE3CC", "n. c #DCE1D1", "m. c #E4E2DC", "M. c #E6DAE5", "N. c #EAE7E2", "B. c #EBE9E3", /* pixels */ "z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.", "z.z.z.z.z.z.z.c.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.", "z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.", "z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.j.z.z.z.z.z.z.z.z.z.z.z.j.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.", "z.z.z.z.v.v.v.v.v.v.v.v.v.v.v.v.c.c.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.c.v.v.m.z.z.z.z.z.z.z.z.z.z.z.", "z.z.z.l.$.&.;.*.*.*.*.;.o.*.<.1.1.<.<.*.o.;.*.*.*.*.*.*.o.;.>.<.1.1.<.%.1.v.z.z.z.z.z.z.z.z.z.z.", "z.z.z.n.n ) { ' { { ' { x ' a , , , e { x ' ' { { { ' ' x { e , , , p ' %.M.z.z.z.z.m.v.z.z.z.z.", "z.z.z.z.m w.w.w.z | w.w.G ] ; * * * = ) H w.w.X.z X.w...H ) = * * * ; ` ;.x.z.z.z.z.i.f.z.z.z.z.", "z.z.c.n.m ] w.! s C e...m t = = = = = 9 L { e.S s S e...L 9 * ; = ; * i >.v.j.l.c.s.. h.c.z.z.", "z.z.z.n.m { e.k s g | w.L 9 * = - = * 1 L { w.h s g w.{ L < * = - * * 1 >.v.z.z.z.c.5. d.c.z.z.", "z.z.z.z.m q.D d d s C e.n w.T q $ 0 U ] n e.S s d s S e.n { U 0 $ t Y ' &.v.l.z.n.s.+ O f.n.z.z.", "z.z.z.n.m ] k s d s f X.G ] r./ @ E r.w.n w.g d d s g q.n w.r.I # ( r.' &.v.z.z.z.z.s.l.c.z.z.z.", "z.z.z.n.m ..^ ^ ^ ^ ! X.G w.w...v } w.w.F ..! ^ ^ ^ ! ..G w.w.} V ..w.' ;.v.z.z.z.z.v.c.z.z.z.z.", "z.z.z.j.3 x H m F H F F 2 n m L P L L G 2 n G H F H G n 2 F n G J F n x b v.j.z.z.z.j.z.z.z.z.z.", "z.z.z.n.m ] ....q.....w.x w.t , , , r w.c ..] ..q.] ....n w.w.] ] ..w.' &.x.z.z.z.z.v.c.z.z.z.z.", "z.z.x.n.m ] w.w...w.w.w.m ) ; * * * * T H w.w.X.w.w.w.w.n w.w.w.w.w.w.{ ;.c.z.z.z.z.i.h.c.z.z.z.", "z.z.z.n.m ] w.w.w.w.w.w.L r = = = ; * 9 L ..w.w.w.w.w.w.n w.w.w.w.w.w.{ ;.c.z.z.z.c.. @.N.l.z.z.", "z.z.z.n.m ..w.w.w...w.w.L w * - = = * 1 L { w.w...w.w.w.n w.w.w.w.w.w.' -.M.z.z.j.B.4.#.B.z.z.z.", "z.z.z.z.m ..w.w.w.w.w.w.n ..) u % q R w.F ..w.w.w.w.w.{ n w.w...w.w.w.' -.c.z.z.z.z.o . j.z.z.z.", "z.z.c.n.m ] w.w.w...w.w.n ..r./ @ E r...F w...w.w.w...w.n w.w.w.w.w.w.{ -.x.z.z.z.z.f.f.z.c.z.z.", "z.z.z.n.m ] w.w.w.w.w.w.n w.w.] V ] w.w.n w.w.w.w.w.w.w.n w.w.w.w.w.w.{ -.c.z.z.z.z.c.c.z.z.z.z.", "z.z.z.z.3 x n n n n n F 2 n n n J F n n 2 n B c n n n n 2 F n n n n F 7 b M.j.z.z.z.j.z.z.z.z.z.", "z.z.z.z.m ..w.w.w.w...w.n ] X.w.....X.w.n w.w...w.w.w.w.n w.w...w.w.w.' -.c.j.z.z.z.m.c.z.z.z.z.", "z.z.z.b.G ] w.w.w.w.w.w.n w.w...l | w.w.n w.w.q.w...w.{ n w.w.w.w.w.w.' -.M.z.z.z.z.8.g.c.z.z.z.", "z.z.z.n.m ..w.w.w.w.w.w.n ..e.S s C e...B ..w.w.w.w.w.{ F w.w.w.X.w.w.{ ;.c.z.z.z.v.. @.N.z.z.z.", "z.z.z.n.m ] w.w.w.X.w.w.n ..w.h s g ..w.F q.w...w.w.w.w.n w.w.w.w.w.w.' -.M.z.z.z.B.4.#.B.l.z.z.", "z.z.z.n.m ] w.w.w.w.w.w.c q.S s d s C e.c ..w.w...w.w.w.n w.w.w.w.w.w.{ -.c.z.z.z.z.o . l.z.z.z.", "z.c.z.z.m ..w.w.w.w.w.w.F ..h d d d g X.F ..w.w.w.w.w.{ n w.w.w.w.w.w.' -.x.z.z.z.z.h.h.z.z.z.z.", "z.z.z.b.m ] ..{ ] ....w.F ] ~ ~ ~ } ^ ..n w...w...w...w.n w...w...w.w.{ =.v.z.z.z.z.z.z.z.z.z.z.", "z.z.z.z.6 x L L L L L F 2 n F F G F F F 2 n n n n n n n 2 F n n n n F 7 b v.z.z.z.z.z.z.z.z.z.z.", "z.z.z.v.*.{ p : > > 1 w.F ..w.w.w.w.w.w.n w.w.w.w.w.w.w.n w.w.w.w.w.w.{ -.v.z.z.z.z.m.z.z.z.z.z.", "z.z.z.M.;.) ; * * * * U H ..w.w.w.X.w.w.n ..w.w.w.w.w...n w.w.w.w.w.w.{ &.v.z.z.z.z.7.h.c.z.z.z.", "c.z.z.x.<.w = = = ; * , L ..w.w.w...w.w.c w.w.w.w.w.w...n w.w.w.w.w.w.{ &.v.z.z.z.c.X @.B.z.z.z.", "z.z.z.x.<.e = = - - * 9 K ] w.w.w.w.w.w.c w.w.w.w.w.w...n w.w.w.w.w.w.{ &.v.z.z.z.B.3.@.B.l.z.z.", "z.z.z.v.*.{ { A & y [ w.F ] X.w.w.w.w.w.n ] w.w.w.w.w...n w.w.w.w.w.w.{ -.v.z.z.z.z.X . l.z.z.z.", "z.z.z.v.;.{ r. .# I r.w.F w.w.w.w.w.w.w.n w.w.w.w.w.w.w.n w.w.w.w.w.w.{ =.v.z.z.z.z.l.l.z.z.z.z.", "z.z.z.v.*.Q { ` Z W ' | x ] ..w.w.....w.n ] ....] w.....x { { ' { { { Q -.v.j.z.z.z.z.z.z.z.z.z.", "z.z.z.j.8 %.*.&.:.-.-.*.5 B F n n n n n 2 n n F F F B B 4 ;.&.&.-.;.&.M O.v.z.z.z.z.z.z.z.z.z.z.", "z.z.z.b.N x.M.v.x.v.v.M.;...w.w.w.w.w.w.n w.w.w.w.w.w...-.M.c.c.v.v.v.x.2.z.z.z.z.z.m.v.z.z.z.z.", "z.z.c.b.N l.l.l.z.z.j.v.&.{ w.w.w.w.w.w.n w.w.w.w.w.w...=.c.z.z.z.z.z.j.,.m.z.z.z.z.5.d.z.z.z.z.", "z.z.z.b.=.l.n.z.z.z.z.c.-.{ w.w.w.w.w.w.F w.w.w.X...w...=.v.z.z.z.z.z.j.2.c.z.z.z.m.o @.N.l.z.z.", "z.z.c.b.N z.c.z.z.z.z.v.=.' ....{ { ....B { { | { ....' =.x.z.z.z.z.z.z.,.c.z.z.z.B.,.O.N.z.z.z.", "z.z.z.z.t.z.z.z.z.z.z.c.y.p.a.a.a.a.a.a.0.a.a.a.a.a.a.p.y.c.z.z.z.z.z.z.9.z.z.z.z.z.+.O.z.z.z.z.", "z.z.z.z.x.z.z.z.z.z.z.z.v.x.x.c.x.x.x.x.v.x.x.x.x.c.x.x.v.z.z.z.z.z.z.z.v.z.z.z.z.z.m.m.z.z.z.z.", "z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.n.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.c.z.z.l.z.z.z.z.z.", "z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.", "z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.c.z.z.z.z.z.z.z.z.z.z.z.z.z.c.z.z.z.", "z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.c.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z." }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/towers-icon.c0000644000175300017530000003611612161170405016161 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 130 2", " c #6B6A67", ". c #73726F", "X c #797875", "o c #30962E", "O c #3A9738", "+ c #409C3E", "@ c #5DA75A", "# c #77B274", "$ c #83827F", "% c #80B57C", "& c #888784", "* c #989693", "= c #9C9A97", "- c #9D9C98", "; c #9F9E9A", ": c #90BC8B", "> c #91BC8C", ", c #A2A19C", "< c #A2A09D", "1 c #A2A19D", "2 c #A3A19D", "3 c #A3A19E", "4 c #A3A29E", "5 c #A4A39F", "6 c #A5A39F", "7 c #A5A4A0", "8 c #A6A4A0", "9 c #A7A5A1", "0 c #ACAAA6", "q c #AFADA9", "w c #AFAFA9", "e c #B0AFAB", "r c #B3B2AE", "t c #B5B4AF", "y c #B5B4B0", "u c #B7B5B1", "i c #B7B6B1", "p c #B9B7B3", "a c #B9B8B3", "s c #BAB8B4", "d c #BBBAB5", "f c #BBBBB5", "g c #BCBAB6", "h c #BCBBB6", "j c #BEBCB8", "k c #BEBDB8", "l c #BFBDB9", "z c #BFBEB9", "x c #C0BEBA", "c c #C0BFBA", "v c #A3C49E", "b c #A7C5A2", "n c #ABC6A6", "m c #B6CCB1", "M c #BDCEB7", "N c #C2C1BC", "B c #C4C2BE", "V c #C4C4BE", "C c #C5C4BF", "Z c #C1CFBB", "A c #C4D0BE", "S c #C6C5C0", "D c #C8C6C1", "F c #C8C6C2", "G c #C9C7C2", "H c #C9C7C3", "J c #C9C8C3", "K c #CAC9C4", "L c #CBC9C4", "P c #CBCAC5", "I c #CDCCC7", "U c #CECCC7", "Y c #CECDC8", "T c #D0CECA", "R c #D1CFCB", "E c #C6D2C0", "W c #CAD3C4", "Q c #CBD3C5", "! c #D1D0CA", "~ c #D2D0CB", "^ c #D2D6CB", "/ c #D3D1CC", "( c #D4D2CD", ") c #D4D2CE", "_ c #D6D4CF", "` c #D5D7CE", "' c #D6D8CF", "] c #D8D6D1", "[ c #D9D7D2", "{ c #DAD6D3", "} c #DBD6D4", "| c #D9D9D2", " . c #DAD8D3", ".. c #DADAD3", "X. c #DAD9D4", "o. c #DBD9D4", "O. c #DCDAD5", "+. c #DCDBD5", "@. c #DDDBD6", "#. c #DEDBD7", "$. c #DEDCD6", "%. c #DEDCD7", "&. c #DFDCD7", "*. c #DFDAD8", "=. c #DFDCD8", "-. c #DFDDD8", ";. c #E0DAD9", ":. c #E0DBD9", ">. c #E1DBDA", ",. c #E0DDD8", "<. c #E0DCD9", "1. c #E1DDD9", "2. c #E0DED8", "3. c #E0DED9", "4. c #E1DCDA", "5. c #E2DDDB", "6. c #E1DFDA", "7. c #E3DADC", "8. c #E3DEDC", "9. c #E3DFDC", "0. c #E2E0DB", "q. c #E3E1DB", "w. c #E4E2DD", "e. c #E5E1DE", "r. c #E5E3DE", "t. c #E7E5E0", "y. c #E8E0E1", "u. c #EBE0E3", "i. c #E9E2E2", "p. c #F0E5E9", /* pixels */ "O.O.O.%.,.O.O.O.O.O.6.,.O.O.O.O.", "O.O.%.( T ,.O.O.O.O.Y ! O.O.O.O.", "O. .0.g $ r.[ O. .r.6 6.o.O.O.", "O.6.r.N . r.O.O.%.0.q X 6.O.O.O.", "O.s s i 0 s j g l O.Y ( O.[ O.O.", "%.* T O.O.6.[ O.e s x g g x i ! ", "0.8 ( 6. .m O.:.l .6.u.:.6.D K ", "-.1 ! y.> + 6.O.h *.Z : Q 8.S K ", "6.1 / .@ O A 9.f o.` # % p.C K ", "-.1 / -.^ b ` *.s :.M o n y.S K ", "0.8 / -.6.y.6.%.N *.A v W r.D L ", "-.& ; 6 1 < 1 ; 1 .O.7.[ O.B K ", "Y = ] / / / / Y 1 r e q e y 3 R ", "o.l -.O.-.-.-. .i L D D D D i *.", "o.o.O.O.O.O.O.o.o.6.-.6.-.6.o.o.", "o.%.O.O.O.O.O.O.o.o.o.o.o.o.O.O." }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 221 2", " c #1E1E1D", ". c #262625", "X c #2A2928", "o c #393837", "O c #3A3938", "+ c #007300", "@ c #007400", "# c #464544", "$ c #4F4E4D", "% c #585755", "& c #5C5B59", "* c #636260", "= c #656462", "- c #696865", "; c #6C6B69", ": c #706F6C", "> c #71706D", ", c #72716E", "< c #72716F", "1 c #777673", "2 c #787774", "3 c #797875", "4 c #7A7976", "5 c #7C7B78", "6 c #7E7D7A", "7 c #807E7C", "8 c #048104", "9 c #0D880D", "0 c #198A18", "q c #1D8C1C", "w c #1F8C1E", "e c #349833", "r c #3A9A38", "t c #3B9A3A", "y c #3C9A3A", "u c #3E9A3C", "i c #419B3F", "p c #419D3F", "a c #479F45", "s c #4BA149", "d c #4FA24C", "f c #55A453", "g c #57A154", "h c #64AB61", "j c #68AB65", "k c #6EAE6A", "l c #74B170", "z c #76B072", "x c #7BB477", "c c #7CB478", "v c #7EB47A", "b c #7FB57B", "n c #82B67E", "m c #82B77E", "M c #8A8885", "N c #8A8985", "B c #8B8986", "V c #8B8A87", "C c #8E8D8A", "Z c #92908D", "A c #92918E", "S c #93928E", "D c #94938F", "F c #949390", "G c #969491", "H c #969592", "J c #999894", "K c #9A9995", "L c #9C9A97", "P c #9F9D9A", "I c #A19F9C", "U c #85B881", "Y c #8AB985", "T c #93BE8F", "R c #94BE8F", "E c #A2A09D", "W c #A3A19E", "Q c #A3A29E", "! c #A4A29F", "~ c #A6A5A1", "^ c #A7A5A1", "/ c #A7A5A2", "( c #A7A6A2", ") c #A8A6A2", "_ c #A8A7A3", "` c #A9A8A4", "' c #AAA8A5", "] c #AAA9A5", "[ c #ABA9A5", "{ c #ABAAA6", "} c #ACAAA6", "| c #ACABA7", " . c #ADABA7", ".. c #AEACA8", "X. c #AEADA9", "o. c #AFADA9", "O. c #AFAEAA", "+. c #B1AFAB", "@. c #B1B0AC", "#. c #B2B0AC", "$. c #B3B2AD", "%. c #B3B2AE", "&. c #B4B2AE", "*. c #B4B3AE", "=. c #B4B3AF", "-. c #B5B3AF", ";. c #B5B4B0", ":. c #B6B4B0", ">. c #B8B7B2", ",. c #B8B7B3", "<. c #B9B8B3", "1. c #BAB8B4", "2. c #BBB9B5", "3. c #BCBBB6", "4. c #BDBBB7", "5. c #BDBCB7", "6. c #BEBDB8", "7. c #BFBDB9", "8. c #BFBEB9", "9. c #C0BFBA", "0. c #C1BFBB", "q. c #A3C39E", "w. c #A3C49E", "e. c #ACC7A6", "r. c #B4CAAE", "t. c #BFCEB9", "y. c #C1C0BB", "u. c #C2C0BB", "i. c #C2C1BC", "p. c #C3C1BD", "a. c #C5C3BF", "s. c #C5C4BF", "d. c #C6C4BF", "f. c #C6C4C0", "g. c #C6C5C0", "h. c #C7C5C0", "j. c #C7C5C1", "k. c #C7C6C1", "l. c #C8C6C1", "z. c #C8C6C2", "x. c #C9C7C2", "c. c #C9C7C3", "v. c #CCCAC5", "b. c #D0CEC9", "n. c #D2D0CC", "m. c #D4D2CE", "M. c #D6D4CF", "N. c #D7D5D0", "B. c #D7D5D1", "V. c #D7D7D0", "C. c #D8D6D1", "Z. c #D8D7D1", "A. c #D8D7D2", "S. c #D9D7D2", "D. c #D8D8D1", "F. c #D8D9D1", "G. c #D8D8D2", "H. c #D9D8D2", "J. c #D8D9D2", "K. c #D9D9D2", "L. c #D9D9D3", "P. c #DAD8D3", "I. c #DAD9D3", "U. c #DAD9D4", "Y. c #DBD9D4", "T. c #DBDAD4", "R. c #DCDAD5", "E. c #DDDAD6", "W. c #DDDBD6", "Q. c #DEDBD7", "!. c #DEDCD7", "~. c #DFDBD8", "^. c #DFDDD8", "/. c #E0DBD8", "(. c #E0DCD9", "). c #E0DED8", "_. c #E0DED9", "`. c #E1DED9", "'. c #E1DCDA", "]. c #E2DCDB", "[. c #E3DDDB", "{. c #E1DFDA", "}. c #E3DDDC", "|. c #E4DDDD", " X c #E5DEDE", ".X c #E6DEDF", "XX c #E2E0DA", "oX c #E2E0DB", "OX c #E3E0DB", "+X c #E3E1DC", "@X c #E4E1DC", "#X c #E4E2DD", "$X c #E5E3DD", "%X c #E5E3DE", "&X c #E6E4DE", "*X c #E6E4DF", "=X c #E7E4DF", "-X c #E7E5DF", ";X c #E7DFE0", ":X c #E8DFE0", ">X c #E9DFE2", ",X c #EADFE2", ".d.l.g.g.c.l.l.C.R.C.Y.g.m.oXP.Y.A.Y.Y.Y.Y.Y.", "!.P.:.1 ~ ~ ~ ] ] ] ) ~ ~ ~ ~ ] Z =.tX#X-XwX-XoX-X@X2X@X#X#X^.Y.", "Y.^.Z , rX#X%X#X#X#X@X2X%X%X%XwXv.V =. .X.X.X.X.X.X.X.X.X.X.B.R.", "Y.@X5.5 ^.J.Y.Y.P.P.B.B.P.P.P.).i.H g.3.6.5.6.5.6.6.6.3.f.H i.#X", "Y.#X:.3 #XY.W.Y.Y.W.7X.XP.R.W.oX0.+.rX[.#XoX#XoXoX#XoX[.rX+.0.@X", "Y.#X:.3 #XY.Y.Y.Y.!.U q.[.R.P.oX0.] #XP.Y.J.B.J.J.Y.Y.J.#X] 0.#X", "P.#X:.3 ^.Y.Y.J.6XY @ g 6XP.P.oXi.] -XC.Y.Xm z >XP.P.oXi.] -XP.R..Xk 9 r..XP.P. c #605F5D", ", c #61615E", "< c #656462", "1 c #6D6C69", "2 c #72716F", "3 c #757472", "4 c #797875", "5 c #7D7C79", "6 c #817F7D", "7 c #038303", "8 c #0C860B", "9 c #0D880C", "0 c #1D8D1C", "q c #218D20", "w c #259024", "e c #289327", "r c #2B942A", "t c #339431", "y c #379936", "u c #389836", "i c #3B9A39", "p c #479D45", "a c #54A351", "s c #5AA657", "d c #61A95E", "f c #66AA63", "g c #69AD65", "h c #6FAF6C", "j c #70AD6D", "k c #72B06E", "l c #75B172", "z c #79B375", "x c #82817E", "c c #858481", "v c #898784", "b c #8A8986", "n c #8F8E8B", "m c #92918D", "M c #959491", "N c #999794", "B c #9B9A96", "V c #9E9D99", "C c #A09F9B", "Z c #85B381", "A c #90BC8B", "S c #96BE92", "D c #A2A19D", "F c #A6A5A1", "G c #A8A7A3", "H c #AAA9A5", "J c #AEACA8", "K c #B0AFAB", "L c #B3B2AE", "P c #B7B5B1", "I c #B8B6B2", "U c #BDBBB7", "Y c #BFBDB9", "T c #C0BEBA", "R c #9BC196", "E c #9FC29A", "W c #AAC6A5", "Q c #B2C9AC", "! c #BACDB4", "~ c #BECEB8", "^ c #C2C1BC", "/ c #C0CFBA", "( c #C3D0BD", ") c #C6C4C0", "_ c #C9C7C2", "` c #CBC9C4", "' c #D1CFCA", "] c #C6D1C0", "[ c #CAD3C3", "{ c #CED4C8", "} c #D5D3CE", "| c #D7D5D0", " . c #D9D7D2", ".. c #D7D8D0", "X. c #DCDAD5", "o. c #DFDDD8", "O. c #E2DEDA", "+. c #E3E1DC", "@. c #E7DFE0", "#. c #E8DFE1", "$. c #E7E5E0", "%. c #E9E5E2", "&. c #ECEAE5", "*. c #EFEDE8", "=. c #F6E5EE", "-. c #F1EEE9", ";. c #F6F3EE", ":. c #F8E5F0", ">. c #F8F5F0", ",. c #FAF8F2", "<. c #FFFFFF", /* pixels */ "X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.o.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.", "X.X.X.X.X.o.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.o.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.o.X.X.O.X.X.", "X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.", "X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.o.X.X.o.X.X.X.X.X.X.X.X.X.o.X.X.X.X.X.X.X.X.o.X.X.X.X.X.X.X.X.X.X.", "X.X.X.X.X.o.X.X.X.X.X.X.| | X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X. . . .X.X.X.X.X.X.X.X.X.X.X.X.X.X.", "X.X.X.o.X.X.X.X.X.X.X.+.*.&.X.X.X.X.X.o.X.X.o.X.X.X.X.X.X.X.O.*.-.&.X.X.X.X.X.X.X.X.X.X.X.X.X.X.", "X.X.X.X.X.X.X.X.X.X.O.P 1 x o.X.X.X.X.X.X.X.X.X.X.o.X.X.X.+.T 1 : v .o.X.X.X.X.X.X.o.X.X.X.o.X.", "X.X.X.X.X.X.X.X.X.X.+.m @ O +...X.X.X.X.X.X.X.X.X.X.X.X.X.+.M 4 B . < &. .X.X.X.X.X.X.X.X.X.X.X.", "X.X.X.o.X.X.X.X.X.X.| ;.v o +.X.X.X.X.X.O.X.X.X.X.X.X.X.X. .$.C 1 O B +.X.o.X.X.X.X.X.X.X.X.X.X.", "X.X.X.X.X.X.X.X.X.X. .&.4 X +.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.+.B , . m $.X.X.X.X.X.X.X.X.X.X.X.X.", "X.X.X.X.X.X.X.X.X.X.} ,.v o >.O.X.X.X.X.X.X.X.X.X.X.X.X.X.O.} &.<.@ = *.| X.X.X.X.X.X.X.X.X.X.X.", "X.X.X.X.X.X.X.X.X. .+.v O - ` O.| X. .X...X.X.X.X.X.X. .+.n # & o J +.X.X.X.X.X.X.o.X.X.X.X.o.", "X.X.X.X.X.X.$.$.+.$.&.T H I H +.+.+.%.+.+.%.+.+.+.O.X.X.X.X.} H D ` +.X.X.X.X.X.X.X.X.X.X.X.X.X.", "X.X.X.X.+.} G H H H G L I L I H H H H J H J J J G T o.X.X.X.O.+.+.O.X.o.X.X.X.X.X.o.X.X.X.X.X.X.", "X.X.o.} ' , 6 H C D D D C C C D D D D D D D D D N 3 +.X.X.X.X.X.| X.X.X.X.X.X.X.X.X.o.X.X.X.X.X.", "o.X.O.^ 3 # _ -.$.$.$.%.$.$.$.%.+.&.%.+.+.%.+.&.+.v o.| X.X. .X.X. .X.X.| X.X.X.| X.| O.X.X.X.X.", "X.X.X.+.) : U O. . .X.X. . .X. .O. . .X.X.X. . . ., x x x x x x 6 x x x x x x x x x c 2 } O.X.X.", "X.X.X.+.U ; Y +. .X.X.X.X.X.X.X.X.X.X.X.X.X.X.O.| x +.X.X.o.o.X.o.o.X.o.o.X.o.o.X.X.$.v ^ O.X.X.", "X.X.X.+.U ; Y +.X.X.X.X.X.X.X.X... .X.O.X.X.X.X.} x +. .X.X.X.X.X.X.X.X.X.X.X.X.X. .$.n _ +.X.X.", "X.X.X.+.U ; Y +. .X.X.X.X.X.X.#.%.O.X. .X.X.X.X.| x O.X.X.X.X.X.X.X.X.X.X.X.X.X.X. .$.v ^ O. .X.", "X.X.X.+.U ; T +.X.X.X.X.X.X.O.z i S #. .X.X.X.O.} x O.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.$.b _ O.X.X.", "X.X.X.+.U ; T +. .X.X.X. .#.S % $ j %.X.X.X. .O.} x O.X.X.X.X.X.O.#.O.X.X.X.X.X.X. .$.b ) O.X.X.", "X.X.X.+.U ; U +.X.X.X.X.O.} 0 p 7 h %. .X.X.O.X.} x O.X.X.X.X.X.! W Q .O. .X.X.X. .$.b ) O.X.X.", "X.X. .+.U ; U +. .X.X.X.%.s q ! % k #. .X.X. .O.| x O.X.X. .%.a % 8 7 r / O.X.X.X. .$.v ) +.X.X.", "X.X. .+.U ; Y +. .X.X.#.W 7 ! } % l :. .X.X.X.o.| x O.X.X.X.O.z S .A % f %.X.X.X. .%.b ) O. .O.", "X.X.X.+.U ; Y +.X.X.} %.Z % e 0 7 8 s O.X.X.X.X.| x O.X.X.X.X.O.O.%.} % s #.X.X.X. .%.b ) @. . .", "X.X.X.+.U ; Y +. .X.X.O.! l l d % t S O.X.X.X.o.} x +.X.X.X.X. .O.} u % E #. .X.X. .%.b ) O. .O.", "X.X.X.+.U ; Y +. .X.X.X.O.#.=. .i E =.X.X.X.X.o.| x O.X.X.X.X.#.! q % z #.X.X.X.X. .#.b ) O. . .", "X.X.X.+.U ; Y +.X.X.X.X.X.X. .X.#.O.| X.X.X.X.X.} x O.X.X.X.O.E 8 % g ] ` O.X.X.X. .%.b ) O. .O.", "X.X.X.+.U ; Y +. .X.X.X.X.X.X.X.X.X.X.X.X.X.X.O.} x O.X.X.X.#.p % 8 9 % a %.| O.X. .%.b ) o. . .", "X.X.| +.U ; Y +.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X. .x O.X.X.X.X.[ / / / ! [ o.X.X.X. .+.b ) @.X.X.", "X.X.X.+.U ; Y O.} X.X.X. .X.X.X. .X.X.X.X.X.X.X.} x O.X.X.X.X.O.O.O.O.O.O.X.X.X.X.X.$.b ) @.X.X.", "X.X.X.+.U : Y +.o.X.X.o.X.o.X.X.o.o.X.o.X.X.o.o.| c O. .X.X.X.X. .X. .X.X.o. .X.o.| $.v ) o.X.X.", "X.X.X.$.) * x m n n n m n n n n n n n n n n n m 5 1 @.| X.X. .X.X.| X. . . . .X. .| +.v ) +.X.X.", "X.X.o.) 1 + n v v v v v v v v v v v v v v v v n 1 P -.$.$.$.$.$.$.$.$.$.$.%.%.#.%.+.;.n ) +.X.X.", "X.X.X.+.* x } Y ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ Y ^ ^ ~ _ 1 B G F F F F F F F F F F F F F F F J 3 _ o.X.X.", "X.X.+._ = F *.O.+.+.+.+.+.+.+.+.+.+.+.+.O.+.O.&.5 B G F F F F F F F D F F F F F F H B < X.X.X.X.", "X.X.$.D x F +.| .X.X.X.X.X.X.X.X.X.X. .X.X.X.O.c .$.+.+.+.+.+.+.+.@.@.O.@.O.O.O.&.H G $.X.X.X.", "X.X.X.X.J V $.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X. .$.3 3 6 6 6 6 6 x j 6 6 6 6 6 6 6 j 5 < X.X.X.X.X.", "X.X.X.$.K B &.| X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.+.c | O.o.O.o.O.o.@.X.@.X.O.O.O.O.X.@.c X.X.X.X.X.", "X.X.X.$.J B %.X.X.X.X.X.X.X.X.o.X.X.X.X.X.X. .+.c ' o.X.X.X.X.X.X.X.X.X.X.X.X.X. .X.6 X.X.X.X.X.", "X.X.X.o.| } o.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.o.} X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.' X.X.X.X.X.", "X.X.X.X.o.o.X.X.X.X.X.X.X.X.X.X.X.X.X.X.o.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.@.X.X.X.o.X.", "X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.o.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.| X.X.X.X.X.", "X.X.X.X.X.X.X.X.X.X.o.X.X.X.X.X.X.X.X.X.X.X.X.o.X.X.X.X.X.X.X.X.X.X.X.X.X.o.X.X.o.X.X.X.X.X.X.X.", "X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.o.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.", "X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.o.", "X.X.X.X.o.X.X.X.X.X.X.X.X.X.X.X.o.X.X.X.X.X.X.X.X.X.X.X.X.o.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X." }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/twiddle-icon.c0000644000175300017530000002427712161170405016277 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 78 1", " c #565553", ". c #61605E", "X c #686765", "o c #7A7976", "O c #9A9995", "+ c #A09F9B", "@ c #A19F9C", "# c #A3A19D", "$ c #A7A5A1", "% c #AAA8A4", "& c #ABAAA6", "* c #AEACA8", "= c #B2B1AC", "- c #B9B8B3", "; c #BEBCB8", ": c #C0BEB9", "> c #C2C0BC", ", c #C8C6C1", "< c #C8C6C2", "1 c #C9C7C2", "2 c #C9C7C3", "3 c #C9C8C3", "4 c #CAC8C4", "5 c #CAC9C4", "6 c #CBC9C4", "7 c #CBC9C5", "8 c #CCCAC5", "9 c #CCCAC6", "0 c #CDCBC6", "q c #CDCCC7", "w c #CECCC7", "e c #CECCC8", "r c #CFCDC8", "t c #CFCDC9", "y c #CFCEC9", "u c #D0CEC9", "i c #D1CFCA", "p c #D1CFCB", "a c #D1D0CB", "s c #D2D0CB", "d c #D2D0CC", "f c #D2D1CC", "g c #D3D1CC", "h c #D4D2CD", "j c #D4D3CE", "k c #D5D3CE", "l c #D6D3CE", "z c #D6D4CF", "x c #D7D4CF", "c c #D6D5D0", "v c #D7D5D0", "b c #D8D6D1", "n c #D8D7D2", "m c #D9D7D2", "M c #D9D8D3", "N c #DAD8D2", "B c #DAD8D3", "V c #DBD8D3", "C c #DAD9D4", "Z c #DBD9D4", "A c #DBDAD5", "S c #DCDAD4", "D c #DCDAD5", "F c #DDDBD6", "G c #DEDBD6", "H c #DEDCD6", "J c #DEDDD8", "K c #DFDDD8", "L c #E0DED8", "P c #E0DED9", "I c #E1DED9", "U c #E1DFD9", "Y c #E1DFDA", "T c #E2E0DB", "R c #E2E1DB", "E c #E3E1DB", "W c #E5E3DE", "Q c #E7E5E0", /* pixels */ "kirrwiwrrrrrrryj", "k44ss4sr4gr404gV", "s4bExb=3CxVYVxKC", "s1LDsVo*HiCxxnYV", "i4Tkkk#4xjCjHxKC", "ikKiskKbyCvs%3KV", "ikvkksiggAjxX$Qn", "i9vDbvk9nxxg%3KV", "gysO>nxwAsgxAxHC", "y4x.#K0jvxxgsgxV", "g4n>rxyVxAvnnrjA", "s3xVxwvnw+sjv4HC", "s,ssssVV; :Cr0KV", "s3xr0Vjgg-wx,yHC", "avYHKACKKWTCCHTV", "jVnvvVVvnvvnVnxj" }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 177 2", " c #2D2D2C", ". c #353533", "X c #3D3C3B", "o c #464543", "O c #535250", "+ c #5A5957", "@ c #5C5B59", "# c #656462", "$ c #666562", "% c #676664", "& c #686764", "* c #696865", "= c #696866", "- c #6C6B68", "; c #6C6B69", ": c #6D6C69", "> c #6E6D6A", ", c #6F6E6B", "< c #73726F", "1 c #777573", "2 c #7B7A77", "3 c #7D7C79", "4 c #807F7C", "5 c #82807D", "6 c #868582", "7 c #878682", "8 c #888683", "9 c #898784", "0 c #8D8C89", "q c #92918D", "w c #999794", "e c #999894", "r c #9B9996", "t c #9C9A97", "y c #A5A4A0", "u c #A9A7A3", "i c #ADABA7", "p c #B0AFAA", "a c #B4B2AE", "s c #B5B3AF", "d c #B6B4B0", "f c #B7B6B1", "g c #B9B7B3", "h c #BBB9B5", "j c #BBBAB5", "k c #BCBAB6", "l c #BCBBB6", "z c #BEBCB8", "x c #BEBDB8", "c c #BFBDB9", "v c #BFBEB9", "b c #C0BEBA", "n c #C0BFBA", "m c #C1BFBB", "M c #C1C0BB", "N c #C2C0BC", "B c #C2C1BC", "V c #C3C1BC", "C c #C3C1BD", "Z c #C3C2BD", "A c #C4C2BE", "S c #C4C3BE", "D c #C5C3BF", "F c #C6C4C0", "G c #C6C5C0", "H c #C7C5C1", "J c #C7C6C1", "K c #C8C6C1", "L c #C8C7C2", "P c #C9C7C2", "I c #C9C7C3", "U c #C9C8C3", "Y c #CAC8C3", "T c #CAC8C4", "R c #CAC9C4", "E c #CBC9C4", "W c #CBCAC5", "Q c #CCCAC5", "! c #CCCAC6", "~ c #CCCBC6", "^ c #CDCBC6", "/ c #CDCCC7", "( c #CECCC7", ") c #CECDC7", "_ c #CECDC8", "` c #CFCDC8", "' c #CFCDC9", "] c #CFCEC8", "[ c #CFCEC9", "{ c #D0CEC9", "} c #D0CFCA", "| c #D1CFCA", " . c #D1CFCB", ".. c #D2D0CB", "X. c #D2D1CB", "o. c #D2D0CC", "O. c #D2D1CC", "+. c #D3D1CC", "@. c #D3D1CD", "#. c #D3D2CC", "$. c #D3D2CD", "%. c #D4D2CD", "&. c #D5D3CD", "*. c #D5D2CE", "=. c #D4D3CE", "-. c #D5D3CE", ";. c #D6D3CE", ":. c #D6D3CF", ">. c #D6D4CE", ",. c #D6D4CF", "<. c #D7D4CF", "1. c #D7D5D0", "2. c #D7D6D1", "3. c #D8D5D0", "4. c #D8D6D1", "5. c #D9D6D1", "6. c #D9D7D2", "7. c #D9D8D3", "8. c #DAD8D3", "9. c #DBD8D3", "0. c #DBD9D3", "q. c #DAD9D4", "w. c #DBD9D4", "e. c #DCD9D4", "r. c #DCDAD4", "t. c #DCDAD5", "y. c #DDDAD5", "u. c #DCDBD5", "i. c #DDDBD5", "p. c #DCDAD6", "a. c #DCDBD6", "s. c #DDDBD6", "d. c #DEDCD6", "f. c #DEDCD7", "g. c #DFDDD7", "h. c #DEDDD8", "j. c #DFDDD8", "k. c #DFDDD9", "l. c #DFDED8", "z. c #DFDED9", "x. c #E0DDD8", "c. c #E0DED8", "v. c #E0DED9", "b. c #E1DFD9", "n. c #E0DFDA", "m. c #E1DFDA", "M. c #E2DFDA", "N. c #E2E0DA", "B. c #E2E0DB", "V. c #E3E0DB", "C. c #E3E1DB", "Z. c #E3E1DC", "A. c #E4E2DD", "S. c #E5E3DD", "D. c #E5E3DE", "F. c #E6E4DE", "G. c #E7E5DF", "H. c #E7E5E0", "J. c #E8E6E0", "K. c #E8E6E1", "L. c #E9E7E1", "P. c #E9E7E2", "I. c #EAE8E2", "U. c #EAE8E3", "Y. c #EBE8E3", "T. c #EAE9E3", "R. c #EBE9E3", "E. c #EBE9E4", "W. c #EAE9E5", "Q. c #EBEAE4", "!. c #ECEAE4", "~. c #ECEAE5", "^. c #EDEAE5", "/. c #ECEBE5", "(. c #EDEBE5", "). c #EEECE6", "_. c #EFEDE7", /* pixels */ "-.-.3.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.3.3.1.3.3.1.1.1.1.1.-.-.", "-.-.~ ( ( ( ( ~ E ( _ ( ( ( ( ( ( ( ~ ~ ~ _ _ ~ ~ _ _ _ ( ~ -.-.", "-.,.~ v b N b D Y v b N M b N N M N Y F v b m m m N N N v _ 3.-.", "-.-.K 1.3.3.-.R.B.1.3.3.1.,.,.1.1.1.w.B.Z.7.1.1.3.3.3.3.-.E.e.O.", "-.-.l -.,.O.e.L...,.O.O.3.0.e.O.,.O.-. .3.+.L.3.-.-.-.-. .H.e.O.", "-.-.l -.1.O.E.e...-.-.0.6 + a e.O.,.-.-.~ b.A.e.b.7.O.-.O.L.w.-.", "-.-.l -.-.e.U.O.,.-.,.O.1.6 1 D.O.-.-.-.~ L. .-.3.b.m.w. .L.e.O.", "-.-.v -.O.R.w.-.-.,...d.t o b 0.-.-.,.~ b.b.-.-.-.O.3.l.l.E.w.-.", "-.-.z .l.E.O.-.-.-.-.e.: O u w.O.1.O.E L.-.-.-.-.-.-.-.-.E.e.-.", "-.-.l -.U.3.-.,.-.-.-.-.b.B.1.,.-.1.E l.b.O.-.-.-. .O.1. .L.e. .", "-.-.l e.E._ Y .,.,.-.-.....O.,.-.O.E E. .-.-.3.f.e.w.-.O.L.e.-.", "-.-.l E.B.w.0._ E ..3.,.-.,.,.O.3.F d.x.-.-.,.~ 3 1 d 7. .L.e.O.", "-. .D _.+.-.,.d.3._ E O.1.,.=.,.-.E E.O.-.-.-.-.9 0 B. .L.e.O.", "-.-.K e.+.-.-...1.e.1.( E O.,.6.F e.l.O.-.,.-.O.N - 5 D.( L.w.-.", "-.-.l +.-.-.-.-.,.O.3.e.1.E ( } K E. .-.,.O.1.Y : * s e. .L.e.O.", "-. .D -.-.-.-.e.O.,.,.O.1.0.,.N e.e.-.,.,.O.,.1.1.0.e.-. .L.w.-.", "-.,. .-.-.1...f 3.-.-.-.-.-.-.D E.! E O.1.1.-.-.O.-. .1. .L.e.-.", "-.-.l O.-.,.; $ t d...-.O.3.D O.b.l.w._ E -.1.-.-.-.-.1. .Y.e...", "-.-.v -.1. .$ 2 @ B...-.,...M Z. .-.w.l.w._ ~ O.1.-.-.1. .b.e.-.", "-.-.l -.O.d.e . 5 b. .-.3.D 0.e.-.-.O.-.3.b.7._ ~ -.-.3.D 3.b.O.", "-.-.l -.O.3.s t -.,.-.,...S D.O.-.-.-.-.O.-.3.b.w._ .O.k H.d...", "-.-.l -.-.-.e.B.-.-.-.3.N e.d...-.O.1.3.-.-.-.O.0.d.0.L F E.e.O.", "-.-.z -.-.-.-...-.-.,._ F H...,.-.3.1.T -.-.-.-.O.O.1.F ..U.d.O.", "-.-.l .,.-.-.-.-.O.0.N b.e.O.,.-. .& * .1.-.-.,.3.F E -.L.e.O.", "-.-.l K ~ -.-.-.,.,.( Y U...,...b.y X , g 0.-.,.1.O.N 1...L.w.O.", "-.-.l -. .Y ~ -.,.1.M B.e.O.,.O.d.i - & q B...O.1.F E 1...L.w.-.", "-.-.l -.,.-. .Y ( ( ( U...,.-.-.-.0.e 6 -.-.-.,.O.b 1.-. .L.e.O.", "-.-.b -.-.-.,.-...L D.1.-.-.-.-.-.O.b.B.-.-.-.1.N E 1.-...L.w.-.", ",...h -.,.,.,.,.,.0.O.D ~ O.,.,.-.3.-...,.-.1. .v 3.-.1.O.E.e.-.", ",.-.d._.U.E.E.R.R.U.E._.D.L._.G.E.E.E.R.E.E.E.L.L.E.E.E.L.E.w...", "-.-.,.3.-.,.,.,.-.-.-.,.3.,.,...3.,.,.,.-.-.-.-.1.-.-.,.-.-.,.-.", "-.-.,.+.-.-.-.-.-.-.-.-.-.,.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-." }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 63 1", " c #030303", ". c #1F1F1E", "X c #2C2C2B", "o c #353433", "O c #3D3C3B", "+ c #403F3D", "@ c #434341", "# c #484746", "$ c #494947", "% c #4E4D4B", "& c #555452", "* c #595957", "= c #5E5D5B", "- c #605F5D", "; c #62615F", ": c #646361", "> c #6A6966", ", c #6E6D6A", "< c #706F6C", "1 c #73726F", "2 c #757471", "3 c #7A7976", "4 c #7D7C79", "5 c #83827F", "6 c #878682", "7 c #908F8B", "8 c #91908D", "9 c #969591", "0 c #989793", "q c #9B9996", "w c #9F9D9A", "e c #A19F9C", "r c #A3A19E", "t c #A5A4A0", "y c #A8A6A2", "u c #ACAAA6", "i c #B4B3AF", "p c #B6B5B1", "a c #B9B7B3", "s c #BBB9B5", "d c #BFBDB9", "f c #C0BFBA", "g c #C4C3BE", "h c #C6C5C0", "j c #C8C7C2", "k c #CBCAC5", "l c #CFCDC9", "z c #D1CFCA", "x c #D5D3CE", "c c #D7D5D0", "v c #D8D6D1", "b c #DCDAD4", "n c #DFDDD8", "m c #E0DED9", "M c #E4E2DD", "N c #E7E5E0", "B c #E8E6E1", "V c #ECEAE5", "C c #EDECE8", "Z c #F1EFE9", "A c #F3F2EC", "S c #F7F6F0", "D c #F9F8F2", /* pixels */ "xxcxxxxxxxxxxxxxxcxxxxxxcxxxxxxxxxxcxxxxxxxcxxxx", "xxxxxxxcxxxxxcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxcx", "xxxxvxcxxccxcxcxcxccxxxxcxvxvxvxxccxcxccxxccxxxx", "xxxxkllzllllllllllllzlzzzllllzzzllllzlllllllxxxx", "xxxxgpipppppsaippppppppppppfaiippppappppppigvxxx", "xxxbMxcxxcccZmccxcxvxvvvcxcmMMcxvxcxvvxccvxAbxxx", "xxxcpjcxxxxMVzxxxxxzzxxxxxxxxmMmxxxxxxxxxxxAvxxx", "xxxcakcxxxxAvxxcxxcMMxxxxxxxxxvkmMxzxxxxxxxZbxxx", "xxxcakcxcxMVzcxxxxlq8zxxxxxxxxxgAMMmcxxxxxxAvxxx", "xxxvakcxxxAvxxxxxme$@&bxxxxxcvgmVzcmMmxxxxxZbxcx", "xxxcakxxxMCzcxxxxxlZaobxxxxxcxgSvxxxvmMmcxxAvxxx", "xxxcakcxcSvxxxxxxxmdorbxxxcxcgmVzxxxxxxmMmcZbxxx", "cxxcakvxVVzcxxxczbu $excxxxxxgZcxxxxxxxxcmmSvcxc", "xxxvakxxScxxxxxxxbu;>2vxcxxvfmCzcxxxxxxxxxxAvxxx", "xxxvakxCVxcxxcxxxxmMMMxxxxczgAcxxcxxxxxxxxxZbxxx", "xxxcakvCkgccxxxxxxczzzxxxxcgbVlcxxxxzzxxxxxAvxxx", "xxxvsgMAblgkxcxxxcxxxxxxccxdAcxxxxxmMMmxxxzAbxxc", "cxxcakDmcMmkgkccxxxxxcxxxvfbCzxxxxve--rcxxxAvxxx", "xxxcpmAzxxvMmkgkcxxxxxxxxzdAcxxxxxv52-ocxczAbxcx", "xxxcpVMxxxxzcMmgglccxxxxvdbCzcxxcxcm-.*mzczAbzxx", "xcxcsbvxxxxxxxvMvjgzccxxzdAcxxxxxxxlMsXxccxAbxxx", "xxxcskcxxxxxxxxzvmvggzxvabCzcxxxczm>$+;bzxzAvxxx", "xxxcigcxxxxxxxxxcxvmvgjkdAvxxxxxxxvg0rcccxzAbxxx", "xxxxgzxxxxxxxxxxxxxxbmxdmMzvxcxxcxxvMmxxxcxZvccx", "xcxvMxcxxxnMMxxxxxxxxxxaClgzxxxxxcxxxzxcxxxAvxxx", "xxxcigvxxbw@4vxxxxxxxvalZMbjgxvxxxxxxxxxxxxAmxxc", "xxxcskcxbfou*8MxxxxxxlaVxxnMbkgxvccxxcxxxxxZvxxx", "xxxcpkcxvdXr%;MzxxxxvaxMxxxxmMbjgzccxxxxxclMmzxx", "xxxvakcxxvq;X4MxxxxvlaCxxxxxxxnMvkjzcxxxxvsMmzxx", "xxxvakcxxn3%%gvxxcxvpbMxcxxxxxxxmMbkgxcxxlaSvxxx", "xxxcakcxxxgwjvxxxxvkaCxxxxcxxxxxzcbMmkkxvagSbxxc", "xxxvakcxxxcMcxxxxxvpnMxxxxxxxxxxxxxxmMvkkfxAvxxx", "xxxvakvxxxxxxxxxxckdAxxxxxxxxxxxxxxxxxbmflxZbxxx", "xxxvalcxxxxxxxxxcbiMMlxxcxcblcxxxxxxxxxkfcxAvxxx", "xxxcagvxxxxxxxxxcggAxxxxxxc@gcxxxcxcslxxzAmzxx", "xxxvskvzggzvcxxvaMMxxxxxzM5;MoumxxxxvgfcxxxAvxxx", "xxxvskcxvxgkzvvggSxxxxxxxcx-X,vxxxxxcslcxxzAbxxx", "xxxvakcxxcxxkgzsVMxxxxxxxxcbxmxxxxxcgfvxxxxAbxxx", "xxxvslcxxxxxcxgcZxcxxxxxxxxxxxxxxccxazcxxvxZvxxx", "xxxvigxlxxzzzxxvggkzxxxzxzxzxzxzxzxfsxzzzxzZmzxx", "xxxxfxMmmmmmmmMbMvzzbmmmmmbmmmMmmmmgvMmmmmmSvxxx", "xxxvMZAZZZZZZZZZZAZZZZZMZZZZZZZZZZZZZZZZZZMZmxxx", "xxxxzxxzxzzxzzzxzxzzzxzzxxzzzxzzlzxzxzzxxzzxxxxx", "xxxxcxxxxcxcxxxxccxcxcxcxxcxccxcxccxxccxxccxxxxx", "xxxcxxxxxxxxxxxxxxxxxxcxxxxxxxxcxxxxxcxxxxxxxxxx", "xxxxxcxxxxxxxcxxxxcxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/undead-icon.c0000644000175300017530000003471612161170406016103 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 128 2", " c #51514F", ". c #7C7A78", "X c #7C7B78", "o c #63B862", "O c #52C553", "+ c #5CC45C", "@ c #7FC57D", "# c #81C87F", "$ c #60C0C0", "% c #62C0C0", "& c #71C9C9", "* c #71CBCA", "= c #868582", "- c #8D8B88", "; c #8F8E8A", ": c #948585", "> c #908F8C", ", c #988989", "< c #999794", "1 c #999895", "2 c #9B9A96", "3 c #A1A09C", "4 c #A5A3A0", "5 c #A6A4A0", "6 c #BFABAB", "7 c #B4B2AE", "8 c #B5B4AF", "9 c #B8B6B2", "0 c #B8B7B2", "q c #B9B8B4", "w c #BBBAB5", "e c #BCBBB6", "r c #BDBBB6", "t c #BCBBB7", "y c #BDBBB7", "u c #BDBCB7", "i c #BFBDB9", "p c #BFBEB9", "a c #C0ADAD", "s c #C1BBB7", "d c #C0BEB9", "f c #C2BDBA", "g c #C3BDBA", "h c #C1BFBB", "j c #C5BEB9", "k c #C6BFBB", "l c #83C783", "z c #A3CB9F", "x c #9DC3BE", "c c #AACBA7", "v c #A8C1BE", "b c #C1C0BB", "n c #C3C1BD", "m c #C5C0BD", "M c #CAC2BF", "N c #8DC5C3", "B c #94C7C5", "V c #A0C4C0", "C c #A8C4C0", "Z c #C6C5C0", "A c #CAC3C0", "S c #C8C6C1", "D c #CAC8C3", "F c #CAC9C4", "G c #CDCAC6", "H c #CECBC7", "J c #CECCC7", "K c #CECCC8", "L c #CECDC8", "P c #CFCEC9", "I c #D2C9CB", "U c #D2CACB", "Y c #D0CEC9", "T c #D6CFCA", "R c #D4CFCD", "E c #DACDCD", "W c #DCCCCE", "Q c #C6D0C0", "! c #C6D1C0", "~ c #CBD1C5", "^ c #D2D0CB", "/ c #D3D0CB", "( c #D7D0CB", ") c #D3D1CC", "_ c #D1D2CC", "` c #D3D2CD", "' c #D5D1CC", "] c #D4D2CD", "[ c #D6D2CD", "{ c #D5D3CE", "} c #D6D3CE", "| c #D5D4CF", " . c #D6D4CF", ".. c #D7D3D0", "X. c #D7D5D0", "o. c #D7D6D0", "O. c #D8D5D1", "+. c #D8D6D2", "@. c #D9D7D2", "#. c #D9D8D2", "$. c #D9D8D3", "%. c #DAD8D3", "&. c #DAD9D3", "*. c #DAD9D4", "=. c #DBD9D4", "-. c #DBDAD4", ";. c #DBDAD5", ":. c #DCDAD5", ">. c #DDDBD6", ",. c #DEDCD7", "<. c #DFDBD8", "1. c #DFDDD8", "2. c #E0DAD9", "3. c #E0DDD8", "4. c #E0DCD9", "5. c #E1DCD9", "6. c #E0DED8", "7. c #E0DED9", "8. c #E1DFDA", "9. c #E0E0DB", "0. c #E2E0DB", "q. c #E3E1DB", "w. c #E3E1DC", "e. c #E4E2DC", "r. c #E5E2DD", "t. c #E5E3DE", "y. c #E6E4DF", "u. c #E9E6E1", /* pixels */ ":.:.:.:.:.:.%.%.:.:.:.*.:.X.*.:.", ":.:.:.:.:.*.:.6.*.:.:.e.6.@.:.:.", ":.:.:.:.*.9.g 5 q.%.6.; g 6.%.:.", ":.:.:.:.#.t.3 - y.@.6. 4 u.%.:.", ":.:.:.:.1.w.S F 6.1.6.` *.1.*.:.", ":.:.:.1.Z 9 J J y 9 y p n p @.1.", ",.@. .6.e ` < > { D X.6.e Y <.*.", "6.K = e.b g , : g K u.7 2 6.:.*.", "1.` X *.h A a a M { 9 < t.*.*.,.", "*.*.X.1.p ` j j X.G p *.#.#.:.*.", ":.1.w.1.9 Z ( / G t ..U U K *.:.", "1.` g 6.p { B N ` R ! @ z 5.:.:.", "1.K X w.j v % % x E l O + ! 6.:.", "<.` < *.m v * * x E c o @ X.:.:.", ":.,.w.1.g / { { { K 5.L X.5.:.:.", ":.:.*.*.=.:.:.1.:.:.:.6.:.*.:.:." }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 121 2", " c #1F201F", ". c #242322", "X c #282827", "o c #2A2A29", "O c #31302F", "+ c #353534", "@ c #3B3B39", "# c #4C4B4A", "$ c #575654", "% c #585755", "& c #595856", "* c #605F5D", "= c #61605E", "- c #666563", "; c #6D6D6A", ": c #72716E", "> c #7D6F70", ", c #747371", "< c #7B7A77", "1 c #877778", "2 c #807F7C", "3 c #59AD59", "4 c #58B058", "5 c #6BAA69", "6 c #64B364", "7 c #6CBA6B", "8 c #5EC85F", "9 c #5FCE60", "0 c #65C365", "q c #64CC64", "w c #63D463", "e c #67DC68", "r c #739593", "t c #6BA5A4", "y c #76A4A2", "u c #77AAA8", "i c #6BB6B5", "p c #65BBBA", "a c #76B6B4", "s c #79B6B4", "d c #70B9B8", "f c #79BEBC", "g c #5EC3C3", "h c #6AC2C2", "j c #70C3C3", "k c #78C9C8", "l c #63DBDB", "z c #6ADFDF", "x c #71D1D1", "c c #64E6E6", "v c #6CE2E2", "b c #69E9E9", "n c #878783", "m c #8B8986", "M c #8F8E8A", "N c #928B8A", "B c #9B8A8A", "V c #92918D", "C c #969491", "Z c #9C9A97", "A c #9E9D9A", "S c #A59394", "D c #A59897", "F c #A09F9A", "G c #AA9999", "H c #B19F9F", "J c #83B680", "K c #8DB78A", "L c #95BD92", "P c #A3A19E", "I c #A2BD9D", "U c #80A3A1", "Y c #82BCBA", "T c #9BBCBA", "R c #A5A4A0", "E c #A8A7A3", "W c #AEACA8", "Q c #B2A3A2", "! c #B6A9A7", "~ c #B3AFAC", "^ c #BAAAAA", "/ c #ABBDA6", "( c #AFBEAA", ") c #B3B3AE", "_ c #BEB1AF", "` c #B7B6B2", "' c #BDB6B3", "] c #BCBBB6", "[ c #BEBDB9", "{ c #C0AEAE", "} c #C1B0B0", "| c #CAB6B6", " . c #CBB8B7", ".. c #C1BFBA", "X. c #CFBBBB", "o. c #B5C3AF", "O. c #B7C5B2", "+. c #BDC3B7", "@. c #B3C0BD", "#. c #C3C2BC", "$. c #C5C6C2", "%. c #C8C6C2", "&. c #CBC9C5", "*. c #D3CECA", "=. c #D3D1CC", "-. c #D7D5D0", ";. c #D9D6D2", ":. c #DCDAD5", ">. c #DFDCD8", ",. c #E0D7D2", "<. c #E3DAD5", "1. c #E0D6D8", "2. c #E1DEDA", "3. c #EADFD9", "4. c #E4E2DD", "5. c #E7E6E0", "6. c #E9E7E2", "7. c #ECEAE4", "8. c #EFEDE8", "9. c #F0EEE8", "0. c #F3F1EC", /* pixels */ ":.:.:.:.:.:.:.:.:.:.:.>.:.>.:.:.:.:.:.:.:.:.:.:.:.:.:.>.:.:.:.:.", ":.:.:.:.:.:.:.:.:.:.;.;.;.;.;.;.:.:.:.:.:.:.:.:.:.:.:.&.:.:.:.:.", ":.:.:.:.:.:.:.:.:.:.;.;.;.;.;.:.:.:.:.:.:.:.:.:.:.:.;.&.:.:.:.:.", ":.:.:.:.:.:.:.:.:.:.:.>.:.4.4.:.:.:.:.:.:.:.:.4.:.:.:.>.:.:.:.:.", ":.:.:.:.:.:.:.:.:.:.:.:.>.W ` 2.:.:.:.:.:.:.:.~ :.>.:.:.:.:.:.:.", ":.:.:.:.:.:.:.:.:.:.:.:.;.C + ) 4.;.:.:.:.4.2 X V 4.;.:.:.:.:.:.", ":.:.:.:.:.:.:.:.:.:.:.;.4.=.; =.>.:.:.:.:.4.= # - 7.;.:.:.:.:.:.", ":.:.:.:.:.:.:.:.:.:.:.>.;.+ * =.>.:.:.:.;.4.2 o M 6.;.:.:.:.:.:.", ":.:.:.:.:.:.:.:.:.:.:.:.-.W P &.:.:.:.:.:.:.;.W :.:.:.:.:.:.:.:.", ":.:.:.:.:.:.:.:.:.>.2.2.2.7.7.4.2.2.2.2.2.2.2.7.2.>.2.>.:.:.:.:.", ":.:.:.:.:.:.:.:.>.=.%.&.&.%.%.&.&.$.&.&.&.&.&.$.&.&.&.&.:.:.:.:.", ":.:.:.:.:.:.:.:.;.m P P F F P F P P V A P P P P P F P P -.:.:.:.", ":.:.:.:.:.:.:.>.=.P 7.4.9.7.5.0.4.4.&.:.4.4.4.4.4.9.4.4.:.:.:.:.", ":.:.:.:.:.:.:.2.=.Z >.>.n X ; ;.2.O.=.:.:.;.:.:.: &.:.:.:.:.:.", ":.:.2.%.@ E 5.:.=.Z 7.P > B N 1 N 4.+.=.>.:.>.4.& M 4.:.:.:.:.:.", ":.:.;.7.; C 7.:.=.A >.! | D Q ^ _ =.#.*.:.:.4.$ V 9.;.:.:.:.:.:.", ":.:.:.4.% < 4.>.=.A >._ .| X.| } =.#.*.4.4.$ V 0.-.:.:.:.:.:.:.", ":.;.4.#.- , =.>.=.A 4...G G H S ' 4.[ =.:.% V 7.-.:.:.:.:.:.:.:.", ":.:.;.2.7.7.>.>.=.A 2.:.' ^ ^ ~ =.4.[ ;.` C 7.-.:.:.;.;.:.:.:.:.", ":.:.:.:.;.;.;.>.=.A 4.:.4.:.-.4.4.4.#.=.4.7.>.2.>.>.4.>.:.:.:.:.", ":.:.:.:.:.:.:.>.=.M #.] ] ] +.] ] #.P ) ..] ] ] ] [ [ +.;.>.;.:.", ":.:.:.:.-.:.:.>.=.Z :.=.;.<.,.:.=.;.] &.;.=.1.1.:.;.=.=.:.:.:.:.", ":.:.:.>.7.2.:.>.=.A 4.2.&.T T $.2.4.#.=.4.:./ L ( 1.>.>.:.:.:.:.", ":.;.4.%.: [ 4.:.=.A 4.$.p l l g @.4.] =.:.J w e w K >.:.:.:.:.:.", ":.:.>.:.# C 7.:.=.A 4.Y u t k r i 1...;.o.q 3 0 4 q +.2.:.:.:.:.", ":.:.;.0.: V 7.:.=.F <.f j h x a h =...;.I w q w q w ( 2.:.:.:.:.", ":.;.4.#.@ $ ;.>.=.Z <.f c z z b h =.+.-.#.8 7 5 q 0 %.2.:.:.:.:.", ":.:.:.;.=.=.:.>.=.P <.y a a s s y -.#.*.4.I 6 0 0 / 2.:.:.:.:.:.", ":.:.:.:.:.>.:.:.=.F 2.*.=.=.*.*.*.:...*.>.2.&.O.&.2.;.:.:.:.:.:.", ":.:.:.:.:.:.:.>.=.P 4.:.1.>.>.:.>.2.$.*.>.;.4.2.2.:.:.:.:.:.:.:.", ":.:.:.:.:.:.:.:.:.;.>.;.>.:.:.:.;.:.:.:.:.:.:.;.:.:.:.:.:.:.:.:.", ":.:.:.:.:.:.:.:.;.>.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:." }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 173 2", " c #070807", ". c #0D0D0D", "X c #10100F", "o c #141514", "O c #181817", "+ c #1C1C1B", "@ c #252524", "# c #2A2A29", "$ c #302F2E", "% c #31302F", "& c #343332", "* c #3C3535", "= c #393937", "- c #3A3938", "; c #403F3E", ": c #3C783C", "> c #454443", ", c #4B4142", "< c #4E4E4C", "1 c #504F4D", "2 c #565654", "3 c #5C5B59", "4 c #635859", "5 c #6B5F5F", "6 c #587F7E", "7 c #656461", "8 c #686765", "9 c #696865", "0 c #6C6B69", "q c #72716E", "w c #767573", "e c #787774", "r c #797875", "t c #7D7C79", "y c #807575", "u c #897E7D", "i c #4C954C", "p c #4D9B4D", "a c #56AE56", "s c #5FBF5F", "d c #6E946C", "f c #798D76", "g c #65AC64", "h c #6CAC6A", "j c #70AE6E", "k c #6FB26E", "l c #75AD73", "z c #78AD76", "x c #7BAE79", "c c #5CC45C", "v c #5EC85E", "b c #5CD65D", "n c #61C461", "m c #64CC65", "M c #68CF68", "N c #62D362", "B c #66DD67", "V c #6BD36B", "C c #67DF68", "Z c #6CD96C", "A c #70DF70", "S c #6BE36C", "D c #83827F", "F c #82AD7F", "G c #729D9C", "H c #7D9F9D", "J c #5FB4B4", "K c #51BEBF", "L c #5BBCBC", "P c #66A2A1", "I c #6DACAB", "U c #79A6A5", "Y c #74A9A8", "T c #7AACAB", "R c #7CB0AE", "E c #64B6B5", "W c #68B2B1", "Q c #67BBBA", "! c #71B7B6", "~ c #7EB2B1", "^ c #5CC2C2", "/ c #5FD5D5", "( c #5FDADB", ") c #6BC6C6", "_ c #69CDCD", "` c #6FD0CF", "' c #65D7D7", "] c #65D7D8", "[ c #64DADB", "{ c #6BDCDD", "} c #71DEDE", "| c #6DE1E1", " . c #72E3E3", ".. c #868582", "X. c #888784", "o. c #8A8986", "O. c #91908D", "+. c #949390", "@. c #9A9995", "#. c #9E9D99", "$. c #A7908C", "%. c #A29493", "&. c #A49E9C", "*. c #AE9C9C", "=. c #89AD86", "-. c #8DA989", ";. c #92AD8E", ":. c #8CB189", ">. c #95AB91", ",. c #98AD94", "<. c #99A29F", "1. c #A3A19D", "2. c #ACA09F", "3. c #A3B39F", "4. c #8BAAA7", "5. c #81AEAC", "6. c #8CACAA", "7. c #9CA6A2", "8. c #93ADAA", "9. c #9CAEAB", "0. c #95B1AF", "q. c #88B3B0", "w. c #ACA6A3", "e. c #ADABA7", "r. c #ACACA9", "t. c #B4A3A3", "y. c #B0A9A7", "u. c #B2AEAA", "i. c #BDAAAA", "p. c #A9B5A4", "a. c #AFB7AA", "s. c #B4B3AE", "d. c #B4B9AF", "f. c #A4B6B2", "g. c #B5B5B1", "h. c #B9B7B2", "j. c #B3B8B4", "k. c #BCBAB6", "l. c #BDBDB9", "z. c #C3AFAF", "x. c #C7B3B3", "c. c #CAB5B5", "v. c #C0BFBA", "b. c #CDB9B9", "n. c #D2BEBE", "m. c #C3C2BD", "M. c #C7C4C1", "N. c #C8C6C1", "B. c #CCCAC5", "V. c #CFCCC8", "C. c #DCC5C5", "Z. c #D1CFCA", "A. c #D3D1CD", "S. c #D9CFD2", "D. c #D7D5D0", "F. c #D9D6D2", "G. c #DCDAD5", "H. c #DFDDD8", "J. c #E1CACA", "K. c #E6D0CF", "L. c #E7D0D0", "P. c #EBD2D3", "I. c #E4DBD5", "U. c #E1D7D9", "Y. c #E1DEDA", "T. c #E3E1DC", "R. c #EBE3DE", "E. c #E7DDE0", "W. c #E7E5E0", "Q. c #E9E6E1", "!. c #EBE9E3", "~. c #F1EEE9", "^. c #F4F2EC", /* pixels */ "G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.G.G.G.G.G.G.Y.G.G.G.G.G.G.Y.G.G.Y.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.T.G.G.G.G.G.G.G.", "G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.D.D.G.A.G.D.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.A.m.A.T.G.G.G.G.G.", "G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.D.D.D.A.G.A.G.A.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.B.l.D.G.G.G.G.G.G.", "G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.Y.G.H.G.Y.G.G.G.G.G.G.G.G.G.G.G.G.G.T.T.G.G.G.G.G.G.G.", "G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.D.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.", "G.G.H.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.Y.Y.G.Q.G.G.G.G.G.G.G.G.G.F.T.T.T.G.G.G.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.H.F.Y.A.< @ e T.G.G.G.G.G.G.G.G.T.e.@ 3 H.G.G.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.F.G.H.A.m.1.+ F.Y.D.G.G.G.G.G.G.T.- 0 # 1.T.F.G.G.H.G.G.G.G.G.G.", "G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.H.G.G.H.^.2 r !.D.G.G.G.G.G.G.G.T.$ 7 O O.!.G.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.F.Y.2 2 T.F.G.G.G.G.G.G.G.D.R.% D % <.R.G.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.F.T.h.X - > A.H.F.G.G.G.G.G.G.T.<.+ < G.H.G.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.G.G.G.H.G.G.G.G.G.G.G.G.G.H.F.A.B.A.F.G.G.G.G.G.G.G.G.G.T.D.I.H.G.G.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.H.F.F.H.H.H.Y.F.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.", "G.G.G.G.H.G.G.G.G.G.G.G.G.G.Y.Y.H.H.T.H.Y.H.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.H.H.T.H.H.H.H.H.H.H.G.G.G.G.G.G.", "G.G.G.G.G.G.G.G.G.G.G.G.G.H.F.N.B.B.B.B.B.B.B.B.N.B.B.B.B.B.B.N.B.B.B.B.B.B.B.B.m.D.H.G.G.G.G.H.", "G.G.G.G.G.G.G.G.G.G.G.G.F.Y.f t X...X.D ............o.t e o.....X.X.D X.D D X.o.D v.H.G.G.G.G.G.", "G.G.G.G.G.G.G.G.G.G.G.G.G.Y...G.!.T.T.T.T.!.T.T.T.T.!.B.m.!.T.T.T.T.T.T.T.T.T.T.T.H.G.G.G.G.G.G.", "G.G.G.G.G.G.G.F.F.G.G.G.H.Y.D Z.H.F.G.!.G.Z.T.Q.F.F.Y.m.k.Y.F.G.G.G.G.F.G.T.H.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.G.Y.~.Q.F.H.G.G.Y.D A.G.H.G.7 X + D T.G.T.m.k.T.G.G.G.G.G.F.~.9 O.!.F.G.G.G.G.G.G.G.", "G.G.G.G.G.Y.v.7 X.Y.F.G.G.Y.D Z.H.G.1 , 4 - 5 * q T.H.m.k.T.G.G.G.G.F.~.9 & A.H.G.G.G.G.G.G.G.G.", "G.H.G.G.F.Y.k.; & W.F.H.F.T.D Z.!.@.$.L.n.L.n.L.u s.!.m.k.T.F.G.G.F.~.8 & Y.T.F.G.G.G.G.G.G.G.G.", "G.G.G.G.H.F.~.#.# T.F.H.G.Y.D A.H.@.P.u 7 n.< 2.C.&.Q.m.k.T.G.G.F.~.8 & Y.H.F.H.G.G.G.G.G.G.G.G.", "G.G.G.G.G.D.~.#.% ^.G.G.G.Y.D Z.G.t.J.n.b.J.c.C.n.y.T.m.l.T.F.F.^.7 & Y.Y.F.G.G.G.G.G.G.G.G.G.G.", "G.G.G.G.F.T.h.& . e H.G.G.T.D Z.T.u.c.*.x.b.i.t.z.k.W.m.k.T.F.^.8 & T.Y.F.G.G.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.Y.m.o.+.@.H.G.F.T.D Z.Y.B.t.*.u x.r i.t.F.T.m.h.T.T.7 & Y.Y.F.G.G.G.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.G.T.!.!.!.G.G.G.Y.D A.G.T.m.&.i.b.t.2.A.H.H.m.k.Q.l.< G.T.F.G.G.G.G.G.G.G.H.G.G.G.G.G.", "G.G.G.G.G.G.G.D.F.F.G.G.G.Y.D Z.H.D.H.F.m.g.m.G.H.F.H.m.k.T.F.T.G.F.G.F.F.G.G.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.G.G.G.G.G.G.G.G.Y...F.T.T.T.T.Q.Q.Q.T.T.T.T.B.m.T.T.T.T.T.T.T.T.T.Y.T.T.H.G.G.G.G.G.G.", "G.G.G.G.G.G.G.G.G.G.G.G.F.T.r w.g.s.s.s.s.s.s.g.s.s.h.1.@.k.s.s.g.s.s.g.s.s.g.s.u.V.H.G.G.G.G.G.", "G.G.G.G.G.G.G.H.G.G.G.G.G.T.D m.Z.V.B.B.B.V.B.B.B.B.A.s.s.A.B.B.B.B.B.B.B.B.B.V.B.F.G.G.G.G.G.G.", "G.G.G.G.G.G.G.F.G.G.G.G.G.T...A.Y.H.H.T.R.I.R.T.H.Y.T.N.l.W.H.Y.T.Q.E.E.R.H.H.H.H.G.G.G.G.G.G.G.", "G.G.G.G.G.G.G.W.T.G.G.G.G.Y.D Z.H.G.Y.k.6.5.8.m.Y.F.T.m.k.T.D.Y.V.,.F -.s.Y.G.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.G.A.g.v.Y.G.G.G.Y.D A.H.H.9.L [ [ [ E j.I.Y.m.k.Y.Y.m.g N B B c -.Y.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.T.r.O & T.D.G.F.T.D Z.T.l.L ) ` .) _ E B.T.m.k.T.D.h B A V V A c p.Y.G.G.G.G.G.G.G.G.", "G.G.G.G.G.F.Q.+.# T.F.G.G.Y.D A.I.q.) $.G ( #.w ^ f.Q.m.k.!.a.m V : V p i S z U.G.G.G.G.G.G.G.G.", "G.G.G.G.G.D.~.@.% ^.G.G.G.T.D Z.I.T ' Y J | ! I / 8.R.m.k.Q.3.m V n V M m S k S.H.G.G.G.G.G.G.G.", "H.G.G.G.G.H.A.8 + g.G.G.F.T.D A.I.Y | { ] { | .' 8.R.v.k.Q.d.n A M M M M S x U.G.G.G.G.G.G.G.G.", "G.G.G.G.F.W.e.> > 7 H.G.G.T.D Z.I.~ ( } { ' .[ / 0.R.m.k.T.F.l b d f a M c s.T.F.G.G.H.G.G.G.G.", "G.G.G.G.G.G.Y.!.!.!.G.G.G.T.D Z.I.G G E W H ^ U P 4.R.m.k.Y.H.M.h v v S s >.Y.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.G.G.F.F.F.G.G.G.T...Z.Y.r.Y.s.l.I.7.A.B.a.!.m.k.T.F.T.Z.p.:.>.k.Y.G.G.G.G.G.G.G.G.G.H.", "G.G.G.G.G.G.G.G.G.G.G.G.F.T.D Z.G.T.G.T.Y.G.T.H.H.Y.Y.m.k.T.F.G.H.T.Y.E.T.G.G.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.G.G.G.G.G.G.G.G.Y.X.Z.H.G.G.G.G.G.G.G.G.F.Y.N.k.T.G.G.G.F.F.G.G.G.G.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.H.G.G.G.G.G.G.G.G.A.G.G.G.G.G.G.G.G.G.G.G.G.G.F.H.G.G.G.G.G.G.G.G.G.G.H.G.G.G.G.G.G.G.", "G.G.G.G.G.G.G.G.G.G.G.G.G.G.H.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.H.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G." }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/unequal-icon.c0000644000175300017530000004101612161170407016305 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 127 2", " c #222221", ". c #333332", "X c #424140", "o c #5D5C5A", "O c #61605D", "+ c #7C7A78", "@ c #308F2E", "# c #4B9949", "$ c #4D9E4B", "% c #58A056", "& c #5AA357", "* c #68A565", "= c #6BAA67", "- c #73AD6F", "; c #78AE74", ": c #82807D", "> c #84837F", ", c #8D8C88", "< c #8F8E8B", "1 c #999894", "2 c #8CB587", "3 c #8EB689", "4 c #8FB68A", "5 c #9ABA94", "6 c #A3BE9E", "7 c #ADABA7", "8 c #AEADA8", "9 c #AFADA9", "0 c #B8B7B2", "q c #BBB9B5", "w c #C1BFBB", "e c #BAC7B4", "r c #BDCAB7", "t c #BFCBB9", "y c #C1C0BC", "u c #C2C0BC", "i c #C2C1BC", "p c #C2C1BD", "a c #C3C2BE", "s c #C4C2BE", "d c #C4C3BE", "f c #C4C3BF", "g c #C5C3BF", "h c #C4C4BE", "j c #C5C4BF", "k c #C6C4C0", "l c #C7C5C1", "z c #C6C6C0", "x c #C7C6C1", "c c #C8C6C1", "v c #C8C6C2", "b c #C9C7C3", "n c #CAC6C4", "m c #CAC7C4", "M c #CCC7C6", "N c #C8C9C2", "B c #C9C8C3", "V c #C9C9C3", "C c #C8CFC1", "Z c #CAC8C4", "A c #CAC9C4", "S c #CBC9C4", "D c #CBC9C5", "F c #CACAC4", "G c #CACAC5", "H c #CBCAC5", "J c #CCCAC5", "K c #CCC8C6", "L c #CCC9C6", "P c #CDC9C6", "I c #CCCAC6", "U c #CCCBC6", "Y c #CDCBC6", "T c #CDCBC7", "R c #CDCCC6", "E c #CDCCC7", "W c #CECCC7", "Q c #CECAC8", "! c #CECCC8", "~ c #CECDC8", "^ c #CFCDC8", "/ c #CFCDC9", "( c #CFCEC9", ") c #D0CAC9", "_ c #D0C9CA", "` c #D0CEC9", "' c #D0CCCA", "] c #D0CECA", "[ c #D1CFCA", "{ c #D1CFCB", "} c #D5CACE", "| c #D6CBCF", " . c #D3CECC", ".. c #C7D0C0", "X. c #CBD1C4", "o. c #D0D0C9", "O. c #D2D0CB", "+. c #D2D0CC", "@. c #D3D1CC", "#. c #D3D1CD", "$. c #D3D3CC", "%. c #D4D2CD", "&. c #D4D3CD", "*. c #D5D3CE", "=. c #D5D3CF", "-. c #D5D4CE", ";. c #D5D4CF", ":. c #D6D4CF", ">. c #D7D4CF", ",. c #D7D5CF", "<. c #D7D4D0", "1. c #D7D5D0", "2. c #D8D6D1", "3. c #DAD5D3", "4. c #D9D7D2", "5. c #DDD7D6", "6. c #DED7D7", "7. c #DAD8D3", "8. c #DBD8D3", "9. c #DCDAD5", "0. c #DEDCD7", "q. c #DFDDD8", "w. c #E1D7DA", "e. c #E0D9D9", "r. c #E7DBDF", "t. c #E4DCDC", "y. c #E9DCE1", /* pixels */ "@.A A S A m [ %.%.( A S S A A %.", "A k ( S ( S a 9.1.w Q A A [ a ( ", "A ( 7.,.1.%.a , k A e.- & r.m Q ", "S S ,.%.%.[ A q O v 9.e % r.j ( ", "S ( 2.%.%.%.S < , A 6.4 @ j X.( ", "m A 1.%.%.%.w 9 7.k 1.6 2 r S R ", "( a k k v k v 0.@.k f Q } m j @.", "<.,.<.%.%.1.1.@.%.1.1.-.@.,.1.%.", "%.@.@.[ @.@.,.@.%.1.@.@.[ @.%.,.", "( a m | m a k ,.%.a j v %.v w [ ", "v ( X.; t 7.k ,.%.f 8.R + ( A ( ", "A S 1.# 5 w.j %.%.f e.: Z ( Q ", "S A y.* 4 w.j ,.O.Q 0 X . 1 @.S ", "A .C $ = 1.k ,.@.m [ 7 o 8 @.R ", "A a A Q Q v a ,.@.a A %.,. .a ( ", "%.( Q ( S ( O.,.%.[ ( A A S [ %." }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 246 2", " c #000000", ". c #010101", "X c #0C0C0C", "o c #191818", "O c #1B1B1A", "+ c #1D1D1C", "@ c #212120", "# c #232221", "$ c #232222", "% c #252424", "& c #323230", "* c #353433", "= c #3B3A39", "- c #007700", "; c #007A00", ": c #007B00", "> c #017E01", ", c #027C02", "< c #037F03", "1 c #047904", "2 c #494846", "3 c #52514F", "4 c #62615F", "5 c #6E6D6A", "6 c #706F6C", "7 c #73726F", "8 c #777573", "9 c #807F7B", "0 c #817F7C", "q c #058004", "w c #0D830D", "e c #1C861B", "r c #258C24", "t c #268D25", "y c #339231", "u c #359133", "i c #349332", "p c #469943", "a c #469944", "s c #4C9C49", "d c #5EA35B", "f c #5EA45B", "g c #63A560", "h c #64A561", "j c #6DA969", "k c #71AB6D", "l c #72AB6F", "z c #74AB70", "x c #74AC70", "c c #7BAF77", "v c #7CAF78", "b c #7FB17B", "n c #81807D", "m c #83827F", "M c #80B17C", "N c #81B27D", "B c #84B27F", "V c #888784", "C c #8F8E8A", "Z c #989693", "A c #9B9996", "S c #9F9D99", "D c #8DB689", "F c #90B68B", "G c #91B88C", "H c #91B88D", "J c #95BA90", "K c #9BBB96", "L c #9ABC95", "P c #9EBD99", "I c #A0BE9B", "U c #A1BE9B", "Y c #A1BE9C", "T c #A7A5A2", "R c #A7A6A2", "E c #A8A7A3", "W c #AAA9A5", "Q c #AFADA9", "! c #AEADAA", "~ c #AFAEAC", "^ c #B1B0AD", "/ c #B2B1AE", "( c #B4B3B0", ") c #B6B4B1", "_ c #B5B6B1", "` c #B6B7B1", "' c #B6B7B2", "] c #B7B6B3", "[ c #B8B7B3", "{ c #B8B7B4", "} c #B9B7B4", "| c #B9B8B4", " . c #B9B8B5", ".. c #BAB8B4", "X. c #BAB8B5", "o. c #BBB9B5", "O. c #B9BAB4", "+. c #BABAB5", "@. c #BAB9B6", "#. c #BBBAB6", "$. c #BBBBB6", "%. c #BCBAB7", "&. c #BCBBB7", "*. c #BEBCB7", "=. c #BCBBB8", "-. c #BDBBB8", ";. c #BEBCB8", ":. c #BEBDB9", ">. c #BFBDBA", ",. c #BFBEBA", "<. c #C0BEBA", "1. c #C0BEBB", "2. c #C0BFBB", "3. c #C1BFBB", "4. c #C1BFBC", "5. c #ABC2A6", "6. c #ADC2A7", "7. c #B8C7B2", "8. c #C2C0BC", "9. c #C3C1BC", "0. c #C2C0BD", "q. c #C3C1BD", "w. c #C3C3BD", "e. c #C3C2BE", "r. c #C4C3BE", "t. c #C5C3BE", "y. c #C5C3BF", "u. c #C5C4BF", "i. c #C5C4C0", "p. c #C7C5C1", "a. c #C7C6C1", "s. c #C8C6C1", "d. c #C8C6C2", "f. c #C8C7C2", "g. c #C9C7C3", "h. c #C9C8C3", "j. c #CAC8C3", "k. c #C9CEC3", "l. c #CAC8C4", "z. c #CAC9C4", "x. c #CBC9C4", "c. c #CBC9C5", "v. c #CBCAC5", "b. c #CCCAC5", "n. c #CCCAC6", "m. c #CDCBC7", "M. c #CDCCC7", "N. c #CECCC7", "B. c #CECCC8", "V. c #CECDC8", "C. c #CFCDC8", "Z. c #CFCDC9", "A. c #D0CEC9", "S. c #D1CFCA", "D. c #D1CFCB", "F. c #CED0C7", "G. c #D0D0C9", "H. c #D0D1CA", "J. c #D1D1CA", "K. c #D1D1CB", "L. c #D2D0CB", "P. c #D2D1CB", "I. c #D2D2CB", "U. c #D2D0CC", "Y. c #D3D1CC", "T. c #D3D2CD", "R. c #D4D2CD", "E. c #D5D2CE", "W. c #D4D3CE", "Q. c #D5D3CE", "!. c #D6D3CE", "~. c #D6D3CF", "^. c #D4D4CD", "/. c #D5D4CE", "(. c #D5D5CE", "). c #D6D4CE", "_. c #D6D4CF", "`. c #D7D4CF", "'. c #D6D5CF", "]. c #D7D5CF", "[. c #D6D6CF", "{. c #D7D5D0", "}. c #D7D6D0", "|. c #D8D4D1", " X c #D8D6D0", ".X c #D8D6D1", "XX c #D9D6D1", "oX c #D9D7D1", "OX c #DAD4D3", "+X c #D9D7D2", "@X c #DAD7D2", "#X c #DBD4D4", "$X c #DBD5D4", "%X c #DCD5D5", "&X c #DDD6D6", "*X c #DED6D6", "=X c #DED6D7", "-X c #DFD6D7", ";X c #DFD7D7", ":X c #DAD8D2", ">X c #DAD8D3", ",X c #DBD8D3", " c #353433", ", c #363634", "< c #006F00", "1 c #007000", "2 c #007200", "3 c #007300", "4 c #007500", "5 c #007600", "6 c #047B04", "7 c #087D08", "8 c #51504E", "9 c #595856", "0 c #5C5B59", "q c #61605D", "w c #62615E", "e c #696865", "r c #696866", "t c #6A6966", "y c #6B6A67", "u c #6C6B69", "i c #6D6C69", "p c #6E6D6A", "a c #71706D", "s c #73726F", "d c #757471", "f c #767472", "g c #787774", "h c #7F7D7A", "j c #807F7C", "k c #5EA45B", "l c #61A55E", "z c #6AA567", "x c #6AA767", "c c #69A866", "v c #6BA967", "b c #6BA968", "n c #6DA969", "m c #6EAA6A", "M c #6EAA6B", "N c #6FAA6B", "B c #70AB6C", "V c #70AB6D", "C c #71AB6D", "Z c #81807D", "A c #83817E", "S c #84837F", "D c #868481", "F c #969491", "G c #9B9995", "H c #9E9D9C", "J c #A09F9B", "K c #99BB94", "L c #9EBD99", "P c #A3A19D", "I c #A3A29E", "U c #A4A39F", "Y c #A0BE9A", "T c #A0BE9B", "R c #A2BF9C", "E c #ADABA9", "W c #ADACA9", "Q c #AEADAA", "! c #AFADAB", "~ c #AFAEAB", "^ c #B0AEAA", "/ c #B1B0AD", "( c #C0BEBA", ") c #C2C1BC", "_ c #C7C6C1", "` c #C8C6C2", "' c #C9C7C2", "] c #C9C7C3", "[ c #C9C8C3", "{ c #CAC8C3", "} c #CAC8C4", "| c #CBC9C4", " . c #CBCAC5", ".. c #CCCAC5", "X. c #CDCBC6", "o. c #CDCBC7", "O. c #CCCFC5", "+. c #CECCC8", "@. c #CFCCC8", "#. c #CFCDC8", "$. c #CFCDC9", "%. c #D0CEC9", "&. c #D0CFCA", "*. c #D1CFCA", "=. c #D1CFCB", "-. c #CED0C7", ";. c #CFD1C8", ":. c #D0D0C9", ">. c #D1D1CA", ",. c #D1D0CB", "<. c #D2D0CB", "1. c #D2D1CB", "2. c #D2D1CC", "3. c #D3D1CC", "4. c #D3D1CD", "5. c #D3D2CC", "6. c #D4D2CD", "7. c #D4D2CE", "8. c #D5D3CE", "9. c #D6D3CF", "0. c #D6D4CF", "q. c #D7D5CF", "w. c #D6D7CF", "e. c #D7D7CF", "r. c #D7D5D0", "t. c #D7D7D0", "y. c #D8D5D0", "u. c #D8D6D0", "i. c #D8D6D1", "p. c #D9D6D1", "a. c #D9D7D1", "s. c #D9D5D2", "d. c #D9D7D2", "f. c #DAD7D2", "g. c #DCD5D5", "h. c #DDD5D6", "j. c #DDD6D6", "k. c #DED6D6", "l. c #DED7D7", "z. c #D8D8D1", "x. c #D9D8D1", "c. c #DAD8D2", "v. c #DAD8D3", "b. c #DBD8D3", "n. c #DBD9D3", "m. c #DBD9D4", "M. c #DCDAD5", "N. c #DDDBD6", "B. c #DEDCD6", "V. c #DEDCD7", "C. c #DFDCD7", "Z. c #DFDDD7", "A. c #DFD6D8", "S. c #DFDDD8", "D. c #E0DED9", "F. c #E1DFD9", "G. c #E1DFDA", "H. c #E4D9DC", "J. c #E4D8DD", "K. c #E5D9DE", "L. c #E7D9DF", "P. c #E6DADE", "I. c #E6DADF", "U. c #E6DBDF", "Y. c #E2E0DB", "T. c #E3E1DB", "R. c #E4E2DC", "E. c #E4E2DD", "W. c #E5E2DD", "Q. c #E5E3DE", "!. c #E6E4DF", "~. c #E7E4DF", "^. c #E7E5DF", "/. c #E8DAE0", "(. c #EADBE3", "). c #F3DFEB", "_. c #E8E6E1", "`. c #E9E6E1", "'. c #E9E7E1", "]. c #F9F7F1", "[. c #FFFFFE", /* pixels */ "8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.", "8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.", "8.8.8.8.r.i.i.i.i.i.i.e.i.i.i.i.i.i.i.u.8.8.8.8.8.8.8.8.u.i.u.i.r.r.n.t.r.t.r.t.r.r.r.r.8.8.8.8.", "8.8.8.8.o.] } [ [ [ [ [ [ [ [ [ [ [ [ o.8.8.8.8.8.8.8.8.o.] ] ] [ [ [ [ } } } } } } ] #.8.8.8.8.", "8.8.r.o.H / ! ! ! ! ! ~ ~ ~ ! ! ^ ! / H o.8.8.8.8.8.r.o.H / ! ! ! ~ ~ ~ ! ! ^ ^ ~ ! ~ J o.r.8.8.", "8.8.i.[ / F.n.n.n.n.n.n.n.n.n.n.M.i.Y./ [ u.8.8.8.8.i.[ ^ Y.n.n.n.n.t.t.8.n.m.f.m.n.Y.~ } t.8.8.", "8.8.i.} ! i.,.4.5.8.8.5.5.8.5.8.8.,.n.! [ n.i.8.8.8.i.} ! i.,.4.4.8.A.H.U.g.,.8.4.4.n.! } r.r.8.", "8.8.i.[ ! n.8.8.8.8.8.8.8.8.8.8.8.4.n.! o.[ [ M.4.8.i.[ ! n.8.8.8.-.K C l Y M.5.8.4.n.! [ r.4.8.", "8.8.i.[ ~ n.5.8.8.8.8.8.8.8.8.8.8.4.n.E 8.G A W.,.i.] ^ n.8.,.I.v 5 7 3 z U.,.8.4.n.~ [ i.8.8.", "8.8.i.[ ~ n.8.8.8.8.8.8.8.8.8.8.8.8.n.! _ Y.j S R.8.] ! n.8.,.g.T R z 3 V U.,.8.8.n.~ [ i.8.8.", "8.8.i.[ ! n.8.8.8.8.8.8.8.8.8.8.8.8.n.! ] 8.W.S A '.] ^ n.8.8.8.i.).C < m /.,.8.8.n.! [ i.8.8.", "8.8.i.[ / n.,.8.8.8.8.8.8.8.8.8.8.4.M.^ [ i.*.R.D h #.! n.,.8.8.;.H.m < m I.,.8.4.n.! [ i.8.8.", "8.8.i.[ ! n.4.8.8.8.8.8.8.8.8.8.8.,.n.^ [ i.,.n.,.@ ; } ! n.4.8.8.,.I.m 3 m I.,.8.4.n.~ [ i.8.8.", "8.8.i.[ ! n.8.8.8.8.8.8.8.8.8.8.8.8.n.^ [ 8.n.o.& - ,.o.! n.5.8.8.8.(.m < m (.8.8.8.n.~ [ i.8.8.", "8.8.i.[ ~ n.8.8.8.8.8.8.8.8.8.8.8.8.n.! ] F.,.& & o.Z._ ~ n.5.8.8.#.g.n 3 m g.-.8.5.n.~ [ i.8.8.", "8.8.i.[ ~ n.4.8.8.8.8.8.8.8.8.8.8.4.n.! #.( % - o.n.8.[ ~ n.8.,.I.z 5 6 5 6 5 v U.,.n.~ [ i.8.8.", "8.8.i.[ ! n.4.8.8.8.8.8.8.8.8.8.8.8.n.! 4.P 0 ] M.,.i.[ ^ m.8.4.g.L k v v v k L g.,.n.^ [ i.8.8.", "8.8.i.[ ! n.,.4.8.4.8.5.8.4.8.5.4.,.n.! ] S.'.r.8.8.i.} ^ d.4.4.4.d.(.I.H.I.(.d.4.4.n.! } i.8.8.", "8.8.n._ / Y.n.n.n.n.n.n.n.n.m.n.n.n.Z./ ] r.;.8.8.8.r.] / Y.i.n.m.n.e.e.8.t.e.n.m.i.F./ [ i.8.8.", "8.8.8.o.H / ! ! ~ ! ~ ~ ~ ! ~ ~ ! ! / H o.r.8.8.8.8.8.o.H / ! ! ~ ~ ~ ! ~ ~ ~ ~ ! ! / H o.r.8.8.", "8.8.8.8.o.[ } [ [ [ [ [ } } [ } [ [ [ o.8.8.8.8.8.8.8.8.o.[ } [ [ [ [ [ [ [ [ [ [ } [ o.8.8.8.8.", "8.8.8.8.r.i.i.i.t.i.i.i.r.r.t.r.t.r.r.r.8.8.8.8.8.8.8.8.8.i.i.i.n.r.r.r.r.r.n.r.n.r.r.r.8.8.8.8.", "8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.4.8.8.8.8.8.8.8.", "8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.", "8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.", "8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.4.8.8.8.8.8.8.8.8.", "8.8.8.8.r.i.i.i.r.t.r.n.i.r.n.r.i.i.i.r.8.8.8.8.8.8.8.8.i.i.i.i.8.n.n.8.r.n.8.n.i.i.i.r.8.8.8.8.", "8.8.8.8.o.[ } [ [ [ [ [ } [ [ [ [ } ] o.8.8.8.8.8.8.8.8.o.[ } [ [ [ [ [ [ [ [ [ [ } ] o.8.8.8.8.", "8.8.r.o.H / ! ! ! ~ ~ ~ ! ^ ^ ^ ! ! / H o.i.8.8.8.8.8.o.H / ! ! ! ! ! ! ! / ! / ! ^ / H o.r.8.8.", "8.8.i.[ / F.n.n.n.n.t.t.t.i.m.n.n.n.F./ [ i.8.8.8.8.i.] / F.i.n.n.n.n.n.8.8.e.n.n.i.F./ [ i.8.8.", "8.8.i.} ! i.,.4.8.8.g.I.I.g.,.8.4.4.i.! } i.8.8.8.8.n.[ ! n.4.8.5.8.5.8.~.~.M.,.4.4.d.! } i.8.8.", "8.8.i.[ ! n.8.8.8.o.K V l T M.5.8.4.n.! [ i.8.8.8.8.8.[ ! n.4.8.8.8.n.) p q H Z.4.8.m.! [ i.8.8.", "8.8.i.[ ~ n.5.,.U.v 5 7 < z I.,.8.4.n.~ [ n.8.8.8.8.r.[ ~ n.5.8.8.,.Z.8 t ~.*.4.n.! [ r.8.8.", "8.8.i.[ ~ n.8.,.g.T T v 3 V U.,.8.8.n.~ [ 8.8.8.8.8.r.[ ! n.8.8.,.Y.F $ p ~.,.4.m.! [ n.8.8.", "8.8.i.[ ! n.8.8.5.i.).C 3 C I.,.8.4.n.~ [ r.8.8.8.8.n.[ ! n.4.4.i.o.# , t p ~.,.,.n.^ [ r.8.8.", "8.8.i.[ / n.,.8.8.;.H.B 3 m U.,.8.8.n.~ [ n.8.8.8.8.r.[ ! n.8.4.Y.q / g u Y.*.8.n.^ [ r.8.8.", "8.8.i.[ ! n.5.8.8.,.I.m < m I.,.8.4.n.~ } i.8.8.8.8.i.} ! n.4.Z.U t [.d g ].4.4.n.~ [ r.8.8.", "8.8.i.[ ! n.8.8.8.,.(.V < V (.,.8.8.n.! [ i.8.8.8.8.t.[ ~ n.#._.t 0 a > : d o.5.n.~ [ n.8.8.", "8.8.i.[ ~ n.8.8.8.;.g.m 3 m g.;.8.4.n.! [ r.8.8.8.8.r.[ ~ n.;.W.a o o + [ 8.n.! } r.8.8.", "8.8.i.[ ~ n.4.,.I.z 3 6 5 6 5 v I.;.n.! [ n.8.8.8.8.r.} ~ n.8.8.#.[ ] Z.u u D.,.8.n.~ [ r.8.8.", "8.8.r.[ ! n.4.4.M.T k v m v k L M.4.n.! } r.8.8.r.5.r.[ ! n.4.8.r.i.r.G.P 0 P F.,.8.n.^ [ i.8.8.", "8.8.r.} ! n.4.4.5.g.I.I.I.H./.g.,.4.i.! } i.8.8.4.r.r.} ! n.4.4.4.4.4.;.m.'.m.,.8.,.i.! } i.8.8.", "8.8.t.} ~ Y.n.m.n.i.8.e.e.e.8.n.m.m.Y.~ } r.8.8.8.8.t.} ~ Y.m.m.n.n.m.m.r.r.n.n.n.i.Y./ [ i.8.8.", "8.8.r.o.J ~ ! ! ^ ^ / ! ~ ~ ~ ! ! ! ~ H o.r.8.8.8.8.r.o.J ~ ! ! ! ! ~ ~ ~ ! ! ! ^ ! / H o.r.8.8.", "8.8.8.8.#._ } } [ [ [ [ [ [ [ [ [ } ] #.8.8.8.8.8.8.8.8.#.] } } [ [ [ [ [ [ [ [ [ } ] o.8.8.8.8.", "8.8.8.8.r.r.r.i.r.n.r.r.t.r.r.n.n.r.r.r.8.8.8.8.8.8.8.8.r.t.r.r.r.i.i.i.i.i.i.i.i.i.i.r.8.8.8.8.", "8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.", "8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8." }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/unruly-icon.c0000644000175300017530000004273612161170407016203 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 150 2", " c #0D0D0D", ". c #171717", "X c #191A1A", "o c #1B1B1B", "O c #1D1D1D", "+ c #1E1E1E", "@ c #1F1F1F", "# c #232323", "$ c #262626", "% c #272727", "& c #282828", "* c #292929", "= c #2A2A2B", "- c #2B2B2B", "; c #2E2E2E", ": c #2F2F2F", "> c #313131", ", c #323232", "< c #323333", "1 c #333333", "2 c #353535", "3 c #363535", "4 c #3A3939", "5 c #3C3C3B", "6 c #3D3D3D", "7 c #3E3E3E", "8 c #3F3F3F", "9 c #40403F", "0 c #444443", "q c #444444", "w c #494848", "e c #4B4B4B", "r c #4C4C4B", "t c #4C4C4C", "y c #4F4F4F", "u c #515151", "i c #535353", "p c #545454", "a c #575656", "s c #595959", "d c #5A5A5A", "f c #5B5B5B", "g c #5E5E5E", "h c #606060", "j c #616161", "k c #626262", "l c #636363", "z c #646463", "x c #656565", "c c #666565", "v c #6A6A6A", "b c #6C6C6C", "n c #6F6F6F", "m c #757575", "M c #767676", "N c #777776", "B c #767777", "V c #777777", "C c #787878", "Z c #797978", "A c #797979", "S c #7A7A7A", "D c #7B7B7B", "F c #7C7B7B", "G c #7B7B7C", "H c #7C7C7C", "J c #7D7C7C", "K c #7D7D7C", "L c #7C7C7D", "P c #7D7D7D", "I c #7E7E7E", "U c #7F7E7E", "Y c #7F7F7F", "T c #80807F", "R c #808080", "E c #818180", "W c #818181", "Q c #828281", "! c #828282", "~ c #848483", "^ c #848484", "/ c #878787", "( c #888888", ") c #8A8A8A", "_ c #8D8D8E", "` c #8F8F8F", "' c #909090", "] c #919191", "[ c #929292", "{ c #969595", "} c #999999", "| c #9A9A99", " . c #9C9C9C", ".. c #9D9D9D", "X. c #9F9F9F", "o. c #A7A7A7", "O. c #AAAAAA", "+. c #AAAAAB", "@. c #ADADAD", "#. c #B0AFAE", "$. c #B1B0B0", "%. c #B5B5B5", "&. c #B6B5B5", "*. c #B7B6B6", "=. c #BABABA", "-. c #BDBDBD", ";. c #BEBEBE", ":. c #C2C1C1", ">. c #C4C4C3", ",. c #C5C5C4", "<. c #C7C6C6", "1. c #C8C8C7", "2. c #CCCAC5", "3. c #CCCAC6", "4. c #CCCBC6", "5. c #CDCBC6", "6. c #CDCBC7", "7. c #C9C9C8", "8. c #CBCBCB", "9. c #CECCC8", "0. c #CECDC8", "q. c #CFCDC8", "w. c #CFCDC9", "e. c #CFCEC9", "r. c #CDCDCD", "t. c #D0CEC9", "y. c #D0CECA", "u. c #D0CFCA", "i. c #D1CFCA", "p. c #D2D0CB", "a. c #D2D0CC", "s. c #D2D1CC", "d. c #D3D1CC", "f. c #D0D0D0", "g. c #D3D3D3", "h. c #D4D4D4", "j. c #D7D7D7", "k. c #DBD9D4", "l. c #D8D8D8", "z. c #D9D9D9", "x. c #DDDDDD", "c. c #E0E0E0", "v. c #E9E9E9", "b. c #EAEAEA", "n. c #EFEFEF", "m. c #EFEFF0", "M. c #F0F0F0", "N. c #F1F1F1", "B. c #F4F4F5", "V. c #F5F5F5", /* pixels */ "k.s.p.i.w.i.w.w.4.4.p.p.w.4.4.k.", "s.a 8 c ! F T ! &.>.c 2 A 1.$.i.", "s.4 . s _ P B L j.n.h X N v.>.5.", "w.~ ) b y v +.X.N A e * N j.&.5.", "5.>.B.A s M.g., $ ; + N m.,.5.", "5.&.j.) a b o.} 8 2 a s B +.} w.", "5.:.z.r.v.o.+ ; , $ o.V./ . 9 s.", "5.&.r.-.g..., 8 8 2 ..z./ * y i.", "i.y , k / H A P -.8.' P P A A i.", "i.q % g ) H H P f.c.[ A P ! T w.", "p.w < q 0 k =.@.' [ ! P A L P w.", "p.5 # ; @ f M.z.P A A ! P T T w.", "w.V V c u k ) / H P A P A H H w.", "5.,.n.o.~ v o * A T H T H T P w.", "4.$.8.{ H n 0 e A ! H T H U ! p.", "k.4.4.4.w.i.p.p.i.w.i.q.p.4.p.k." }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 223 2", " c #191919", ". c #1A1A1A", "X c #212121", "o c #222222", "O c #232323", "+ c #252525", "@ c #262626", "# c #272727", "$ c #282828", "% c #292929", "& c #2A2A2A", "* c #2B2B2B", "= c #2C2C2C", "- c #2D2D2D", "; c #2E2E2E", ": c #2F2F2F", "> c #303030", ", c #313131", "< c #323232", "1 c #333333", "2 c #333334", "3 c #343434", "4 c #353535", "5 c #363636", "6 c #373737", "7 c #383838", "8 c #393939", "9 c #3A3A3A", "0 c #3B3B3B", "q c #3C3C3C", "w c #3D3D3D", "e c #3E3E3E", "r c #3F3F3E", "t c #3F3F3F", "y c #414141", "u c #424242", "i c #434342", "p c #434343", "a c #444444", "s c #454444", "d c #454545", "f c #464545", "g c #464645", "h c #464646", "j c #474746", "k c #474747", "l c #484847", "z c #484848", "x c #494949", "c c #4B4B4B", "v c #504F4F", "b c #525252", "n c #535353", "m c #545353", "M c #545454", "N c #575757", "B c #595958", "V c #5B5B5B", "C c #5C5C5C", "Z c #6C6B6B", "A c #6E6E6D", "S c #6E6E6E", "D c #6F6F6E", "F c #6F6F6F", "G c #706F6F", "H c #70706F", "J c #707070", "K c #717170", "L c #717171", "P c #727272", "I c #737272", "U c #737372", "Y c #737373", "T c #747473", "R c #747474", "E c #757575", "W c #767675", "Q c #767676", "! c #777777", "~ c #787878", "^ c #797979", "/ c #79797A", "( c #7A7A7A", ") c #7B7B7B", "_ c #7C7C7C", "` c #7D7D7D", "' c #7E7E7E", "] c #7F7F7F", "[ c #80807F", "{ c #808080", "} c #818181", "| c #828282", " . c #838383", ".. c #848484", "X. c #858585", "o. c #868686", "O. c #878787", "+. c #898989", "@. c #8A8A8A", "#. c #8B8B8A", "$. c #929292", "%. c #949493", "&. c #979797", "*. c #989897", "=. c #999999", "-. c #9A9A9A", ";. c #9B9B9B", ":. c #9C9C9C", ">. c #9D9D9D", ",. c #9F9F9F", "<. c #A0A0A0", "1. c #A1A1A0", "2. c #A2A2A2", "3. c #A3A2A2", "4. c #A3A3A2", "5. c #A3A3A3", "6. c #A4A4A3", "7. c #A5A5A4", "8. c #A5A5A5", "9. c #A6A5A5", "0. c #A6A6A5", "q. c #A6A6A6", "w. c #A7A7A6", "e. c #A7A7A7", "r. c #A8A8A7", "t. c #A9A9A9", "y. c #AAAAA9", "u. c #ABABAA", "i. c #ABABAB", "p. c #ADADAD", "a. c #AEAEAE", "s. c #B0B0B0", "d. c #B1B1B1", "f. c #B2B2B2", "g. c #B3B3B3", "h. c #B4B4B4", "j. c #B6B6B6", "k. c #B7B7B7", "l. c #B8B8B8", "z. c #B9B9B9", "x. c #BABABA", "c. c #BBBBBB", "v. c #BDBDBD", "b. c #BEBEBE", "n. c #BFBFBF", "m. c #C0C0C0", "M. c #C1C1C1", "N. c #C2C2C2", "B. c #C3C3C3", "V. c #C7C5C0", "C. c #C7C5C1", "Z. c #C7C6C1", "A. c #C4C4C4", "S. c #C5C5C5", "D. c #C6C6C6", "F. c #C7C7C7", "G. c #C8C6C1", "H. c #C8C6C2", "J. c #C8C7C2", "K. c #C9C7C2", "L. c #C9C7C3", "P. c #CAC8C3", "I. c #CAC8C4", "U. c #CDCBC6", "Y. c #CECCC7", "T. c #C8C8C8", "R. c #C9C9C9", "E. c #CACACA", "W. c #CBCBCB", "Q. c #CECCC8", "!. c #CECDC8", "~. c #CFCDC8", "^. c #CCCCCC", "/. c #CDCDCD", "(. c #CFCFCF", "). c #D0CECA", "_. c #D1CFCB", "`. c #D0CFCF", "'. c #D2D0CC", "]. c #D3D1CC", "[. c #D3D1CD", "{. c #D3D2CD", "}. c #D4D2CD", "|. c #D4D2CE", " X c #D0D0D0", ".X c #D1D1D1", "XX c #D2D2D2", "oX c #D3D3D3", "OX c #D4D3D3", "+X c #D4D4D4", "@X c #D5D5D5", "#X c #D6D6D6", "$X c #D7D7D7", "%X c #DBD9D4", "&X c #DCDAD5", "*X c #DEDCD7", "=X c #DFDCD7", "-X c #DFDDD7", ";X c #D8D8D8", ":X c #D9D9D9", ">X c #D9D9DA", ",X c #DADADA", " = 8 { ....{ / ] ] ] _ ^.4X$XiXo.+ 5 , , l.3X;XyXt.L.6X", "1X_.v u j u c L Y E Y ! { ....{ s.x.l.N.! = 7 5 3 p. XR..X>.L.6X", "6XL.<. XD..Xh., , 5 * { ;XT..Xv.6 ; , , 6 3 2 2 3 d..XR..X<.L.6X", "8XL.5.;XXX4Xn., 2 5 @ o.wXXX2XD.7 > 2 3 5 , , , , c.3X+X5Xw.L.6X", "8XL.2.$XXX4Xv.2 6 8 * ..3X(.@XA.7 , 3 2 7 5 5 5 5 j.;X X;X5.L.6X", "6XL.2.3X$XwXx.@ @ & o.aX3XwX(.8 , 5 5 , # * * # M.yX3XpXi.L.6X", "8XI.=.T.n.D.s.{ o...o.Y Y Y E L 8 5 7 , N #.{ o.{ L R R R Z Q.6X", "8XL.6.3X.X;XD.;XwX3XaXY * + & 5 , 5 + ~ aX3XrX;Xq O & O t |.=X", "6XL.w. q _ ] ] ] / { { { ] D.$X X;X,./ { { _ / [ [ { L Y.6X", "&X{.j , , , t o.o.o.o.~ / ] _ / E.8X;XrXw./ ..{ ] / { { ..T Q.6X", "1X{.z , 8 5 q B B C N ~ >.&.=.%.:.5.,.8.o.R _ / E E / / / S ).6X", "=X{.j > , 5 5 - = ; O o.iX$X4XE.! / _ ! E { { { ] / { { { Y Q.6X", "&X{.j - 3 , 5 5 , 5 * { 4X(.$XD.{ { { ../ { { { _ / { { { L Q.6X", "=X{.j > 2 , 5 5 , 5 * o.wXXX2XD.{ { ..{ / { { { _ _ { { o.Y Q.6X", "=X{.j - 3 , 5 3 2 6 * { 2XE..XN./ ] ] _ E ] / ] ^ ^ [ _ ] H Y.6X", "6XL.%.x.f.c.q.F Y Y E N t j u j T / / ] T / / _ ^ T _ _ _ G Q.6X", "8XL.w. c #2F2F30", ", c #303030", "< c #313131", "1 c #323232", "2 c #333333", "3 c #343434", "4 c #353535", "5 c #363636", "6 c #373737", "7 c #383838", "8 c #393939", "9 c #3A3A3A", "0 c #3B3B3B", "q c #3C3C3C", "w c #3D3D3D", "e c #3E3E3E", "r c #3E3E3F", "t c #3F3F3F", "y c #404040", "u c #414141", "i c #424242", "p c #434343", "a c #444444", "s c #454545", "d c #474747", "f c #484848", "g c #4A4A4A", "h c #4B4B4B", "j c #4D4D4D", "k c #4F4F4F", "l c #505050", "z c #515050", "x c #515151", "c c #525251", "v c #535352", "b c #545353", "n c #545453", "m c #555454", "M c #575656", "N c #5B5B5B", "B c #5D5C5C", "V c #606060", "C c #616161", "Z c #646464", "A c #656564", "S c #656565", "D c #666666", "F c #676767", "G c #686868", "H c #696969", "J c #6A6A69", "K c #6A6A6A", "L c #6B6B6A", "P c #6C6C6B", "I c #6C6C6C", "U c #6D6C6C", "Y c #6D6D6C", "T c #6D6D6D", "R c #6E6E6D", "E c #6F6F6E", "W c #6F6F6F", "Q c #707070", "! c #717171", "~ c #727272", "^ c #737372", "/ c #737373", "( c #747474", ") c #757575", "_ c #767676", "` c #777777", "' c #787878", "] c #797979", "[ c #7A7A7A", "{ c #7B7B7B", "} c #7C7C7C", "| c #7C7C7D", " . c #7D7D7D", ".. c #7E7E7E", "X. c #7F7F7F", "o. c #7F7F80", "O. c #7F8080", "+. c #808080", "@. c #818181", "#. c #828282", "$. c #838383", "%. c #848483", "&. c #848484", "*. c #858585", "=. c #868686", "-. c #878686", ";. c #878786", ":. c #878787", ">. c #888787", ",. c #888887", "<. c #888888", "1. c #898988", "2. c #898989", "3. c #8A8A8A", "4. c #8B8B8A", "5. c #8B8B8B", "6. c #8C8C8C", "7. c #8D8D8D", "8. c #8E8E8E", "9. c #8F8F8F", "0. c #909090", "q. c #919191", "w. c #919292", "e. c #929292", "r. c #939393", "t. c #939494", "y. c #949494", "u. c #959595", "i. c #969696", "p. c #979797", "a. c #989898", "s. c #9E9E9E", "d. c #9F9F9F", "f. c #A2A2A2", "g. c #A4A4A4", "h. c #A5A5A5", "j. c #A6A6A6", "k. c #ABABAB", "l. c #AEAEAE", "z. c #B0B0B0", "x. c #B1B1B1", "c. c #B2B2B2", "v. c #B3B3B3", "b. c #B4B4B4", "n. c #B5B5B5", "m. c #B6B6B6", "M. c #B7B7B7", "N. c #BAB8B4", "B. c #BAB9B4", "V. c #BAB9B5", "C. c #BBB9B5", "Z. c #BBBAB5", "A. c #BBBAB6", "S. c #BDBBB7", "D. c #B8B8B8", "F. c #B9B9B9", "G. c #BABABA", "H. c #BBBBBB", "J. c #BCBCBC", "K. c #BDBDBD", "L. c #BFBFBF", "P. c #C1BFBB", "I. c #C2C0BC", "U. c #C3C1BD", "Y. c #C4C2BE", "T. c #C0C0C0", "R. c #C1C1C1", "E. c #C2C2C2", "W. c #C3C3C3", "Q. c #C6C4C0", "!. c #C6C5C0", "~. c #C7C5C1", "^. c #C7C6C2", "/. c #C4C4C4", "(. c #C5C5C5", "). c #C6C6C6", "_. c #C7C7C7", "`. c #C8C8C8", "'. c #C9C9C9", "]. c #CACACA", "[. c #CBCBCB", "{. c #CFCDC8", "}. c #CFCDC9", "|. c #CCCCCC", " X c #CDCDCD", ".X c #CECECE", "XX c #CFCFCF", "oX c #D0CEC9", "OX c #D1CFCA", "+X c #D3D2CD", "@X c #D4D3CE", "#X c #D5D3CE", "$X c #D5D3CF", "%X c #D0D0D0", "&X c #D1D1D1", "*X c #D2D2D2", "=X c #D3D3D3", "-X c #D6D4D0", ";X c #D7D5D1", ":X c #D4D4D4", ">X c #D5D5D5", ",X c #D6D6D6", ".{.pX6X6X", "6X5XaX~.q = > > > : 9 @.$.$.$.$.@.) @.X.X.@.X.X.&XuXrXrXqXbXI % 3 2 2 > y |.tXlXiXcXvX>.oXpX6X6X", "6X5XaX~.d a a a s p f Q ^ ~ ~ ^ ~ ~ @.X.X.X.X.] k.n.v.v.c.G.V > 0 6 6 2 p D./.E.W.T.W.@.oXpX6X6X", "6X5XgXN.0.,X'.].`.=Xl.: 2 2 2 3 # &.,X].].].&Xl.: : > : : : 9 3 3 3 3 2 y L.].`.`._.|.$.oX6X6X6X", "6X5XhXN.i..oXpX6X6X", "6X5XgXS.t.,X&X:X&XvXn.: 3 3 2 6 @ 3.lX=X,X=XrXn.: 2 2 2 3 3 9 > 2 2 2 2 y [..oX6X6X6X", "6X5XgXN.y.5X,X0X.{.pX6X6X", "7X5XhXN.q.tX,XqXqXuXz.O @ % @ % 6.BXwXtXtXbXK.2 3 3 3 3 3 3 O % $ % O 6 :XiXuXuXrXvX3.{.pX6X6X", "6X5XgXS.&.E.n.G.M.K.h.$.3.3.3.3.6.I D G G G G V 9 6 9 3 9 3 F q.>.6.3.6.+.Z G G G G Z Z @X6X6X6X", "6X5XgXN.i.lX,X,X,XwXE.=XnXuXlXaXBXD . % % O O : 3 : 2 : 3 O 8.BXuXiXiXMXT.$ $ % $ & O l .lX&X:X&XrXN.< 3 3 3 3 = b 6X6X6X6X", "eX5XhXN.a.bXwXwXwXuX).XXtXqXqX9XvXG O 3 2 2 2 3 2 : 2 : 3 O 3.mX9XrX0XzXH., < < , 2 = b 2XpX6X6X", "5X5XhXS.$.K.v.M.n.G.h.c.K.G.H.G.E.Z 3 r q q 0 r p p p p a 9 X./.G.H.G.T.g.9 q 9 q e 6 M 2X6X6X6X", "5X5XeX~.w : > 2 2 : 9 ) ] ] ] ] ) ~ X.X.{ X.X.] E..X|..X[.&X3./ [ ' ' [ ~ ' X.X.} X.} P OXpX5X7X", "7X5XfX~.r 2 2 2 2 : 9 +.@.@.@.@.@.) @.@.@.@.@.X.|.qX 4 2 2 2 w X.@.@.+.+.+.) @.+.+.+.o.X.].9X:X,X:XqX0.} @.+.+.$.' { @.+.+.+.o.U OXpX7X5X", "6X5XdX~.w 2 2 2 2 2 q X.+.@.X.+.X.) &.@.$.$.@.o.]..iX&X:X&XqXH.' +.+.+.+.X.) +.+.+.X.+.' { +.+.+.+.+.I @X6X6X6X", "6X4XdX~.w : , : : : 3 2 2 2 2 3 @ 8.MX9XwXqXiXT.{ &.+.$.$.@._ $.+.$.+.&.] } &.@.$.@.+.Y @XpX6X6X", "6X5XdXY.f t p t p t p q 9 9 q q 2 X.T.n.D.n.H.g.~ ] ' ' ' ' W ' ' ' ' { ~ ) ' ' _ [ _ J #X6X5X5X", "6X5XhXZ.8.=X(.`.(..Xc.) X.{ X.{ +.j = 2 2 2 , 9 ' } { } { { ! { { } ] } ) ) } } { { [ P #XpX6X6X", "6X5XjXN.i..>.;.,.O.G Y Y Y I I B b b m m b M P I I Y I P G Y U U I I J P U U U U P W ;XpX6X6X", "6X6X6X6XOXoXoXoXoXoXoX@X@X@X@X@X@X;X2X2X2X2X2X2X@X@X@X#X@X@X@X@X#X@X#X#XOX;X;XOX#X#X@X=X5X6X6X6X", "6X6X6X6XpXwXpX6XpX6XpXpX6XeX6XaXpX6X6XpXpX6XpX6XpX6X6XpXpX7XpX6XpX6X5XpX5XpXpX5XpX5XpXpX5X6X6X6X", "6X6X6X6X6X6X6X6X5X6X6X6X6X6X6X6X6X6X6X6X5X5X6X6X6X6X6X6X6X6X6X6X5X5X5X5X5X5X5X5X2X5X6X6X6X6X6X6X", "6X6X6X6X6X6X6X6X6X6X6X6X6X6X6X6X6X6X6X6X6X5X6X6X6X6X6X6X6X6X6X6X5X5X5XpXpX5X5X5XpX5X6X6X6X6X6X6X" }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/icons/untangle-icon.c0000644000175300017530000003463512161170410016453 0ustar simonsimon/* XPM */ static const char *const xpm_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 113 2", " c #3433BC", ". c #706FBA", "X c #3938C6", "o c #6362C3", "O c #6665C3", "+ c #6E6DC1", "@ c #8483BB", "# c #9D9CB2", "$ c #9998BD", "% c #9F9DBF", "& c #B1B0AB", "* c #B1B0AE", "= c #B4B3AE", "- c #BAB8AE", "; c #A7A6BE", ": c #B5B3B2", "> c #BCBAB6", ", c #BDBBB9", "< c #BFBDB8", "1 c #BFBDBA", "2 c #C0BEB9", "3 c #C1BFBB", "4 c #C3C1B3", "5 c #C1C0BB", "6 c #C2C0BB", "7 c #C7C5BB", "8 c #C2C0BC", "9 c #C3C2BD", "0 c #C3C1BE", "q c #C4C2BD", "w c #C4C2BE", "e c #C4C3BE", "r c #C5C3BF", "t c #C8C6BA", "y c #CFCDB9", "u c #C9C8BC", "i c #CAC8BF", "p c #CECCBF", "a c #D1CFBE", "s c #D3D1BE", "d c #8483CA", "f c #9D9BC9", "g c #A4A2C3", "h c #A7A6CB", "j c #B1AFC8", "k c #BDBCC9", "l c #C1C0C3", "z c #C6C4C0", "x c #C7C5C0", "c c #C7C5C1", "v c #C9C7C1", "b c #C8C6C2", "n c #C9C7C2", "m c #C9C7C3", "M c #C9C7C4", "N c #CAC9C4", "B c #CBC9C4", "V c #CCCAC5", "C c #CDCBC5", "Z c #CCCAC6", "A c #CDCBC6", "S c #CECCC7", "D c #C3C1CA", "F c #CFCDC8", "G c #CECDC9", "H c #CFCDCA", "J c #CECCCC", "K c #D0CEC9", "L c #D0CECB", "P c #D4D2C3", "I c #D2D0CA", "U c #D2D0CB", "Y c #D5D3C8", "T c #D2D0CC", "R c #D3D1CC", "E c #D2D0CD", "W c #D3D1CD", "Q c #D2D0CE", "! c #D3D1CE", "~ c #D4D2CD", "^ c #D5D3CD", "/ c #D4D2CE", "( c #D5D3CE", ") c #D4D2CF", "_ c #D5D3CF", "` c #D6D4CE", "' c #D7D5CE", "] c #D6D4CF", "[ c #D7D4CF", "{ c #D9D7C9", "} c #D8D6CA", "| c #D9D7CF", " . c #DBD9CC", ".. c #DBD9CD", "X. c #DBD9CF", "o. c #DDDBCC", "O. c #DCDACF", "+. c #DFDDCC", "@. c #DFDDCF", "#. c #D5D3D0", "$. c #D7D5D0", "%. c #D6D4D1", "&. c #D7D5D2", "*. c #D8D6D0", "=. c #D8D6D1", "-. c #D9D7D2", ";. c #DBD9D3", ":. c #DFDDD0", ">. c #E0DECA", ",. c #E0DECE", "<. c #E0DED1", "1. c #E2E0CD", "2. c #E3E1CD", /* pixels */ "( ( ( ( ( .+.( Y ( ( ( ` ! ( ( ", "( ( ( ! ,.f o +.#.( ! #.Z q ` ( ", "( ( ( ( ;.j @ u c L <. ., ` ` ( ", "( ( ( ( #.Y a $.M u g $ } ( ( ( ", "( ( ( ( ` G 8 ;.#.s . + 2.! ( ( ", "( ( ! ( ` ( 8 c 1 M :.P 1 $.( ( ", "( ` O.$.! 8 & Z ` $.! $.B 8 $.( ", "` J k v q v q ;.! ` ( ( $.B G ( ", ">.h 2 G ( z *.` ! ` ( ( ` $.( ", "( | & 2 v 1 = Z ( *.` ` ( ! ( ( ", "Y *.Z : ! ( > v 5 e Z ( *.` ( ( ", "( #.G a u L 8 | ( S e 8 q Z ( ( ", "( ( O.O f y i ( ( $.*.( K c L $.", "( ( .d ; - # ( ( ( ( ( $.*.( ( ", "( ( ( ,.:.l X D | ( ( ( ( ( ( ( ", "( ( ( L ! *.K ( ( ( ( ( ( ( ( ( " }; /* XPM */ static const char *const xpm_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 213 2", " c #73729C", ". c #4544AC", "X c #4848AF", "o c #5F5EA6", "O c #6B6AA6", "+ c #7776A9", "@ c #6261B9", "# c #6665BD", "$ c #6D6CBA", "% c #7977B1", "& c #7978B6", "* c #0101D3", "= c #0505D6", "- c #0B0BD2", "; c #0808D9", ": c #2120C3", "> c #2F2EC8", ", c #0101E5", "< c #0101E9", "1 c #0000F3", "2 c #0000F6", "3 c #0000FB", "4 c #4645C1", "5 c #4241C6", "6 c #99978B", "7 c #868591", "8 c #8F8D9D", "9 c #959490", "0 c #9B9A96", "q c #9E9D99", "w c #B8B69F", "e c #9291A6", "r c #9D9BAC", "t c #8180BB", "y c #8988BA", "u c #9291BA", "i c #9593BB", "p c #A8A7A3", "a c #A9A7A3", "s c #A9A8A4", "d c #AAA9A5", "f c #A9A8A7", "g c #AAA8A6", "h c #ABA9A6", "j c #ABAAA6", "k c #ACABA7", "l c #ADABA7", "z c #AEACA4", "x c #AEACA8", "c c #AEADA8", "v c #AEADA9", "b c #AFADA9", "n c #AFADAA", "m c #AFAEAA", "M c #AFAEAE", "N c #B1AFA7", "B c #B0AEAA", "V c #B0AFAA", "C c #B0AFAB", "Z c #B1AFAB", "A c #B3B1A7", "S c #B1B0AB", "D c #B5B4A8", "F c #B6B5AB", "G c #B2B0AC", "H c #B2B1AC", "J c #B2B1AD", "K c #B3B1AD", "L c #B3B2AD", "P c #B3B1AE", "I c #B2B0AF", "U c #B3B2AE", "Y c #B4B2AE", "T c #B5B3AF", "R c #B5B4AF", "E c #A6A4B2", "W c #B6B4B0", "Q c #B6B5B0", "! c #B7B5B1", "~ c #B7B6B1", "^ c #B7B5B2", "/ c #B7B6B7", "( c #B8B6B2", ") c #B8B7B2", "_ c #B9B7B3", "` c #B9B8B3", "' c #BDBBB1", "] c #BAB8B4", "[ c #BAB9B4", "{ c #BBB9B4", "} c #BBB9B5", "| c #BBBAB5", " . c #BCBAB5", ".. c #BCBAB6", "X. c #BCBBB6", "o. c #BDBCB7", "O. c #BAB8BB", "+. c #BEBCB9", "@. c #BFBDB9", "#. c #BFBDBA", "$. c #BCBABF", "%. c #C0BEAE", "&. c #C1BFAE", "*. c #C1BFB0", "=. c #C0BEB9", "-. c #C0BEBA", ";. c #C1BFBA", ":. c #C0BEBB", ">. c #C1BFBB", ",. c #C8C6B0", "<. c #C2C0B8", "1. c #C2C1B8", "2. c #C2C0BB", "3. c #C5C3BA", "4. c #C7C5BB", "5. c #C2C0BC", "6. c #C3C1BC", "7. c #C4C2BD", "8. c #C4C2BE", "9. c #C5C3BE", "0. c #C6C4BF", "q. c #D0CEBC", "w. c #C7C5C0", "e. c #C7C5C2", "r. c #C9C7C2", "t. c #C9C7C3", "y. c #CAC8C1", "u. c #CAC8C2", "i. c #CAC8C3", "p. c #CBC9C4", "a. c #CCCAC5", "s. c #CCCAC6", "d. c #CDCBC6", "f. c #CCCAC7", "g. c #CECCC7", "h. c #CDCBC8", "j. c #CFCDC8", "k. c #D0CEC9", "l. c #D1CFC9", "z. c #D0CFCA", "x. c #D1CFCA", "c. c #D1CFCC", "v. c #D1CFCD", "b. c #D8D6C3", "n. c #D9D7C5", "m. c #DEDCC7", "M. c #D2D0CB", "N. c #D4D2C9", "B. c #D7D5CA", "V. c #D3D1CC", "C. c #D3D1CD", "Z. c #D2D0CE", "A. c #D3D1CE", "S. c #D3D1CF", "D. c #D4D2CD", "F. c #D4D2CE", "G. c #D5D3CE", "H. c #D5D3CF", "J. c #D6D4CE", "K. c #D6D4CF", "L. c #D8D6C9", "P. c #DAD8CA", "I. c #DCDACA", "U. c #DBD9CD", "Y. c #DCDACD", "T. c #DFDDCD", "R. c #D3D1D0", "E. c #D4D2D0", "W. c #D5D3D1", "Q. c #D6D4D0", "!. c #D7D5D0", "~. c #D7D5D1", "^. c #D6D4D2", "/. c #D7D5D2", "(. c #D8D6D0", "). c #D9D7D0", "_. c #D8D6D1", "`. c #D9D6D1", "'. c #D8D6D2", "]. c #D9D6D2", "[. c #D9D7D2", "{. c #D9D7D3", "}. c #DAD7D2", "|. c #DAD8D0", " X c #DAD8D1", ".X c #DAD8D2", "XX c #DBD9D2", "oX c #DAD8D3", "OX c #DBD8D3", "+X c #DBD9D3", "@X c #DDDBD1", "#X c #DCDAD2", "$X c #DFDDD1", "%X c #DFDDD3", "&X c #DAD8D4", "*X c #DBD9D4", "=X c #DAD8D7", "-X c #DCDAD4", ";X c #DCDAD5", ":X c #DEDCD6", ">X c #DEDCD7", ",X c #E0DEC7", "X_ ] .n Y 5.M.oXoXK.G.G.G.G.G.G.G.G.G.G.", "K.C.K.C.{.;.+.) +.*XM.oX~ i.>X~.p._ n G ;.x.oX_.G.G.G.K.G.G.G.G.", "M.K.K.K.K.M.n ~.p z.K.oX] 9.~.G._.oXK.d.[ B G ;.x._._.K.G.G.G.G.", "K.K.K.M.D.oXK B.g.d oXoX] 1.oXN.G.G.G.K._._.d.{ B B +.x.G.G.G.G.", "K.K.M.K.M.oX1.8 Y.( ~ 3X] 5.oXM.G.G.G.G.G.G.K.oX_.g. .B d.K.G.G.", "K.M.K.K.C.6X$ 1 @ 9Xf j.+.+.oXG.G.G.G.G.G.G.G.G.G.K.OX_.G.G.G.G.", "K.K.K.K.C.T.y ; O <.p.K 3.9.oXC.G.G.G.G.G.G.G.G.G.G.G.M.G.G.G.G.", "K.M.K.M.G.K.Y.q.Y.#.n A 7 r %XM.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.G.C.~.C.oX_.A - , ] OXM.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.G.K.R.K.C.~.B.5 > i._.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.G.K.G.G.G.G.~.n.b.K.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.", "G.G.G.G.G.G.G.G.G.G.G.C.R.~.C.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G." }; /* XPM */ static const char *const xpm_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 93 2", " c #7A7976", ". c #7F7E7B", "X c #908F7E", "o c #232397", "O c #0C0CBF", "+ c #1111BE", "@ c #1C1CB8", "# c #2625A6", "$ c #3938AC", "% c #2B2BB9", "& c #3131B1", "* c #3D3DB0", "= c #454597", "- c #414098", "; c #4E4D98", ": c #666585", "> c #6D6C8F", ", c #727185", "< c #4645A8", "1 c #4A49A9", "2 c #5E5DA8", "3 c #6564A1", "4 c #6B6AA4", "5 c #6C6BA9", "6 c #0000C4", "7 c #0E0EC2", "8 c #0000CD", "9 c #1111C4", "0 c #0000DE", "q c #0000E1", "w c #0000EC", "e c #0000F5", "r c #0000FF", "t c #83818D", "y c #8B8A8D", "u c #908F8A", "i c #92918E", "p c #969591", "a c #989693", "s c #9C9A96", "d c #96959B", "f c #9E9D99", "g c #A09F9B", "h c #A3A29D", "j c #A9A79C", "k c #A9A89D", "l c #B0AE9F", "z c #B2B09F", "x c #8383AC", "c c #8F8DAC", "v c #918FAB", "b c #9796A2", "n c #9594AC", "m c #9D9CAD", "M c #A6A4A1", "N c #A8A7A2", "B c #ABAAA5", "V c #A5A4A9", "C c #AEADA9", "Z c #B0AEA3", "A c #B1AFAC", "S c #B5B3AE", "D c #A9A7B1", "F c #B7B5B2", "G c #B9B7B3", "H c #BCBAB5", "J c #BFBDB9", "K c #C0BEBA", "L c #C3C2BD", "P c #CDCBBA", "I c #D4D2BD", "U c #C7C5C0", "Y c #C8C6C2", "T c #CCCAC5", "R c #CFCDC8", "E c #D0CECA", "W c #D4D2C4", "Q c #DCDAC3", "! c #D5D3CE", "~ c #D8D6C9", "^ c #DDDBCF", "/ c #D6D4D0", "( c #D9D7D2", ") c #DCDAD4", "_ c #DEDCDA", "` c #E2DFCC", "' c #E1DFD4", "] c #E1DFDB", "[ c #E3E1CC", "{ c #E3E1D4", "} c #E8E6D0", "| c #EBE9D6", " . c #E5E3DB", /* pixels */ "! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ! ! ! / ! ! ! ! ! ( ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! / ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! / ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! / ! ! / ! ! ! ! ! ! ! / ! ! ! ! ! ! ! ! ! ( R ( ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ! ! / ! ! ! ! ! ' k l { ! / ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) h J ( ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) m 7 @ F ( E ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) A B ) ! ! ! ! ! ! ! ", "! ! ! ! ! ( ! ! ! ! ! ! ! ! ! ! [ 1 r e 3 | / ! ! ! ! ! ! ! ! ! ! ! ! ! ( J s ) ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) n 8 9 t N U ) ( ! ! / ! ! ! ! ! ! ! ! R p ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! { s j .K g h L ) ( ! ! ! ! ! ! ! ! / p Y ! ! ! / ! ! ! ! ! / ", "! ! ! ! ! ! ! ! ! ! ^ ! ! ! ! ! ! ( H G ( ( ) L h g K ) ( ! ! ! ! E ) f H ) ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ! ! ! ! ( ! ! ! ( G A ) ! E ( ) L h g K ( ( ( ' .N B _ ! ! ! ! ! ! ! ! ! ! ! ", "! ( ! ! ! ! ! ! / ! ! ! ! ! ! ! ! ( G A ) ! ! ! ! ( ) T M s J W v d B ) ! ! ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) H A ) ! ! ! ! ! ! ( ) Y Z = e 6 V ' ! ! ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ! ! ! / ! ! ! ! ( H B ) ! ! ! ! ! ! ! ! ] I # r w v { ! ! ! ! ! ! ! ! ! ! ! ^ ", "! ! ! ! ! ! ! ( ! ! ! ! ! ! ! ! ! ) K B ) ! ! ! ! ! ! ) Y h h b % - k _ ! ! ! ! ! / ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ( K B ) ! ! E ( ) Y h g K ) ' Q [ h A ) ! ! ! ! ! ! ! ! ! ! ! ", "! / ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ( L N ) E ( ) L h g K ) ( ! E / / ) f F ) ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! / L M ] ( L g h L ) ( ! ! ! ! ! ! ! ( s J ) ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ( T B Y g h L ) ( ! ! ! ! ! ! ! ! ! ! / a L ) ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ( ) F N Y ) ( ! ! ! ! ! ! ! ! ! ! ! ! / ! p L ( ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ! ! ! ! ! ( / H g f f ] ! E ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! i T ( ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ! ! ! ) ( H f N T ! h ( ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ( R p T ! ! ! ! ! ! ", "( ! ! ! ! ! ! ' ( ! ) ( F s B R ) ( T h ( ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) L C ( ! ! ! ! ! ", "! ! ! ! ! / ~ V J ( S s B R ) ! ! ! E h ( ! ! ! ! ! ! ! ( ! ! ! ! ! ! ! ! ! ! ! ( ( ! ! ! ! ! ! ", "! ! ! ! ! ^ 5 0 9 y S E ) ! ! ! ! ( E g ( ! ! ! ! ! ( ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ( ! ! ! ! ! ", "! ! ! ! / Q & r w > ~ / ) ( ! ! ! ! E g / ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ^ ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! ^ n + o B M f C L / ) ( ! ! g ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! / ! ", "! ! ! ! ! ! ) I X h .R H h g B L ! ) h ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ) C s C _ ( ) E H M f N i ! ) ( ! E ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ) H S M J ( E / ) ) ! J . s N K ! ) ( ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! f E a / ! ! ! E ! ) N R K N f N K ! ) ) ! ! ! ! ! ! ! ! ( ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ) M Y K h ) ! ! ! ! ( h E ) ) ! K N s N H ! ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ) J B .h G ) ! ! ! ( h T / ! ! ) ) ! L C s j H E ) ) / ! ! ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ! s ! ! a R ! ! ! ( h T ( ! ! ! ! ! ( ) ! L B g M H R ) ) ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ) h L ) L s ) ! ! ) M Y ! ! ! ! ! ! ! ! ! ( ) ( L B f M G R ) ) ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ( H S .) C A ) E ) M Y ( ! ! ! ! ! ! ! ! ! ! ! ( ) ( L C f h F R ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ! W , m ~ ( s L ( ) M U ( ! ! ! ! ! ! ! ! ! ! ! ! ! ! ( ) / Y B G ) ! ! ! ! ! ", "! ! ! ! ! ! ! / ! { x q 0 x { T a ! ) N L / ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ( ) ( ! ! ! / ! ! ", "! ! / ! ! ! ! ! E [ 2 r r ; ` ] S h .M L / ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ( K $ * h f S .V J F L ) ! ! ! ! ! ! ! ! ! ! ! / ! ! ! ! ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ! ( Q Q ] E B s H j u C ' ! ! ! ! ! ! ( ! ! ! / ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ! ! / / E ! _ T Z : 8 7 V ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ( ! ! ! ! ! ! ! ! ! ! ! ! } < r r 4 } E ! ! ! ! ! ! ! ! ! ! ! ^ ! ! ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) m 7 @ G ( ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ^ P I ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ", "! ! ! ! ! / ! ! ! ! ! / ! ! ! ! ! ! ! / / ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ( ", "! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ( ! ! ! ! / ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ", "! ! ( ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ( ! ! ! ! ! ! ! ! ! ! ! " }; const char *const *const xpm_icons[] = { xpm_icon_0, xpm_icon_1, xpm_icon_2, }; const int n_xpm_icons = 3; puzzles-r9872/blackbox.c0000644000175300017530000013145312132232554014364 0ustar simonsimon/* * blackbox.c: implementation of 'Black Box'. */ #include #include #include #include #include #include #include "puzzles.h" #define PREFERRED_TILE_SIZE 32 #define FLASH_FRAME 0.2F /* Terminology, for ease of reading various macros scattered about the place. * * The 'arena' is the inner area where the balls are placed. This is * indexed from (0,0) to (w-1,h-1) but its offset in the grid is (1,1). * * The 'range' (firing range) is the bit around the edge where * the lasers are fired from. This is indexed from 0 --> (2*(w+h) - 1), * starting at the top left ((1,0) on the grid) and moving clockwise. * * The 'grid' is just the big array containing arena and range; * locations (0,0), (0,w+1), (h+1,w+1) and (h+1,0) are unused. */ enum { COL_BACKGROUND, COL_COVER, COL_LOCK, COL_TEXT, COL_FLASHTEXT, COL_HIGHLIGHT, COL_LOWLIGHT, COL_GRID, COL_BALL, COL_WRONG, COL_BUTTON, COL_CURSOR, NCOLOURS }; struct game_params { int w, h; int minballs, maxballs; }; static game_params *default_params(void) { game_params *ret = snew(game_params); ret->w = ret->h = 8; ret->minballs = ret->maxballs = 5; return ret; } static const game_params blackbox_presets[] = { { 5, 5, 3, 3 }, { 8, 8, 5, 5 }, { 8, 8, 3, 6 }, { 10, 10, 5, 5 }, { 10, 10, 4, 10 } }; static int game_fetch_preset(int i, char **name, game_params **params) { char str[80]; game_params *ret; if (i < 0 || i >= lenof(blackbox_presets)) return FALSE; ret = snew(game_params); *ret = blackbox_presets[i]; if (ret->minballs == ret->maxballs) sprintf(str, "%dx%d, %d balls", ret->w, ret->h, ret->minballs); else sprintf(str, "%dx%d, %d-%d balls", ret->w, ret->h, ret->minballs, ret->maxballs); *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) { char const *p = string; game_params *defs = default_params(); *params = *defs; free_params(defs); while (*p) { switch (*p++) { case 'w': params->w = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; break; case 'h': params->h = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; break; case 'm': params->minballs = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; break; case 'M': params->maxballs = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; break; default: ; } } } static char *encode_params(const game_params *params, int full) { char str[256]; sprintf(str, "w%dh%dm%dM%d", params->w, params->h, params->minballs, params->maxballs); return dupstr(str); } 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].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "No. of balls"; ret[2].type = C_STRING; if (params->minballs == params->maxballs) sprintf(buf, "%d", params->minballs); else sprintf(buf, "%d-%d", params->minballs, params->maxballs); ret[2].sval = dupstr(buf); ret[2].ival = 0; ret[3].name = NULL; ret[3].type = C_END; ret[3].sval = NULL; ret[3].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); /* Allow 'a-b' for a range, otherwise assume a single number. */ if (sscanf(cfg[2].sval, "%d-%d", &ret->minballs, &ret->maxballs) < 2) ret->minballs = ret->maxballs = atoi(cfg[2].sval); return ret; } static char *validate_params(const game_params *params, int full) { if (params->w < 2 || params->h < 2) return "Width and height must both be at least two"; /* next one is just for ease of coding stuff into 'char' * types, and could be worked around if required. */ if (params->w > 255 || params->h > 255) return "Widths and heights greater than 255 are not supported"; if (params->minballs > params->maxballs) return "Minimum number of balls may not be greater than maximum"; if (params->minballs >= params->w * params->h) return "Too many balls to fit in grid"; return NULL; } /* * We store: width | height | ball1x | ball1y | [ ball2x | ball2y | [...] ] * all stored as unsigned chars; validate_params has already * checked this won't overflow an 8-bit char. * Then we obfuscate it. */ static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { int nballs = params->minballs, i; char *grid, *ret; unsigned char *bmp; if (params->maxballs > params->minballs) nballs += random_upto(rs, params->maxballs - params->minballs + 1); grid = snewn(params->w*params->h, char); memset(grid, 0, params->w * params->h * sizeof(char)); bmp = snewn(nballs*2 + 2, unsigned char); memset(bmp, 0, (nballs*2 + 2) * sizeof(unsigned char)); bmp[0] = params->w; bmp[1] = params->h; for (i = 0; i < nballs; i++) { int x, y; do { x = random_upto(rs, params->w); y = random_upto(rs, params->h); } while (grid[y*params->w + x]); grid[y*params->w + x] = 1; bmp[(i+1)*2 + 0] = x; bmp[(i+1)*2 + 1] = y; } sfree(grid); obfuscate_bitmap(bmp, (nballs*2 + 2) * 8, FALSE); ret = bin2hex(bmp, nballs*2 + 2); sfree(bmp); return ret; } static char *validate_desc(const game_params *params, const char *desc) { int nballs, dlen = strlen(desc), i; unsigned char *bmp; char *ret; /* the bitmap is 2+(nballs*2) long; the hex version is double that. */ nballs = ((dlen/2)-2)/2; if (dlen < 4 || dlen % 4 || nballs < params->minballs || nballs > params->maxballs) return "Game description is wrong length"; bmp = hex2bin(desc, nballs*2 + 2); obfuscate_bitmap(bmp, (nballs*2 + 2) * 8, TRUE); ret = "Game description is corrupted"; /* check general grid size */ if (bmp[0] != params->w || bmp[1] != params->h) goto done; /* check each ball will fit on that grid */ for (i = 0; i < nballs; i++) { int x = bmp[(i+1)*2 + 0], y = bmp[(i+1)*2 + 1]; if (x < 0 || y < 0 || x >= params->w || y >= params->h) goto done; } ret = NULL; done: sfree(bmp); return ret; } #define BALL_CORRECT 0x01 #define BALL_GUESS 0x02 #define BALL_LOCK 0x04 #define LASER_FLAGMASK 0x1f800 #define LASER_OMITTED 0x0800 #define LASER_REFLECT 0x1000 #define LASER_HIT 0x2000 #define LASER_WRONG 0x4000 #define LASER_FLASHED 0x8000 #define LASER_EMPTY (~0) #define FLAG_CURSOR 0x10000 /* needs to be disjoint from both sets */ struct game_state { int w, h, minballs, maxballs, nballs, nlasers; unsigned int *grid; /* (w+2)x(h+2), to allow for laser firing range */ unsigned int *exits; /* one per laser */ int done; /* user has finished placing his own balls. */ int laserno; /* number of next laser to be fired. */ int nguesses, reveal, justwrong, nright, nwrong, nmissed; }; #define GRID(s,x,y) ((s)->grid[(y)*((s)->w+2) + (x)]) #define RANGECHECK(s,x) ((x) >= 0 && (x) <= (s)->nlasers) /* specify numbers because they must match array indexes. */ enum { DIR_UP = 0, DIR_RIGHT = 1, DIR_DOWN = 2, DIR_LEFT = 3 }; struct offset { int x, y; }; static const struct offset offsets[] = { { 0, -1 }, /* up */ { 1, 0 }, /* right */ { 0, 1 }, /* down */ { -1, 0 } /* left */ }; #ifdef DEBUGGING static const char *dirstrs[] = { "UP", "RIGHT", "DOWN", "LEFT" }; #endif static int range2grid(const game_state *state, int rangeno, int *x, int *y, int *direction) { if (rangeno < 0) return 0; if (rangeno < state->w) { /* top row; from (1,0) to (w,0) */ *x = rangeno + 1; *y = 0; *direction = DIR_DOWN; return 1; } rangeno -= state->w; if (rangeno < state->h) { /* RHS; from (w+1, 1) to (w+1, h) */ *x = state->w+1; *y = rangeno + 1; *direction = DIR_LEFT; return 1; } rangeno -= state->h; if (rangeno < state->w) { /* bottom row; from (1, h+1) to (w, h+1); counts backwards */ *x = (state->w - rangeno); *y = state->h+1; *direction = DIR_UP; return 1; } rangeno -= state->w; if (rangeno < state->h) { /* LHS; from (0, 1) to (0, h); counts backwards */ *x = 0; *y = (state->h - rangeno); *direction = DIR_RIGHT; return 1; } return 0; } static int grid2range(const game_state *state, int x, int y, int *rangeno) { int ret, x1 = state->w+1, y1 = state->h+1; if (x > 0 && x < x1 && y > 0 && y < y1) return 0; /* in arena */ if (x < 0 || x > x1 || y < 0 || y > y1) return 0; /* outside grid */ if ((x == 0 || x == x1) && (y == 0 || y == y1)) return 0; /* one of 4 corners */ if (y == 0) { /* top line */ ret = x - 1; } else if (x == x1) { /* RHS */ ret = y - 1 + state->w; } else if (y == y1) { /* Bottom [and counts backwards] */ ret = (state->w - x) + state->w + state->h; } else { /* LHS [and counts backwards ] */ ret = (state->h-y) + state->w + state->w + state->h; } *rangeno = ret; debug(("grid2range: (%d,%d) rangeno = %d\n", x, y, ret)); return 1; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { game_state *state = snew(game_state); int dlen = strlen(desc), i; unsigned char *bmp; state->minballs = params->minballs; state->maxballs = params->maxballs; state->nballs = ((dlen/2)-2)/2; bmp = hex2bin(desc, state->nballs*2 + 2); obfuscate_bitmap(bmp, (state->nballs*2 + 2) * 8, TRUE); state->w = bmp[0]; state->h = bmp[1]; state->nlasers = 2 * (state->w + state->h); state->grid = snewn((state->w+2)*(state->h+2), unsigned int); memset(state->grid, 0, (state->w+2)*(state->h+2) * sizeof(unsigned int)); state->exits = snewn(state->nlasers, unsigned int); memset(state->exits, LASER_EMPTY, state->nlasers * sizeof(unsigned int)); for (i = 0; i < state->nballs; i++) { GRID(state, bmp[(i+1)*2 + 0]+1, bmp[(i+1)*2 + 1]+1) = BALL_CORRECT; } sfree(bmp); state->done = state->nguesses = state->reveal = state->justwrong = state->nright = state->nwrong = state->nmissed = 0; state->laserno = 1; return state; } #define XFER(x) ret->x = state->x static game_state *dup_game(const game_state *state) { game_state *ret = snew(game_state); XFER(w); XFER(h); XFER(minballs); XFER(maxballs); XFER(nballs); XFER(nlasers); ret->grid = snewn((ret->w+2)*(ret->h+2), unsigned int); memcpy(ret->grid, state->grid, (ret->w+2)*(ret->h+2) * sizeof(unsigned int)); ret->exits = snewn(ret->nlasers, unsigned int); memcpy(ret->exits, state->exits, ret->nlasers * sizeof(unsigned int)); XFER(done); XFER(laserno); XFER(nguesses); XFER(reveal); XFER(justwrong); XFER(nright); XFER(nwrong); XFER(nmissed); return ret; } #undef XFER static void free_game(game_state *state) { sfree(state->exits); sfree(state->grid); sfree(state); } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { return dupstr("S"); } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { return NULL; } struct game_ui { int flash_laserno; int errors, newmove; int cur_x, cur_y, cur_visible; int flash_laser; /* 0 = never, 1 = always, 2 = if anim. */ }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->flash_laserno = LASER_EMPTY; ui->errors = 0; ui->newmove = FALSE; ui->cur_x = ui->cur_y = 1; ui->cur_visible = 0; ui->flash_laser = 0; return ui; } static void free_ui(game_ui *ui) { sfree(ui); } static char *encode_ui(const game_ui *ui) { char buf[80]; /* * The error counter needs preserving across a serialisation. */ sprintf(buf, "E%d", ui->errors); return dupstr(buf); } static void decode_ui(game_ui *ui, const char *encoding) { sscanf(encoding, "E%d", &ui->errors); } static void game_changed_state(game_ui *ui, const game_state *oldstate, const game_state *newstate) { /* * If we've encountered a `justwrong' state as a result of * actually making a move, increment the ui error counter. */ if (newstate->justwrong && ui->newmove) ui->errors++; ui->newmove = FALSE; } #define OFFSET(gx,gy,o) do { \ int off = (4 + (o) % 4) % 4; \ (gx) += offsets[off].x; \ (gy) += offsets[off].y; \ } while(0) enum { LOOK_LEFT, LOOK_FORWARD, LOOK_RIGHT }; /* Given a position and a direction, check whether we can see a ball in front * of us, or to our front-left or front-right. */ static int isball(game_state *state, int gx, int gy, int direction, int lookwhere) { debug(("isball, (%d, %d), dir %s, lookwhere %s\n", gx, gy, dirstrs[direction], lookwhere == LOOK_LEFT ? "LEFT" : lookwhere == LOOK_FORWARD ? "FORWARD" : "RIGHT")); OFFSET(gx,gy,direction); if (lookwhere == LOOK_LEFT) OFFSET(gx,gy,direction-1); else if (lookwhere == LOOK_RIGHT) OFFSET(gx,gy,direction+1); else if (lookwhere != LOOK_FORWARD) assert(!"unknown lookwhere"); debug(("isball, new (%d, %d)\n", gx, gy)); /* if we're off the grid (into the firing range) there's never a ball. */ if (gx < 1 || gy < 1 || gx > state->w || gy > state->h) return 0; if (GRID(state, gx,gy) & BALL_CORRECT) return 1; return 0; } static int fire_laser_internal(game_state *state, int x, int y, int direction) { int unused, lno, tmp; tmp = grid2range(state, x, y, &lno); assert(tmp); /* deal with strange initial reflection rules (that stop * you turning down the laser range) */ /* I've just chosen to prioritise instant-hit over instant-reflection; * I can't find anywhere that gives me a definite algorithm for this. */ if (isball(state, x, y, direction, LOOK_FORWARD)) { debug(("Instant hit at (%d, %d)\n", x, y)); return LASER_HIT; /* hit */ } if (isball(state, x, y, direction, LOOK_LEFT) || isball(state, x, y, direction, LOOK_RIGHT)) { debug(("Instant reflection at (%d, %d)\n", x, y)); return LASER_REFLECT; /* reflection */ } /* move us onto the grid. */ OFFSET(x, y, direction); while (1) { debug(("fire_laser: looping at (%d, %d) pointing %s\n", x, y, dirstrs[direction])); if (grid2range(state, x, y, &unused)) { int exitno; tmp = grid2range(state, x, y, &exitno); assert(tmp); return (lno == exitno ? LASER_REFLECT : exitno); } /* paranoia. This obviously should never happen */ assert(!(GRID(state, x, y) & BALL_CORRECT)); if (isball(state, x, y, direction, LOOK_FORWARD)) { /* we're facing a ball; send back a reflection. */ debug(("Ball ahead of (%d, %d)", x, y)); return LASER_HIT; /* hit */ } if (isball(state, x, y, direction, LOOK_LEFT)) { /* ball to our left; rotate clockwise and look again. */ debug(("Ball to left; turning clockwise.\n")); direction += 1; direction %= 4; continue; } if (isball(state, x, y, direction, LOOK_RIGHT)) { /* ball to our right; rotate anti-clockwise and look again. */ debug(("Ball to rightl turning anti-clockwise.\n")); direction += 3; direction %= 4; continue; } /* ... otherwise, we have no balls ahead of us so just move one step. */ debug(("No balls; moving forwards.\n")); OFFSET(x, y, direction); } } static int laser_exit(game_state *state, int entryno) { int tmp, x, y, direction; tmp = range2grid(state, entryno, &x, &y, &direction); assert(tmp); return fire_laser_internal(state, x, y, direction); } static void fire_laser(game_state *state, int entryno) { int tmp, exitno, x, y, direction; tmp = range2grid(state, entryno, &x, &y, &direction); assert(tmp); exitno = fire_laser_internal(state, x, y, direction); if (exitno == LASER_HIT || exitno == LASER_REFLECT) { GRID(state, x, y) = state->exits[entryno] = exitno; } else { int newno = state->laserno++; int xend, yend, unused; tmp = range2grid(state, exitno, &xend, ¥d, &unused); assert(tmp); GRID(state, x, y) = GRID(state, xend, yend) = newno; state->exits[entryno] = exitno; state->exits[exitno] = entryno; } } /* Checks that the guessed balls in the state match up with the real balls * for all possible lasers (i.e. not just the ones that the player might * have already guessed). This is required because any layout with >4 balls * might have multiple valid solutions. Returns non-zero for a 'correct' * (i.e. consistent) layout. */ static int check_guesses(game_state *state, int cagey) { game_state *solution, *guesses; int i, x, y, n, unused, tmp; int ret = 0; if (cagey) { /* * First, check that each laser the player has already * fired is consistent with the layout. If not, show them * one error they've made and reveal no further * information. * * Failing that, check to see whether the player would have * been able to fire any laser which distinguished the real * solution from their guess. If so, show them one such * laser and reveal no further information. */ guesses = dup_game(state); /* clear out BALL_CORRECT on guess, make BALL_GUESS BALL_CORRECT. */ for (x = 1; x <= state->w; x++) { for (y = 1; y <= state->h; y++) { GRID(guesses, x, y) &= ~BALL_CORRECT; if (GRID(guesses, x, y) & BALL_GUESS) GRID(guesses, x, y) |= BALL_CORRECT; } } n = 0; for (i = 0; i < guesses->nlasers; i++) { if (guesses->exits[i] != LASER_EMPTY && guesses->exits[i] != laser_exit(guesses, i)) n++; } if (n) { /* * At least one of the player's existing lasers * contradicts their ball placement. Pick a random one, * highlight it, and return. * * A temporary random state is created from the current * grid, so that repeating the same marking will give * the same answer instead of a different one. */ random_state *rs = random_new((char *)guesses->grid, (state->w+2)*(state->h+2) * sizeof(unsigned int)); n = random_upto(rs, n); random_free(rs); for (i = 0; i < guesses->nlasers; i++) { if (guesses->exits[i] != LASER_EMPTY && guesses->exits[i] != laser_exit(guesses, i) && n-- == 0) { state->exits[i] |= LASER_WRONG; tmp = laser_exit(state, i); if (RANGECHECK(state, tmp)) state->exits[tmp] |= LASER_WRONG; state->justwrong = TRUE; free_game(guesses); return 0; } } } n = 0; for (i = 0; i < guesses->nlasers; i++) { if (guesses->exits[i] == LASER_EMPTY && laser_exit(state, i) != laser_exit(guesses, i)) n++; } if (n) { /* * At least one of the player's unfired lasers would * demonstrate their ball placement to be wrong. Pick a * random one, highlight it, and return. * * A temporary random state is created from the current * grid, so that repeating the same marking will give * the same answer instead of a different one. */ random_state *rs = random_new((char *)guesses->grid, (state->w+2)*(state->h+2) * sizeof(unsigned int)); n = random_upto(rs, n); random_free(rs); for (i = 0; i < guesses->nlasers; i++) { if (guesses->exits[i] == LASER_EMPTY && laser_exit(state, i) != laser_exit(guesses, i) && n-- == 0) { fire_laser(state, i); state->exits[i] |= LASER_OMITTED; tmp = laser_exit(state, i); if (RANGECHECK(state, tmp)) state->exits[tmp] |= LASER_OMITTED; state->justwrong = TRUE; free_game(guesses); return 0; } } } free_game(guesses); } /* duplicate the state (to solution) */ solution = dup_game(state); /* clear out the lasers of solution */ for (i = 0; i < solution->nlasers; i++) { tmp = range2grid(solution, i, &x, &y, &unused); assert(tmp); GRID(solution, x, y) = 0; solution->exits[i] = LASER_EMPTY; } /* duplicate solution to guess. */ guesses = dup_game(solution); /* clear out BALL_CORRECT on guess, make BALL_GUESS BALL_CORRECT. */ for (x = 1; x <= state->w; x++) { for (y = 1; y <= state->h; y++) { GRID(guesses, x, y) &= ~BALL_CORRECT; if (GRID(guesses, x, y) & BALL_GUESS) GRID(guesses, x, y) |= BALL_CORRECT; } } /* for each laser (on both game_states), fire it if it hasn't been fired. * If one has been fired (or received a hit) and another hasn't, we know * the ball layouts didn't match and can short-circuit return. */ for (i = 0; i < solution->nlasers; i++) { if (solution->exits[i] == LASER_EMPTY) fire_laser(solution, i); if (guesses->exits[i] == LASER_EMPTY) fire_laser(guesses, i); } /* check each game_state's laser against the other; if any differ, return 0 */ ret = 1; for (i = 0; i < solution->nlasers; i++) { tmp = range2grid(solution, i, &x, &y, &unused); assert(tmp); if (solution->exits[i] != guesses->exits[i]) { /* If the original state didn't have this shot fired, * and it would be wrong between the guess and the solution, * add it. */ if (state->exits[i] == LASER_EMPTY) { state->exits[i] = solution->exits[i]; if (state->exits[i] == LASER_REFLECT || state->exits[i] == LASER_HIT) GRID(state, x, y) = state->exits[i]; else { /* add a new shot, incrementing state's laser count. */ int ex, ey, newno = state->laserno++; tmp = range2grid(state, state->exits[i], &ex, &ey, &unused); assert(tmp); GRID(state, x, y) = newno; GRID(state, ex, ey) = newno; } state->exits[i] |= LASER_OMITTED; } else { state->exits[i] |= LASER_WRONG; } ret = 0; } } if (ret == 0 || state->nguesses < state->minballs || state->nguesses > state->maxballs) goto done; /* fix up original state so the 'correct' balls end up matching the guesses, * as we've just proved that they were equivalent. */ for (x = 1; x <= state->w; x++) { for (y = 1; y <= state->h; y++) { if (GRID(state, x, y) & BALL_GUESS) GRID(state, x, y) |= BALL_CORRECT; else GRID(state, x, y) &= ~BALL_CORRECT; } } done: /* fill in nright and nwrong. */ state->nright = state->nwrong = state->nmissed = 0; for (x = 1; x <= state->w; x++) { for (y = 1; y <= state->h; y++) { int bs = GRID(state, x, y) & (BALL_GUESS | BALL_CORRECT); if (bs == (BALL_GUESS | BALL_CORRECT)) state->nright++; else if (bs == BALL_GUESS) state->nwrong++; else if (bs == BALL_CORRECT) state->nmissed++; } } free_game(solution); free_game(guesses); state->reveal = 1; return ret; } #define TILE_SIZE (ds->tilesize) #define TODRAW(x) ((TILE_SIZE * (x)) + (TILE_SIZE / 2)) #define FROMDRAW(x) (((x) - (TILE_SIZE / 2)) / TILE_SIZE) #define CAN_REVEAL(state) ((state)->nguesses >= (state)->minballs && \ (state)->nguesses <= (state)->maxballs && \ !(state)->reveal && !(state)->justwrong) struct game_drawstate { int tilesize, crad, rrad, w, h; /* w and h to make macros work... */ unsigned int *grid; /* as the game_state grid */ int started, reveal; int flash_laserno, isflash; }; static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { int gx = -1, gy = -1, rangeno = -1, wouldflash = 0; enum { NONE, TOGGLE_BALL, TOGGLE_LOCK, FIRE, REVEAL, TOGGLE_COLUMN_LOCK, TOGGLE_ROW_LOCK} action = NONE; char buf[80], *nullret = NULL; if (IS_CURSOR_MOVE(button)) { int cx = ui->cur_x, cy = ui->cur_y; move_cursor(button, &cx, &cy, state->w+2, state->h+2, 0); if ((cx == 0 && cy == 0 && !CAN_REVEAL(state)) || (cx == 0 && cy == state->h+1) || (cx == state->w+1 && cy == 0) || (cx == state->w+1 && cy == state->h+1)) return NULL; /* disallow moving cursor to corners. */ ui->cur_x = cx; ui->cur_y = cy; ui->cur_visible = 1; return ""; } if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { gx = FROMDRAW(x); gy = FROMDRAW(y); ui->cur_visible = 0; wouldflash = 1; } else if (button == LEFT_RELEASE) { ui->flash_laser = 0; return ""; } else if (IS_CURSOR_SELECT(button)) { if (ui->cur_visible) { gx = ui->cur_x; gy = ui->cur_y; ui->flash_laser = 0; wouldflash = 2; } else { ui->cur_visible = 1; return ""; } /* Fix up 'button' for the below logic. */ if (button == CURSOR_SELECT2) button = RIGHT_BUTTON; else button = LEFT_BUTTON; } if (gx != -1 && gy != -1) { if (gx == 0 && gy == 0 && button == LEFT_BUTTON) action = REVEAL; if (gx >= 1 && gx <= state->w && gy >= 1 && gy <= state->h) { if (button == LEFT_BUTTON) { if (!(GRID(state, gx,gy) & BALL_LOCK)) action = TOGGLE_BALL; } else action = TOGGLE_LOCK; } if (grid2range(state, gx, gy, &rangeno)) { if (button == LEFT_BUTTON) action = FIRE; else if (gy == 0 || gy > state->h) action = TOGGLE_COLUMN_LOCK; /* and use gx */ else action = TOGGLE_ROW_LOCK; /* and use gy */ } } switch (action) { case TOGGLE_BALL: sprintf(buf, "T%d,%d", gx, gy); break; case TOGGLE_LOCK: sprintf(buf, "LB%d,%d", gx, gy); break; case TOGGLE_COLUMN_LOCK: sprintf(buf, "LC%d", gx); break; case TOGGLE_ROW_LOCK: sprintf(buf, "LR%d", gy); break; case FIRE: if (state->reveal && state->exits[rangeno] == LASER_EMPTY) return nullret; ui->flash_laserno = rangeno; ui->flash_laser = wouldflash; nullret = ""; if (state->exits[rangeno] != LASER_EMPTY) return ""; sprintf(buf, "F%d", rangeno); break; case REVEAL: if (!CAN_REVEAL(state)) return nullret; if (ui->cur_visible == 1) ui->cur_x = ui->cur_y = 1; sprintf(buf, "R"); break; default: return nullret; } if (state->reveal) return nullret; ui->newmove = TRUE; return dupstr(buf); } static game_state *execute_move(const game_state *from, const char *move) { game_state *ret = dup_game(from); int gx = -1, gy = -1, rangeno = -1; if (ret->justwrong) { int i; ret->justwrong = FALSE; for (i = 0; i < ret->nlasers; i++) if (ret->exits[i] != LASER_EMPTY) ret->exits[i] &= ~(LASER_OMITTED | LASER_WRONG); } if (!strcmp(move, "S")) { check_guesses(ret, FALSE); return ret; } if (from->reveal) goto badmove; if (!*move) goto badmove; switch (move[0]) { case 'T': sscanf(move+1, "%d,%d", &gx, &gy); if (gx < 1 || gy < 1 || gx > ret->w || gy > ret->h) goto badmove; if (GRID(ret, gx, gy) & BALL_GUESS) { ret->nguesses--; GRID(ret, gx, gy) &= ~BALL_GUESS; } else { ret->nguesses++; GRID(ret, gx, gy) |= BALL_GUESS; } break; case 'F': sscanf(move+1, "%d", &rangeno); if (ret->exits[rangeno] != LASER_EMPTY) goto badmove; if (!RANGECHECK(ret, rangeno)) goto badmove; fire_laser(ret, rangeno); break; case 'R': if (ret->nguesses < ret->minballs || ret->nguesses > ret->maxballs) goto badmove; check_guesses(ret, TRUE); break; case 'L': { int lcount = 0; if (strlen(move) < 2) goto badmove; switch (move[1]) { case 'B': sscanf(move+2, "%d,%d", &gx, &gy); if (gx < 1 || gy < 1 || gx > ret->w || gy > ret->h) goto badmove; GRID(ret, gx, gy) ^= BALL_LOCK; break; #define COUNTLOCK do { if (GRID(ret, gx, gy) & BALL_LOCK) lcount++; } while (0) #define SETLOCKIF(c) do { \ if (lcount > (c)) GRID(ret, gx, gy) &= ~BALL_LOCK; \ else GRID(ret, gx, gy) |= BALL_LOCK; \ } while(0) case 'C': sscanf(move+2, "%d", &gx); if (gx < 1 || gx > ret->w) goto badmove; for (gy = 1; gy <= ret->h; gy++) { COUNTLOCK; } for (gy = 1; gy <= ret->h; gy++) { SETLOCKIF(ret->h/2); } break; case 'R': sscanf(move+2, "%d", &gy); if (gy < 1 || gy > ret->h) goto badmove; for (gx = 1; gx <= ret->w; gx++) { COUNTLOCK; } for (gx = 1; gx <= ret->w; gx++) { SETLOCKIF(ret->w/2); } break; #undef COUNTLOCK #undef SETLOCKIF default: goto badmove; } } break; default: goto badmove; } return ret; badmove: free_game(ret); return NULL; } /* ---------------------------------------------------------------------- * Drawing routines. */ static void game_compute_size(const game_params *params, int tilesize, int *x, int *y) { /* Border is ts/2, to make things easier. * Thus we have (width) + 2 (firing range*2) + 1 (border*2) tiles * across, and similarly height + 2 + 1 tiles down. */ *x = (params->w + 3) * tilesize; *y = (params->h + 3) * tilesize; } static void game_set_size(drawing *dr, game_drawstate *ds, const game_params *params, int tilesize) { ds->tilesize = tilesize; ds->crad = (tilesize-1)/2; ds->rrad = (3*tilesize)/8; } 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_BALL * 3 + 0] = 0.0F; ret[COL_BALL * 3 + 1] = 0.0F; ret[COL_BALL * 3 + 2] = 0.0F; ret[COL_WRONG * 3 + 0] = 1.0F; ret[COL_WRONG * 3 + 1] = 0.0F; ret[COL_WRONG * 3 + 2] = 0.0F; ret[COL_BUTTON * 3 + 0] = 0.0F; ret[COL_BUTTON * 3 + 1] = 1.0F; ret[COL_BUTTON * 3 + 2] = 0.0F; ret[COL_CURSOR * 3 + 0] = 1.0F; ret[COL_CURSOR * 3 + 1] = 0.0F; ret[COL_CURSOR * 3 + 2] = 0.0F; for (i = 0; i < 3; i++) { ret[COL_GRID * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.9F; ret[COL_LOCK * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.7F; ret[COL_COVER * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.5F; ret[COL_TEXT * 3 + i] = 0.0F; } ret[COL_FLASHTEXT * 3 + 0] = 0.0F; ret[COL_FLASHTEXT * 3 + 1] = 1.0F; ret[COL_FLASHTEXT * 3 + 2] = 0.0F; *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->w = state->w; ds->h = state->h; ds->grid = snewn((state->w+2)*(state->h+2), unsigned int); memset(ds->grid, 0, (state->w+2)*(state->h+2)*sizeof(unsigned int)); ds->started = ds->reveal = 0; ds->flash_laserno = LASER_EMPTY; ds->isflash = 0; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->grid); sfree(ds); } static void draw_square_cursor(drawing *dr, game_drawstate *ds, int dx, int dy) { int coff = TILE_SIZE/8; draw_rect_outline(dr, dx + coff, dy + coff, TILE_SIZE - coff*2, TILE_SIZE - coff*2, COL_CURSOR); } static void draw_arena_tile(drawing *dr, const game_state *gs, game_drawstate *ds, const game_ui *ui, int ax, int ay, int force, int isflash) { int gx = ax+1, gy = ay+1; int gs_tile = GRID(gs, gx, gy), ds_tile = GRID(ds, gx, gy); int dx = TODRAW(gx), dy = TODRAW(gy); if (ui->cur_visible && ui->cur_x == gx && ui->cur_y == gy) gs_tile |= FLAG_CURSOR; if (gs_tile != ds_tile || gs->reveal != ds->reveal || force) { int bcol, ocol, bg; bg = (gs->reveal ? COL_BACKGROUND : (gs_tile & BALL_LOCK) ? COL_LOCK : COL_COVER); draw_rect(dr, dx, dy, TILE_SIZE, TILE_SIZE, bg); draw_rect_outline(dr, dx, dy, TILE_SIZE, TILE_SIZE, COL_GRID); if (gs->reveal) { /* Guessed balls are always black; if they're incorrect they'll * have a red cross added later. * Missing balls are red. */ if (gs_tile & BALL_GUESS) { bcol = isflash ? bg : COL_BALL; } else if (gs_tile & BALL_CORRECT) { bcol = isflash ? bg : COL_WRONG; } else { bcol = bg; } } else { /* guesses are black/black, all else background. */ if (gs_tile & BALL_GUESS) { bcol = COL_BALL; } else { bcol = bg; } } ocol = (gs_tile & FLAG_CURSOR && bcol != bg) ? COL_CURSOR : bcol; draw_circle(dr, dx + TILE_SIZE/2, dy + TILE_SIZE/2, ds->crad-1, ocol, ocol); draw_circle(dr, dx + TILE_SIZE/2, dy + TILE_SIZE/2, ds->crad-3, bcol, bcol); if (gs_tile & FLAG_CURSOR && bcol == bg) draw_square_cursor(dr, ds, dx, dy); if (gs->reveal && (gs_tile & BALL_GUESS) && !(gs_tile & BALL_CORRECT)) { int x1 = dx + 3, y1 = dy + 3; int x2 = dx + TILE_SIZE - 3, y2 = dy + TILE_SIZE-3; int coords[8]; /* Incorrect guess; draw a red cross over the ball. */ coords[0] = x1-1; coords[1] = y1+1; coords[2] = x1+1; coords[3] = y1-1; coords[4] = x2+1; coords[5] = y2-1; coords[6] = x2-1; coords[7] = y2+1; draw_polygon(dr, coords, 4, COL_WRONG, COL_WRONG); coords[0] = x2+1; coords[1] = y1+1; coords[2] = x2-1; coords[3] = y1-1; coords[4] = x1-1; coords[5] = y2-1; coords[6] = x1+1; coords[7] = y2+1; draw_polygon(dr, coords, 4, COL_WRONG, COL_WRONG); } draw_update(dr, dx, dy, TILE_SIZE, TILE_SIZE); } GRID(ds,gx,gy) = gs_tile; } static void draw_laser_tile(drawing *dr, const game_state *gs, game_drawstate *ds, const game_ui *ui, int lno, int force) { int gx, gy, dx, dy, unused; int wrong, omitted, reflect, hit, laserval, flash = 0, tmp; unsigned int gs_tile, ds_tile, exitno; tmp = range2grid(gs, lno, &gx, &gy, &unused); assert(tmp); gs_tile = GRID(gs, gx, gy); ds_tile = GRID(ds, gx, gy); dx = TODRAW(gx); dy = TODRAW(gy); wrong = gs->exits[lno] & LASER_WRONG; omitted = gs->exits[lno] & LASER_OMITTED; exitno = gs->exits[lno] & ~LASER_FLAGMASK; reflect = gs_tile & LASER_REFLECT; hit = gs_tile & LASER_HIT; laserval = gs_tile & ~LASER_FLAGMASK; if (lno == ds->flash_laserno) gs_tile |= LASER_FLASHED; else if (!(gs->exits[lno] & (LASER_HIT | LASER_REFLECT))) { if (exitno == ds->flash_laserno) gs_tile |= LASER_FLASHED; } if (gs_tile & LASER_FLASHED) flash = 1; gs_tile |= wrong | omitted; if (ui->cur_visible && ui->cur_x == gx && ui->cur_y == gy) gs_tile |= FLAG_CURSOR; if (gs_tile != ds_tile || force) { draw_rect(dr, dx, dy, TILE_SIZE, TILE_SIZE, COL_BACKGROUND); draw_rect_outline(dr, dx, dy, TILE_SIZE, TILE_SIZE, COL_GRID); if (gs_tile &~ (LASER_WRONG | LASER_OMITTED | FLAG_CURSOR)) { char str[32]; int tcol = flash ? COL_FLASHTEXT : omitted ? COL_WRONG : COL_TEXT; if (reflect || hit) sprintf(str, "%s", reflect ? "R" : "H"); else sprintf(str, "%d", laserval); if (wrong) { draw_circle(dr, dx + TILE_SIZE/2, dy + TILE_SIZE/2, ds->rrad, COL_WRONG, COL_WRONG); draw_circle(dr, dx + TILE_SIZE/2, dy + TILE_SIZE/2, ds->rrad - TILE_SIZE/16, COL_BACKGROUND, COL_WRONG); } draw_text(dr, dx + TILE_SIZE/2, dy + TILE_SIZE/2, FONT_VARIABLE, TILE_SIZE/2, ALIGN_VCENTRE | ALIGN_HCENTRE, tcol, str); } if (gs_tile & FLAG_CURSOR) draw_square_cursor(dr, ds, dx, dy); draw_update(dr, dx, dy, TILE_SIZE, TILE_SIZE); } GRID(ds, gx, gy) = gs_tile; } #define CUR_ANIM 0.2F 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 i, x, y, ts = TILE_SIZE, isflash = 0, force = 0; if (flashtime > 0) { int frame = (int)(flashtime / FLASH_FRAME); isflash = (frame % 2) == 0; debug(("game_redraw: flashtime = %f", flashtime)); } if (!ds->started) { int x0 = TODRAW(0)-1, y0 = TODRAW(0)-1; int x1 = TODRAW(state->w+2), y1 = TODRAW(state->h+2); draw_rect(dr, 0, 0, TILE_SIZE * (state->w+3), TILE_SIZE * (state->h+3), COL_BACKGROUND); /* clockwise around the outline starting at pt behind (1,1). */ draw_line(dr, x0+ts, y0+ts, x0+ts, y0, COL_HIGHLIGHT); draw_line(dr, x0+ts, y0, x1-ts, y0, COL_HIGHLIGHT); draw_line(dr, x1-ts, y0, x1-ts, y0+ts, COL_LOWLIGHT); draw_line(dr, x1-ts, y0+ts, x1, y0+ts, COL_HIGHLIGHT); draw_line(dr, x1, y0+ts, x1, y1-ts, COL_LOWLIGHT); draw_line(dr, x1, y1-ts, x1-ts, y1-ts, COL_LOWLIGHT); draw_line(dr, x1-ts, y1-ts, x1-ts, y1, COL_LOWLIGHT); draw_line(dr, x1-ts, y1, x0+ts, y1, COL_LOWLIGHT); draw_line(dr, x0+ts, y1, x0+ts, y1-ts, COL_HIGHLIGHT); draw_line(dr, x0+ts, y1-ts, x0, y1-ts, COL_LOWLIGHT); draw_line(dr, x0, y1-ts, x0, y0+ts, COL_HIGHLIGHT); draw_line(dr, x0, y0+ts, x0+ts, y0+ts, COL_HIGHLIGHT); /* phew... */ draw_update(dr, 0, 0, TILE_SIZE * (state->w+3), TILE_SIZE * (state->h+3)); force = 1; ds->started = 1; } if (isflash != ds->isflash) force = 1; /* draw the arena */ for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { draw_arena_tile(dr, state, ds, ui, x, y, force, isflash); } } /* draw the lasers */ ds->flash_laserno = LASER_EMPTY; if (ui->flash_laser == 1) ds->flash_laserno = ui->flash_laserno; else if (ui->flash_laser == 2 && animtime > 0) ds->flash_laserno = ui->flash_laserno; for (i = 0; i < 2*(state->w+state->h); i++) { draw_laser_tile(dr, state, ds, ui, i, force); } /* draw the 'finish' button */ if (CAN_REVEAL(state)) { int outline = (ui->cur_visible && ui->cur_x == 0 && ui->cur_y == 0) ? COL_CURSOR : COL_BALL; clip(dr, TODRAW(0)-1, TODRAW(0)-1, TILE_SIZE+1, TILE_SIZE+1); draw_circle(dr, TODRAW(0) + ds->crad, TODRAW(0) + ds->crad, ds->crad, outline, outline); draw_circle(dr, TODRAW(0) + ds->crad, TODRAW(0) + ds->crad, ds->crad-2, COL_BUTTON, COL_BUTTON); unclip(dr); } else { draw_rect(dr, TODRAW(0)-1, TODRAW(0)-1, TILE_SIZE+1, TILE_SIZE+1, COL_BACKGROUND); } draw_update(dr, TODRAW(0), TODRAW(0), TILE_SIZE, TILE_SIZE); ds->reveal = state->reveal; ds->isflash = isflash; { char buf[256]; if (ds->reveal) { if (state->nwrong == 0 && state->nmissed == 0 && state->nright >= state->minballs) sprintf(buf, "CORRECT!"); else sprintf(buf, "%d wrong and %d missed balls.", state->nwrong, state->nmissed); } else if (state->justwrong) { sprintf(buf, "Wrong! Guess again."); } else { if (state->nguesses > state->maxballs) sprintf(buf, "%d too many balls marked.", state->nguesses - state->maxballs); else if (state->nguesses <= state->maxballs && state->nguesses >= state->minballs) sprintf(buf, "Click button to verify guesses."); else if (state->maxballs == state->minballs) sprintf(buf, "Balls marked: %d / %d", state->nguesses, state->minballs); else sprintf(buf, "Balls marked: %d / %d-%d.", state->nguesses, state->minballs, state->maxballs); } if (ui->errors) { sprintf(buf + strlen(buf), " (%d error%s)", ui->errors, ui->errors > 1 ? "s" : ""); } status_bar(dr, buf); } } static float game_anim_length(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui) { return (ui->flash_laser == 2) ? CUR_ANIM : 0.0F; } static float game_flash_length(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui) { if (!oldstate->reveal && newstate->reveal) return 4.0F * FLASH_FRAME; else return 0.0F; } static int game_status(const game_state *state) { if (state->reveal) { /* * We return nonzero whenever the solution has been revealed, * even (on spoiler grounds) if it wasn't guessed correctly. */ if (state->nwrong == 0 && state->nmissed == 0 && state->nright >= state->minballs) return +1; else return -1; } return 0; } static int 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 blackbox #endif const struct game thegame = { "Black Box", "games.blackbox", "blackbox", default_params, game_fetch_preset, 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, FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, decode_ui, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, FALSE, FALSE, game_print_size, game_print, TRUE, /* wants_statusbar */ FALSE, game_timing_state, REQUIRE_RBUTTON, /* flags */ }; /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/manifest0000644000175300017530000011221712161170564014165 0ustar simonsimon322eb9a5a6fd3327a51f845f544f6628 blackbox.c 08bb97d2e95e62a3de5490ae557bf009 bridges.c bf43b3ca986831f66815bd850a8e139f combi.c 87ca5e8e8aca7f5e04350f92efb87c74 cube.c 4ac4c90c52fad59d660213a8fdc63b59 divvy.c 58d91177fdc6e770de44665f48c7cd73 dominosa.c 9248f2044949b26c9d34698ac22be459 drawing.c 15d0f91446ddc5277d2616aa3c7d3de3 dsf.c 4bacb5d560a89a680aa56cbe4251baf2 emcc.c e49d19a6624e525a90950188fd7fa936 fifteen.c f4e46a658208d90d76512ff21c8f66a6 filling.c 4e7af8d93de97b6c27448f022e624c8f flip.c 7e9ce3c7f6591f177f235e9c3976d811 galaxies.c be6480a84e9caf027b2bee99a7f31a72 grid.c a7534763ff449bb2f765b904c91fd83a gtk.c 95ed8ade2b08a3c091ea2b5fb91a60d8 guess.c d8ffe8df11a60efc1b7646bc6bdc485b inertia.c df619cd781c976892ef23d391c87b359 keen.c bc446d0d3a698a2ac0993bfa9cf029f0 latin.c a28def84e7ab0e43e11e59e67ad4a889 laydomino.c 07393cc3c4a8560d09f097140220f3f8 lightup.c 34f82ba089e9c34a688ae69ff3a00691 list.c ea70e2412f02ee167d6e508aa41e101a loopgen.c 51778857e11ca1610091307530bff457 loopy.c 94f245067fa4e5f64e8035feed1d14d2 magnets.c 6e377a27462e3f457168386b94ed3c87 malloc.c bd7d43530a852038920d72f220fd2644 map.c 76188048008e3cf8edea94543aad00cc maxflow.c c0edb4820c2b0c5311d43408f69b4a8e midend.c dacdaed8fd50d4384a40f93d0b0c41cc mines.c 0be891991d35e9a94f67a767b280081a misc.c 570af001eca6bfadcb89863b4e4e77ef nestedvm.c 0c09f9c4a998aab8b8ce77579572dcca net.c dc015544af3504663c518d16cc71afff netslide.c f4b028e6f72164a8f077aacaad31c6c4 no-icon.c 728f6d6f1566aed6eca2c444fdaa5863 nullfe.c fccb880e4ba99b595a35bb3c1411b3eb nullgame.c 8d7751ea958545213fa620d1c8a54028 obfusc.c 7bbd4ef479233b6551173b44c3a993b3 pattern.c d259d967e7e70cb30d63565e00ac1dbb pearl.c d4c5458b6c0f7d914aa44ee10b677318 pegs.c 69ff95e39ae1f0348d2b5366d47fedf4 penrose.c d3d7de0f4c97211cd9b1d79d38972973 printing.c 60c1f9eef3e5a180ed7b013da9bdc2d5 ps.c 55acfbb6e94123e36a77970861b7ebd8 random.c ad3fa257da84aee84f52b182fef6d3cd range.c ab066fcf92764b50e84fcc96cd4077bd rect.c b1761d90e97e77873c38f5d07e7de925 samegame.c 4ad281682053645922b6a0bf908906ad signpost.c 8a4227c0273f9d00935e1ac5ba6d5a79 singles.c 97ebfb107dd8cf22895d2512e71b1077 sixteen.c bf5dcc333101c17a760ce0864f4a5a6c slant.c 99ed7cf851d7f9295c7dc5a54bc11cee solo.c 54d4efb91231910bf8d09487288ddd59 tdq.c 601f1a665a3820d76702d9ce145b74cb tents.c 17970cad0398de9d55cd314650ed67b9 towers.c 3ccd031f9652bc5658cd7e029187c132 tree234.c 5f09da59d07dadf4c27874a0f60d7acf twiddle.c 2d372ea58639bd4a515659eaa89868b7 undead.c 04419732df7a1c07f75ebc7f5c7cf51a unequal.c a6b6d65fcbd91adfe3a96f15c65403b8 unruly.c 622b2f58a33c979adeca2ac62c44a6ad untangle.c 71ab8a59325fe1ae7defcb075479e445 version.c d4b589c9c07b689c3531d7906b14b1b1 windows.c 0543a6c9a15be2ac24f73341f547c454 osx.m b308f056c3865c83b544ddcdf3264989 grid.h 91af25cae1f24f9fd9d7827b6e00665f latin.h cd2ff0c42aea7aaaa5912bd207a205e6 loopgen.h 4e7cf5a9fc13c537f1750d7ae8a2bd1f maxflow.h 542c5101639786b0bcda2bb51f23cce7 penrose.h 04a1bb7a6164a78511478ff8e781a7f3 puzzles.h 4f2bb8086b2caedd0fe4170537cbbc72 resource.h d65a9dd5069cb8cab301a4fd8e190efa tree234.h 6afb21d2dc00add80761dfc522c309dc blackbox.R c786b16f9dddada882a4a5bce54e1a4b bridges.R ec377ed51afe19ebc870e6011fd40ac0 cube.R 832bd48b0b1bf3addbb712c524c360fc dominosa.R 7daccb94a0826b6650952a692fc6dadc fifteen.R f9bb9f118982cffe8261f5072d652498 filling.R 7b3b9d76d37132b63f6ea3094abde4a4 flip.R 47ba1357a863234794ca90c44f85ef0d galaxies.R 8d7e4e2bc9aabccbad5c1a6480c5fd3b guess.R 4bcceb1039ab01aa9efa7b12aa426e60 inertia.R 83d15c844d01f160aad42789718507cc keen.R a5f90a2a29b3f25a865ad3e7bd23adf6 lightup.R 02797671953f95344ba1de795eaee7d3 loopy.R 09815c0798b3bfbd9c4c2d56e1207f5c magnets.R 2cd2536c0753b55bdb33e0f9f88e67a6 map.R 6208e7cbfcd509bd28bdbb08d7eb25d0 mines.R 9f9446e85f2a316a3b1134ccd6541951 net.R dc398d4ab16a75ccbf043c9842bdd921 netslide.R 68f54bd9eafe2ca08851fe4203d5cec4 nullgame.R 365eec05598b12d7c7c95d743b00f30f pattern.R a2e52db3e361ddb9e9aa57c73b7769ca pearl.R 91a44bae1cf9f883f30a784d06064d50 pegs.R e23f125b049be3df75c94eec22a312c5 range.R 7b6ba79b878728e7049dec0057f0ee69 rect.R 22c00bc8c5f204418f66a62ab66702c2 samegame.R 2a02e957f98464d3cf717b79a3a8a492 signpost.R f9c9b7cbaa218b51365bf5435dc0dff0 singles.R 879772dda167e6248a8f249aa461c506 sixteen.R 4da10f51862683437a36330b5886ed4d slant.R d83e72f54e09d4bd6afcf035bc572343 solo.R 3e3f6bc2249a1ad1fc5c297123e207bd tents.R 9ebe7e0b954f071ef0429523be2c6cb7 towers.R 3c011d7cd1efc2b7597bed2ab04aa128 twiddle.R ac1b07b82583bd11c77cb8d784e6247b undead.R 6ac24c7d9234000d82325cd2089b6cd0 unequal.R a54bbcbd3eda4713507dd778a3be512d unruly.R febdb4fecf043c911e6ba0a30a287851 untangle.R 6c5cd0db404e5a893ab3995f3815cb7c noicon.rc 251f9f2426f5ba863dc9c1466a90cc6e chm.but aa329c0e9bce470f1458f46d0ea86852 devel.but e06434c48b8a998d29bc8197113958b9 osx-help.but ebc00d8a67897188e4d63d0ba8879bc6 preprocessed.but 0d7a85554d640369c9f603f922cc13d9 puzzles.but 6cb2d5930db4df5fc962c58754e55d58 osx-info.plist 92ff899bb81ce16eb83f4bf2f87b191c osx.icns fd6d46f3f0c6aef2a36e5f4b8e9279dd LICENCE 350a561de747ab881995032c56db7d0a README 388c20cbb8bad4b9bbd43860b31e4787 Recipe 6552a456b7e60fd01c5063f39e8cc8fd puzzles.rc2 28528075e699dfa9d846255263930216 mkfiles.pl 749d0e6035c04fbfc096a1b686024712 Makefile ac6d0369a6d8387e98ddcff21f0ca5dc Makefile.cyg b9ca5d5cbd7fbbc8280a8a92bdaf561e Makefile.doc 1759974d0beb70eabaca7fa978e2c784 Makefile.emcc 4650f844a368772233c842803fad82c8 Makefile.gnustep 8a596ad1c933b206f418ec061f981fb0 Makefile.nestedvm 8b69ad304bafa35775b1374e0c437837 Makefile.osx b783709f0c480e8047cb9b160be9aec4 Makefile.vc bf7cd2fb98446208feba4ee2cb1fe376 Makefile.wce 5b0f2f7ae6e2c6164608ef7b1f7b8e2e HACKING 83530dd00e24d455789b1254dd3af592 puzzles.txt 37212e8f3ddb16406249485b11a30f1e puzzles.hlp a1de611d85beacd165dd34d9bb3814f1 puzzles.cnt f715f556db9bec8526bd1422fb47cb31 puzzles.chm e9860e43d7defd273fa1b0e5d97093c1 icons/Makefile f2963038924869dba8db72b6f8f23d08 icons/win16pal.xpm 2fffccc5903a30a96057bd00e37bd0b1 icons/blackbox-16d24.png e2a4736f369280dbf0ec203f185c89bd icons/blackbox-16d4.png efff28a224ca3e7e42d5b4ebb15792d0 icons/blackbox-16d8.png 2fdb085e29486f84e403d049576ce834 icons/blackbox-32d24.png a967a29e29a5f974c66e39e38f2a00c9 icons/blackbox-32d4.png 6473dd551ff552d31c81c4c1d0e84bff icons/blackbox-32d8.png aeefb9aaaff4db81caa3b0606d61855c icons/blackbox-48d24.png 16bd2915c4c367c7a275c460047fa892 icons/blackbox-48d4.png 993350cd65140e4e465f802a423b10e7 icons/blackbox-48d8.png 25e11d4de8c00598e7db9991561f4173 icons/blackbox-base.png 2ce21668f56a21c663af17f5b7427a92 icons/blackbox-ibase.png ad6fde1de62b98ff5289036fbf0f6c5a icons/blackbox-ibase4.png 0d734fb61d7d308f6fa0659c9fb324e4 icons/blackbox-web.png ec312671b71326a8f638a5f67a6583ef icons/bridges-16d24.png c89af7800c3db7cc1d7361ceb79a195d icons/bridges-16d4.png da5ce80379eda7485267bd892bf38546 icons/bridges-16d8.png c06baf28db4045e3a82cbfc5798c1659 icons/bridges-32d24.png 6e5d662bddf6fe57611bfa05179aee05 icons/bridges-32d4.png 321e64715fb1f8074a7139b672981ecd icons/bridges-32d8.png 9a0f2ecd270c42777d1e0da2ce68dc99 icons/bridges-48d24.png 855e6a81ec0aaad3ec83979df5ba8d09 icons/bridges-48d4.png 6896b0ad71637b0659c5f8b940fd01c7 icons/bridges-48d8.png c823b95f3f52116e0c2e813f3c595a04 icons/bridges-base.png 0ed4a084d75ac7ebb2a4cad945470a38 icons/bridges-ibase.png 26aed7ef03c74859c37c7a3869f4fd9c icons/bridges-ibase4.png fd07193bf3813cb9f0693011425ebfba icons/bridges-web.png de2d3fc0ff894c4a7011b6446b8d08cf icons/cube-16d24.png 88309c88f5a37292ad2dc5150a9f0f19 icons/cube-16d4.png 9a20feac4e0a239c660d85fafe5cd882 icons/cube-16d8.png 0d1cd3a07bfd41928f194b2f719dd4fe icons/cube-32d24.png 0d4af3ca6446abe045523a0d6453d429 icons/cube-32d4.png c423a2c61b382870058def3e05151399 icons/cube-32d8.png 8800784ad9b14b7b7b41538d9bbcfe06 icons/cube-48d24.png 377184ac42b6c7e7edc766dc1ed8f666 icons/cube-48d4.png 9f14cf50003e3b2bd238a5dafdececf2 icons/cube-48d8.png d6792caddb53a9145a251a3ff6264de2 icons/cube-base.png d6792caddb53a9145a251a3ff6264de2 icons/cube-ibase.png 4c141e8679185853c595496ce510f4cc icons/cube-ibase4.png b0ac7fda0783d636e66ebcd80cee68ac icons/cube-web.png 539703e248847d25c9d492a4077332ff icons/dominosa-16d24.png 351e7f9385a79322616ca217eabeb05b icons/dominosa-16d4.png 3e8ff2c282c21f7e350c8fa7dc3bea9d icons/dominosa-16d8.png 299b801a98857f436cc608cb8c9ba64c icons/dominosa-32d24.png 6d7e8bbb0042b3dd9f89aa64af2da10e icons/dominosa-32d4.png 04d0dd8a095655c4648bcd62edbbd898 icons/dominosa-32d8.png d996a3da16d50e0854bb88642a1f35c5 icons/dominosa-48d24.png ee379ea1e3cabbd2d4a4fb5ca6fd1510 icons/dominosa-48d4.png 607dfd78129764a27beaea190a585dbb icons/dominosa-48d8.png 4b6c6a1622eee9eb2b46d194fe3789c9 icons/dominosa-base.png 3987d2e54f3e8f7f780fea8e1afe9cf9 icons/dominosa-ibase.png 0b4090470d6aed65e41dbe21b75c6d1a icons/dominosa-ibase4.png 4228b8e7b7c24eebe41b585c794e8932 icons/dominosa-web.png d752a32ec08dcb40128f952b3bcfbd07 icons/fifteen-16d24.png f2b120cc0678bbb44a480d159f5f7388 icons/fifteen-16d4.png fe114452251342785bb73045179d0bb4 icons/fifteen-16d8.png dd23d2d39766983ee1aaae6c9924d085 icons/fifteen-32d24.png 35a224019ee6576dd7dc411c8575cc15 icons/fifteen-32d4.png 789b0bb7f99b72024f3c86c0f12708a7 icons/fifteen-32d8.png 4effd5dd607966f7aac160bb26b81980 icons/fifteen-48d24.png 040fc85a9d7fc750d41cd70a673d73f5 icons/fifteen-48d4.png 17aa24f4335370f5b3e803cdb0ff73d8 icons/fifteen-48d8.png 4d0a73c800f0480ca7d9f988ea0c94cf icons/fifteen-base.png 3a7f9a64091f7611b3710562a82a19ce icons/fifteen-ibase.png 1de226bfd2da45f9c189e2fe0afed160 icons/fifteen-ibase4.png 98a379f5b1c2500ea59a745cdfce438a icons/fifteen-web.png 8bb2b885dd629a89f8cdbf8888f2ce25 icons/filling-16d24.png 458ac13148fd2e9d0ee5a6e697746960 icons/filling-16d4.png 324f10c014477dfbaf493f418e2d4067 icons/filling-16d8.png 7b9d49c98b8a313b567b7c3c7f3e1662 icons/filling-32d24.png 9fd5cbdcafba0f6f6fa3067e4638c14a icons/filling-32d4.png 9fd45aa2e79dfcc5d8194276f29a4dd1 icons/filling-32d8.png d11a4316996b24c7c13ef13b2c2d1f99 icons/filling-48d24.png 1ced4d7cd4c931ce15a2eaad9e00aa7d icons/filling-48d4.png 09ddea283831be19a5aa5f7af4c6c3b9 icons/filling-48d8.png b6b723e651877fbce7bfcad4cb0e0555 icons/filling-base.png cfacc51f5f45353eb4dab0517fc0e326 icons/filling-ibase.png edef82291bbda5ebcd2b3da609e93b81 icons/filling-ibase4.png 868bf341f44a803a73c8cfac247d753a icons/filling-web.png 6053f606d4c0bdf61d0604e5f2ed235c icons/flip-16d24.png d424df9f8872df071e237b84043decf2 icons/flip-16d4.png d7876488e542c61dc211c89e74f37221 icons/flip-16d8.png 4b024f0ff93550968df191c730878984 icons/flip-32d24.png ef9fd170f12f9a9686cbb1971800eabd icons/flip-32d4.png 11416b010df67b801e4a4c19f96950cc icons/flip-32d8.png 14d7a893973f9024b5a901e12a7ab805 icons/flip-48d24.png 43b808f5bafdcab341fad4e67eac7830 icons/flip-48d4.png 210cfd95630e15c7eed465f49821ff12 icons/flip-48d8.png 802dd52f8971e55f285fec94381619cb icons/flip-base.png f8a744cc52cc198dbf8969592d73d0ec icons/flip-ibase.png 145803092da7e9ae1ceef4df081e4e59 icons/flip-ibase4.png 0c403b955819a0954af0ed56d432f3e8 icons/flip-web.png c659e9447beb084fbb5f17f330adb9d0 icons/galaxies-16d24.png e5ff56929d70e20e85de84aef637f846 icons/galaxies-16d4.png 3bfe54db9ce23672563164b1d6870f0c icons/galaxies-16d8.png 9053341e63135b59cbb0f61dc5581cf1 icons/galaxies-32d24.png aaabd425de5b3d093549cf80da7e37bb icons/galaxies-32d4.png bf07ba5fc891ebe089a16d6c5c7fa7ee icons/galaxies-32d8.png a02c4b684cf6646c14dfd760f0782b14 icons/galaxies-48d24.png cdc3e5b9c36171e3f8a129f9ed812dac icons/galaxies-48d4.png 40ad77ecbe8079eabc1089b1a87c7618 icons/galaxies-48d8.png ea3d27a80086429d679217dc6df14f88 icons/galaxies-base.png f387f7eb6cf2fb76741f264a45b37859 icons/galaxies-ibase.png 781b1a96f67c9871e204f00a25f042db icons/galaxies-ibase4.png ad2076b9cd0266db605901be35b59d25 icons/galaxies-web.png bbadc8c77249db960bef0a813a6aa481 icons/guess-16d24.png 8e1e2de650af30df28532db07e10d25e icons/guess-16d4.png b2865d5f936346c67ae28d5ec14d1640 icons/guess-16d8.png 3cf6ba59fc11147a27765c2aba317665 icons/guess-32d24.png eda8d544bd29d7851ec8fe0e743d770c icons/guess-32d4.png 111d5b5049d51a984231270cf120740f icons/guess-32d8.png e8da71e6737de6828c5a60b444dfee17 icons/guess-48d24.png 9c091b7f5abc912fe4f1199bcb9f1197 icons/guess-48d4.png fd0c7ea81649e9f3543edd260b2a8e4a icons/guess-48d8.png 861148ad53bf7236fa9422f324582e28 icons/guess-base.png b29f790a117c4e35c9f03f5a8ff756fd icons/guess-ibase.png 3ca322271db5181d71d925fd400c359e icons/guess-ibase4.png fcdce7964819fe1a743b1e28dc698e72 icons/guess-web.png 3a9de6cd8ee092b036dea537026e31fc icons/inertia-16d24.png 4ab275fc06cc46841b5813b4fc474951 icons/inertia-16d4.png 9a6336581298a99acada623dc01de90b icons/inertia-16d8.png 5ab220d369d85a6d71d3cfbaef799932 icons/inertia-32d24.png 807ef29ee0dd871889dcd32ab416a088 icons/inertia-32d4.png 64334ae15b5da4710855f4e8d79837ef icons/inertia-32d8.png 62a68f2a7c68bd769dcf6642d5eb2136 icons/inertia-48d24.png 8e8a43d467d30f5fe7c6c7aa51b159e5 icons/inertia-48d4.png f6e61fa31f680ba28e7bbdcf15e8f767 icons/inertia-48d8.png d706cafdcf5e83ad1a444c714e171839 icons/inertia-base.png 3499727e5c7427c56f90a3cb4da50be0 icons/inertia-ibase.png 32a8e2ca12e5842cd85bb5026f242f6a icons/inertia-ibase4.png 8ba2c68da4fdd3a72233b76831050fc9 icons/inertia-web.png abc59410087da71ece5ec65d9f930350 icons/keen-16d24.png 9d92e97f11beb7f6b6f7a69a463658fc icons/keen-16d4.png 45de227b19a17879bb65ced537792539 icons/keen-16d8.png f56a8b845c35c90f1fcfc9ae691e5a3c icons/keen-32d24.png a3577ad4e31b5af1a36628990e92283b icons/keen-32d4.png 048a7a273e2d6e6456f02866e7de27e7 icons/keen-32d8.png 6a83edea47bcb9e546dc57c675dd821f icons/keen-48d24.png ad04d61b6e90b99245244d268545a91b icons/keen-48d4.png 6d0ca7eb0ef3a5200508dad44af2d3c3 icons/keen-48d8.png bd7b4084a87030c003e159c952ef8a0b icons/keen-base.png 3f0cf1809cd1cfa7609e97552f101b77 icons/keen-ibase.png 53941d343eb3d9cbd850bd7fd17e0195 icons/keen-ibase4.png f201a530323114f55c0c0661168ea71f icons/keen-web.png 69fd3f913b4154eecec40d2faf66908c icons/lightup-16d24.png 5beeb80d3503214fcfc3a427a9c0ad5e icons/lightup-16d4.png ba455704d47b953e525c2c6d0166d003 icons/lightup-16d8.png 3a2a08392195c090aa95d3bd2365ee5f icons/lightup-32d24.png 56673ab8c755ee323e07e3b0110244cf icons/lightup-32d4.png bb342b99f3e16f6f49410e7830c76a70 icons/lightup-32d8.png ee6cbcc4254c83e25622b2574d65dd8c icons/lightup-48d24.png 5ee0f49f3ce86d8ab1411ad6d7da94e9 icons/lightup-48d4.png 50e86c88457da3f6b61e5f99b7dd5ab3 icons/lightup-48d8.png 087edf33d70f98d41ad7005601d91722 icons/lightup-base.png 1ee35d6e296c1f15415b73e297faa580 icons/lightup-ibase.png 8b0c257d3747b942f4423843b95a2d08 icons/lightup-ibase4.png 310fe3a24b8786e065262778ea5f72b6 icons/lightup-web.png 00c96f67a4d35d8116f254d38da30181 icons/loopy-16d24.png 3d4c14d2c067c1c786f6327768be9033 icons/loopy-16d4.png 4cc0d8fe5ce9ad2258e701c3fba7edb0 icons/loopy-16d8.png 2430e44732880f22fa7c0679cc92c4d8 icons/loopy-32d24.png 34456c78a6828a11c25714e40a495dd0 icons/loopy-32d4.png f090426aeae85fca23e0444f8ab7f846 icons/loopy-32d8.png 524fb781da294709307ae722a08ad743 icons/loopy-48d24.png 40dc014400dd6217ae99e72c28fa58be icons/loopy-48d4.png 240465a721fffb4a78083e5a76d345be icons/loopy-48d8.png 298731cf5842e31418a5fb2437255e91 icons/loopy-base.png 7cda87724faa44d3f1aa8a15748fe62f icons/loopy-ibase.png b4a16c782c0a75e6fdfb28f0c0b594de icons/loopy-ibase4.png 723721cb2af90a3a27762bf9bb6bc334 icons/loopy-web.png bbea02c5aed15c001a6dda19da4e27f6 icons/magnets-16d24.png 455423302760116cc15f4cde29ee5d27 icons/magnets-16d4.png eaeda2a0ae3a8e0126a062574f3a9bfb icons/magnets-16d8.png e1f0d971f9f3cc27d336ea931a5a64f2 icons/magnets-32d24.png 08cc45e69154034529c860c21001d73d icons/magnets-32d4.png 4a8605acfac080b6bd57894632c11c93 icons/magnets-32d8.png 1970d697fec3f93e1898f1782ba9ca3e icons/magnets-48d24.png c985ea8d31ddad4c233e3465c23e6602 icons/magnets-48d4.png b1c4aad0c66e2d3babec95e92f071da6 icons/magnets-48d8.png cbc3af4fdb5ab9b8d78e702315bb5a43 icons/magnets-base.png ef5b72826455e17d9a8cec351ae74f67 icons/magnets-ibase.png 7db040dc83ee1c0f21ed14927531d883 icons/magnets-ibase4.png 78de3da57a275b6145847a4a36151b2b icons/magnets-web.png 5265c3e9cbd033a1d0ba6bef05b7fd4e icons/map-16d24.png 43dc29e94a43c9ad723e3ddf27e981ef icons/map-16d4.png 2ac9260ec6662674c25b0badc42f87c5 icons/map-16d8.png 8085614429816820d28715fe551c6abb icons/map-32d24.png 992c6bcf3aed3ea366540f669150827f icons/map-32d4.png 446f15abe42b72ebd04fbf3abb2b88c0 icons/map-32d8.png d748fd69ab39099f69951dc9013d774e icons/map-48d24.png cb57075deebcc945fefa088dd06b33f4 icons/map-48d4.png ae2420bc40b6828a9573c6c4b07ecb2a icons/map-48d8.png f8ffd90f072513721f5d2ab9abe49cce icons/map-base.png f8ffd90f072513721f5d2ab9abe49cce icons/map-ibase.png 070186d401ae461a3dc3f56bc9da15d4 icons/map-ibase4.png 83f5c275577336107694cdcc049460f4 icons/map-web.png 1fd2c0786d5d03f5a56b5d85efdbe007 icons/mines-16d24.png 488aeabb3b9771a791bfc0b2dee2591b icons/mines-16d4.png f182161c9ccc23bd09c2f8dca5608103 icons/mines-16d8.png bf43a925f82a0d6edd50f3c87bda65c1 icons/mines-32d24.png fcce783acc22d51e90f7181e2cd8448d icons/mines-32d4.png 4b1c12476bfeecaccee850d608fb58af icons/mines-32d8.png d7dde5d861b51a28c96cc4f6d8d7eb04 icons/mines-48d24.png 61d5eac81f866eaa808973fda32f25c4 icons/mines-48d4.png 3e008d960faf5c1d5397c4f1f933aa11 icons/mines-48d8.png 073cb0cee196d1dd8e7edc9d45194049 icons/mines-base.png d54d82394c0ada010a48d32d0e0e4beb icons/mines-ibase.png f378e3d8f3c1c40d585cec7a1a671687 icons/mines-ibase4.png f28abc613164b9fcc1b3a7a307709cfc icons/mines-web.png 3fca4a2439fa4779f5b19f4e2243e5ec icons/net-16d24.png 687e386811472e4565ab412f368bc660 icons/net-16d4.png af7292e98c9de7a4e5ca72b58caa0330 icons/net-16d8.png fb5059714205e64de6b4f99d704ab616 icons/net-32d24.png 8bbfa1c431fdb63bf490807b0ce20d9e icons/net-32d4.png 6e1128e7be87b9fdc105659d9a42343d icons/net-32d8.png be161f3cc71d1c140684c63145abd83f icons/net-48d24.png 6beb057a50e7036e4e1251001b030b56 icons/net-48d4.png 85243deee4adb4dfbeb3e4ea73fffbb8 icons/net-48d8.png c6eaf9bbe946101cf53c409809f77fea icons/net-base.png 48570f26ec2eb890a980bafb62dcf98d icons/net-ibase.png f287f468ff3910faa2e3606ef6d92d37 icons/net-ibase4.png 2718221ce59ecd70cc2400abb2c44c72 icons/net-web.png a53e563626d0783d1dd096e020c841a4 icons/netslide-16d24.png a2a78c0a5467ff6e1f4771f12f7bc5cc icons/netslide-16d4.png 710ad218aff6b959935a8abfe6e397e3 icons/netslide-16d8.png e08289ec19270d7aebcff211aa5b27a0 icons/netslide-32d24.png 283dc77a3ffab391157a02904b4adc51 icons/netslide-32d4.png 6814002d960d09fae1db5e39f39d67b5 icons/netslide-32d8.png 43145e4784384684df4cef3a5af8edd7 icons/netslide-48d24.png f6c96e93d905b5954aba9ade8e4c6f26 icons/netslide-48d4.png bee65fb8645551ddab1a044e4424b0ab icons/netslide-48d8.png ab642350bd5ec71e6cd35e64efbfaaab icons/netslide-base.png 2405352467f9af46696bcbf4a0ad205d icons/netslide-ibase.png 95d142ad462059ef823d175c8c9c976c icons/netslide-ibase4.png 1b7269010535d3bffdb779557ba1759a icons/netslide-web.png a959cb2c98daee9277e9d2ddcc431c74 icons/pattern-16d24.png f877efe38d6e7a1ac898a72d9a1ccda8 icons/pattern-16d4.png 757289a591f259d9ce36b84b10b85242 icons/pattern-16d8.png b830e21b449dea3252d4d78467f7a15d icons/pattern-32d24.png 8f00f9c296bb2ee7bb7764a9b8beda62 icons/pattern-32d4.png 357b9c4f101ad198bae97516d2214ede icons/pattern-32d8.png 597f0f06edb688b4c9cc7a32810b9c87 icons/pattern-48d24.png 49823fa316905e418db79b5728fdf12b icons/pattern-48d4.png ad73e46399e29a91aba74e8ec3494ca0 icons/pattern-48d8.png 392a297febcf405df25421de23d62f7c icons/pattern-base.png 0bcf426cb18276d8ce1a1f5fb6b9ec25 icons/pattern-ibase.png ae0decf002b325e689d8d17a795c6b54 icons/pattern-ibase4.png d89e9afc3f70353910a55afabfe13ed7 icons/pattern-web.png 64f347a68d69267e157c820e9b016aec icons/pearl-16d24.png 61fac5f2de3a10d59df4a20086bd3b49 icons/pearl-16d4.png 92554358975279eae048bbe0322487d2 icons/pearl-16d8.png fba6b1ef4ae3817664e5a86a5f73fd58 icons/pearl-32d24.png a6a76a750cd33c8134d31b2aef482897 icons/pearl-32d4.png b9a3240b48352fab22384fefaaf7c320 icons/pearl-32d8.png d4110dbb296bd2c95caf87aff4ddc8b3 icons/pearl-48d24.png a2f7e834597cc0267a537a47f5bb2299 icons/pearl-48d4.png a3c2f23bc9672a16f0227b5448802a54 icons/pearl-48d8.png c090014e3a468894202c8f0e9633172a icons/pearl-base.png c84af2d372d7ee98e2eadffb5d6aec6d icons/pearl-ibase.png b9c89b138e036ffbfd36fb4aea7cf71e icons/pearl-ibase4.png 15ae4a5e6cff5ed9dc9232419533b7ba icons/pearl-web.png ed9f1a6fec83c53123bfd16a5f257b0c icons/pegs-16d24.png ccf7b86ccf18254dc204ac033bfd5531 icons/pegs-16d4.png e2ab5007e8776ce37bff356f60951cc8 icons/pegs-16d8.png 68935618b658d355b8cfb14eda493a38 icons/pegs-32d24.png d1b114af6787a8c9a933c3c3ab439180 icons/pegs-32d4.png a3cf11cd0550d4d2a7ee87477275b0c9 icons/pegs-32d8.png 752ee61a28da944f230f8775407529ce icons/pegs-48d24.png a10d71316cd624300235e94746a0c77d icons/pegs-48d4.png 015052ff4134d23e91845b76abe7fa76 icons/pegs-48d8.png 27546be511e0f198b9b75e5644d61581 icons/pegs-base.png 92920a49ebb50b7a8d904437142f0c9d icons/pegs-ibase.png 51baf7965a3dcad7137f0b837a18292b icons/pegs-ibase4.png 858dc39ed120588018b6514a1bddacc1 icons/pegs-web.png 4d848fd27a0ca4f8e11bdaceb08b231d icons/range-16d24.png 63bf5aa262ea6c076403a4733003cc7c icons/range-16d4.png 3b3b382abcb69d2cbdede2a5efb75123 icons/range-16d8.png 2d03d5f8eeffb27e8719b942998bb915 icons/range-32d24.png 879b6957fa3fe1630a709110234baadf icons/range-32d4.png def89d19f1cdb2870aa759d011c7a435 icons/range-32d8.png bc501e85287bc7b73f9235fc5aba6b74 icons/range-48d24.png a3cc41e3796f32f158a5db07da3abe18 icons/range-48d4.png c73a367287dbc6c05df3da294fb67a19 icons/range-48d8.png 39cfd9cadf15f5b1590e2d674b3ef883 icons/range-base.png ba134febf3eee9566da063f9a793cdaf icons/range-ibase.png a879ca6b127707db24e78bc57c363e43 icons/range-ibase4.png 5bd0fe8ab0662bfcce0c757940ada675 icons/range-web.png 3bed45d6a0c6cd5c836b519e530bc414 icons/rect-16d24.png 42e10285809e6165dec616f3cf62d0b3 icons/rect-16d4.png 5036bd3b778b2aece499c7c79da51f74 icons/rect-16d8.png 747d7db260f3c4b1ed9d560aafad9884 icons/rect-32d24.png 82c3b47786c06cabfea51af4ed0aab94 icons/rect-32d4.png cd6957146e590be1f4e43814f9a90c8d icons/rect-32d8.png 493649afe547836f5f60a877a9c66f8e icons/rect-48d24.png c5e138be447f06efe347ea7294475766 icons/rect-48d4.png 7aee8532a5e9b3939cdb981f7bc1862b icons/rect-48d8.png b7c637ce16a51ebe1a59995de859f1d5 icons/rect-base.png c3d25acf19dae4a4b576ea0e142804c7 icons/rect-ibase.png fe1945284d6960b44043d1b8b767e949 icons/rect-ibase4.png 03d1194077317f24b1350d10bbb928bb icons/rect-web.png 16ee6a11dfcc037d6f19005175d36b78 icons/samegame-16d24.png d26561fc1e9762117aeed75eb09e5e23 icons/samegame-16d4.png b64c9957ba6bc561ff6e3b3eeb0f4ec0 icons/samegame-16d8.png aef6676cf2c74a0cd62eba8e49cb10c0 icons/samegame-32d24.png 9013ca3d16770b16a551805481f49e4a icons/samegame-32d4.png e5862ad530aa7d677948d2eb42693086 icons/samegame-32d8.png fa4786cb7d36541fed4fe68761f933e6 icons/samegame-48d24.png 1746ab26b613cf7f5bd6c62dcbdbfb1f icons/samegame-48d4.png fcd376cd1bf56e2be56be2c33a1b8c31 icons/samegame-48d8.png a7ff89d09c5801edda3e1f3c6d23055f icons/samegame-base.png a7ff89d09c5801edda3e1f3c6d23055f icons/samegame-ibase.png a1a10ec5e96817704696a8a44ab0ae47 icons/samegame-ibase4.png 770b08e39b19548d3afae14bcdb24894 icons/samegame-web.png b75af40754f4547142e3fb4caa5e1e51 icons/signpost-16d24.png 59dbc5559cbff53d3725d75df44e3321 icons/signpost-16d4.png 099694a0ad3ecd00065e4b29505501c8 icons/signpost-16d8.png 039bc36350333d2da82b78beccc732d6 icons/signpost-32d24.png 4cdd6d8b32a19326b2b340d2e10c221c icons/signpost-32d4.png ee30bf7e40cb9bf3bcd7499ba6729907 icons/signpost-32d8.png c98d11261d4aab11988aba11fe36319b icons/signpost-48d24.png 13ffa123b81b80a0fd1b6d00813f6578 icons/signpost-48d4.png fd07e9c11dac5e1c246d3acbc05f5129 icons/signpost-48d8.png 2b6731a37a3409904ac4b235ea257928 icons/signpost-base.png dc07c30bfb060479bc053812685fa508 icons/signpost-ibase.png ca0f8a305ab4c223a72a4c475b1a2b1b icons/signpost-ibase4.png be6ab433ed1d8da2063f0008648a3b8b icons/signpost-web.png d4ef6ffff5c2c63294fd37a6858e09ac icons/singles-16d24.png 7aa95458627b7a7d46d900d567db552b icons/singles-16d4.png 6be285639a4e5e38fc9fd62e6e749a43 icons/singles-16d8.png a376e569530418a28d3c83c5410fd4a6 icons/singles-32d24.png ad065f3d37b57165f3e42b8629f88f26 icons/singles-32d4.png 9c4ae0ac6a56a0031f8cd31baaaae980 icons/singles-32d8.png d3e60239f3c2875424ce9135cf8fbb2d icons/singles-48d24.png d594aa1bf2a18be3760ac0c7b503d41a icons/singles-48d4.png 1cb8f1f7702a49f1f57d78c6cbd2b970 icons/singles-48d8.png 43bc1c70b8a227d9a29fd8e743c5d801 icons/singles-base.png 83f9de287b404d23cf132cbf17fa1dca icons/singles-ibase.png b6f7b18f8746d0bc0bdbd37c75813376 icons/singles-ibase4.png 7cb8c51443f0ccd917a6524d2bd76de6 icons/singles-web.png 72dddbb9b62fd4d31d40330ccda453c9 icons/sixteen-16d24.png fc23a910f27be51ec9c222937df67195 icons/sixteen-16d4.png 7c8e018858cdc5dc3adc3e09c67827f9 icons/sixteen-16d8.png 5c137296f98c30f55644e2217e1068eb icons/sixteen-32d24.png 733bcb57ea32d5c5a964734a6a5372a3 icons/sixteen-32d4.png 2864ef8f140e24b8401907877cad618d icons/sixteen-32d8.png bfda9457c625a3c79056dca623ab71b8 icons/sixteen-48d24.png 6de0065cf6352b9cadc41a213f21a302 icons/sixteen-48d4.png c372412bed944999dccae6f364cf8685 icons/sixteen-48d8.png 9c7dcebcfc671929a7093c0410b38349 icons/sixteen-base.png 7b7dc392c9a18e5c0feda0d1e89284f2 icons/sixteen-ibase.png 17a98e3536625fb9bbba02caaa5d66b4 icons/sixteen-ibase4.png 539741771ffaef40f1f93f24b4b56459 icons/sixteen-web.png d54ad7deddb7e438e75a277808f25716 icons/slant-16d24.png 0f81c1d69a8056223d66bb9ea68a604b icons/slant-16d4.png 1fd98a6c2de84c78928af2ae0181ef25 icons/slant-16d8.png dea90bbf84e60bf8d0d073a147b68740 icons/slant-32d24.png ff1354755cd607698f42c1d39ce8927c icons/slant-32d4.png 8e92b0c1c0b88e76d14ea30ee617865f icons/slant-32d8.png ac7fb70b6abe70f3e7851b1d33d2dcbd icons/slant-48d24.png a6c881346e190cb4b6d9825403f9790c icons/slant-48d4.png aef6cfd55e42f9943bdc84a46eb3ecf1 icons/slant-48d8.png e1bedcbe670cff9be6bb4c6fcb93a472 icons/slant-base.png 6a87b79304da4b577af7f0dc557c7f8b icons/slant-ibase.png e8cdb769a21db5a47da0d46cbfa43466 icons/slant-ibase4.png 10ab3edccb16e00c4cf451ae7a85a889 icons/slant-web.png 767c9db958991cebf2c67f92853510ad icons/solo-16d24.png 40668401354e6612ba30d6170ed4ab80 icons/solo-16d4.png 4074e5d42508c0adc42f32c0d6adb785 icons/solo-16d8.png acab0ed0fddaf6265305eab5cd1ebde1 icons/solo-32d24.png 4d5a3b9440302a100abf81f14923d32b icons/solo-32d4.png 2915f5e1e45f29f9ad7cb17f1ddffd0d icons/solo-32d8.png 8ce285fde3887a81f4c63a60f795d923 icons/solo-48d24.png a735a0cfc0acf85f1b5e6d1d099ae77c icons/solo-48d4.png 2c4d56a21bffa10e836b296e887488fa icons/solo-48d8.png 96bc232f42502550296ca9ab7d514a0e icons/solo-base.png 560e63daadd0edfecd8deecd903b7d7b icons/solo-ibase.png 4e7c565fb8a510bab7a6245b261817ef icons/solo-ibase4.png 54f6050b1f69628ba88ae61fab4ca13c icons/solo-web.png d900ae4651135fe0bbd26702a71a9339 icons/tents-16d24.png 9d2fce5d115ea7eacb27ed74f18ec5bb icons/tents-16d4.png b08d3cbc9cc6acbd3ba2d79641b2dbe2 icons/tents-16d8.png 4bef7092b1f667e104317d31e478ee21 icons/tents-32d24.png 474a488d8b41463959208b3b0b98e077 icons/tents-32d4.png 3dd48bdff4e091cf18eb338eb329cea1 icons/tents-32d8.png 876a45ecc0249eeb64525ce03b30ebb9 icons/tents-48d24.png f0613996aff77ba299caa2d549f509b0 icons/tents-48d4.png 56b631a0aad9eab3eadf5f13d3b634f3 icons/tents-48d8.png ddef507cd0f953e47dfc2e7cab29e1d2 icons/tents-base.png 5e2436f42daebf99cf2f702fcb558773 icons/tents-ibase.png b376ab95cda2131e451788de6669a535 icons/tents-ibase4.png ad642dd3ef509c79330d16964888bdd6 icons/tents-web.png f1a10d8759746b79dc27de6ee68f9de8 icons/towers-16d24.png 6da1ec4fa14e4b842c87a19655fda59a icons/towers-16d4.png 190c532e680e2e7b2fe5854fff31f557 icons/towers-16d8.png 864250b1662e47129fdceb885ce48dd6 icons/towers-32d24.png 207aca2bf3f52d30fddfb92b7bb65a85 icons/towers-32d4.png 427202aa921e02bf48f10d948f4d70ca icons/towers-32d8.png 4f05551c43adc1e37c58b9497145f0e1 icons/towers-48d24.png 5ef9e8eedff4d7ddf08b90183b17a638 icons/towers-48d4.png 452206033ba92cb1d1fb92b52f6ffde0 icons/towers-48d8.png 4f1563c1f1429f48757b27ff045b320b icons/towers-base.png 1e6da8e54af8e9506159bd4aaffa867c icons/towers-ibase.png df5f92a733bf0c65209037f4e26e3095 icons/towers-ibase4.png 3d4bd109fc81bd2ec64f7e3310570557 icons/towers-web.png bb4898b87114da4a9f26c64d17c7346e icons/twiddle-16d24.png 90a74dd611c9e120fe8c943102c746e0 icons/twiddle-16d4.png 67c3798ed5dea3b84af9a9ec77505615 icons/twiddle-16d8.png 57a5e5ca1662bb5229af7abe2ba1f167 icons/twiddle-32d24.png 2a022290c228eb3ff218669d35365365 icons/twiddle-32d4.png 49cadf30cee78ce49877388a78a1043d icons/twiddle-32d8.png 9dfcfc8db4cdb8cdbdb40efa060dd895 icons/twiddle-48d24.png 994332bb909ad81e60c7643cf57c915e icons/twiddle-48d4.png c8021efe4e81fadc2a078f1bcf2369ee icons/twiddle-48d8.png 2fb24b446212dce37de1def26d00b93e icons/twiddle-base.png b7e0df2c7ec6fa8cf5a5a7bb8d2d585f icons/twiddle-ibase.png 6dd28ba52c2b0ff726489f65bb686d33 icons/twiddle-ibase4.png bf93b60e979ee741ff251a6148d3671d icons/twiddle-web.png afb043f59e23718be5fc6a03ef0c7298 icons/undead-16d24.png d2f3989372c37e51bda7db977a64c30d icons/undead-16d4.png c0c44a4ec4e0cbba334fc5ab316002aa icons/undead-16d8.png e1757988e00d35155009ad287571da55 icons/undead-32d24.png b0bfd1f7e67023e894b2e26877007c02 icons/undead-32d4.png 18153ca911e16bd6cc7fcc180f41fbfa icons/undead-32d8.png e63673066dd3646869b04ad5bf97b2d3 icons/undead-48d24.png 2d8ac51c853d9fee7d7a7068c0b87b22 icons/undead-48d4.png 0afafc4514659c7f14c3533f42d5d089 icons/undead-48d8.png 1faac292d54d2c7a5f6c0220819eb629 icons/undead-base.png 68b3988b1db3f64ca7475b4a39779d32 icons/undead-ibase.png f95effef511731d6f4686b52db3ef3e1 icons/undead-ibase4.png cfe1d6d2c6eb54e1da92fce966f1d8d8 icons/undead-web.png 1b547cf89d5a29c198b80aa412f1ad5d icons/unequal-16d24.png 53ab366339553da42983eff588e83a0b icons/unequal-16d4.png 70d31d8968cc94048370c78806e9306b icons/unequal-16d8.png 2b2b609cdeae7fcfca2d57f5fa9e51ac icons/unequal-32d24.png 7f891eeff722e23eb958b76d594d18a9 icons/unequal-32d4.png a953e63dd90af7747e7936436b2967c1 icons/unequal-32d8.png 9d72b850069535d59b5816c4f8c27cd0 icons/unequal-48d24.png 5705e535cfd67756ea85baa76b012098 icons/unequal-48d4.png 99a52eff6df1f59254ac520ef9abe7c6 icons/unequal-48d8.png 7bf908faddd1876a7b92db835334d4c8 icons/unequal-base.png 575de5838fe107a87ac0036fd0f93439 icons/unequal-ibase.png 11c4cf4ad94dc06f9e879fc54983da1c icons/unequal-ibase4.png 4383bb01ac706552789612b143042b9c icons/unequal-web.png 32ea3712f6fafcd320cdbbce41e89814 icons/unruly-16d24.png 12dc6e4d80e1e78e0bb0bf5cf09e300c icons/unruly-16d4.png 27e53d37ed5aa1f4e442ad409761e1a3 icons/unruly-16d8.png 5c128ea574401fd328630bb27b13ca42 icons/unruly-32d24.png 331d8bb0fcbaeed7b859a43ea385eed2 icons/unruly-32d4.png 9667cf881c9861bc02444d1e7649847b icons/unruly-32d8.png 38b0542b81d44b76af175e433c14482a icons/unruly-48d24.png 331ce3369fc015681d6734e51298911a icons/unruly-48d4.png 15e6179da783f8b152ccb0deeab45e52 icons/unruly-48d8.png d65872460dd80de43c07570b5a8c0781 icons/unruly-base.png d65872460dd80de43c07570b5a8c0781 icons/unruly-ibase.png 49443d52cf6c260ee8a3eaaab1565d70 icons/unruly-ibase4.png 88cdf2c8e6b09bb333d1d2f8ef8ab2b6 icons/unruly-web.png 0c22f3cc1c85efb75865b0ed719d5f44 icons/untangle-16d24.png 4a5a6ea783602736daef481dec1f8067 icons/untangle-16d4.png be1bfe3778147351374b6c1f400a2b1a icons/untangle-16d8.png 98fbe0273b87642e248c1b172845f3e3 icons/untangle-32d24.png 06ff98f11e164cfccbd334c78b563827 icons/untangle-32d4.png 8bd46bd094f39e48cf0be5d54c0fdb96 icons/untangle-32d8.png c24c14d8f6ae9efd4346038faf211a43 icons/untangle-48d24.png c54f93f58839438290d31fbd9fcce008 icons/untangle-48d4.png b471b9b119fa005b693273c97f28f9eb icons/untangle-48d8.png 7f600d989e90c247f55dba982d544e44 icons/untangle-base.png 7fd76c439523fd9af4d41b764c7d0a5c icons/untangle-ibase.png 127bd3d437b4c7e381a2146e9e2ee1cc icons/untangle-ibase4.png d32864db9a30d18b97c0fca10002e633 icons/untangle-web.png 98e197b9af29a2cd427fb3667db147ea icons/blackbox.ico 16ff8f3378438bca63b5479efc5b5033 icons/bridges.ico 5e4366f400ccd31ff427fcbafed479cc icons/cube.ico a9e8d8b7c68535a2ef2e7a42400325eb icons/dominosa.ico 5fb20300bd78bc91a49f0dd3f3d0b1b3 icons/fifteen.ico 72d33050164f641d4e1e4b1bda4c35ed icons/filling.ico fa3d8fcb29527dc832849c1eb1dbf786 icons/flip.ico 933867492fcacc0df078e8372f60055e icons/galaxies.ico cc4a431d0d7d8089fef64001ae4ff3ed icons/guess.ico cfefcc17cba7f3a814b6640192b08f0a icons/inertia.ico 6bbc54fc48ee424f94f5ced387dd24a9 icons/keen.ico 343390c846720a626c097d676a4dfcff icons/lightup.ico 859bbfcd6e9442d39eaf5c1477233993 icons/loopy.ico 96295065c6f6252e738463243966eeb7 icons/magnets.ico 967fb93fe6319bf48d4cf6a7cce48562 icons/map.ico 3ade1838f226a5a67f1f57166869ded6 icons/mines.ico cd7b3aa60c6a6d7766aa95df94cd1d70 icons/net.ico b15e30eb9d58f4725ca5fd96c32d60f2 icons/netslide.ico 21af1184a569f5df7a0ff4cfd756fd86 icons/pattern.ico be6baff6b0d7b50c4828da6b3e8ed66e icons/pearl.ico 08f240a07c940e6ddad69e6f826841ce icons/pegs.ico d9ee13371dd204e37e7e9d16f3fb5d16 icons/range.ico 99fe6b1107de71e895d8aeae7d129f09 icons/rect.ico 4cf692e5a39c9035c69c7f4365f34cd4 icons/samegame.ico 3ee90c8f7b65c06d9029c5135d20b8b2 icons/signpost.ico 279e1e63b1d6da4eedabc39da987a181 icons/singles.ico e71d514dc63eb99fc03998a25b3f9c37 icons/sixteen.ico 64b1bcd594f29b10e3045cac85c6c677 icons/slant.ico 9e64fb41355c72dd53c41d7c1d92dbf3 icons/solo.ico 46aac064773e1713fd097ad6e7a160f5 icons/tents.ico 53e17196c7f19001fc7c1974b6997b80 icons/towers.ico e4a773be71ab7f69e105daa06df6d1cf icons/twiddle.ico 69f36656b112b683328388147dda7701 icons/undead.ico 14d818f3b3c3b99ec2c1c6e074b8b515 icons/unequal.ico 2fdbed2c4b79f369de39c09acbca42c3 icons/unruly.ico a263d8fa219ed9d7c89327581849fc1d icons/untangle.ico 31ba205df7b6d79a417cd75a7e949a30 icons/blackbox.rc 20edc8f3bc02bfecd2e1409d5dbf86ef icons/bridges.rc a23b70265c2c8482ad30c192e1798061 icons/cube.rc 4710cebb2cd6604d22e9f8898e6e0853 icons/dominosa.rc 18d56ca963e76dc3d304185456b7484b icons/fifteen.rc 30d20e8b68d40ba5c800df4c4014e71a icons/filling.rc 70680e38e7b46295e081c9a0bebec856 icons/flip.rc 9d855b6f10194fd27eb72221915ecc8e icons/galaxies.rc f7d82c8f028c8e57f3f9421bf3b71ecd icons/guess.rc 3bc7ce17539f438152fc3b744d8e765b icons/inertia.rc ccee1f0d1347814578d3f415d6cae07a icons/keen.rc 9da797b460f0652bdb369562021dd8f8 icons/lightup.rc d9a79b4b4d14ebed52457cdce35d3ba5 icons/loopy.rc a09b3331eae1c4b1e0fb97548f9c88a6 icons/magnets.rc 9e1f3e24c30f00ab0a2e435a94afb74d icons/map.rc edf02ba8dabfb738f1895628874e256c icons/mines.rc 34b6ee7056e39f7152151d9ac0ca77c9 icons/net.rc 1cc0c61cfe2cbe59bcbbe6b0169f1d8f icons/netslide.rc 8a58fc2c7c1605560ad56e09995c8c9d icons/pattern.rc 739db01a54d1d866e09c2f8e0fd5f588 icons/pearl.rc 12aa35a829753a6e358c429cc757025d icons/pegs.rc 74d02c1a85dafb1abf1fff6a6b46dbee icons/range.rc 9385ed9b7bacd5d36fa9acd34a18b6cf icons/rect.rc 112a62cf5acf80f1d921b7220cc5ad63 icons/samegame.rc e90816bf060c3f04160e1513d035ca78 icons/signpost.rc 6660d9444bae7653ab836b746ff7bf2d icons/singles.rc 32db98a792cf4021b18739782c111db2 icons/sixteen.rc 5a231cb581644648b1f0349cefceaf95 icons/slant.rc d014e41be66f6959f1f0969ebefe2abf icons/solo.rc 4bf508891af2c393fb38ec7706ec8600 icons/tents.rc 1d9a4e2beab82a6dd1eb4f02d5df631b icons/towers.rc 5126879107cc776218fed7033e3c3e9d icons/twiddle.rc f96ddd613545dc0c826dd4065a642a1d icons/undead.rc 9dc2fee33a6101173e6f0edcfba07703 icons/unequal.rc 534be30c05e53ef5fb6a815af34c15b5 icons/unruly.rc c605a70ec5d09c750d9bbe424c6afa84 icons/untangle.rc 3fb4b6d03179d1123b48b2208927606e icons/blackbox-icon.c d25d89ba1200893b90039a0e65a7e3b8 icons/bridges-icon.c 3c1ffde9360e3570df87cae38b3c86da icons/cube-icon.c a38af59fbb7709ec72d161c82a23d82e icons/dominosa-icon.c 094cd7d79885f7629f0caf370944d24f icons/fifteen-icon.c 72d1b972d42aa68c28b6e2a1227cd9e4 icons/filling-icon.c 14a8bc1bc5c6ba725df72bd4d2881230 icons/flip-icon.c 94b91a80fc5f77a5e0ff86bb4fd96ce0 icons/galaxies-icon.c 9535483bf9be7bed23b1f3b38a51689d icons/guess-icon.c 29fce5cc6506fee2f24934b2c3a97ca6 icons/inertia-icon.c 5370973a1188b2e4f98fe7b0da55c53d icons/keen-icon.c 26625929fbd07e2ae6655f3420c441f4 icons/lightup-icon.c 36cc6a057c4776a661cda09e485acb1b icons/loopy-icon.c 1f2f976fb80aca251b65139b4ffc5a1c icons/magnets-icon.c 61a00e26970a75237f172c1ab801da7c icons/map-icon.c c6294dcbf6a0b82f64a38c480ccd3a1c icons/mines-icon.c 2b987d3c34e64c1858ed73fbe23b49be icons/net-icon.c c7def0725da891b31691e1a006da9dcd icons/netslide-icon.c 5f1941955b7effad7ab9a84251ec1b00 icons/pattern-icon.c 59ac44d9f10ff9e1790ceb2fbc9a039b icons/pearl-icon.c e9b07b7d9c55772b71862f7cf3961f14 icons/pegs-icon.c fbb94b1ed84efc7045396c9010efede0 icons/range-icon.c 832b26fafc2d58f581320de9d49682e4 icons/rect-icon.c 0fc82444647886ad70574512d1944aee icons/samegame-icon.c ed66cb968f3032e80db877cf8f0f1a8f icons/signpost-icon.c 95e10eedb745e04318e201adbefc3c7b icons/singles-icon.c 0848c314d69c26f322d3b62bb99b67c6 icons/sixteen-icon.c 742e1cc09a9e5b63c5ff4bd77ce3a8a7 icons/slant-icon.c d4a1e64138217fa98ecc011ec9cca639 icons/solo-icon.c 545530c2ccf27e3ac2d1f54f9fab58dc icons/tents-icon.c 49dc73c83f76ab66216a56a119ec0c9c icons/towers-icon.c e2e11de270b3bb9d07884afa99082385 icons/twiddle-icon.c f021058083dd3d156b495077d5e06df7 icons/undead-icon.c 3c1e0a2d17749095d212e5ffa62955a9 icons/unequal-icon.c c0ea11539899b68829087abc62f2adb6 icons/unruly-icon.c 5b16412143235fbbe3f5080988ebbe64 icons/untangle-icon.c puzzles-r9872/bridges.c0000644000175300017530000027176212132232554014226 0ustar simonsimon/* * bridges.c: Implementation of the Nikoli game 'Bridges'. * * Things still to do: * * - The solver's algorithmic design is not really ideal. It makes * use of the same data representation as gameplay uses, which * often looks like a tempting reuse of code but isn't always a * good idea. In this case, it's unpleasant that each edge of the * graph ends up represented as multiple squares on a grid, with * flags indicating when edges and non-edges cross; that's useful * when the result can be directly translated into positions of * graphics on the display, but in purely internal work it makes * even simple manipulations during solving more painful than they * should be, and complex ones have no choice but to modify the * data structures temporarily, test things, and put them back. I * envisage a complete solver rewrite along the following lines: * + We have a collection of vertices (islands) and edges * (potential bridge locations, i.e. pairs of horizontal or * vertical islands with no other island in between). * + Each edge has an associated list of edges that cross it, and * hence with which it is mutually exclusive. * + For each edge, we track the min and max number of bridges we * currently think possible. * + For each vertex, we track the number of _liberties_ it has, * i.e. its clue number minus the min bridge count for each edge * out of it. * + We also maintain a dsf that identifies sets of vertices which * are connected components of the puzzle so far, and for each * equivalence class we track the total number of liberties for * that component. (The dsf mechanism will also already track * the size of each component, i.e. number of islands.) * + So incrementing the min for an edge requires processing along * the lines of: * - set the max for all edges crossing that one to zero * - decrement the liberty count for the vertex at each end, * and also for each vertex's equivalence class (NB they may * be the same class) * - unify the two equivalence classes if they're not already, * and if so, set the liberty count for the new class to be * the sum of the previous two. * + Decrementing the max is much easier, however. * + With this data structure the really fiddly stuff in stage3() * becomes more or less trivial, because it's now a quick job to * find out whether an island would form an isolated subgraph if * connected to a given subset of its neighbours: * - identify the connected components containing the test * vertex and its putative new neighbours (but be careful not * to count a component more than once if two or more of the * vertices involved are already in the same one) * - find the sum of those components' liberty counts, and also * the total number of islands involved * - if the total liberty count of the connected components is * exactly equal to twice the number of edges we'd be adding * (of course each edge destroys two liberties, one at each * end) then these components would become a subgraph with * zero liberties if connected together. * - therefore, if that subgraph also contains fewer than the * total number of islands, it's disallowed. * - As mentioned in stage3(), once we've identified such a * disallowed pattern, we have two choices for what to do * with it: if the candidate set of neighbours has size 1 we * can reduce the max for the edge to that one neighbour, * whereas if its complement has size 1 we can increase the * min for the edge to the _omitted_ neighbour. * * - write a recursive solver? */ #include #include #include #include #include #include #include "puzzles.h" /* Turn this on for hints about which lines are considered possibilities. */ #undef DRAW_GRID #undef DRAW_DSF /* --- structures for params, state, etc. --- */ #define MAX_BRIDGES 4 #define PREFERRED_TILE_SIZE 24 #define TILE_SIZE (ds->tilesize) #define BORDER (TILE_SIZE / 2) #define COORD(x) ( (x) * TILE_SIZE + BORDER ) #define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 ) #define FLASH_TIME 0.50F enum { COL_BACKGROUND, COL_FOREGROUND, COL_HIGHLIGHT, COL_LOWLIGHT, COL_SELECTED, COL_MARK, COL_HINT, COL_GRID, COL_WARNING, COL_CURSOR, NCOLOURS }; struct game_params { int w, h, maxb; int islands, expansion; /* %age of island squares, %age chance of expansion */ int allowloops, difficulty; }; /* general flags used by all structs */ #define G_ISLAND 0x0001 #define G_LINEV 0x0002 /* contains a vert. line */ #define G_LINEH 0x0004 /* contains a horiz. line (mutex with LINEV) */ #define G_LINE (G_LINEV|G_LINEH) #define G_MARKV 0x0008 #define G_MARKH 0x0010 #define G_MARK (G_MARKV|G_MARKH) #define G_NOLINEV 0x0020 #define G_NOLINEH 0x0040 #define G_NOLINE (G_NOLINEV|G_NOLINEH) /* flags used by the drawstate */ #define G_ISSEL 0x0080 #define G_REDRAW 0x0100 #define G_FLASH 0x0200 #define G_WARN 0x0400 #define G_CURSOR 0x0800 /* flags used by the solver etc. */ #define G_SWEEP 0x1000 #define G_FLAGSH (G_LINEH|G_MARKH|G_NOLINEH) #define G_FLAGSV (G_LINEV|G_MARKV|G_NOLINEV) typedef unsigned int grid_type; /* change me later if we invent > 16 bits of flags. */ struct solver_state { int *dsf, *comptspaces; int *tmpdsf, *tmpcompspaces; int refcount; }; /* state->gridi is an optimisation; it stores the pointer to the island * structs indexed by (x,y). It's not strictly necessary (we could use * find234 instead), but Purify showed that board generation (mostly the solver) * was spending 60% of its time in find234. */ struct surrounds { /* cloned from lightup.c */ struct { int x, y, dx, dy, off; } points[4]; int npoints, nislands; }; struct island { game_state *state; int x, y, count; struct surrounds adj; }; struct game_state { int w, h, completed, solved, allowloops, maxb; grid_type *grid, *scratch; struct island *islands; int n_islands, n_islands_alloc; game_params params; /* used by the aux solver. */ #define N_WH_ARRAYS 5 char *wha, *possv, *possh, *lines, *maxv, *maxh; struct island **gridi; struct solver_state *solver; /* refcounted */ }; #define GRIDSZ(s) ((s)->w * (s)->h * sizeof(grid_type)) #define INGRID(s,x,y) ((x) >= 0 && (x) < (s)->w && (y) >= 0 && (y) < (s)->h) #define DINDEX(x,y) ((y)*state->w + (x)) #define INDEX(s,g,x,y) ((s)->g[(y)*((s)->w) + (x)]) #define IDX(s,g,i) ((s)->g[(i)]) #define GRID(s,x,y) INDEX(s,grid,x,y) #define SCRATCH(s,x,y) INDEX(s,scratch,x,y) #define POSSIBLES(s,dx,x,y) ((dx) ? (INDEX(s,possh,x,y)) : (INDEX(s,possv,x,y))) #define MAXIMUM(s,dx,x,y) ((dx) ? (INDEX(s,maxh,x,y)) : (INDEX(s,maxv,x,y))) #define GRIDCOUNT(s,x,y,f) ((GRID(s,x,y) & (f)) ? (INDEX(s,lines,x,y)) : 0) #define WITHIN2(x,min,max) (((x) < (min)) ? 0 : (((x) > (max)) ? 0 : 1)) #define WITHIN(x,min,max) ((min) > (max) ? \ WITHIN2(x,max,min) : WITHIN2(x,min,max)) /* --- island struct and tree support functions --- */ #define ISLAND_ORTH(is,j,f,df) \ (is->f + (is->adj.points[(j)].off*is->adj.points[(j)].df)) #define ISLAND_ORTHX(is,j) ISLAND_ORTH(is,j,x,dx) #define ISLAND_ORTHY(is,j) ISLAND_ORTH(is,j,y,dy) static void fixup_islands_for_realloc(game_state *state) { int i; for (i = 0; i < state->w*state->h; i++) state->gridi[i] = NULL; for (i = 0; i < state->n_islands; i++) { struct island *is = &state->islands[i]; is->state = state; INDEX(state, gridi, is->x, is->y) = is; } } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { int x, y, len, nl; char *ret, *p; struct island *is; grid_type grid; len = (state->h) * (state->w+1) + 1; ret = snewn(len, char); p = ret; for (y = 0; y < state->h; y++) { for (x = 0; x < state->w; x++) { grid = GRID(state,x,y); nl = INDEX(state,lines,x,y); is = INDEX(state, gridi, x, y); if (is) { *p++ = '0' + is->count; } else if (grid & G_LINEV) { *p++ = (nl > 1) ? '"' : (nl == 1) ? '|' : '!'; /* gaah, want a double-bar. */ } else if (grid & G_LINEH) { *p++ = (nl > 1) ? '=' : (nl == 1) ? '-' : '~'; } else { *p++ = '.'; } } *p++ = '\n'; } *p++ = '\0'; assert(p - ret == len); return ret; } static void debug_state(game_state *state) { char *textversion = game_text_format(state); debug(("%s", textversion)); sfree(textversion); } /*static void debug_possibles(game_state *state) { int x, y; debug(("possh followed by possv\n")); for (y = 0; y < state->h; y++) { for (x = 0; x < state->w; x++) { debug(("%d", POSSIBLES(state, 1, x, y))); } debug((" ")); for (x = 0; x < state->w; x++) { debug(("%d", POSSIBLES(state, 0, x, y))); } debug(("\n")); } debug(("\n")); for (y = 0; y < state->h; y++) { for (x = 0; x < state->w; x++) { debug(("%d", MAXIMUM(state, 1, x, y))); } debug((" ")); for (x = 0; x < state->w; x++) { debug(("%d", MAXIMUM(state, 0, x, y))); } debug(("\n")); } debug(("\n")); }*/ static void island_set_surrounds(struct island *is) { assert(INGRID(is->state,is->x,is->y)); is->adj.npoints = is->adj.nislands = 0; #define ADDPOINT(cond,ddx,ddy) do {\ if (cond) { \ is->adj.points[is->adj.npoints].x = is->x+(ddx); \ is->adj.points[is->adj.npoints].y = is->y+(ddy); \ is->adj.points[is->adj.npoints].dx = (ddx); \ is->adj.points[is->adj.npoints].dy = (ddy); \ is->adj.points[is->adj.npoints].off = 0; \ is->adj.npoints++; \ } } while(0) ADDPOINT(is->x > 0, -1, 0); ADDPOINT(is->x < (is->state->w-1), +1, 0); ADDPOINT(is->y > 0, 0, -1); ADDPOINT(is->y < (is->state->h-1), 0, +1); } static void island_find_orthogonal(struct island *is) { /* fills in the rest of the 'surrounds' structure, assuming * all other islands are now in place. */ int i, x, y, dx, dy, off; is->adj.nislands = 0; for (i = 0; i < is->adj.npoints; i++) { dx = is->adj.points[i].dx; dy = is->adj.points[i].dy; x = is->x + dx; y = is->y + dy; off = 1; is->adj.points[i].off = 0; while (INGRID(is->state, x, y)) { if (GRID(is->state, x, y) & G_ISLAND) { is->adj.points[i].off = off; is->adj.nislands++; /*debug(("island (%d,%d) has orth is. %d*(%d,%d) away at (%d,%d).\n", is->x, is->y, off, dx, dy, ISLAND_ORTHX(is,i), ISLAND_ORTHY(is,i)));*/ goto foundisland; } off++; x += dx; y += dy; } foundisland: ; } } static int island_hasbridge(struct island *is, int direction) { int x = is->adj.points[direction].x; int y = is->adj.points[direction].y; grid_type gline = is->adj.points[direction].dx ? G_LINEH : G_LINEV; if (GRID(is->state, x, y) & gline) return 1; return 0; } static struct island *island_find_connection(struct island *is, int adjpt) { struct island *is_r; assert(adjpt < is->adj.npoints); if (!is->adj.points[adjpt].off) return NULL; if (!island_hasbridge(is, adjpt)) return NULL; is_r = INDEX(is->state, gridi, ISLAND_ORTHX(is, adjpt), ISLAND_ORTHY(is, adjpt)); assert(is_r); return is_r; } static struct island *island_add(game_state *state, int x, int y, int count) { struct island *is; int realloced = 0; assert(!(GRID(state,x,y) & G_ISLAND)); GRID(state,x,y) |= G_ISLAND; state->n_islands++; if (state->n_islands > state->n_islands_alloc) { state->n_islands_alloc = state->n_islands * 2; state->islands = sresize(state->islands, state->n_islands_alloc, struct island); realloced = 1; } is = &state->islands[state->n_islands-1]; memset(is, 0, sizeof(struct island)); is->state = state; is->x = x; is->y = y; is->count = count; island_set_surrounds(is); if (realloced) fixup_islands_for_realloc(state); else INDEX(state, gridi, x, y) = is; return is; } /* n = -1 means 'flip NOLINE flags [and set line to 0].' */ static void island_join(struct island *i1, struct island *i2, int n, int is_max) { game_state *state = i1->state; int s, e, x, y; assert(i1->state == i2->state); assert(n >= -1 && n <= i1->state->maxb); if (i1->x == i2->x) { x = i1->x; if (i1->y < i2->y) { s = i1->y+1; e = i2->y-1; } else { s = i2->y+1; e = i1->y-1; } for (y = s; y <= e; y++) { if (is_max) { INDEX(state,maxv,x,y) = n; } else { if (n < 0) { GRID(state,x,y) ^= G_NOLINEV; } else if (n == 0) { GRID(state,x,y) &= ~G_LINEV; } else { GRID(state,x,y) |= G_LINEV; INDEX(state,lines,x,y) = n; } } } } else if (i1->y == i2->y) { y = i1->y; if (i1->x < i2->x) { s = i1->x+1; e = i2->x-1; } else { s = i2->x+1; e = i1->x-1; } for (x = s; x <= e; x++) { if (is_max) { INDEX(state,maxh,x,y) = n; } else { if (n < 0) { GRID(state,x,y) ^= G_NOLINEH; } else if (n == 0) { GRID(state,x,y) &= ~G_LINEH; } else { GRID(state,x,y) |= G_LINEH; INDEX(state,lines,x,y) = n; } } } } else { assert(!"island_join: islands not orthogonal."); } } /* Counts the number of bridges currently attached to the island. */ static int island_countbridges(struct island *is) { int i, c = 0; for (i = 0; i < is->adj.npoints; i++) { c += GRIDCOUNT(is->state, is->adj.points[i].x, is->adj.points[i].y, is->adj.points[i].dx ? G_LINEH : G_LINEV); } /*debug(("island count for (%d,%d) is %d.\n", is->x, is->y, c));*/ return c; } static int island_adjspace(struct island *is, int marks, int missing, int direction) { int x, y, poss, curr, dx; grid_type gline, mline; x = is->adj.points[direction].x; y = is->adj.points[direction].y; dx = is->adj.points[direction].dx; gline = dx ? G_LINEH : G_LINEV; if (marks) { mline = dx ? G_MARKH : G_MARKV; if (GRID(is->state,x,y) & mline) return 0; } poss = POSSIBLES(is->state, dx, x, y); poss = min(poss, missing); curr = GRIDCOUNT(is->state, x, y, gline); poss = min(poss, MAXIMUM(is->state, dx, x, y) - curr); return poss; } /* Counts the number of bridge spaces left around the island; * expects the possibles to be up-to-date. */ static int island_countspaces(struct island *is, int marks) { int i, c = 0, missing; missing = is->count - island_countbridges(is); if (missing < 0) return 0; for (i = 0; i < is->adj.npoints; i++) { c += island_adjspace(is, marks, missing, i); } return c; } static int island_isadj(struct island *is, int direction) { int x, y; grid_type gline, mline; x = is->adj.points[direction].x; y = is->adj.points[direction].y; mline = is->adj.points[direction].dx ? G_MARKH : G_MARKV; gline = is->adj.points[direction].dx ? G_LINEH : G_LINEV; if (GRID(is->state, x, y) & mline) { /* If we're marked (i.e. the thing to attach to is complete) * only count an adjacency if we're already attached. */ return GRIDCOUNT(is->state, x, y, gline); } else { /* If we're unmarked, count possible adjacency iff it's * flagged as POSSIBLE. */ return POSSIBLES(is->state, is->adj.points[direction].dx, x, y); } return 0; } /* Counts the no. of possible adjacent islands (including islands * we're already connected to). */ static int island_countadj(struct island *is) { int i, nadj = 0; for (i = 0; i < is->adj.npoints; i++) { if (island_isadj(is, i)) nadj++; } return nadj; } static void island_togglemark(struct island *is) { int i, j, x, y, o; struct island *is_loop; /* mark the island... */ GRID(is->state, is->x, is->y) ^= G_MARK; /* ...remove all marks on non-island squares... */ for (x = 0; x < is->state->w; x++) { for (y = 0; y < is->state->h; y++) { if (!(GRID(is->state, x, y) & G_ISLAND)) GRID(is->state, x, y) &= ~G_MARK; } } /* ...and add marks to squares around marked islands. */ for (i = 0; i < is->state->n_islands; i++) { is_loop = &is->state->islands[i]; if (!(GRID(is_loop->state, is_loop->x, is_loop->y) & G_MARK)) continue; for (j = 0; j < is_loop->adj.npoints; j++) { /* if this direction takes us to another island, mark all * squares between the two islands. */ if (!is_loop->adj.points[j].off) continue; assert(is_loop->adj.points[j].off > 1); for (o = 1; o < is_loop->adj.points[j].off; o++) { GRID(is_loop->state, is_loop->x + is_loop->adj.points[j].dx*o, is_loop->y + is_loop->adj.points[j].dy*o) |= is_loop->adj.points[j].dy ? G_MARKV : G_MARKH; } } } } static int island_impossible(struct island *is, int strict) { int curr = island_countbridges(is), nspc = is->count - curr, nsurrspc; int i, poss; struct island *is_orth; if (nspc < 0) { debug(("island at (%d,%d) impossible because full.\n", is->x, is->y)); return 1; /* too many bridges */ } else if ((curr + island_countspaces(is, 0)) < is->count) { debug(("island at (%d,%d) impossible because not enough spaces.\n", is->x, is->y)); return 1; /* impossible to create enough bridges */ } else if (strict && curr < is->count) { debug(("island at (%d,%d) impossible because locked.\n", is->x, is->y)); return 1; /* not enough bridges and island is locked */ } /* Count spaces in surrounding islands. */ nsurrspc = 0; for (i = 0; i < is->adj.npoints; i++) { int ifree, dx = is->adj.points[i].dx; if (!is->adj.points[i].off) continue; poss = POSSIBLES(is->state, dx, is->adj.points[i].x, is->adj.points[i].y); if (poss == 0) continue; is_orth = INDEX(is->state, gridi, ISLAND_ORTHX(is,i), ISLAND_ORTHY(is,i)); assert(is_orth); ifree = is_orth->count - island_countbridges(is_orth); if (ifree > 0) { /* * ifree is the number of bridges unfilled in the other * island, which is clearly an upper bound on the number * of extra bridges this island may run to it. * * Another upper bound is the number of bridges unfilled * on the specific line between here and there. We must * take the minimum of both. */ int bmax = MAXIMUM(is->state, dx, is->adj.points[i].x, is->adj.points[i].y); int bcurr = GRIDCOUNT(is->state, is->adj.points[i].x, is->adj.points[i].y, dx ? G_LINEH : G_LINEV); assert(bcurr <= bmax); nsurrspc += min(ifree, bmax - bcurr); } } if (nsurrspc < nspc) { debug(("island at (%d,%d) impossible: surr. islands %d spc, need %d.\n", is->x, is->y, nsurrspc, nspc)); return 1; /* not enough spaces around surrounding islands to fill this one. */ } return 0; } /* --- Game parameter functions --- */ #define DEFAULT_PRESET 0 const struct game_params bridges_presets[] = { { 7, 7, 2, 30, 10, 1, 0 }, { 7, 7, 2, 30, 10, 1, 1 }, { 7, 7, 2, 30, 10, 1, 2 }, { 10, 10, 2, 30, 10, 1, 0 }, { 10, 10, 2, 30, 10, 1, 1 }, { 10, 10, 2, 30, 10, 1, 2 }, { 15, 15, 2, 30, 10, 1, 0 }, { 15, 15, 2, 30, 10, 1, 1 }, { 15, 15, 2, 30, 10, 1, 2 }, }; static game_params *default_params(void) { game_params *ret = snew(game_params); *ret = bridges_presets[DEFAULT_PRESET]; return ret; } static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char buf[80]; if (i < 0 || i >= lenof(bridges_presets)) return FALSE; ret = default_params(); *ret = bridges_presets[i]; *params = ret; sprintf(buf, "%dx%d %s", ret->w, ret->h, ret->difficulty == 0 ? "easy" : ret->difficulty == 1 ? "medium" : "hard"); *name = dupstr(buf); 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; } #define EATNUM(x) do { \ (x) = atoi(string); \ while (*string && isdigit((unsigned char)*string)) string++; \ } while(0) static void decode_params(game_params *params, char const *string) { EATNUM(params->w); params->h = params->w; if (*string == 'x') { string++; EATNUM(params->h); } if (*string == 'i') { string++; EATNUM(params->islands); } if (*string == 'e') { string++; EATNUM(params->expansion); } if (*string == 'm') { string++; EATNUM(params->maxb); } params->allowloops = 1; if (*string == 'L') { string++; params->allowloops = 0; } if (*string == 'd') { string++; EATNUM(params->difficulty); } } static char *encode_params(const game_params *params, int full) { char buf[80]; if (full) { sprintf(buf, "%dx%di%de%dm%d%sd%d", params->w, params->h, params->islands, params->expansion, params->maxb, params->allowloops ? "" : "L", params->difficulty); } else { sprintf(buf, "%dx%dm%d%s", params->w, params->h, params->maxb, params->allowloops ? "" : "L"); } return dupstr(buf); } static config_item *game_configure(const game_params *params) { config_item *ret; char buf[80]; ret = snewn(8, config_item); ret[0].name = "Width"; ret[0].type = C_STRING; sprintf(buf, "%d", params->w); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Difficulty"; ret[2].type = C_CHOICES; ret[2].sval = ":Easy:Medium:Hard"; ret[2].ival = params->difficulty; ret[3].name = "Allow loops"; ret[3].type = C_BOOLEAN; ret[3].sval = NULL; ret[3].ival = params->allowloops; ret[4].name = "Max. bridges per direction"; ret[4].type = C_CHOICES; ret[4].sval = ":1:2:3:4"; /* keep up-to-date with MAX_BRIDGES */ ret[4].ival = params->maxb - 1; ret[5].name = "%age of island squares"; ret[5].type = C_CHOICES; ret[5].sval = ":5%:10%:15%:20%:25%:30%"; ret[5].ival = (params->islands / 5)-1; ret[6].name = "Expansion factor (%age)"; ret[6].type = C_CHOICES; ret[6].sval = ":0%:10%:20%:30%:40%:50%:60%:70%:80%:90%:100%"; ret[6].ival = params->expansion / 10; ret[7].name = NULL; ret[7].type = C_END; ret[7].sval = NULL; ret[7].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); ret->difficulty = cfg[2].ival; ret->allowloops = cfg[3].ival; ret->maxb = cfg[4].ival + 1; ret->islands = (cfg[5].ival + 1) * 5; ret->expansion = cfg[6].ival * 10; return ret; } static char *validate_params(const game_params *params, int full) { if (params->w < 3 || params->h < 3) return "Width and height must be at least 3"; if (params->maxb < 1 || params->maxb > MAX_BRIDGES) return "Too many bridges."; if (full) { if (params->islands <= 0 || params->islands > 30) return "%age of island squares must be between 1% and 30%"; if (params->expansion < 0 || params->expansion > 100) return "Expansion factor must be between 0 and 100"; } return NULL; } /* --- Game encoding and differences --- */ static char *encode_game(game_state *state) { char *ret, *p; int wh = state->w*state->h, run, x, y; struct island *is; ret = snewn(wh + 1, char); p = ret; run = 0; for (y = 0; y < state->h; y++) { for (x = 0; x < state->w; x++) { is = INDEX(state, gridi, x, y); if (is) { if (run) { *p++ = ('a'-1) + run; run = 0; } if (is->count < 10) *p++ = '0' + is->count; else *p++ = 'A' + (is->count - 10); } else { if (run == 26) { *p++ = ('a'-1) + run; run = 0; } run++; } } } if (run) { *p++ = ('a'-1) + run; run = 0; } *p = '\0'; assert(p - ret <= wh); return ret; } static char *game_state_diff(const game_state *src, const game_state *dest) { int movesize = 256, movelen = 0; char *move = snewn(movesize, char), buf[80]; int i, d, x, y, len; grid_type gline, nline; struct island *is_s, *is_d, *is_orth; #define APPEND do { \ if (movelen + len >= movesize) { \ movesize = movelen + len + 256; \ move = sresize(move, movesize, char); \ } \ strcpy(move + movelen, buf); \ movelen += len; \ } while(0) move[movelen++] = 'S'; move[movelen] = '\0'; assert(src->n_islands == dest->n_islands); for (i = 0; i < src->n_islands; i++) { is_s = &src->islands[i]; is_d = &dest->islands[i]; assert(is_s->x == is_d->x); assert(is_s->y == is_d->y); assert(is_s->adj.npoints == is_d->adj.npoints); /* more paranoia */ for (d = 0; d < is_s->adj.npoints; d++) { if (is_s->adj.points[d].dx == -1 || is_s->adj.points[d].dy == -1) continue; x = is_s->adj.points[d].x; y = is_s->adj.points[d].y; gline = is_s->adj.points[d].dx ? G_LINEH : G_LINEV; nline = is_s->adj.points[d].dx ? G_NOLINEH : G_NOLINEV; is_orth = INDEX(dest, gridi, ISLAND_ORTHX(is_d, d), ISLAND_ORTHY(is_d, d)); if (GRIDCOUNT(src, x, y, gline) != GRIDCOUNT(dest, x, y, gline)) { assert(is_orth); len = sprintf(buf, ";L%d,%d,%d,%d,%d", is_s->x, is_s->y, is_orth->x, is_orth->y, GRIDCOUNT(dest, x, y, gline)); APPEND; } if ((GRID(src,x,y) & nline) != (GRID(dest, x, y) & nline)) { assert(is_orth); len = sprintf(buf, ";N%d,%d,%d,%d", is_s->x, is_s->y, is_orth->x, is_orth->y); APPEND; } } if ((GRID(src, is_s->x, is_s->y) & G_MARK) != (GRID(dest, is_d->x, is_d->y) & G_MARK)) { len = sprintf(buf, ";M%d,%d", is_s->x, is_s->y); APPEND; } } return move; } /* --- Game setup and solving utilities --- */ /* This function is optimised; a Quantify showed that lots of grid-generation time * (>50%) was spent in here. Hence the IDX() stuff. */ static void map_update_possibles(game_state *state) { int x, y, s, e, bl, i, np, maxb, w = state->w, idx; struct island *is_s = NULL, *is_f = NULL; /* Run down vertical stripes [un]setting possv... */ for (x = 0; x < state->w; x++) { idx = x; s = e = -1; bl = 0; maxb = state->params.maxb; /* placate optimiser */ /* Unset possible flags until we find an island. */ for (y = 0; y < state->h; y++) { is_s = IDX(state, gridi, idx); if (is_s) { maxb = is_s->count; break; } IDX(state, possv, idx) = 0; idx += w; } for (; y < state->h; y++) { maxb = min(maxb, IDX(state, maxv, idx)); is_f = IDX(state, gridi, idx); if (is_f) { assert(is_s); np = min(maxb, is_f->count); if (s != -1) { for (i = s; i <= e; i++) { INDEX(state, possv, x, i) = bl ? 0 : np; } } s = y+1; bl = 0; is_s = is_f; maxb = is_s->count; } else { e = y; if (IDX(state,grid,idx) & (G_LINEH|G_NOLINEV)) bl = 1; } idx += w; } if (s != -1) { for (i = s; i <= e; i++) INDEX(state, possv, x, i) = 0; } } /* ...and now do horizontal stripes [un]setting possh. */ /* can we lose this clone'n'hack? */ for (y = 0; y < state->h; y++) { idx = y*w; s = e = -1; bl = 0; maxb = state->params.maxb; /* placate optimiser */ for (x = 0; x < state->w; x++) { is_s = IDX(state, gridi, idx); if (is_s) { maxb = is_s->count; break; } IDX(state, possh, idx) = 0; idx += 1; } for (; x < state->w; x++) { maxb = min(maxb, IDX(state, maxh, idx)); is_f = IDX(state, gridi, idx); if (is_f) { assert(is_s); np = min(maxb, is_f->count); if (s != -1) { for (i = s; i <= e; i++) { INDEX(state, possh, i, y) = bl ? 0 : np; } } s = x+1; bl = 0; is_s = is_f; maxb = is_s->count; } else { e = x; if (IDX(state,grid,idx) & (G_LINEV|G_NOLINEH)) bl = 1; } idx += 1; } if (s != -1) { for (i = s; i <= e; i++) INDEX(state, possh, i, y) = 0; } } } static void map_count(game_state *state) { int i, n, ax, ay; grid_type flag, grid; struct island *is; for (i = 0; i < state->n_islands; i++) { is = &state->islands[i]; is->count = 0; for (n = 0; n < is->adj.npoints; n++) { ax = is->adj.points[n].x; ay = is->adj.points[n].y; flag = (ax == is->x) ? G_LINEV : G_LINEH; grid = GRID(state,ax,ay); if (grid & flag) { is->count += INDEX(state,lines,ax,ay); } } } } static void map_find_orthogonal(game_state *state) { int i; for (i = 0; i < state->n_islands; i++) { island_find_orthogonal(&state->islands[i]); } } static int grid_degree(game_state *state, int x, int y, int *nx_r, int *ny_r) { grid_type grid = SCRATCH(state, x, y), gline = grid & G_LINE; struct island *is; int x1, y1, x2, y2, c = 0, i, nx, ny; nx = ny = -1; /* placate optimiser */ is = INDEX(state, gridi, x, y); if (is) { for (i = 0; i < is->adj.npoints; i++) { gline = is->adj.points[i].dx ? G_LINEH : G_LINEV; if (SCRATCH(state, is->adj.points[i].x, is->adj.points[i].y) & gline) { nx = is->adj.points[i].x; ny = is->adj.points[i].y; c++; } } } else if (gline) { if (gline & G_LINEV) { x1 = x2 = x; y1 = y-1; y2 = y+1; } else { x1 = x-1; x2 = x+1; y1 = y2 = y; } /* Non-island squares with edges in should never be pointing off the * edge of the grid. */ assert(INGRID(state, x1, y1)); assert(INGRID(state, x2, y2)); if (SCRATCH(state, x1, y1) & (gline | G_ISLAND)) { nx = x1; ny = y1; c++; } if (SCRATCH(state, x2, y2) & (gline | G_ISLAND)) { nx = x2; ny = y2; c++; } } if (c == 1) { assert(nx != -1 && ny != -1); /* paranoia */ *nx_r = nx; *ny_r = ny; } return c; } static int map_hasloops(game_state *state, int mark) { int x, y, ox, oy, nx = 0, ny = 0, loop = 0; memcpy(state->scratch, state->grid, GRIDSZ(state)); /* This algorithm is actually broken; if there are two loops connected * by bridges this will also highlight bridges. The correct algorithm * uses a dsf and a two-pass edge-detection algorithm (see check_correct * in slant.c); this is BALGE for now, especially since disallow-loops * is not the default for this puzzle. If we want to fix this later then * copy the alg in slant.c to the empty statement in map_group. */ /* Remove all 1-degree edges. */ for (y = 0; y < state->h; y++) { for (x = 0; x < state->w; x++) { ox = x; oy = y; while (grid_degree(state, ox, oy, &nx, &ny) == 1) { /*debug(("hasloops: removing 1-degree at (%d,%d).\n", ox, oy));*/ SCRATCH(state, ox, oy) &= ~(G_LINE|G_ISLAND); ox = nx; oy = ny; } } } /* Mark any remaining edges as G_WARN, if required. */ for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { if (GRID(state,x,y) & G_ISLAND) continue; if (SCRATCH(state, x, y) & G_LINE) { if (mark) { /*debug(("hasloops: marking loop square at (%d,%d).\n", x, y));*/ GRID(state,x,y) |= G_WARN; loop = 1; } else return 1; /* short-cut as soon as we find one */ } else { if (mark) GRID(state,x,y) &= ~G_WARN; } } } return loop; } static void map_group(game_state *state) { int i, wh = state->w*state->h, d1, d2; int x, y, x2, y2; int *dsf = state->solver->dsf; struct island *is, *is_join; /* Initialise dsf. */ dsf_init(dsf, wh); /* For each island, find connected islands right or down * and merge the dsf for the island squares as well as the * bridge squares. */ for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { GRID(state,x,y) &= ~(G_SWEEP|G_WARN); /* for group_full. */ is = INDEX(state, gridi, x, y); if (!is) continue; d1 = DINDEX(x,y); for (i = 0; i < is->adj.npoints; i++) { /* only want right/down */ if (is->adj.points[i].dx == -1 || is->adj.points[i].dy == -1) continue; is_join = island_find_connection(is, i); if (!is_join) continue; d2 = DINDEX(is_join->x, is_join->y); if (dsf_canonify(dsf,d1) == dsf_canonify(dsf,d2)) { ; /* we have a loop. See comment in map_hasloops. */ /* However, we still want to merge all squares joining * this side-that-makes-a-loop. */ } /* merge all squares between island 1 and island 2. */ for (x2 = x; x2 <= is_join->x; x2++) { for (y2 = y; y2 <= is_join->y; y2++) { d2 = DINDEX(x2,y2); if (d1 != d2) dsf_merge(dsf,d1,d2); } } } } } } static int map_group_check(game_state *state, int canon, int warn, int *nislands_r) { int *dsf = state->solver->dsf, nislands = 0; int x, y, i, allfull = 1; struct island *is; for (i = 0; i < state->n_islands; i++) { is = &state->islands[i]; if (dsf_canonify(dsf, DINDEX(is->x,is->y)) != canon) continue; GRID(state, is->x, is->y) |= G_SWEEP; nislands++; if (island_countbridges(is) != is->count) allfull = 0; } if (warn && allfull && nislands != state->n_islands) { /* we're full and this island group isn't the whole set. * Mark all squares with this dsf canon as ERR. */ for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { if (dsf_canonify(dsf, DINDEX(x,y)) == canon) { GRID(state,x,y) |= G_WARN; } } } } if (nislands_r) *nislands_r = nislands; return allfull; } static int map_group_full(game_state *state, int *ngroups_r) { int *dsf = state->solver->dsf, ngroups = 0; int i, anyfull = 0; struct island *is; /* NB this assumes map_group (or sth else) has cleared G_SWEEP. */ for (i = 0; i < state->n_islands; i++) { is = &state->islands[i]; if (GRID(state,is->x,is->y) & G_SWEEP) continue; ngroups++; if (map_group_check(state, dsf_canonify(dsf, DINDEX(is->x,is->y)), 1, NULL)) anyfull = 1; } *ngroups_r = ngroups; return anyfull; } static int map_check(game_state *state) { int ngroups; /* Check for loops, if necessary. */ if (!state->allowloops) { if (map_hasloops(state, 1)) return 0; } /* Place islands into island groups and check for early * satisfied-groups. */ map_group(state); /* clears WARN and SWEEP */ if (map_group_full(state, &ngroups)) { if (ngroups == 1) return 1; } return 0; } static void map_clear(game_state *state) { int x, y; for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { /* clear most flags; might want to be slightly more careful here. */ GRID(state,x,y) &= G_ISLAND; } } } static void solve_join(struct island *is, int direction, int n, int is_max) { struct island *is_orth; int d1, d2, *dsf = is->state->solver->dsf; game_state *state = is->state; /* for DINDEX */ is_orth = INDEX(is->state, gridi, ISLAND_ORTHX(is, direction), ISLAND_ORTHY(is, direction)); assert(is_orth); /*debug(("...joining (%d,%d) to (%d,%d) with %d bridge(s).\n", is->x, is->y, is_orth->x, is_orth->y, n));*/ island_join(is, is_orth, n, is_max); if (n > 0 && !is_max) { d1 = DINDEX(is->x, is->y); d2 = DINDEX(is_orth->x, is_orth->y); if (dsf_canonify(dsf, d1) != dsf_canonify(dsf, d2)) dsf_merge(dsf, d1, d2); } } static int solve_fillone(struct island *is) { int i, nadded = 0; debug(("solve_fillone for island (%d,%d).\n", is->x, is->y)); for (i = 0; i < is->adj.npoints; i++) { if (island_isadj(is, i)) { if (island_hasbridge(is, i)) { /* already attached; do nothing. */; } else { solve_join(is, i, 1, 0); nadded++; } } } return nadded; } static int solve_fill(struct island *is) { /* for each unmarked adjacent, make sure we convert every possible bridge * to a real one, and then work out the possibles afresh. */ int i, nnew, ncurr, nadded = 0, missing; debug(("solve_fill for island (%d,%d).\n", is->x, is->y)); missing = is->count - island_countbridges(is); if (missing < 0) return 0; /* very like island_countspaces. */ for (i = 0; i < is->adj.npoints; i++) { nnew = island_adjspace(is, 1, missing, i); if (nnew) { ncurr = GRIDCOUNT(is->state, is->adj.points[i].x, is->adj.points[i].y, is->adj.points[i].dx ? G_LINEH : G_LINEV); solve_join(is, i, nnew + ncurr, 0); nadded += nnew; } } return nadded; } static int solve_island_stage1(struct island *is, int *didsth_r) { int bridges = island_countbridges(is); int nspaces = island_countspaces(is, 1); int nadj = island_countadj(is); int didsth = 0; assert(didsth_r); /*debug(("island at (%d,%d) filled %d/%d (%d spc) nadj %d\n", is->x, is->y, bridges, is->count, nspaces, nadj));*/ if (bridges > is->count) { /* We only ever add bridges when we're sure they fit, or that's * the only place they can go. If we've added bridges such that * another island has become wrong, the puzzle must not have had * a solution. */ debug(("...island at (%d,%d) is overpopulated!\n", is->x, is->y)); return 0; } else if (bridges == is->count) { /* This island is full. Make sure it's marked (and update * possibles if we did). */ if (!(GRID(is->state, is->x, is->y) & G_MARK)) { debug(("...marking island (%d,%d) as full.\n", is->x, is->y)); island_togglemark(is); didsth = 1; } } else if (GRID(is->state, is->x, is->y) & G_MARK) { debug(("...island (%d,%d) is marked but unfinished!\n", is->x, is->y)); return 0; /* island has been marked unfinished; no solution from here. */ } else { /* This is the interesting bit; we try and fill in more information * about this island. */ if (is->count == bridges + nspaces) { if (solve_fill(is) > 0) didsth = 1; } else if (is->count > ((nadj-1) * is->state->maxb)) { /* must have at least one bridge in each possible direction. */ if (solve_fillone(is) > 0) didsth = 1; } } if (didsth) { map_update_possibles(is->state); *didsth_r = 1; } return 1; } /* returns non-zero if a new line here would cause a loop. */ static int solve_island_checkloop(struct island *is, int direction) { struct island *is_orth; int *dsf = is->state->solver->dsf, d1, d2; game_state *state = is->state; if (is->state->allowloops) return 0; /* don't care anyway */ if (island_hasbridge(is, direction)) return 0; /* already has a bridge */ if (island_isadj(is, direction) == 0) return 0; /* no adj island */ is_orth = INDEX(is->state, gridi, ISLAND_ORTHX(is,direction), ISLAND_ORTHY(is,direction)); if (!is_orth) return 0; d1 = DINDEX(is->x, is->y); d2 = DINDEX(is_orth->x, is_orth->y); if (dsf_canonify(dsf, d1) == dsf_canonify(dsf, d2)) { /* two islands are connected already; don't join them. */ return 1; } return 0; } static int solve_island_stage2(struct island *is, int *didsth_r) { int added = 0, removed = 0, navail = 0, nadj, i; assert(didsth_r); for (i = 0; i < is->adj.npoints; i++) { if (solve_island_checkloop(is, i)) { debug(("removing possible loop at (%d,%d) direction %d.\n", is->x, is->y, i)); solve_join(is, i, -1, 0); map_update_possibles(is->state); removed = 1; } else { navail += island_isadj(is, i); /*debug(("stage2: navail for (%d,%d) direction (%d,%d) is %d.\n", is->x, is->y, is->adj.points[i].dx, is->adj.points[i].dy, island_isadj(is, i)));*/ } } /*debug(("island at (%d,%d) navail %d: checking...\n", is->x, is->y, navail));*/ for (i = 0; i < is->adj.npoints; i++) { if (!island_hasbridge(is, i)) { nadj = island_isadj(is, i); if (nadj > 0 && (navail - nadj) < is->count) { /* we couldn't now complete the island without at * least one bridge here; put it in. */ /*debug(("nadj %d, navail %d, is->count %d.\n", nadj, navail, is->count));*/ debug(("island at (%d,%d) direction (%d,%d) must have 1 bridge\n", is->x, is->y, is->adj.points[i].dx, is->adj.points[i].dy)); solve_join(is, i, 1, 0); added = 1; /*debug_state(is->state); debug_possibles(is->state);*/ } } } if (added) map_update_possibles(is->state); if (added || removed) *didsth_r = 1; return 1; } static int solve_island_subgroup(struct island *is, int direction) { struct island *is_join; int nislands, *dsf = is->state->solver->dsf; game_state *state = is->state; debug(("..checking subgroups.\n")); /* if is isn't full, return 0. */ if (island_countbridges(is) < is->count) { debug(("...orig island (%d,%d) not full.\n", is->x, is->y)); return 0; } if (direction >= 0) { is_join = INDEX(state, gridi, ISLAND_ORTHX(is, direction), ISLAND_ORTHY(is, direction)); assert(is_join); /* if is_join isn't full, return 0. */ if (island_countbridges(is_join) < is_join->count) { debug(("...dest island (%d,%d) not full.\n", is_join->x, is_join->y)); return 0; } } /* Check group membership for is->dsf; if it's full return 1. */ if (map_group_check(state, dsf_canonify(dsf, DINDEX(is->x,is->y)), 0, &nislands)) { if (nislands < state->n_islands) { /* we have a full subgroup that isn't the whole set. * This isn't allowed. */ debug(("island at (%d,%d) makes full subgroup, disallowing.\n", is->x, is->y)); return 1; } else { debug(("...has finished puzzle.\n")); } } return 0; } static int solve_island_impossible(game_state *state) { struct island *is; int i; /* If any islands are impossible, return 1. */ for (i = 0; i < state->n_islands; i++) { is = &state->islands[i]; if (island_impossible(is, 0)) { debug(("island at (%d,%d) has become impossible, disallowing.\n", is->x, is->y)); return 1; } } return 0; } /* Bear in mind that this function is really rather inefficient. */ static int solve_island_stage3(struct island *is, int *didsth_r) { int i, n, x, y, missing, spc, curr, maxb, didsth = 0; int wh = is->state->w * is->state->h; struct solver_state *ss = is->state->solver; assert(didsth_r); missing = is->count - island_countbridges(is); if (missing <= 0) return 1; for (i = 0; i < is->adj.npoints; i++) { x = is->adj.points[i].x; y = is->adj.points[i].y; spc = island_adjspace(is, 1, missing, i); if (spc == 0) continue; curr = GRIDCOUNT(is->state, x, y, is->adj.points[i].dx ? G_LINEH : G_LINEV); debug(("island at (%d,%d) s3, trying %d - %d bridges.\n", is->x, is->y, curr+1, curr+spc)); /* Now we know that this island could have more bridges, * to bring the total from curr+1 to curr+spc. */ maxb = -1; /* We have to squirrel the dsf away and restore it afterwards; * it is additive only, and can't be removed from. */ memcpy(ss->tmpdsf, ss->dsf, wh*sizeof(int)); for (n = curr+1; n <= curr+spc; n++) { solve_join(is, i, n, 0); map_update_possibles(is->state); if (solve_island_subgroup(is, i) || solve_island_impossible(is->state)) { maxb = n-1; debug(("island at (%d,%d) d(%d,%d) new max of %d bridges:\n", is->x, is->y, is->adj.points[i].dx, is->adj.points[i].dy, maxb)); break; } } solve_join(is, i, curr, 0); /* put back to before. */ memcpy(ss->dsf, ss->tmpdsf, wh*sizeof(int)); if (maxb != -1) { /*debug_state(is->state);*/ if (maxb == 0) { debug(("...adding NOLINE.\n")); solve_join(is, i, -1, 0); /* we can't have any bridges here. */ } else { debug(("...setting maximum\n")); solve_join(is, i, maxb, 1); } didsth = 1; } map_update_possibles(is->state); } for (i = 0; i < is->adj.npoints; i++) { /* * Now check to see if any currently empty direction must have * at least one bridge in order to avoid forming an isolated * subgraph. This differs from the check above in that it * considers multiple target islands. For example: * * 2 2 4 * 1 3 2 * 3 * 4 * * The example on the left can be handled by the above loop: * it will observe that connecting the central 2 twice to the * left would form an isolated subgraph, and hence it will * restrict that 2 to at most one bridge in that direction. * But the example on the right won't be handled by that loop, * because the deduction requires us to imagine connecting the * 3 to _both_ the 1 and 2 at once to form an isolated * subgraph. * * This pass is necessary _as well_ as the above one, because * neither can do the other's job. In the left one, * restricting the direction which _would_ cause trouble can * be done even if it's not yet clear which of the remaining * directions has to have a compensatory bridge; whereas the * pass below that can handle the right-hand example does need * to know what direction to point the necessary bridge in. * * Neither pass can handle the most general case, in which we * observe that an arbitrary subset of an island's neighbours * would form an isolated subgraph with it if it connected * maximally to them, and hence that at least one bridge must * point to some neighbour outside that subset but we don't * know which neighbour. To handle that, we'd have to have a * richer data format for the solver, which could cope with * recording the idea that at least one of two edges must have * a bridge. */ int got = 0; int before[4]; int j; spc = island_adjspace(is, 1, missing, i); if (spc == 0) continue; for (j = 0; j < is->adj.npoints; j++) before[j] = GRIDCOUNT(is->state, is->adj.points[j].x, is->adj.points[j].y, is->adj.points[j].dx ? G_LINEH : G_LINEV); if (before[i] != 0) continue; /* this idea is pointless otherwise */ memcpy(ss->tmpdsf, ss->dsf, wh*sizeof(int)); for (j = 0; j < is->adj.npoints; j++) { spc = island_adjspace(is, 1, missing, j); if (spc == 0) continue; if (j == i) continue; solve_join(is, j, before[j] + spc, 0); } map_update_possibles(is->state); if (solve_island_subgroup(is, -1)) got = 1; for (j = 0; j < is->adj.npoints; j++) solve_join(is, j, before[j], 0); memcpy(ss->dsf, ss->tmpdsf, wh*sizeof(int)); if (got) { debug(("island at (%d,%d) must connect in direction (%d,%d) to" " avoid full subgroup.\n", is->x, is->y, is->adj.points[i].dx, is->adj.points[i].dy)); solve_join(is, i, 1, 0); didsth = 1; } map_update_possibles(is->state); } if (didsth) *didsth_r = didsth; return 1; } #define CONTINUE_IF_FULL do { \ if (GRID(state, is->x, is->y) & G_MARK) { \ /* island full, don't try fixing it */ \ continue; \ } } while(0) static int solve_sub(game_state *state, int difficulty, int depth) { struct island *is; int i, didsth; while (1) { didsth = 0; /* First island iteration: things we can work out by looking at * properties of the island as a whole. */ for (i = 0; i < state->n_islands; i++) { is = &state->islands[i]; if (!solve_island_stage1(is, &didsth)) return 0; } if (didsth) continue; else if (difficulty < 1) break; /* Second island iteration: thing we can work out by looking at * properties of individual island connections. */ for (i = 0; i < state->n_islands; i++) { is = &state->islands[i]; CONTINUE_IF_FULL; if (!solve_island_stage2(is, &didsth)) return 0; } if (didsth) continue; else if (difficulty < 2) break; /* Third island iteration: things we can only work out by looking * at groups of islands. */ for (i = 0; i < state->n_islands; i++) { is = &state->islands[i]; if (!solve_island_stage3(is, &didsth)) return 0; } if (didsth) continue; else if (difficulty < 3) break; /* If we can be bothered, write a recursive solver to finish here. */ break; } if (map_check(state)) return 1; /* solved it */ return 0; } static void solve_for_hint(game_state *state) { map_group(state); solve_sub(state, 10, 0); } static int solve_from_scratch(game_state *state, int difficulty) { map_clear(state); map_group(state); map_update_possibles(state); return solve_sub(state, difficulty, 0); } /* --- New game functions --- */ static game_state *new_state(const game_params *params) { game_state *ret = snew(game_state); int wh = params->w * params->h, i; ret->w = params->w; ret->h = params->h; ret->allowloops = params->allowloops; ret->maxb = params->maxb; ret->params = *params; ret->grid = snewn(wh, grid_type); memset(ret->grid, 0, GRIDSZ(ret)); ret->scratch = snewn(wh, grid_type); memset(ret->scratch, 0, GRIDSZ(ret)); ret->wha = snewn(wh*N_WH_ARRAYS, char); memset(ret->wha, 0, wh*N_WH_ARRAYS*sizeof(char)); ret->possv = ret->wha; ret->possh = ret->wha + wh; ret->lines = ret->wha + wh*2; ret->maxv = ret->wha + wh*3; ret->maxh = ret->wha + wh*4; memset(ret->maxv, ret->maxb, wh*sizeof(char)); memset(ret->maxh, ret->maxb, wh*sizeof(char)); ret->islands = NULL; ret->n_islands = 0; ret->n_islands_alloc = 0; ret->gridi = snewn(wh, struct island *); for (i = 0; i < wh; i++) ret->gridi[i] = NULL; ret->solved = ret->completed = 0; ret->solver = snew(struct solver_state); ret->solver->dsf = snew_dsf(wh); ret->solver->tmpdsf = snewn(wh, int); ret->solver->refcount = 1; return ret; } static game_state *dup_game(const game_state *state) { game_state *ret = snew(game_state); int wh = state->w*state->h; ret->w = state->w; ret->h = state->h; ret->allowloops = state->allowloops; ret->maxb = state->maxb; ret->params = state->params; ret->grid = snewn(wh, grid_type); memcpy(ret->grid, state->grid, GRIDSZ(ret)); ret->scratch = snewn(wh, grid_type); memcpy(ret->scratch, state->scratch, GRIDSZ(ret)); ret->wha = snewn(wh*N_WH_ARRAYS, char); memcpy(ret->wha, state->wha, wh*N_WH_ARRAYS*sizeof(char)); ret->possv = ret->wha; ret->possh = ret->wha + wh; ret->lines = ret->wha + wh*2; ret->maxv = ret->wha + wh*3; ret->maxh = ret->wha + wh*4; ret->islands = snewn(state->n_islands, struct island); memcpy(ret->islands, state->islands, state->n_islands * sizeof(struct island)); ret->n_islands = ret->n_islands_alloc = state->n_islands; ret->gridi = snewn(wh, struct island *); fixup_islands_for_realloc(ret); ret->solved = state->solved; ret->completed = state->completed; ret->solver = state->solver; ret->solver->refcount++; return ret; } static void free_game(game_state *state) { if (--state->solver->refcount <= 0) { sfree(state->solver->dsf); sfree(state->solver->tmpdsf); sfree(state->solver); } sfree(state->islands); sfree(state->gridi); sfree(state->wha); sfree(state->scratch); sfree(state->grid); sfree(state); } #define MAX_NEWISLAND_TRIES 50 #define MIN_SENSIBLE_ISLANDS 3 #define ORDER(a,b) do { if (a < b) { int tmp=a; int a=b; int b=tmp; } } while(0) static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { game_state *tobuild = NULL; int i, j, wh = params->w * params->h, x, y, dx, dy; int minx, miny, maxx, maxy, joinx, joiny, newx, newy, diffx, diffy; int ni_req = max((params->islands * wh) / 100, MIN_SENSIBLE_ISLANDS), ni_curr, ni_bad; struct island *is, *is2; char *ret; unsigned int echeck; /* pick a first island position randomly. */ generate: if (tobuild) free_game(tobuild); tobuild = new_state(params); x = random_upto(rs, params->w); y = random_upto(rs, params->h); island_add(tobuild, x, y, 0); ni_curr = 1; ni_bad = 0; debug(("Created initial island at (%d,%d).\n", x, y)); while (ni_curr < ni_req) { /* Pick a random island to try and extend from. */ i = random_upto(rs, tobuild->n_islands); is = &tobuild->islands[i]; /* Pick a random direction to extend in. */ j = random_upto(rs, is->adj.npoints); dx = is->adj.points[j].x - is->x; dy = is->adj.points[j].y - is->y; /* Find out limits of where we could put a new island. */ joinx = joiny = -1; minx = is->x + 2*dx; miny = is->y + 2*dy; /* closest is 2 units away. */ x = is->x+dx; y = is->y+dy; if (GRID(tobuild,x,y) & (G_LINEV|G_LINEH)) { /* already a line next to the island, continue. */ goto bad; } while (1) { if (x < 0 || x >= params->w || y < 0 || y >= params->h) { /* got past the edge; put a possible at the island * and exit. */ maxx = x-dx; maxy = y-dy; goto foundmax; } if (GRID(tobuild,x,y) & G_ISLAND) { /* could join up to an existing island... */ joinx = x; joiny = y; /* ... or make a new one 2 spaces away. */ maxx = x - 2*dx; maxy = y - 2*dy; goto foundmax; } else if (GRID(tobuild,x,y) & (G_LINEV|G_LINEH)) { /* could make a new one 1 space away from the line. */ maxx = x - dx; maxy = y - dy; goto foundmax; } x += dx; y += dy; } foundmax: debug(("Island at (%d,%d) with d(%d,%d) has new positions " "(%d,%d) -> (%d,%d), join (%d,%d).\n", is->x, is->y, dx, dy, minx, miny, maxx, maxy, joinx, joiny)); /* Now we know where we could either put a new island * (between min and max), or (if loops are allowed) could join on * to an existing island (at join). */ if (params->allowloops && joinx != -1 && joiny != -1) { if (random_upto(rs, 100) < (unsigned long)params->expansion) { is2 = INDEX(tobuild, gridi, joinx, joiny); debug(("Joining island at (%d,%d) to (%d,%d).\n", is->x, is->y, is2->x, is2->y)); goto join; } } diffx = (maxx - minx) * dx; diffy = (maxy - miny) * dy; if (diffx < 0 || diffy < 0) goto bad; if (random_upto(rs,100) < (unsigned long)params->expansion) { newx = maxx; newy = maxy; debug(("Creating new island at (%d,%d) (expanded).\n", newx, newy)); } else { newx = minx + random_upto(rs,diffx+1)*dx; newy = miny + random_upto(rs,diffy+1)*dy; debug(("Creating new island at (%d,%d).\n", newx, newy)); } /* check we're not next to island in the other orthogonal direction. */ if ((INGRID(tobuild,newx+dy,newy+dx) && (GRID(tobuild,newx+dy,newy+dx) & G_ISLAND)) || (INGRID(tobuild,newx-dy,newy-dx) && (GRID(tobuild,newx-dy,newy-dx) & G_ISLAND))) { debug(("New location is adjacent to island, skipping.\n")); goto bad; } is2 = island_add(tobuild, newx, newy, 0); /* Must get is again at this point; the array might have * been realloced by island_add... */ is = &tobuild->islands[i]; /* ...but order will not change. */ ni_curr++; ni_bad = 0; join: island_join(is, is2, random_upto(rs, tobuild->maxb)+1, 0); debug_state(tobuild); continue; bad: ni_bad++; if (ni_bad > MAX_NEWISLAND_TRIES) { debug(("Unable to create any new islands after %d tries; " "created %d [%d%%] (instead of %d [%d%%] requested).\n", MAX_NEWISLAND_TRIES, ni_curr, ni_curr * 100 / wh, ni_req, ni_req * 100 / wh)); goto generated; } } generated: if (ni_curr == 1) { debug(("Only generated one island (!), retrying.\n")); goto generate; } /* Check we have at least one island on each extremity of the grid. */ echeck = 0; for (x = 0; x < params->w; x++) { if (INDEX(tobuild, gridi, x, 0)) echeck |= 1; if (INDEX(tobuild, gridi, x, params->h-1)) echeck |= 2; } for (y = 0; y < params->h; y++) { if (INDEX(tobuild, gridi, 0, y)) echeck |= 4; if (INDEX(tobuild, gridi, params->w-1, y)) echeck |= 8; } if (echeck != 15) { debug(("Generated grid doesn't fill to sides, retrying.\n")); goto generate; } map_count(tobuild); map_find_orthogonal(tobuild); if (params->difficulty > 0) { if ((ni_curr > MIN_SENSIBLE_ISLANDS) && (solve_from_scratch(tobuild, params->difficulty-1) > 0)) { debug(("Grid is solvable at difficulty %d (too easy); retrying.\n", params->difficulty-1)); goto generate; } } if (solve_from_scratch(tobuild, params->difficulty) == 0) { debug(("Grid not solvable at difficulty %d, (too hard); retrying.\n", params->difficulty)); goto generate; } /* ... tobuild is now solved. We rely on this making the diff for aux. */ debug_state(tobuild); ret = encode_game(tobuild); { game_state *clean = dup_game(tobuild); map_clear(clean); map_update_possibles(clean); *aux = game_state_diff(clean, tobuild); free_game(clean); } free_game(tobuild); return ret; } static char *validate_desc(const game_params *params, const char *desc) { int i, wh = params->w * params->h; for (i = 0; i < wh; i++) { if (*desc >= '1' && *desc <= '9') /* OK */; else if (*desc >= 'a' && *desc <= 'z') i += *desc - 'a'; /* plus the i++ */ else if (*desc >= 'A' && *desc <= 'G') /* OK */; else if (*desc == 'V' || *desc == 'W' || *desc == 'X' || *desc == 'Y' || *desc == 'H' || *desc == 'I' || *desc == 'J' || *desc == 'K') /* OK */; else if (!*desc) return "Game description shorter than expected"; else return "Game description containers unexpected character"; desc++; } if (*desc || i > wh) return "Game description longer than expected"; return NULL; } static game_state *new_game_sub(const game_params *params, const char *desc) { game_state *state = new_state(params); int x, y, run = 0; debug(("new_game[_sub]: desc = '%s'.\n", desc)); for (y = 0; y < params->h; y++) { for (x = 0; x < params->w; x++) { char c = '\0'; if (run == 0) { c = *desc++; assert(c != 'S'); if (c >= 'a' && c <= 'z') run = c - 'a' + 1; } if (run > 0) { c = 'S'; run--; } switch (c) { case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': island_add(state, x, y, (c - '0')); break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': island_add(state, x, y, (c - 'A') + 10); break; case 'S': /* empty square */ break; default: assert(!"Malformed desc."); break; } } } if (*desc) assert(!"Over-long desc."); map_find_orthogonal(state); map_update_possibles(state); return state; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { return new_game_sub(params, desc); } struct game_ui { int dragx_src, dragy_src; /* source; -1 means no drag */ int dragx_dst, dragy_dst; /* src's closest orth island. */ grid_type todraw; int dragging, drag_is_noline, nlines; int cur_x, cur_y, cur_visible; /* cursor position */ int show_hints; }; static char *ui_cancel_drag(game_ui *ui) { ui->dragx_src = ui->dragy_src = -1; ui->dragx_dst = ui->dragy_dst = -1; ui->dragging = 0; return ""; } static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui_cancel_drag(ui); ui->cur_x = state->islands[0].x; ui->cur_y = state->islands[0].y; ui->cur_visible = 0; ui->show_hints = 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) { } struct game_drawstate { int tilesize; int w, h; grid_type *grid; int *lv, *lh; int started, dragging; int show_hints; }; static char *update_drag_dst(const game_state *state, game_ui *ui, const game_drawstate *ds, int nx, int ny) { int ox, oy, dx, dy, i, currl, maxb; struct island *is; grid_type gtype, ntype, mtype, curr; if (ui->dragx_src == -1 || ui->dragy_src == -1) return NULL; ui->dragx_dst = -1; ui->dragy_dst = -1; /* work out which of the four directions we're closest to... */ ox = COORD(ui->dragx_src) + TILE_SIZE/2; oy = COORD(ui->dragy_src) + TILE_SIZE/2; if (abs(nx-ox) < abs(ny-oy)) { dx = 0; dy = (ny-oy) < 0 ? -1 : 1; gtype = G_LINEV; ntype = G_NOLINEV; mtype = G_MARKV; maxb = INDEX(state, maxv, ui->dragx_src+dx, ui->dragy_src+dy); } else { dy = 0; dx = (nx-ox) < 0 ? -1 : 1; gtype = G_LINEH; ntype = G_NOLINEH; mtype = G_MARKH; maxb = INDEX(state, maxh, ui->dragx_src+dx, ui->dragy_src+dy); } if (ui->drag_is_noline) { ui->todraw = ntype; } else { curr = GRID(state, ui->dragx_src+dx, ui->dragy_src+dy); currl = INDEX(state, lines, ui->dragx_src+dx, ui->dragy_src+dy); if (curr & gtype) { if (currl == maxb) { ui->todraw = 0; ui->nlines = 0; } else { ui->todraw = gtype; ui->nlines = currl + 1; } } else { ui->todraw = gtype; ui->nlines = 1; } } /* ... and see if there's an island off in that direction. */ is = INDEX(state, gridi, ui->dragx_src, ui->dragy_src); for (i = 0; i < is->adj.npoints; i++) { if (is->adj.points[i].off == 0) continue; curr = GRID(state, is->x+dx, is->y+dy); if (curr & mtype) continue; /* don't allow changes to marked lines. */ if (ui->drag_is_noline) { if (curr & gtype) continue; /* no no-line where already a line */ } else { if (POSSIBLES(state, dx, is->x+dx, is->y+dy) == 0) continue; /* no line if !possible. */ if (curr & ntype) continue; /* can't have a bridge where there's a no-line. */ } if (is->adj.points[i].dx == dx && is->adj.points[i].dy == dy) { ui->dragx_dst = ISLAND_ORTHX(is,i); ui->dragy_dst = ISLAND_ORTHY(is,i); } } /*debug(("update_drag src (%d,%d) d(%d,%d) dst (%d,%d)\n", ui->dragx_src, ui->dragy_src, dx, dy, ui->dragx_dst, ui->dragy_dst));*/ return ""; } static char *finish_drag(const game_state *state, game_ui *ui) { char buf[80]; if (ui->dragx_src == -1 || ui->dragy_src == -1) return NULL; if (ui->dragx_dst == -1 || ui->dragy_dst == -1) return ui_cancel_drag(ui); if (ui->drag_is_noline) { sprintf(buf, "N%d,%d,%d,%d", ui->dragx_src, ui->dragy_src, ui->dragx_dst, ui->dragy_dst); } else { sprintf(buf, "L%d,%d,%d,%d,%d", ui->dragx_src, ui->dragy_src, ui->dragx_dst, ui->dragy_dst, ui->nlines); } ui_cancel_drag(ui); return dupstr(buf); } static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { int gx = FROMCOORD(x), gy = FROMCOORD(y); char buf[80], *ret; grid_type ggrid = INGRID(state,gx,gy) ? GRID(state,gx,gy) : 0; if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { if (!INGRID(state, gx, gy)) return NULL; ui->cur_visible = 0; if ((ggrid & G_ISLAND) && !(ggrid & G_MARK)) { ui->dragx_src = gx; ui->dragy_src = gy; return ""; } else return ui_cancel_drag(ui); } else if (button == LEFT_DRAG || button == RIGHT_DRAG) { if (gx != ui->dragx_src || gy != ui->dragy_src) { ui->dragging = 1; ui->drag_is_noline = (button == RIGHT_DRAG) ? 1 : 0; return update_drag_dst(state, ui, ds, x, y); } else { /* cancel a drag when we go back to the starting point */ ui->dragx_dst = -1; ui->dragy_dst = -1; return ""; } } else if (button == LEFT_RELEASE || button == RIGHT_RELEASE) { if (ui->dragging) { return finish_drag(state, ui); } else { ui_cancel_drag(ui); if (!INGRID(state, gx, gy)) return NULL; if (!(GRID(state, gx, gy) & G_ISLAND)) return NULL; sprintf(buf, "M%d,%d", gx, gy); return dupstr(buf); } } else if (button == 'h' || button == 'H') { game_state *solved = dup_game(state); solve_for_hint(solved); ret = game_state_diff(state, solved); free_game(solved); return ret; } else if (IS_CURSOR_MOVE(button)) { ui->cur_visible = 1; if (ui->dragging) { int nx = ui->cur_x, ny = ui->cur_y; move_cursor(button, &nx, &ny, state->w, state->h, 0); update_drag_dst(state, ui, ds, COORD(nx)+TILE_SIZE/2, COORD(ny)+TILE_SIZE/2); return finish_drag(state, ui); } else { int dx = (button == CURSOR_RIGHT) ? +1 : (button == CURSOR_LEFT) ? -1 : 0; int dy = (button == CURSOR_DOWN) ? +1 : (button == CURSOR_UP) ? -1 : 0; int dorthx = 1 - abs(dx), dorthy = 1 - abs(dy); int dir, orth, nx = x, ny = y; /* 'orthorder' is a tweak to ensure that if you press RIGHT and * happen to move upwards, when you press LEFT you then tend * downwards (rather than upwards again). */ int orthorder = (button == CURSOR_LEFT || button == CURSOR_UP) ? 1 : -1; /* This attempts to find an island in the direction you're * asking for, broadly speaking. If you ask to go right, for * example, it'll look for islands to the right and slightly * above or below your current horiz. position, allowing * further above/below the further away it searches. */ assert(GRID(state, ui->cur_x, ui->cur_y) & G_ISLAND); /* currently this is depth-first (so orthogonally-adjacent * islands across the other side of the grid will be moved to * before closer islands slightly offset). Swap the order of * these two loops to change to breadth-first search. */ for (orth = 0; ; orth++) { int oingrid = 0; for (dir = 1; ; dir++) { int dingrid = 0; if (orth > dir) continue; /* only search in cone outwards. */ nx = ui->cur_x + dir*dx + orth*dorthx*orthorder; ny = ui->cur_y + dir*dy + orth*dorthy*orthorder; if (INGRID(state, nx, ny)) { dingrid = oingrid = 1; if (GRID(state, nx, ny) & G_ISLAND) goto found; } nx = ui->cur_x + dir*dx - orth*dorthx*orthorder; ny = ui->cur_y + dir*dy - orth*dorthy*orthorder; if (INGRID(state, nx, ny)) { dingrid = oingrid = 1; if (GRID(state, nx, ny) & G_ISLAND) goto found; } if (!dingrid) break; } if (!oingrid) return ""; } /* not reached */ found: ui->cur_x = nx; ui->cur_y = ny; return ""; } } else if (IS_CURSOR_SELECT(button)) { if (!ui->cur_visible) { ui->cur_visible = 1; return ""; } if (ui->dragging) { ui_cancel_drag(ui); if (ui->dragx_dst == -1 && ui->dragy_dst == -1) { sprintf(buf, "M%d,%d", ui->cur_x, ui->cur_y); return dupstr(buf); } else return ""; } else { grid_type v = GRID(state, ui->cur_x, ui->cur_y); if (v & G_ISLAND) { ui->dragging = 1; ui->dragx_src = ui->cur_x; ui->dragy_src = ui->cur_y; ui->dragx_dst = ui->dragy_dst = -1; ui->drag_is_noline = (button == CURSOR_SELECT2) ? 1 : 0; return ""; } } } else if (button == 'g' || button == 'G') { ui->show_hints = 1 - ui->show_hints; return ""; } return NULL; } static game_state *execute_move(const game_state *state, const char *move) { game_state *ret = dup_game(state); int x1, y1, x2, y2, nl, n; struct island *is1, *is2; char c; debug(("execute_move: %s\n", move)); if (!*move) goto badmove; while (*move) { c = *move++; if (c == 'S') { ret->solved = TRUE; n = 0; } else if (c == 'L') { if (sscanf(move, "%d,%d,%d,%d,%d%n", &x1, &y1, &x2, &y2, &nl, &n) != 5) goto badmove; if (!INGRID(ret, x1, y1) || !INGRID(ret, x2, y2)) goto badmove; is1 = INDEX(ret, gridi, x1, y1); is2 = INDEX(ret, gridi, x2, y2); if (!is1 || !is2) goto badmove; if (nl < 0 || nl > state->maxb) goto badmove; island_join(is1, is2, nl, 0); } else if (c == 'N') { if (sscanf(move, "%d,%d,%d,%d%n", &x1, &y1, &x2, &y2, &n) != 4) goto badmove; if (!INGRID(ret, x1, y1) || !INGRID(ret, x2, y2)) goto badmove; is1 = INDEX(ret, gridi, x1, y1); is2 = INDEX(ret, gridi, x2, y2); if (!is1 || !is2) goto badmove; island_join(is1, is2, -1, 0); } else if (c == 'M') { if (sscanf(move, "%d,%d%n", &x1, &y1, &n) != 2) goto badmove; if (!INGRID(ret, x1, y1)) goto badmove; is1 = INDEX(ret, gridi, x1, y1); if (!is1) goto badmove; island_togglemark(is1); } else goto badmove; move += n; if (*move == ';') move++; else if (*move) goto badmove; } map_update_possibles(ret); if (map_check(ret)) { debug(("Game completed.\n")); ret->completed = 1; } return ret; badmove: debug(("%s: unrecognised move.\n", move)); free_game(ret); return NULL; } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { char *ret; game_state *solved; if (aux) { debug(("solve_game: aux = %s\n", aux)); solved = execute_move(state, aux); if (!solved) { *error = "Generated aux string is not a valid move (!)."; return NULL; } } else { solved = dup_game(state); /* solve with max strength... */ if (solve_from_scratch(solved, 10) == 0) { free_game(solved); *error = "Game does not have a (non-recursive) solution."; return NULL; } } ret = game_state_diff(currstate, solved); free_game(solved); debug(("solve_game: ret = %s\n", ret)); 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 = TILE_SIZE * params->w + 2 * BORDER; *y = TILE_SIZE * params->h + 2 * BORDER; } 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); for (i = 0; i < 3; i++) { ret[COL_FOREGROUND * 3 + i] = 0.0F; ret[COL_HINT * 3 + i] = ret[COL_LOWLIGHT * 3 + i]; ret[COL_GRID * 3 + i] = (ret[COL_HINT * 3 + i] + ret[COL_BACKGROUND * 3 + i]) * 0.5F; ret[COL_MARK * 3 + i] = ret[COL_HIGHLIGHT * 3 + i]; } ret[COL_WARNING * 3 + 0] = 1.0F; ret[COL_WARNING * 3 + 1] = 0.25F; ret[COL_WARNING * 3 + 2] = 0.25F; ret[COL_SELECTED * 3 + 0] = 0.25F; ret[COL_SELECTED * 3 + 1] = 1.00F; ret[COL_SELECTED * 3 + 2] = 0.25F; ret[COL_CURSOR * 3 + 0] = min(ret[COL_BACKGROUND * 3 + 0] * 1.4F, 1.0F); ret[COL_CURSOR * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.8F; ret[COL_CURSOR * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 0.8F; *ncolours = NCOLOURS; return ret; } static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { struct game_drawstate *ds = snew(struct game_drawstate); int wh = state->w*state->h; ds->tilesize = 0; ds->w = state->w; ds->h = state->h; ds->started = 0; ds->grid = snewn(wh, grid_type); memset(ds->grid, -1, wh*sizeof(grid_type)); ds->lv = snewn(wh, int); ds->lh = snewn(wh, int); memset(ds->lv, 0, wh*sizeof(int)); memset(ds->lh, 0, wh*sizeof(int)); ds->show_hints = 0; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->lv); sfree(ds->lh); sfree(ds->grid); sfree(ds); } #define LINE_WIDTH (TILE_SIZE/8) #define TS8(x) (((x)*TILE_SIZE)/8) #define OFFSET(thing) ((TILE_SIZE/2) - ((thing)/2)) static void lines_vert(drawing *dr, game_drawstate *ds, int ox, int oy, int lv, int col, grid_type v) { int lw = LINE_WIDTH, gw = LINE_WIDTH, bw, i, loff; while ((bw = lw * lv + gw * (lv+1)) > TILE_SIZE) gw--; loff = OFFSET(bw); if (v & G_MARKV) draw_rect(dr, ox + loff, oy, bw, TILE_SIZE, COL_MARK); for (i = 0; i < lv; i++, loff += lw + gw) draw_rect(dr, ox + loff + gw, oy, lw, TILE_SIZE, col); } static void lines_horiz(drawing *dr, game_drawstate *ds, int ox, int oy, int lh, int col, grid_type v) { int lw = LINE_WIDTH, gw = LINE_WIDTH, bw, i, loff; while ((bw = lw * lh + gw * (lh+1)) > TILE_SIZE) gw--; loff = OFFSET(bw); if (v & G_MARKH) draw_rect(dr, ox, oy + loff, TILE_SIZE, bw, COL_MARK); for (i = 0; i < lh; i++, loff += lw + gw) draw_rect(dr, ox, oy + loff + gw, TILE_SIZE, lw, col); } static void line_cross(drawing *dr, game_drawstate *ds, int ox, int oy, int col, grid_type v) { int off = TS8(2); draw_line(dr, ox, oy, ox+off, oy+off, col); draw_line(dr, ox+off, oy, ox, oy+off, col); } static int between_island(const game_state *state, int sx, int sy, int dx, int dy) { int x = sx - dx, y = sy - dy; while (INGRID(state, x, y)) { if (GRID(state, x, y) & G_ISLAND) goto found; x -= dx; y -= dy; } return 0; found: x = sx + dx, y = sy + dy; while (INGRID(state, x, y)) { if (GRID(state, x, y) & G_ISLAND) return 1; x += dx; y += dy; } return 0; } static void lines_lvlh(const game_state *state, const game_ui *ui, int x, int y, grid_type v, int *lv_r, int *lh_r) { int lh = 0, lv = 0; if (v & G_LINEV) lv = INDEX(state,lines,x,y); if (v & G_LINEH) lh = INDEX(state,lines,x,y); if (ui->show_hints) { if (between_island(state, x, y, 0, 1) && !lv) lv = 1; if (between_island(state, x, y, 1, 0) && !lh) lh = 1; } /*debug(("lvlh: (%d,%d) v 0x%x lv %d lh %d.\n", x, y, v, lv, lh));*/ *lv_r = lv; *lh_r = lh; } static void dsf_debug_draw(drawing *dr, const game_state *state, game_drawstate *ds, int x, int y) { #ifdef DRAW_DSF int ts = TILE_SIZE/2; int ox = COORD(x) + ts/2, oy = COORD(y) + ts/2; char str[32]; sprintf(str, "%d", dsf_canonify(state->solver->dsf, DINDEX(x,y))); draw_text(dr, ox, oy, FONT_VARIABLE, ts, ALIGN_VCENTRE | ALIGN_HCENTRE, COL_WARNING, str); #endif } static void lines_redraw(drawing *dr, const game_state *state, game_drawstate *ds, const game_ui *ui, int x, int y, grid_type v, int lv, int lh) { int ox = COORD(x), oy = COORD(y); int vcol = (v & G_FLASH) ? COL_HIGHLIGHT : (v & G_WARN) ? COL_WARNING : COL_FOREGROUND, hcol = vcol; grid_type todraw = v & G_NOLINE; if (v & G_ISSEL) { if (ui->todraw & G_FLAGSH) hcol = COL_SELECTED; if (ui->todraw & G_FLAGSV) vcol = COL_SELECTED; todraw |= ui->todraw; } draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE, COL_BACKGROUND); /*if (v & G_CURSOR) draw_rect(dr, ox+TILE_SIZE/4, oy+TILE_SIZE/4, TILE_SIZE/2, TILE_SIZE/2, COL_CURSOR);*/ if (ui->show_hints) { if (between_island(state, x, y, 0, 1) && !(v & G_LINEV)) vcol = COL_HINT; if (between_island(state, x, y, 1, 0) && !(v & G_LINEH)) hcol = COL_HINT; } #ifdef DRAW_GRID draw_rect_outline(dr, ox, oy, TILE_SIZE, TILE_SIZE, COL_GRID); #endif if (todraw & G_NOLINEV) { line_cross(dr, ds, ox + TS8(3), oy + TS8(1), vcol, todraw); line_cross(dr, ds, ox + TS8(3), oy + TS8(5), vcol, todraw); } if (todraw & G_NOLINEH) { line_cross(dr, ds, ox + TS8(1), oy + TS8(3), hcol, todraw); line_cross(dr, ds, ox + TS8(5), oy + TS8(3), hcol, todraw); } /* if we're drawing a real line and a hint, make sure we draw the real * line on top. */ if (lv && vcol == COL_HINT) lines_vert(dr, ds, ox, oy, lv, vcol, v); if (lh) lines_horiz(dr, ds, ox, oy, lh, hcol, v); if (lv && vcol != COL_HINT) lines_vert(dr, ds, ox, oy, lv, vcol, v); dsf_debug_draw(dr, state, ds, x, y); draw_update(dr, ox, oy, TILE_SIZE, TILE_SIZE); } #define ISLAND_RADIUS ((TILE_SIZE*12)/20) #define ISLAND_NUMSIZE(is) \ (((is)->count < 10) ? (TILE_SIZE*7)/10 : (TILE_SIZE*5)/10) static void island_redraw(drawing *dr, const game_state *state, game_drawstate *ds, struct island *is, grid_type v) { /* These overlap the edges of their squares, which is why they're drawn later. * We know they can't overlap each other because they're not allowed within 2 * squares of each other. */ int half = TILE_SIZE/2; int ox = COORD(is->x) + half, oy = COORD(is->y) + half; int orad = ISLAND_RADIUS, irad = orad - LINE_WIDTH; int updatesz = orad*2+1; int tcol = (v & G_FLASH) ? COL_HIGHLIGHT : (v & G_WARN) ? COL_WARNING : COL_FOREGROUND; int col = (v & G_ISSEL) ? COL_SELECTED : tcol; int bg = (v & G_CURSOR) ? COL_CURSOR : (v & G_MARK) ? COL_MARK : COL_BACKGROUND; char str[32]; #ifdef DRAW_GRID draw_rect_outline(dr, COORD(is->x), COORD(is->y), TILE_SIZE, TILE_SIZE, COL_GRID); #endif /* draw a thick circle */ draw_circle(dr, ox, oy, orad, col, col); draw_circle(dr, ox, oy, irad, bg, bg); sprintf(str, "%d", is->count); draw_text(dr, ox, oy, FONT_VARIABLE, ISLAND_NUMSIZE(is), ALIGN_VCENTRE | ALIGN_HCENTRE, tcol, str); dsf_debug_draw(dr, state, ds, is->x, is->y); draw_update(dr, ox - orad, oy - orad, updatesz, updatesz); } 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 x, y, force = 0, i, j, redraw, lv, lh; grid_type v, dsv, flash = 0; struct island *is, *is_drag_src = NULL, *is_drag_dst = NULL; if (flashtime) { int f = (int)(flashtime * 5 / FLASH_TIME); if (f == 1 || f == 3) flash = G_FLASH; } /* Clear screen, if required. */ if (!ds->started) { draw_rect(dr, 0, 0, TILE_SIZE * ds->w + 2 * BORDER, TILE_SIZE * ds->h + 2 * BORDER, COL_BACKGROUND); #ifdef DRAW_GRID draw_rect_outline(dr, COORD(0)-1, COORD(0)-1, TILE_SIZE * ds->w + 2, TILE_SIZE * ds->h + 2, COL_GRID); #endif draw_update(dr, 0, 0, TILE_SIZE * ds->w + 2 * BORDER, TILE_SIZE * ds->h + 2 * BORDER); ds->started = 1; force = 1; } if (ui->dragx_src != -1 && ui->dragy_src != -1) { ds->dragging = 1; is_drag_src = INDEX(state, gridi, ui->dragx_src, ui->dragy_src); assert(is_drag_src); if (ui->dragx_dst != -1 && ui->dragy_dst != -1) { is_drag_dst = INDEX(state, gridi, ui->dragx_dst, ui->dragy_dst); assert(is_drag_dst); } } else ds->dragging = 0; if (ui->show_hints != ds->show_hints) { force = 1; ds->show_hints = ui->show_hints; } /* Draw all lines (and hints, if we want), but *not* islands. */ for (x = 0; x < ds->w; x++) { for (y = 0; y < ds->h; y++) { v = GRID(state, x, y) | flash; dsv = GRID(ds,x,y) & ~G_REDRAW; if (v & G_ISLAND) continue; if (is_drag_dst) { if (WITHIN(x,is_drag_src->x, is_drag_dst->x) && WITHIN(y,is_drag_src->y, is_drag_dst->y)) v |= G_ISSEL; } lines_lvlh(state, ui, x, y, v, &lv, &lh); /*if (ui->cur_visible && ui->cur_x == x && ui->cur_y == y) v |= G_CURSOR;*/ if (v != dsv || lv != INDEX(ds,lv,x,y) || lh != INDEX(ds,lh,x,y) || force) { GRID(ds, x, y) = v | G_REDRAW; INDEX(ds,lv,x,y) = lv; INDEX(ds,lh,x,y) = lh; lines_redraw(dr, state, ds, ui, x, y, v, lv, lh); } else GRID(ds,x,y) &= ~G_REDRAW; } } /* Draw islands. */ for (i = 0; i < state->n_islands; i++) { is = &state->islands[i]; v = GRID(state, is->x, is->y) | flash; redraw = 0; for (j = 0; j < is->adj.npoints; j++) { if (GRID(ds,is->adj.points[j].x,is->adj.points[j].y) & G_REDRAW) { redraw = 1; } } if (is_drag_src) { if (is == is_drag_src) v |= G_ISSEL; else if (is_drag_dst && is == is_drag_dst) v |= G_ISSEL; } if (island_impossible(is, v & G_MARK)) v |= G_WARN; if (ui->cur_visible && ui->cur_x == is->x && ui->cur_y == is->y) v |= G_CURSOR; if ((v != GRID(ds, is->x, is->y)) || force || redraw) { GRID(ds,is->x,is->y) = v; island_redraw(dr, state, ds, is, 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 && !oldstate->solved && !newstate->solved) return FLASH_TIME; return 0.0F; } static int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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) { int pw, ph; /* 10mm squares by default. */ game_compute_size(params, 1000, &pw, &ph); *x = pw / 100.0F; *y = ph / 100.0F; } static void game_print(drawing *dr, const game_state *state, int ts) { int ink = print_mono_colour(dr, 0); int paper = print_mono_colour(dr, 1); int x, y, cx, cy, i, nl; int loff; grid_type grid; /* Ick: fake up `ds->tilesize' for macro expansion purposes */ game_drawstate ads, *ds = &ads; ads.tilesize = ts; /* I don't think this wants a border. */ /* Bridges */ loff = ts / (8 * sqrt((state->params.maxb - 1))); print_line_width(dr, ts / 12); for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { cx = COORD(x); cy = COORD(y); grid = GRID(state,x,y); nl = INDEX(state,lines,x,y); if (grid & G_ISLAND) continue; if (grid & G_LINEV) { for (i = 0; i < nl; i++) draw_line(dr, cx+ts/2+(2*i-nl+1)*loff, cy, cx+ts/2+(2*i-nl+1)*loff, cy+ts, ink); } if (grid & G_LINEH) { for (i = 0; i < nl; i++) draw_line(dr, cx, cy+ts/2+(2*i-nl+1)*loff, cx+ts, cy+ts/2+(2*i-nl+1)*loff, ink); } } } /* Islands */ for (i = 0; i < state->n_islands; i++) { char str[32]; struct island *is = &state->islands[i]; grid = GRID(state, is->x, is->y); cx = COORD(is->x) + ts/2; cy = COORD(is->y) + ts/2; draw_circle(dr, cx, cy, ISLAND_RADIUS, paper, ink); sprintf(str, "%d", is->count); draw_text(dr, cx, cy, FONT_VARIABLE, ISLAND_NUMSIZE(is), ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str); } } #ifdef COMBINED #define thegame bridges #endif const struct game thegame = { "Bridges", "games.bridges", "bridges", default_params, game_fetch_preset, 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, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, REQUIRE_RBUTTON, /* flags */ }; /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/combi.c0000644000175300017530000000371410305566304013671 0ustar simonsimon#include #include #include "puzzles.h" /* horrific and doesn't check overflow. */ static long factx(long x, long y) { long acc = 1, i; for (i = y; i <= x; i++) acc *= i; return acc; } void reset_combi(combi_ctx *combi) { int i; combi->nleft = combi->total; for (i = 0; i < combi->r; i++) combi->a[i] = i; } combi_ctx *new_combi(int r, int n) { long nfr, nrf; combi_ctx *combi; assert(r <= n); assert(n >= 1); combi = snew(combi_ctx); memset(combi, 0, sizeof(combi_ctx)); combi->r = r; combi->n = n; combi->a = snewn(r, int); memset(combi->a, 0, r * sizeof(int)); nfr = factx(n, r+1); nrf = factx(n-r, 1); combi->total = (int)(nfr / nrf); reset_combi(combi); return combi; } /* returns NULL when we're done otherwise returns input. */ combi_ctx *next_combi(combi_ctx *combi) { int i = combi->r - 1, j; if (combi->nleft == combi->total) goto done; else if (combi->nleft <= 0) return NULL; while (combi->a[i] == combi->n - combi->r + i) i--; combi->a[i] += 1; for (j = i+1; j < combi->r; j++) combi->a[j] = combi->a[i] + j - i; done: combi->nleft--; return combi; } void free_combi(combi_ctx *combi) { sfree(combi->a); sfree(combi); } /* compile this with: * gcc -o combi.exe -DSTANDALONE_COMBI_TEST combi.c malloc.c */ #ifdef STANDALONE_COMBI_TEST #include void fatal(char *fmt, ...) { abort(); } int main(int argc, char *argv[]) { combi_ctx *c; int i, r, n; if (argc < 3) { fprintf(stderr, "Usage: combi R N\n"); exit(1); } r = atoi(argv[1]); n = atoi(argv[2]); c = new_combi(r, n); printf("combi %d of %d, %d elements.\n", c->r, c->n, c->total); while (next_combi(c)) { for (i = 0; i < c->r; i++) { printf("%d ", c->a[i]); } printf("\n"); } free_combi(c); } #endif puzzles-r9872/cube.c0000644000175300017530000014317512132232554013521 0ustar simonsimon/* * cube.c: Cube game. */ #include #include #include #include #include #include #include "puzzles.h" #define MAXVERTICES 20 #define MAXFACES 20 #define MAXORDER 4 struct solid { int nvertices; float vertices[MAXVERTICES * 3]; /* 3*npoints coordinates */ int order; int nfaces; int faces[MAXFACES * MAXORDER]; /* order*nfaces point indices */ float normals[MAXFACES * 3]; /* 3*npoints vector components */ float shear; /* isometric shear for nice drawing */ float border; /* border required around arena */ }; static const struct solid s_tetrahedron = { 4, { 0.0F, -0.57735026919F, -0.20412414523F, -0.5F, 0.28867513459F, -0.20412414523F, 0.0F, -0.0F, 0.6123724357F, 0.5F, 0.28867513459F, -0.20412414523F, }, 3, 4, { 0,2,1, 3,1,2, 2,0,3, 1,3,0 }, { -0.816496580928F, -0.471404520791F, 0.333333333334F, 0.0F, 0.942809041583F, 0.333333333333F, 0.816496580928F, -0.471404520791F, 0.333333333334F, 0.0F, 0.0F, -1.0F, }, 0.0F, 0.3F }; static const struct solid s_cube = { 8, { -0.5F,-0.5F,-0.5F, -0.5F,-0.5F,+0.5F, -0.5F,+0.5F,-0.5F, -0.5F,+0.5F,+0.5F, +0.5F,-0.5F,-0.5F, +0.5F,-0.5F,+0.5F, +0.5F,+0.5F,-0.5F, +0.5F,+0.5F,+0.5F, }, 4, 6, { 0,1,3,2, 1,5,7,3, 5,4,6,7, 4,0,2,6, 0,4,5,1, 3,7,6,2 }, { -1.0F,0.0F,0.0F, 0.0F,0.0F,+1.0F, +1.0F,0.0F,0.0F, 0.0F,0.0F,-1.0F, 0.0F,-1.0F,0.0F, 0.0F,+1.0F,0.0F }, 0.3F, 0.5F }; static const struct solid s_octahedron = { 6, { -0.5F, -0.28867513459472505F, 0.4082482904638664F, 0.5F, 0.28867513459472505F, -0.4082482904638664F, -0.5F, 0.28867513459472505F, -0.4082482904638664F, 0.5F, -0.28867513459472505F, 0.4082482904638664F, 0.0F, -0.57735026918945009F, -0.4082482904638664F, 0.0F, 0.57735026918945009F, 0.4082482904638664F, }, 3, 8, { 4,0,2, 0,5,2, 0,4,3, 5,0,3, 1,4,2, 5,1,2, 4,1,3, 1,5,3 }, { -0.816496580928F, -0.471404520791F, -0.333333333334F, -0.816496580928F, 0.471404520791F, 0.333333333334F, 0.0F, -0.942809041583F, 0.333333333333F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.942809041583F, -0.333333333333F, 0.816496580928F, -0.471404520791F, -0.333333333334F, 0.816496580928F, 0.471404520791F, 0.333333333334F, }, 0.0F, 0.5F }; static const struct solid s_icosahedron = { 12, { 0.0F, 0.57735026919F, 0.75576131408F, 0.0F, -0.93417235896F, 0.17841104489F, 0.0F, 0.93417235896F, -0.17841104489F, 0.0F, -0.57735026919F, -0.75576131408F, -0.5F, -0.28867513459F, 0.75576131408F, -0.5F, 0.28867513459F, -0.75576131408F, 0.5F, -0.28867513459F, 0.75576131408F, 0.5F, 0.28867513459F, -0.75576131408F, -0.80901699437F, 0.46708617948F, 0.17841104489F, 0.80901699437F, 0.46708617948F, 0.17841104489F, -0.80901699437F, -0.46708617948F, -0.17841104489F, 0.80901699437F, -0.46708617948F, -0.17841104489F, }, 3, 20, { 8,0,2, 0,9,2, 1,10,3, 11,1,3, 0,4,6, 4,1,6, 5,2,7, 3,5,7, 4,8,10, 8,5,10, 9,6,11, 7,9,11, 0,8,4, 9,0,6, 10,1,4, 1,11,6, 8,2,5, 2,9,7, 3,10,5, 11,3,7, }, { -0.356822089773F, 0.87267799625F, 0.333333333333F, 0.356822089773F, 0.87267799625F, 0.333333333333F, -0.356822089773F, -0.87267799625F, -0.333333333333F, 0.356822089773F, -0.87267799625F, -0.333333333333F, -0.0F, 0.0F, 1.0F, 0.0F, -0.666666666667F, 0.745355992501F, 0.0F, 0.666666666667F, -0.745355992501F, 0.0F, 0.0F, -1.0F, -0.934172358963F, -0.12732200375F, 0.333333333333F, -0.934172358963F, 0.12732200375F, -0.333333333333F, 0.934172358963F, -0.12732200375F, 0.333333333333F, 0.934172358963F, 0.12732200375F, -0.333333333333F, -0.57735026919F, 0.333333333334F, 0.745355992501F, 0.57735026919F, 0.333333333334F, 0.745355992501F, -0.57735026919F, -0.745355992501F, 0.333333333334F, 0.57735026919F, -0.745355992501F, 0.333333333334F, -0.57735026919F, 0.745355992501F, -0.333333333334F, 0.57735026919F, 0.745355992501F, -0.333333333334F, -0.57735026919F, -0.333333333334F, -0.745355992501F, 0.57735026919F, -0.333333333334F, -0.745355992501F, }, 0.0F, 0.8F }; enum { TETRAHEDRON, CUBE, OCTAHEDRON, ICOSAHEDRON }; static const struct solid *solids[] = { &s_tetrahedron, &s_cube, &s_octahedron, &s_icosahedron }; enum { COL_BACKGROUND, COL_BORDER, COL_BLUE, NCOLOURS }; enum { LEFT, RIGHT, UP, DOWN, UP_LEFT, UP_RIGHT, DOWN_LEFT, DOWN_RIGHT }; #define PREFERRED_GRID_SCALE 48 #define GRID_SCALE (ds->gridscale) #define ROLLTIME 0.13F #define SQ(x) ( (x) * (x) ) #define MATMUL(ra,m,a) do { \ float rx, ry, rz, xx = (a)[0], yy = (a)[1], zz = (a)[2], *mat = (m); \ rx = mat[0] * xx + mat[3] * yy + mat[6] * zz; \ ry = mat[1] * xx + mat[4] * yy + mat[7] * zz; \ rz = mat[2] * xx + mat[5] * yy + mat[8] * zz; \ (ra)[0] = rx; (ra)[1] = ry; (ra)[2] = rz; \ } while (0) #define APPROXEQ(x,y) ( SQ(x-y) < 0.1 ) struct grid_square { float x, y; int npoints; float points[8]; /* maximum */ int directions[8]; /* bit masks showing point pairs */ int flip; int tetra_class; }; struct game_params { int solid; /* * Grid dimensions. For a square grid these are width and * height respectively; otherwise the grid is a hexagon, with * the top side and the two lower diagonals having length d1 * and the remaining three sides having length d2 (so that * d1==d2 gives a regular hexagon, and d2==0 gives a triangle). */ int d1, d2; }; typedef struct game_grid game_grid; struct game_grid { int refcount; struct grid_square *squares; int nsquares; }; #define SET_SQUARE(state, i, val) \ ((state)->bluemask[(i)/32] &= ~(1 << ((i)%32)), \ (state)->bluemask[(i)/32] |= ((!!val) << ((i)%32))) #define GET_SQUARE(state, i) \ (((state)->bluemask[(i)/32] >> ((i)%32)) & 1) struct game_state { struct game_params params; const struct solid *solid; int *facecolours; game_grid *grid; unsigned long *bluemask; int current; /* index of current grid square */ int sgkey[2]; /* key-point indices into grid sq */ int dgkey[2]; /* key-point indices into grid sq */ int spkey[2]; /* key-point indices into polyhedron */ int dpkey[2]; /* key-point indices into polyhedron */ int previous; float angle; int completed; int movecount; }; static game_params *default_params(void) { game_params *ret = snew(game_params); ret->solid = CUBE; ret->d1 = 4; ret->d2 = 4; return ret; } static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret = snew(game_params); char *str; switch (i) { case 0: str = "Cube"; ret->solid = CUBE; ret->d1 = 4; ret->d2 = 4; break; case 1: str = "Tetrahedron"; ret->solid = TETRAHEDRON; ret->d1 = 1; ret->d2 = 2; break; case 2: str = "Octahedron"; ret->solid = OCTAHEDRON; ret->d1 = 2; ret->d2 = 2; break; case 3: str = "Icosahedron"; ret->solid = ICOSAHEDRON; ret->d1 = 3; ret->d2 = 3; break; default: sfree(ret); return FALSE; } *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 *ret, char const *string) { switch (*string) { case 't': ret->solid = TETRAHEDRON; string++; break; case 'c': ret->solid = CUBE; string++; break; case 'o': ret->solid = OCTAHEDRON; string++; break; case 'i': ret->solid = ICOSAHEDRON; string++; break; default: break; } ret->d1 = ret->d2 = atoi(string); while (*string && isdigit((unsigned char)*string)) string++; if (*string == 'x') { string++; ret->d2 = atoi(string); } } static char *encode_params(const game_params *params, int full) { char data[256]; assert(params->solid >= 0 && params->solid < 4); sprintf(data, "%c%dx%d", "tcoi"[params->solid], params->d1, params->d2); return dupstr(data); } typedef void (*egc_callback)(void *, struct grid_square *); static void enum_grid_squares(const game_params *params, egc_callback callback, void *ctx) { const struct solid *solid = solids[params->solid]; if (solid->order == 4) { int x, y; for (y = 0; y < params->d2; y++) for (x = 0; x < params->d1; x++) { struct grid_square sq; sq.x = (float)x; sq.y = (float)y; sq.points[0] = x - 0.5F; sq.points[1] = y - 0.5F; sq.points[2] = x - 0.5F; sq.points[3] = y + 0.5F; sq.points[4] = x + 0.5F; sq.points[5] = y + 0.5F; sq.points[6] = x + 0.5F; sq.points[7] = y - 0.5F; sq.npoints = 4; sq.directions[LEFT] = 0x03; /* 0,1 */ sq.directions[RIGHT] = 0x0C; /* 2,3 */ sq.directions[UP] = 0x09; /* 0,3 */ sq.directions[DOWN] = 0x06; /* 1,2 */ sq.directions[UP_LEFT] = 0; /* no diagonals in a square */ sq.directions[UP_RIGHT] = 0; /* no diagonals in a square */ sq.directions[DOWN_LEFT] = 0; /* no diagonals in a square */ sq.directions[DOWN_RIGHT] = 0; /* no diagonals in a square */ sq.flip = FALSE; /* * This is supremely irrelevant, but just to avoid * having any uninitialised structure members... */ sq.tetra_class = 0; callback(ctx, &sq); } } else { int row, rowlen, other, i, firstix = -1; float theight = (float)(sqrt(3) / 2.0); for (row = 0; row < params->d1 + params->d2; row++) { if (row < params->d2) { other = +1; rowlen = row + params->d1; } else { other = -1; rowlen = 2*params->d2 + params->d1 - row; } /* * There are `rowlen' down-pointing triangles. */ for (i = 0; i < rowlen; i++) { struct grid_square sq; int ix; float x, y; ix = (2 * i - (rowlen-1)); x = ix * 0.5F; y = theight * row; sq.x = x; sq.y = y + theight / 3; sq.points[0] = x - 0.5F; sq.points[1] = y; sq.points[2] = x; sq.points[3] = y + theight; sq.points[4] = x + 0.5F; sq.points[5] = y; sq.npoints = 3; sq.directions[LEFT] = 0x03; /* 0,1 */ sq.directions[RIGHT] = 0x06; /* 1,2 */ sq.directions[UP] = 0x05; /* 0,2 */ sq.directions[DOWN] = 0; /* invalid move */ /* * Down-pointing triangle: both the up diagonals go * up, and the down ones go left and right. */ sq.directions[UP_LEFT] = sq.directions[UP_RIGHT] = sq.directions[UP]; sq.directions[DOWN_LEFT] = sq.directions[LEFT]; sq.directions[DOWN_RIGHT] = sq.directions[RIGHT]; sq.flip = TRUE; if (firstix < 0) firstix = ix & 3; ix -= firstix; sq.tetra_class = ((row+(ix&1)) & 2) ^ (ix & 3); callback(ctx, &sq); } /* * There are `rowlen+other' up-pointing triangles. */ for (i = 0; i < rowlen+other; i++) { struct grid_square sq; int ix; float x, y; ix = (2 * i - (rowlen+other-1)); x = ix * 0.5F; y = theight * row; sq.x = x; sq.y = y + 2*theight / 3; sq.points[0] = x + 0.5F; sq.points[1] = y + theight; sq.points[2] = x; sq.points[3] = y; sq.points[4] = x - 0.5F; sq.points[5] = y + theight; sq.npoints = 3; sq.directions[LEFT] = 0x06; /* 1,2 */ sq.directions[RIGHT] = 0x03; /* 0,1 */ sq.directions[DOWN] = 0x05; /* 0,2 */ sq.directions[UP] = 0; /* invalid move */ /* * Up-pointing triangle: both the down diagonals go * down, and the up ones go left and right. */ sq.directions[DOWN_LEFT] = sq.directions[DOWN_RIGHT] = sq.directions[DOWN]; sq.directions[UP_LEFT] = sq.directions[LEFT]; sq.directions[UP_RIGHT] = sq.directions[RIGHT]; sq.flip = FALSE; if (firstix < 0) firstix = (ix - 1) & 3; ix -= firstix; sq.tetra_class = ((row+(ix&1)) & 2) ^ (ix & 3); callback(ctx, &sq); } } } } static int grid_area(int d1, int d2, int order) { /* * An NxM grid of squares has NM squares in it. * * A grid of triangles with dimensions A and B has a total of * A^2 + B^2 + 4AB triangles in it. (You can divide it up into * a side-A triangle containing A^2 subtriangles, a side-B * triangle containing B^2, and two congruent parallelograms, * each with side lengths A and B, each therefore containing AB * two-triangle rhombuses.) */ if (order == 4) return d1 * d2; else return d1*d1 + d2*d2 + 4*d1*d2; } static config_item *game_configure(const game_params *params) { config_item *ret = snewn(4, config_item); char buf[80]; ret[0].name = "Type of solid"; ret[0].type = C_CHOICES; ret[0].sval = ":Tetrahedron:Cube:Octahedron:Icosahedron"; ret[0].ival = params->solid; ret[1].name = "Width / top"; ret[1].type = C_STRING; sprintf(buf, "%d", params->d1); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Height / bottom"; ret[2].type = C_STRING; sprintf(buf, "%d", params->d2); ret[2].sval = dupstr(buf); ret[2].ival = 0; ret[3].name = NULL; ret[3].type = C_END; ret[3].sval = NULL; ret[3].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->solid = cfg[0].ival; ret->d1 = atoi(cfg[1].sval); ret->d2 = atoi(cfg[2].sval); return ret; } static void count_grid_square_callback(void *ctx, struct grid_square *sq) { int *classes = (int *)ctx; int thisclass; if (classes[4] == 4) thisclass = sq->tetra_class; else if (classes[4] == 2) thisclass = sq->flip; else thisclass = 0; classes[thisclass]++; } static char *validate_params(const game_params *params, int full) { int classes[5]; int i; if (params->solid < 0 || params->solid >= lenof(solids)) return "Unrecognised solid type"; if (solids[params->solid]->order == 4) { if (params->d1 <= 0 || params->d2 <= 0) return "Both grid dimensions must be greater than zero"; } else { if (params->d1 <= 0 && params->d2 <= 0) return "At least one grid dimension must be greater than zero"; } for (i = 0; i < 4; i++) classes[i] = 0; if (params->solid == TETRAHEDRON) classes[4] = 4; else if (params->solid == OCTAHEDRON) classes[4] = 2; else classes[4] = 1; enum_grid_squares(params, count_grid_square_callback, classes); for (i = 0; i < classes[4]; i++) if (classes[i] < solids[params->solid]->nfaces / classes[4]) return "Not enough grid space to place all blue faces"; if (grid_area(params->d1, params->d2, solids[params->solid]->order) < solids[params->solid]->nfaces + 1) return "Not enough space to place the solid on an empty square"; return NULL; } struct grid_data { int *gridptrs[4]; int nsquares[4]; int nclasses; int squareindex; }; static void classify_grid_square_callback(void *ctx, struct grid_square *sq) { struct grid_data *data = (struct grid_data *)ctx; int thisclass; if (data->nclasses == 4) thisclass = sq->tetra_class; else if (data->nclasses == 2) thisclass = sq->flip; else thisclass = 0; data->gridptrs[thisclass][data->nsquares[thisclass]++] = data->squareindex++; } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { struct grid_data data; int i, j, k, m, area, facesperclass; int *flags; char *desc, *p; /* * Enumerate the grid squares, dividing them into equivalence * classes as appropriate. (For the tetrahedron, there is one * equivalence class for each face; for the octahedron there * are two classes; for the other two solids there's only one.) */ area = grid_area(params->d1, params->d2, solids[params->solid]->order); if (params->solid == TETRAHEDRON) data.nclasses = 4; else if (params->solid == OCTAHEDRON) data.nclasses = 2; else data.nclasses = 1; data.gridptrs[0] = snewn(data.nclasses * area, int); for (i = 0; i < data.nclasses; i++) { data.gridptrs[i] = data.gridptrs[0] + i * area; data.nsquares[i] = 0; } data.squareindex = 0; enum_grid_squares(params, classify_grid_square_callback, &data); facesperclass = solids[params->solid]->nfaces / data.nclasses; for (i = 0; i < data.nclasses; i++) assert(data.nsquares[i] >= facesperclass); assert(data.squareindex == area); /* * So now we know how many faces to allocate in each class. Get * on with it. */ flags = snewn(area, int); for (i = 0; i < area; i++) flags[i] = FALSE; for (i = 0; i < data.nclasses; i++) { for (j = 0; j < facesperclass; j++) { int n = random_upto(rs, data.nsquares[i]); assert(!flags[data.gridptrs[i][n]]); flags[data.gridptrs[i][n]] = TRUE; /* * Move everything else up the array. I ought to use a * better data structure for this, but for such small * numbers it hardly seems worth the effort. */ while (n < data.nsquares[i]-1) { data.gridptrs[i][n] = data.gridptrs[i][n+1]; n++; } data.nsquares[i]--; } } /* * Now we know precisely which squares are blue. Encode this * information in hex. While we're looping over this, collect * the non-blue squares into a list in the now-unused gridptrs * array. */ desc = snewn(area / 4 + 40, char); p = desc; j = 0; k = 8; m = 0; for (i = 0; i < area; i++) { if (flags[i]) { j |= k; } else { data.gridptrs[0][m++] = i; } k >>= 1; if (!k) { *p++ = "0123456789ABCDEF"[j]; k = 8; j = 0; } } if (k != 8) *p++ = "0123456789ABCDEF"[j]; /* * Choose a non-blue square for the polyhedron. */ sprintf(p, ",%d", data.gridptrs[0][random_upto(rs, m)]); sfree(data.gridptrs[0]); sfree(flags); return desc; } static void add_grid_square_callback(void *ctx, struct grid_square *sq) { game_grid *grid = (game_grid *)ctx; grid->squares[grid->nsquares++] = *sq; /* structure copy */ } static int lowest_face(const struct solid *solid) { int i, j, best; float zmin; best = 0; zmin = 0.0; for (i = 0; i < solid->nfaces; i++) { float z = 0; for (j = 0; j < solid->order; j++) { int f = solid->faces[i*solid->order + j]; z += solid->vertices[f*3+2]; } if (i == 0 || zmin > z) { zmin = z; best = i; } } return best; } static int align_poly(const struct solid *solid, struct grid_square *sq, int *pkey) { float zmin; int i, j; int flip = (sq->flip ? -1 : +1); /* * First, find the lowest z-coordinate present in the solid. */ zmin = 0.0; for (i = 0; i < solid->nvertices; i++) if (zmin > solid->vertices[i*3+2]) zmin = solid->vertices[i*3+2]; /* * Now go round the grid square. For each point in the grid * square, we're looking for a point of the polyhedron with the * same x- and y-coordinates (relative to the square's centre), * and z-coordinate equal to zmin (near enough). */ for (j = 0; j < sq->npoints; j++) { int matches, index; matches = 0; index = -1; for (i = 0; i < solid->nvertices; i++) { float dist = 0; dist += SQ(solid->vertices[i*3+0] * flip - sq->points[j*2+0] + sq->x); dist += SQ(solid->vertices[i*3+1] * flip - sq->points[j*2+1] + sq->y); dist += SQ(solid->vertices[i*3+2] - zmin); if (dist < 0.1) { matches++; index = i; } } if (matches != 1 || index < 0) return FALSE; pkey[j] = index; } return TRUE; } static void flip_poly(struct solid *solid, int flip) { int i; if (flip) { for (i = 0; i < solid->nvertices; i++) { solid->vertices[i*3+0] *= -1; solid->vertices[i*3+1] *= -1; } for (i = 0; i < solid->nfaces; i++) { solid->normals[i*3+0] *= -1; solid->normals[i*3+1] *= -1; } } } static struct solid *transform_poly(const struct solid *solid, int flip, int key0, int key1, float angle) { struct solid *ret = snew(struct solid); float vx, vy, ax, ay; float vmatrix[9], amatrix[9], vmatrix2[9]; int i; *ret = *solid; /* structure copy */ flip_poly(ret, flip); /* * Now rotate the polyhedron through the given angle. We must * rotate about the Z-axis to bring the two vertices key0 and * key1 into horizontal alignment, then rotate about the * X-axis, then rotate back again. */ vx = ret->vertices[key1*3+0] - ret->vertices[key0*3+0]; vy = ret->vertices[key1*3+1] - ret->vertices[key0*3+1]; assert(APPROXEQ(vx*vx + vy*vy, 1.0)); vmatrix[0] = vx; vmatrix[3] = vy; vmatrix[6] = 0; vmatrix[1] = -vy; vmatrix[4] = vx; vmatrix[7] = 0; vmatrix[2] = 0; vmatrix[5] = 0; vmatrix[8] = 1; ax = (float)cos(angle); ay = (float)sin(angle); amatrix[0] = 1; amatrix[3] = 0; amatrix[6] = 0; amatrix[1] = 0; amatrix[4] = ax; amatrix[7] = ay; amatrix[2] = 0; amatrix[5] = -ay; amatrix[8] = ax; memcpy(vmatrix2, vmatrix, sizeof(vmatrix)); vmatrix2[1] = vy; vmatrix2[3] = -vy; for (i = 0; i < ret->nvertices; i++) { MATMUL(ret->vertices + 3*i, vmatrix, ret->vertices + 3*i); MATMUL(ret->vertices + 3*i, amatrix, ret->vertices + 3*i); MATMUL(ret->vertices + 3*i, vmatrix2, ret->vertices + 3*i); } for (i = 0; i < ret->nfaces; i++) { MATMUL(ret->normals + 3*i, vmatrix, ret->normals + 3*i); MATMUL(ret->normals + 3*i, amatrix, ret->normals + 3*i); MATMUL(ret->normals + 3*i, vmatrix2, ret->normals + 3*i); } return ret; } static char *validate_desc(const game_params *params, const char *desc) { int area = grid_area(params->d1, params->d2, solids[params->solid]->order); int i, j; i = (area + 3) / 4; for (j = 0; j < i; j++) { int c = desc[j]; if (c >= '0' && c <= '9') continue; if (c >= 'A' && c <= 'F') continue; if (c >= 'a' && c <= 'f') continue; return "Not enough hex digits at start of string"; /* NB if desc[j]=='\0' that will also be caught here, so we're safe */ } if (desc[i] != ',') return "Expected ',' after hex digits"; i++; do { if (desc[i] < '0' || desc[i] > '9') return "Expected decimal integer after ','"; i++; } while (desc[i]); return NULL; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { game_grid *grid = snew(game_grid); game_state *state = snew(game_state); int area; state->params = *params; /* structure copy */ state->solid = solids[params->solid]; area = grid_area(params->d1, params->d2, state->solid->order); grid->squares = snewn(area, struct grid_square); grid->nsquares = 0; enum_grid_squares(params, add_grid_square_callback, grid); assert(grid->nsquares == area); state->grid = grid; grid->refcount = 1; state->facecolours = snewn(state->solid->nfaces, int); memset(state->facecolours, 0, state->solid->nfaces * sizeof(int)); state->bluemask = snewn((state->grid->nsquares + 31) / 32, unsigned long); memset(state->bluemask, 0, (state->grid->nsquares + 31) / 32 * sizeof(unsigned long)); /* * Set up the blue squares and polyhedron position according to * the game description. */ { const char *p = desc; int i, j, v; j = 8; v = 0; for (i = 0; i < state->grid->nsquares; i++) { if (j == 8) { v = *p++; if (v >= '0' && v <= '9') v -= '0'; else if (v >= 'A' && v <= 'F') v -= 'A' - 10; else if (v >= 'a' && v <= 'f') v -= 'a' - 10; else break; } if (v & j) SET_SQUARE(state, i, TRUE); j >>= 1; if (j == 0) j = 8; } if (*p == ',') p++; state->current = atoi(p); if (state->current < 0 || state->current >= state->grid->nsquares) state->current = 0; /* got to do _something_ */ } /* * Align the polyhedron with its grid square and determine * initial key points. */ { int pkey[4]; int ret; ret = align_poly(state->solid, &state->grid->squares[state->current], pkey); assert(ret); state->dpkey[0] = state->spkey[0] = pkey[0]; state->dpkey[1] = state->spkey[0] = pkey[1]; state->dgkey[0] = state->sgkey[0] = 0; state->dgkey[1] = state->sgkey[0] = 1; } state->previous = state->current; state->angle = 0.0; state->completed = 0; state->movecount = 0; return state; } static game_state *dup_game(const game_state *state) { game_state *ret = snew(game_state); ret->params = state->params; /* structure copy */ ret->solid = state->solid; ret->facecolours = snewn(ret->solid->nfaces, int); memcpy(ret->facecolours, state->facecolours, ret->solid->nfaces * sizeof(int)); ret->current = state->current; ret->grid = state->grid; ret->grid->refcount++; ret->bluemask = snewn((ret->grid->nsquares + 31) / 32, unsigned long); memcpy(ret->bluemask, state->bluemask, (ret->grid->nsquares + 31) / 32 * sizeof(unsigned long)); ret->dpkey[0] = state->dpkey[0]; ret->dpkey[1] = state->dpkey[1]; ret->dgkey[0] = state->dgkey[0]; ret->dgkey[1] = state->dgkey[1]; ret->spkey[0] = state->spkey[0]; ret->spkey[1] = state->spkey[1]; ret->sgkey[0] = state->sgkey[0]; ret->sgkey[1] = state->sgkey[1]; ret->previous = state->previous; ret->angle = state->angle; ret->completed = state->completed; ret->movecount = state->movecount; return ret; } static void free_game(game_state *state) { if (--state->grid->refcount <= 0) { sfree(state->grid->squares); sfree(state->grid); } sfree(state->bluemask); sfree(state->facecolours); sfree(state); } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { return NULL; } static int 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 { float gridscale; int ox, oy; /* pixel position of float origin */ }; /* * Code shared between interpret_move() and execute_move(). */ static int find_move_dest(const game_state *from, int direction, int *skey, int *dkey) { int mask, dest, i, j; float points[4]; /* * Find the two points in the current grid square which * correspond to this move. */ mask = from->grid->squares[from->current].directions[direction]; if (mask == 0) return -1; for (i = j = 0; i < from->grid->squares[from->current].npoints; i++) if (mask & (1 << i)) { points[j*2] = from->grid->squares[from->current].points[i*2]; points[j*2+1] = from->grid->squares[from->current].points[i*2+1]; skey[j] = i; j++; } assert(j == 2); /* * Now find the other grid square which shares those points. * This is our move destination. */ dest = -1; for (i = 0; i < from->grid->nsquares; i++) if (i != from->current) { int match = 0; float dist; for (j = 0; j < from->grid->squares[i].npoints; j++) { dist = (SQ(from->grid->squares[i].points[j*2] - points[0]) + SQ(from->grid->squares[i].points[j*2+1] - points[1])); if (dist < 0.1) dkey[match++] = j; dist = (SQ(from->grid->squares[i].points[j*2] - points[2]) + SQ(from->grid->squares[i].points[j*2+1] - points[3])); if (dist < 0.1) dkey[match++] = j; } if (match == 2) { dest = i; break; } } return dest; } static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { int direction, mask, i; int skey[2], dkey[2]; button = button & (~MOD_MASK | MOD_NUM_KEYPAD); /* * Moves can be made with the cursor keys or numeric keypad, or * alternatively you can left-click and the polyhedron will * move in the general direction of the mouse pointer. */ if (button == CURSOR_UP || button == (MOD_NUM_KEYPAD | '8')) direction = UP; else if (button == CURSOR_DOWN || button == (MOD_NUM_KEYPAD | '2')) direction = DOWN; else if (button == CURSOR_LEFT || button == (MOD_NUM_KEYPAD | '4')) direction = LEFT; else if (button == CURSOR_RIGHT || button == (MOD_NUM_KEYPAD | '6')) direction = RIGHT; else if (button == (MOD_NUM_KEYPAD | '7')) direction = UP_LEFT; else if (button == (MOD_NUM_KEYPAD | '1')) direction = DOWN_LEFT; else if (button == (MOD_NUM_KEYPAD | '9')) direction = UP_RIGHT; else if (button == (MOD_NUM_KEYPAD | '3')) direction = DOWN_RIGHT; else if (button == LEFT_BUTTON) { /* * Find the bearing of the click point from the current * square's centre. */ int cx, cy; double angle; cx = (int)(state->grid->squares[state->current].x * GRID_SCALE) + ds->ox; cy = (int)(state->grid->squares[state->current].y * GRID_SCALE) + ds->oy; if (x == cx && y == cy) return NULL; /* clicked in exact centre! */ angle = atan2(y - cy, x - cx); /* * There are three possibilities. * * - This square is a square, so we choose between UP, * DOWN, LEFT and RIGHT by dividing the available angle * at the 45-degree points. * * - This square is an up-pointing triangle, so we choose * between DOWN, LEFT and RIGHT by dividing into * 120-degree arcs. * * - This square is a down-pointing triangle, so we choose * between UP, LEFT and RIGHT in the inverse manner. * * Don't forget that since our y-coordinates increase * downwards, `angle' is measured _clockwise_ from the * x-axis, not anticlockwise as most mathematicians would * instinctively assume. */ if (state->grid->squares[state->current].npoints == 4) { /* Square. */ if (fabs(angle) > 3*PI/4) direction = LEFT; else if (fabs(angle) < PI/4) direction = RIGHT; else if (angle > 0) direction = DOWN; else direction = UP; } else if (state->grid->squares[state->current].directions[UP] == 0) { /* Up-pointing triangle. */ if (angle < -PI/2 || angle > 5*PI/6) direction = LEFT; else if (angle > PI/6) direction = DOWN; else direction = RIGHT; } else { /* Down-pointing triangle. */ assert(state->grid->squares[state->current].directions[DOWN] == 0); if (angle > PI/2 || angle < -5*PI/6) direction = LEFT; else if (angle < -PI/6) direction = UP; else direction = RIGHT; } } else return NULL; mask = state->grid->squares[state->current].directions[direction]; if (mask == 0) return NULL; /* * Translate diagonal directions into orthogonal ones. */ if (direction > DOWN) { for (i = LEFT; i <= DOWN; i++) if (state->grid->squares[state->current].directions[i] == mask) { direction = i; break; } assert(direction <= DOWN); } if (find_move_dest(state, direction, skey, dkey) < 0) return NULL; if (direction == LEFT) return dupstr("L"); if (direction == RIGHT) return dupstr("R"); if (direction == UP) return dupstr("U"); if (direction == DOWN) return dupstr("D"); return NULL; /* should never happen */ } static game_state *execute_move(const game_state *from, const char *move) { game_state *ret; float angle; struct solid *poly; int pkey[2]; int skey[2], dkey[2]; int i, j, dest; int direction; switch (*move) { case 'L': direction = LEFT; break; case 'R': direction = RIGHT; break; case 'U': direction = UP; break; case 'D': direction = DOWN; break; default: return NULL; } dest = find_move_dest(from, direction, skey, dkey); if (dest < 0) return NULL; ret = dup_game(from); ret->current = dest; /* * So we know what grid square we're aiming for, and we also * know the two key points (as indices in both the source and * destination grid squares) which are invariant between source * and destination. * * Next we must roll the polyhedron on to that square. So we * find the indices of the key points within the polyhedron's * vertex array, then use those in a call to transform_poly, * and align the result on the new grid square. */ { int all_pkey[4]; align_poly(from->solid, &from->grid->squares[from->current], all_pkey); pkey[0] = all_pkey[skey[0]]; pkey[1] = all_pkey[skey[1]]; /* * Now pkey[0] corresponds to skey[0] and dkey[0], and * likewise [1]. */ } /* * Now find the angle through which to rotate the polyhedron. * Do this by finding the two faces that share the two vertices * we've found, and taking the dot product of their normals. */ { int f[2], nf = 0; float dp; for (i = 0; i < from->solid->nfaces; i++) { int match = 0; for (j = 0; j < from->solid->order; j++) if (from->solid->faces[i*from->solid->order + j] == pkey[0] || from->solid->faces[i*from->solid->order + j] == pkey[1]) match++; if (match == 2) { assert(nf < 2); f[nf++] = i; } } assert(nf == 2); dp = 0; for (i = 0; i < 3; i++) dp += (from->solid->normals[f[0]*3+i] * from->solid->normals[f[1]*3+i]); angle = (float)acos(dp); } /* * Now transform the polyhedron. We aren't entirely sure * whether we need to rotate through angle or -angle, and the * simplest way round this is to try both and see which one * aligns successfully! * * Unfortunately, _both_ will align successfully if this is a * cube, which won't tell us anything much. So for that * particular case, I resort to gross hackery: I simply negate * the angle before trying the alignment, depending on the * direction. Which directions work which way is determined by * pure trial and error. I said it was gross :-/ */ { int all_pkey[4]; int success; if (from->solid->order == 4 && direction == UP) angle = -angle; /* HACK */ poly = transform_poly(from->solid, from->grid->squares[from->current].flip, pkey[0], pkey[1], angle); flip_poly(poly, from->grid->squares[ret->current].flip); success = align_poly(poly, &from->grid->squares[ret->current], all_pkey); if (!success) { sfree(poly); angle = -angle; poly = transform_poly(from->solid, from->grid->squares[from->current].flip, pkey[0], pkey[1], angle); flip_poly(poly, from->grid->squares[ret->current].flip); success = align_poly(poly, &from->grid->squares[ret->current], all_pkey); } assert(success); } /* * Now we have our rotated polyhedron, which we expect to be * exactly congruent to the one we started with - but with the * faces permuted. So we map that congruence and thereby figure * out how to permute the faces as a result of the polyhedron * having rolled. */ { int *newcolours = snewn(from->solid->nfaces, int); for (i = 0; i < from->solid->nfaces; i++) newcolours[i] = -1; for (i = 0; i < from->solid->nfaces; i++) { int nmatch = 0; /* * Now go through the transformed polyhedron's faces * and figure out which one's normal is approximately * equal to this one. */ for (j = 0; j < poly->nfaces; j++) { float dist; int k; dist = 0; for (k = 0; k < 3; k++) dist += SQ(poly->normals[j*3+k] - from->solid->normals[i*3+k]); if (APPROXEQ(dist, 0)) { nmatch++; newcolours[i] = ret->facecolours[j]; } } assert(nmatch == 1); } for (i = 0; i < from->solid->nfaces; i++) assert(newcolours[i] != -1); sfree(ret->facecolours); ret->facecolours = newcolours; } ret->movecount++; /* * And finally, swap the colour between the bottom face of the * polyhedron and the face we've just landed on. * * We don't do this if the game is already complete, since we * allow the user to roll the fully blue polyhedron around the * grid as a feeble reward. */ if (!ret->completed) { i = lowest_face(from->solid); j = ret->facecolours[i]; ret->facecolours[i] = GET_SQUARE(ret, ret->current); SET_SQUARE(ret, ret->current, j); /* * Detect game completion. */ j = 0; for (i = 0; i < ret->solid->nfaces; i++) if (ret->facecolours[i]) j++; if (j == ret->solid->nfaces) ret->completed = ret->movecount; } sfree(poly); /* * Align the normal polyhedron with its grid square, to get key * points for non-animated display. */ { int pkey[4]; int success; success = align_poly(ret->solid, &ret->grid->squares[ret->current], pkey); assert(success); ret->dpkey[0] = pkey[0]; ret->dpkey[1] = pkey[1]; ret->dgkey[0] = 0; ret->dgkey[1] = 1; } ret->spkey[0] = pkey[0]; ret->spkey[1] = pkey[1]; ret->sgkey[0] = skey[0]; ret->sgkey[1] = skey[1]; ret->previous = from->current; ret->angle = angle; return ret; } /* ---------------------------------------------------------------------- * Drawing routines. */ struct bbox { float l, r, u, d; }; static void find_bbox_callback(void *ctx, struct grid_square *sq) { struct bbox *bb = (struct bbox *)ctx; int i; for (i = 0; i < sq->npoints; i++) { if (bb->l > sq->points[i*2]) bb->l = sq->points[i*2]; if (bb->r < sq->points[i*2]) bb->r = sq->points[i*2]; if (bb->u > sq->points[i*2+1]) bb->u = sq->points[i*2+1]; if (bb->d < sq->points[i*2+1]) bb->d = sq->points[i*2+1]; } } static struct bbox find_bbox(const game_params *params) { struct bbox bb; /* * These should be hugely more than the real bounding box will * be. */ bb.l = 2.0F * (params->d1 + params->d2); bb.r = -2.0F * (params->d1 + params->d2); bb.u = 2.0F * (params->d1 + params->d2); bb.d = -2.0F * (params->d1 + params->d2); enum_grid_squares(params, find_bbox_callback, &bb); return bb; } #define XSIZE(gs, bb, solid) \ ((int)(((bb).r - (bb).l + 2*(solid)->border) * gs)) #define YSIZE(gs, bb, solid) \ ((int)(((bb).d - (bb).u + 2*(solid)->border) * gs)) static void game_compute_size(const game_params *params, int tilesize, int *x, int *y) { struct bbox bb = find_bbox(params); *x = XSIZE(tilesize, bb, solids[params->solid]); *y = YSIZE(tilesize, bb, solids[params->solid]); } static void game_set_size(drawing *dr, game_drawstate *ds, const game_params *params, int tilesize) { struct bbox bb = find_bbox(params); ds->gridscale = (float)tilesize; ds->ox = (int)(-(bb.l - solids[params->solid]->border) * ds->gridscale); ds->oy = (int)(-(bb.u - solids[params->solid]->border) * ds->gridscale); } static float *game_colours(frontend *fe, int *ncolours) { float *ret = snewn(3 * NCOLOURS, float); frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); ret[COL_BORDER * 3 + 0] = 0.0; ret[COL_BORDER * 3 + 1] = 0.0; ret[COL_BORDER * 3 + 2] = 0.0; ret[COL_BLUE * 3 + 0] = 0.0; ret[COL_BLUE * 3 + 1] = 0.0; ret[COL_BLUE * 3 + 2] = 1.0; *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->ox = ds->oy = 0; ds->gridscale = 0.0F; /* not decided yet */ 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) { int i, j; struct bbox bb = find_bbox(&state->params); struct solid *poly; const int *pkey, *gkey; float t[3]; float angle; int square; draw_rect(dr, 0, 0, XSIZE(GRID_SCALE, bb, state->solid), YSIZE(GRID_SCALE, bb, state->solid), COL_BACKGROUND); if (dir < 0) { const game_state *t; /* * This is an Undo. So reverse the order of the states, and * run the roll timer backwards. */ assert(oldstate); t = oldstate; oldstate = state; state = t; animtime = ROLLTIME - animtime; } if (!oldstate) { oldstate = state; angle = 0.0; square = state->current; pkey = state->dpkey; gkey = state->dgkey; } else { angle = state->angle * animtime / ROLLTIME; square = state->previous; pkey = state->spkey; gkey = state->sgkey; } state = oldstate; for (i = 0; i < state->grid->nsquares; i++) { int coords[8]; for (j = 0; j < state->grid->squares[i].npoints; j++) { coords[2*j] = ((int)(state->grid->squares[i].points[2*j] * GRID_SCALE) + ds->ox); coords[2*j+1] = ((int)(state->grid->squares[i].points[2*j+1]*GRID_SCALE) + ds->oy); } draw_polygon(dr, coords, state->grid->squares[i].npoints, GET_SQUARE(state, i) ? COL_BLUE : COL_BACKGROUND, COL_BORDER); } /* * Now compute and draw the polyhedron. */ poly = transform_poly(state->solid, state->grid->squares[square].flip, pkey[0], pkey[1], angle); /* * Compute the translation required to align the two key points * on the polyhedron with the same key points on the current * face. */ for (i = 0; i < 3; i++) { float tc = 0.0; for (j = 0; j < 2; j++) { float grid_coord; if (i < 2) { grid_coord = state->grid->squares[square].points[gkey[j]*2+i]; } else { grid_coord = 0.0; } tc += (grid_coord - poly->vertices[pkey[j]*3+i]); } t[i] = tc / 2; } for (i = 0; i < poly->nvertices; i++) for (j = 0; j < 3; j++) poly->vertices[i*3+j] += t[j]; /* * Now actually draw each face. */ for (i = 0; i < poly->nfaces; i++) { float points[8]; int coords[8]; for (j = 0; j < poly->order; j++) { int f = poly->faces[i*poly->order + j]; points[j*2] = (poly->vertices[f*3+0] - poly->vertices[f*3+2] * poly->shear); points[j*2+1] = (poly->vertices[f*3+1] - poly->vertices[f*3+2] * poly->shear); } for (j = 0; j < poly->order; j++) { coords[j*2] = (int)floor(points[j*2] * GRID_SCALE) + ds->ox; coords[j*2+1] = (int)floor(points[j*2+1] * GRID_SCALE) + ds->oy; } /* * Find out whether these points are in a clockwise or * anticlockwise arrangement. If the latter, discard the * face because it's facing away from the viewer. * * This would involve fiddly winding-number stuff for a * general polygon, but for the simple parallelograms we'll * be seeing here, all we have to do is check whether the * corners turn right or left. So we'll take the vector * from point 0 to point 1, turn it right 90 degrees, * and check the sign of the dot product with that and the * next vector (point 1 to point 2). */ { float v1x = points[2]-points[0]; float v1y = points[3]-points[1]; float v2x = points[4]-points[2]; float v2y = points[5]-points[3]; float dp = v1x * v2y - v1y * v2x; if (dp <= 0) continue; } draw_polygon(dr, coords, poly->order, state->facecolours[i] ? COL_BLUE : COL_BACKGROUND, COL_BORDER); } sfree(poly); draw_update(dr, 0, 0, XSIZE(GRID_SCALE, bb, state->solid), YSIZE(GRID_SCALE, bb, state->solid)); /* * Update the status bar. */ { char statusbuf[256]; sprintf(statusbuf, "%sMoves: %d", (state->completed ? "COMPLETED! " : ""), (state->completed ? state->completed : state->movecount)); status_bar(dr, statusbuf); } } static float game_anim_length(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui) { return ROLLTIME; } static float game_flash_length(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui) { return 0.0F; } static int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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 cube #endif const struct game thegame = { "Cube", "games.cube", "cube", default_params, game_fetch_preset, 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, game_changed_state, interpret_move, execute_move, PREFERRED_GRID_SCALE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, FALSE, FALSE, game_print_size, game_print, TRUE, /* wants_statusbar */ FALSE, game_timing_state, 0, /* flags */ }; puzzles-r9872/divvy.c0000644000175300017530000005453110776442072013753 0ustar simonsimon/* * Library code to divide up a rectangle into a number of equally * sized ominoes, in a random fashion. * * Could use this for generating solved grids of * http://www.nikoli.co.jp/ja/puzzles/block_puzzle/ * or for generating the playfield for Jigsaw Sudoku. */ /* * This code is restricted to simply connected solutions: that is, * no single polyomino may completely surround another (not even * with a corner visible to the outside world, in the sense that a * 7-omino can `surround' a single square). * * It's tempting to think that this is a natural consequence of * all the ominoes being the same size - after all, a division of * anything into 7-ominoes must necessarily have all of them * simply connected, because if one was not then the 1-square * space in the middle could not be part of any 7-omino - but in * fact, for sufficiently large k, it is perfectly possible for a * k-omino to completely surround another k-omino. A simple * example is this one with two 25-ominoes: * * +--+--+--+--+--+--+--+ * | | * + +--+--+--+--+--+ + * | | | | * + + + + * | | | | * + + + +--+ * | | | | * + + + +--+ * | | | | * + + + + * | | | | * + +--+--+--+--+--+ + * | | * +--+--+--+--+--+--+--+ * * I claim the smallest k which can manage this is 23. More * formally: * * If a k-omino P is completely surrounded by another k-omino Q, * such that every edge of P borders on Q, then k >= 23. * * Proof: * * It's relatively simple to find the largest _rectangle_ a * k-omino can enclose. So I'll construct my proof in two parts: * firstly, show that no 22-omino or smaller can enclose a * rectangle as large as itself, and secondly, show that no * polyomino can enclose a larger non-rectangle than a rectangle. * * The first of those claims: * * To surround an m x n rectangle, a polyomino must have 2m * squares along the two m-sides of the rectangle, 2n squares * along the two n-sides, and must fill in at least three of the * corners in order to be connected. Thus, 2(m+n)+3 <= k. We wish * to find the largest value of mn subject to that constraint, and * it's clear that this is achieved when m and n are as close to * equal as possible. (If they aren't, WLOG suppose m < n; then * (m+1)(n-1) = mn + n - m - 1 >= mn, with equality only when * m=n-1.) * * So the area of the largest rectangle which can be enclosed by a * k-omino is given by floor(k'/2) * ceil(k'/2), where k' = * (k-3)/2. This is a monotonic function in k, so there will be a * unique point at which it goes from being smaller than k to * being larger than k. That point is between 22 (maximum area 20) * and 23 (maximum area 25). * * The second claim: * * Suppose we have an inner polyomino P surrounded by an outer * polyomino Q. I seek to show that if P is non-rectangular, then * P is also non-maximal, in the sense that we can transform P and * Q into a new pair of polyominoes in which P is larger and Q is * at most the same size. * * Consider walking along the boundary of P in a clockwise * direction. (We may assume, of course, that there is only _one_ * boundary of P, i.e. P has no hole in the middle. If it does * have a hole in the middle, it's _trivially_ non-maximal because * we can just fill the hole in!) Our walk will take us along many * edges between squares; sometimes we might turn left, and * certainly sometimes we will turn right. Always there will be a * square of P on our right, and a square of Q on our left. * * The net angle through which we turn during the entire walk must * add up to 360 degrees rightwards. So if there are no left * turns, then we must turn right exactly four times, meaning we * have described a rectangle. Hence, if P is _not_ rectangular, * then there must have been a left turn at some point. A left * turn must mean we walk along two edges of the same square of Q. * * Thus, there is some square X in Q which is adjacent to two * diagonally separated squares in P. Let us call those two * squares N and E; let us refer to the other two neighbours of X * as S and W; let us refer to the other mutual neighbour of S and * W as D; and let us refer to the other mutual neighbour of S and * E as Y. In other words, we have named seven squares, arranged * thus: * * N * W X E * D S Y * * where N and E are in P, and X is in Q. * * Clearly at least one of W and S must be in Q (because otherwise * X would not be connected to any other square in Q, and would * hence have to be the whole of Q; and evidently if Q were a * 1-omino it could not enclose _anything_). So we divide into * cases: * * If both W and S are in Q, then we take X out of Q and put it in * P, which does not expose any edge of P. If this disconnects Q, * then we can reconnect it by adding D to Q. * * If only one of W and S is in Q, then wlog let it be W. If S is * in _P_, then we have a particularly easy case: we can simply * take X out of Q and add it to P, and this cannot disconnect X * since X was a leaf square of Q. * * Our remaining case is that W is in Q and S is in neither P nor * Q. Again we take X out of Q and put it in P; we also add S to * Q. This ensures we do not expose an edge of P, but we must now * prove that S is adjacent to some other existing square of Q so * that we haven't disconnected Q by adding it. * * To do this, we recall that we walked along the edge XE, and * then turned left to walk along XN. So just before doing all * that, we must have reached the corner XSE, and we must have * done it by walking along one of the three edges meeting at that * corner which are _not_ XE. It can't have been SY, since S would * then have been on our left and it isn't in Q; and it can't have * been XS, since S would then have been on our right and it isn't * in P. So it must have been YE, in which case Y was on our left, * and hence is in Q. * * So in all cases we have shown that we can take X out of Q and * add it to P, and add at most one square to Q to restore the * containment and connectedness properties. Hence, we can keep * doing this until we run out of left turns and P becomes * rectangular. [] * * ------------ * * Anyway, that entire proof was a bit of a sidetrack. The point * is, although constructions of this type are possible for * sufficiently large k, divvy_rectangle() will never generate * them. This could be considered a weakness for some purposes, in * the sense that we can't generate all possible divisions. * However, there are many divisions which we are highly unlikely * to generate anyway, so in practice it probably isn't _too_ bad. * * If I wanted to fix this issue, I would have to make the rules * more complicated for determining when a square can safely be * _removed_ from a polyomino. Adding one becomes easier (a square * may be added to a polyomino iff it is 4-adjacent to any square * currently part of the polyomino, and the current test for loop * formation may be dispensed with), but to determine which * squares may be removed we must now resort to analysis of the * overall structure of the polyomino rather than the simple local * properties we can currently get away with measuring. */ /* * Possible improvements which might cut the fail rate: * * - instead of picking one omino to extend in an iteration, try * them all in succession (in a randomised order) * * - (for real rigour) instead of bfsing over ominoes, bfs over * the space of possible _removed squares_. That way we aren't * limited to randomly choosing a single square to remove from * an omino and failing if that particular square doesn't * happen to work. * * However, I don't currently think it's necessary to do either of * these, because the failure rate is already low enough to be * easily tolerable, under all circumstances I've been able to * think of. */ #include #include #include #include #include "puzzles.h" /* * Subroutine which implements a function used in computing both * whether a square can safely be added to an omino, and whether * it can safely be removed. * * We enumerate the eight squares 8-adjacent to this one, in * cyclic order. We go round that loop and count the number of * times we find a square owned by the target omino next to one * not owned by it. We then return success iff that count is 2. * * When adding a square to an omino, this is precisely the * criterion which tells us that adding the square won't leave a * hole in the middle of the omino. (If it did, then things get * more complicated; see above.) * * When removing a square from an omino, the _same_ criterion * tells us that removing the square won't disconnect the omino. * (This only works _because_ we've ensured the omino is simply * connected.) */ static int addremcommon(int w, int h, int x, int y, int *own, int val) { int neighbours[8]; int dir, count; for (dir = 0; dir < 8; dir++) { int dx = ((dir & 3) == 2 ? 0 : dir > 2 && dir < 6 ? +1 : -1); int dy = ((dir & 3) == 0 ? 0 : dir < 4 ? -1 : +1); int sx = x+dx, sy = y+dy; if (sx < 0 || sx >= w || sy < 0 || sy >= h) neighbours[dir] = -1; /* outside the grid */ else neighbours[dir] = own[sy*w+sx]; } /* * To begin with, check 4-adjacency. */ if (neighbours[0] != val && neighbours[2] != val && neighbours[4] != val && neighbours[6] != val) return FALSE; count = 0; for (dir = 0; dir < 8; dir++) { int next = (dir + 1) & 7; int gotthis = (neighbours[dir] == val); int gotnext = (neighbours[next] == val); if (gotthis != gotnext) count++; } return (count == 2); } /* * w and h are the dimensions of the rectangle. * * k is the size of the required ominoes. (So k must divide w*h, * of course.) * * The returned result is a w*h-sized dsf. * * In both of the above suggested use cases, the user would * probably want w==h==k, but that isn't a requirement. */ static int *divvy_internal(int w, int h, int k, random_state *rs) { int *order, *queue, *tmp, *own, *sizes, *addable, *removable, *retdsf; int wh = w*h; int i, j, n, x, y, qhead, qtail; n = wh / k; assert(wh == k*n); order = snewn(wh, int); tmp = snewn(wh, int); own = snewn(wh, int); sizes = snewn(n, int); queue = snewn(n, int); addable = snewn(wh*4, int); removable = snewn(wh, int); /* * Permute the grid squares into a random order, which will be * used for iterating over the grid whenever we need to search * for something. This prevents directional bias and arranges * for the answer to be non-deterministic. */ for (i = 0; i < wh; i++) order[i] = i; shuffle(order, wh, sizeof(*order), rs); /* * Begin by choosing a starting square at random for each * omino. */ for (i = 0; i < wh; i++) { own[i] = -1; } for (i = 0; i < n; i++) { own[order[i]] = i; sizes[i] = 1; } /* * Now repeatedly pick a random omino which isn't already at * the target size, and find a way to expand it by one. This * may involve stealing a square from another omino, in which * case we then re-expand that omino, forming a chain of * square-stealing which terminates in an as yet unclaimed * square. Hence every successful iteration around this loop * causes the number of unclaimed squares to drop by one, and * so the process is bounded in duration. */ while (1) { #ifdef DIVVY_DIAGNOSTICS { int x, y; printf("Top of loop. Current grid:\n"); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) printf("%3d", own[y*w+x]); printf("\n"); } } #endif /* * Go over the grid and figure out which squares can * safely be added to, or removed from, each omino. We * don't take account of other ominoes in this process, so * we will often end up knowing that a square can be * poached from one omino by another. * * For each square, there may be up to four ominoes to * which it can be added (those to which it is * 4-adjacent). */ for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { int yx = y*w+x; int curr = own[yx]; int dir; if (curr < 0) { removable[yx] = FALSE; /* can't remove if not owned! */ } else if (sizes[curr] == 1) { removable[yx] = TRUE; /* can always remove a singleton */ } else { /* * See if this square can be removed from its * omino without disconnecting it. */ removable[yx] = addremcommon(w, h, x, y, own, curr); } 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 sx = x + dx, sy = y + dy; int syx = sy*w+sx; addable[yx*4+dir] = -1; if (sx < 0 || sx >= w || sy < 0 || sy >= h) continue; /* no omino here! */ if (own[syx] < 0) continue; /* also no omino here */ if (own[syx] == own[yx]) continue; /* we already got one */ if (!addremcommon(w, h, x, y, own, own[syx])) continue; /* would non-simply connect the omino */ addable[yx*4+dir] = own[syx]; } } } for (i = j = 0; i < n; i++) if (sizes[i] < k) tmp[j++] = i; if (j == 0) break; /* all ominoes are complete! */ j = tmp[random_upto(rs, j)]; #ifdef DIVVY_DIAGNOSTICS printf("Trying to extend %d\n", j); #endif /* * So we're trying to expand omino j. We breadth-first * search out from j across the space of ominoes. * * For bfs purposes, we use two elements of tmp per omino: * tmp[2*i+0] tells us which omino we got to i from, and * tmp[2*i+1] numbers the grid square that omino stole * from us. * * This requires that wh (the size of tmp) is at least 2n, * i.e. k is at least 2. There would have been nothing to * stop a user calling this function with k=1, but if they * did then we wouldn't have got to _here_ in the code - * we would have noticed above that all ominoes were * already at their target sizes, and terminated :-) */ assert(wh >= 2*n); for (i = 0; i < n; i++) tmp[2*i] = tmp[2*i+1] = -1; qhead = qtail = 0; queue[qtail++] = j; tmp[2*j] = tmp[2*j+1] = -2; /* special value: `starting point' */ while (qhead < qtail) { int tmpsq; j = queue[qhead]; /* * We wish to expand omino j. However, we might have * got here by omino j having a square stolen from it, * so first of all we must temporarily mark that * square as not belonging to j, so that our adjacency * calculations don't assume j _does_ belong to us. */ tmpsq = tmp[2*j+1]; if (tmpsq >= 0) { assert(own[tmpsq] == j); own[tmpsq] = -3; } /* * OK. Now begin by seeing if we can find any * unclaimed square into which we can expand omino j. * If we find one, the entire bfs terminates. */ for (i = 0; i < wh; i++) { int dir; if (own[order[i]] != -1) continue; /* this square is claimed */ /* * Special case: if our current omino was size 1 * and then had a square stolen from it, it's now * size zero, which means it's valid to `expand' * it into _any_ unclaimed square. */ if (sizes[j] == 1 && tmpsq >= 0) break; /* got one */ /* * Failing that, we must do the full test for * addability. */ for (dir = 0; dir < 4; dir++) if (addable[order[i]*4+dir] == j) { /* * We know this square is addable to this * omino with the grid in the state it had * at the top of the loop. However, we * must now check that it's _still_ * addable to this omino when the omino is * missing a square. To do this it's only * necessary to re-check addremcommon. */ if (!addremcommon(w, h, order[i]%w, order[i]/w, own, j)) continue; break; } if (dir == 4) continue; /* we can't add this square to j */ break; /* got one! */ } if (i < wh) { i = order[i]; /* * Restore the temporarily removed square _before_ * we start shifting ownerships about. */ if (tmpsq >= 0) own[tmpsq] = j; /* * We are done. We can add square i to omino j, * and then backtrack along the trail in tmp * moving squares between ominoes, ending up * expanding our starting omino by one. */ #ifdef DIVVY_DIAGNOSTICS printf("(%d,%d)", i%w, i/w); #endif while (1) { own[i] = j; #ifdef DIVVY_DIAGNOSTICS printf(" -> %d", j); #endif if (tmp[2*j] == -2) break; i = tmp[2*j+1]; j = tmp[2*j]; #ifdef DIVVY_DIAGNOSTICS printf("; (%d,%d)", i%w, i/w); #endif } #ifdef DIVVY_DIAGNOSTICS printf("\n"); #endif /* * Increment the size of the starting omino. */ sizes[j]++; /* * Terminate the bfs loop. */ break; } /* * If we get here, we haven't been able to expand * omino j into an unclaimed square. So now we begin * to investigate expanding it into squares which are * claimed by ominoes the bfs has not yet visited. */ for (i = 0; i < wh; i++) { int dir, nj; nj = own[order[i]]; if (nj < 0 || tmp[2*nj] != -1) continue; /* unclaimed, or owned by wrong omino */ if (!removable[order[i]]) continue; /* its omino won't let it go */ for (dir = 0; dir < 4; dir++) if (addable[order[i]*4+dir] == j) { /* * As above, re-check addremcommon. */ if (!addremcommon(w, h, order[i]%w, order[i]/w, own, j)) continue; /* * We have found a square we can use to * expand omino j, at the expense of the * as-yet unvisited omino nj. So add this * to the bfs queue. */ assert(qtail < n); queue[qtail++] = nj; tmp[2*nj] = j; tmp[2*nj+1] = order[i]; /* * Now terminate the loop over dir, to * ensure we don't accidentally add the * same omino twice to the queue. */ break; } } /* * Restore the temporarily removed square. */ if (tmpsq >= 0) own[tmpsq] = j; /* * Advance the queue head. */ qhead++; } if (qhead == qtail) { /* * We have finished the bfs and not found any way to * expand omino j. Panic, and return failure. * * FIXME: or should we loop over all ominoes before we * give up? */ #ifdef DIVVY_DIAGNOSTICS printf("FAIL!\n"); #endif retdsf = NULL; goto cleanup; } } #ifdef DIVVY_DIAGNOSTICS { int x, y; printf("SUCCESS! Final grid:\n"); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) printf("%3d", own[y*w+x]); printf("\n"); } } #endif /* * Construct the output dsf. */ for (i = 0; i < wh; i++) { assert(own[i] >= 0 && own[i] < n); tmp[own[i]] = i; } retdsf = snew_dsf(wh); for (i = 0; i < wh; i++) { dsf_merge(retdsf, i, tmp[own[i]]); } /* * Construct the output dsf a different way, to verify that * the ominoes really are k-ominoes and we haven't * accidentally split one into two disconnected pieces. */ dsf_init(tmp, wh); for (y = 0; y < h; y++) for (x = 0; x+1 < w; x++) if (own[y*w+x] == own[y*w+(x+1)]) dsf_merge(tmp, y*w+x, y*w+(x+1)); for (x = 0; x < w; x++) for (y = 0; y+1 < h; y++) if (own[y*w+x] == own[(y+1)*w+x]) dsf_merge(tmp, y*w+x, (y+1)*w+x); for (i = 0; i < wh; i++) { j = dsf_canonify(retdsf, i); assert(dsf_canonify(tmp, j) == dsf_canonify(tmp, i)); } cleanup: /* * Free our temporary working space. */ sfree(order); sfree(tmp); sfree(own); sfree(sizes); sfree(queue); sfree(addable); sfree(removable); /* * And we're done. */ return retdsf; } #ifdef TESTMODE static int fail_counter = 0; #endif int *divvy_rectangle(int w, int h, int k, random_state *rs) { int *ret; do { ret = divvy_internal(w, h, k, rs); #ifdef TESTMODE if (!ret) fail_counter++; #endif } while (!ret); return ret; } #ifdef TESTMODE /* * gcc -g -O0 -DTESTMODE -I.. -o divvy divvy.c ../random.c ../malloc.c ../dsf.c ../misc.c ../nullfe.c * * or to debug * * gcc -g -O0 -DDIVVY_DIAGNOSTICS -DTESTMODE -I.. -o divvy divvy.c ../random.c ../malloc.c ../dsf.c ../misc.c ../nullfe.c */ int main(int argc, char **argv) { int *dsf; int i; int w = 9, h = 4, k = 6, tries = 100; random_state *rs; rs = random_new("123456", 6); if (argc > 1) w = atoi(argv[1]); if (argc > 2) h = atoi(argv[2]); if (argc > 3) k = atoi(argv[3]); if (argc > 4) tries = atoi(argv[4]); for (i = 0; i < tries; i++) { int x, y; dsf = divvy_rectangle(w, h, k, rs); assert(dsf); for (y = 0; y <= 2*h; y++) { for (x = 0; x <= 2*w; x++) { int miny = y/2 - 1, maxy = y/2; int minx = x/2 - 1, maxx = x/2; int classes[4], tx, ty; for (ty = 0; ty < 2; ty++) for (tx = 0; tx < 2; tx++) { int cx = minx+tx, cy = miny+ty; if (cx < 0 || cx >= w || cy < 0 || cy >= h) classes[ty*2+tx] = -1; else classes[ty*2+tx] = dsf_canonify(dsf, cy*w+cx); } switch (y%2 * 2 + x%2) { case 0: /* corner */ /* * Cases for the corner: * * - if all four surrounding squares belong * to the same omino, we print a space. * * - if the top two are the same and the * bottom two are the same, we print a * horizontal line. * * - if the left two are the same and the * right two are the same, we print a * vertical line. * * - otherwise, we print a cross. */ if (classes[0] == classes[1] && classes[1] == classes[2] && classes[2] == classes[3]) printf(" "); else if (classes[0] == classes[1] && classes[2] == classes[3]) printf("-"); else if (classes[0] == classes[2] && classes[1] == classes[3]) printf("|"); else printf("+"); break; case 1: /* horiz edge */ if (classes[1] == classes[3]) printf(" "); else printf("--"); break; case 2: /* vert edge */ if (classes[2] == classes[3]) printf(" "); else printf("|"); break; case 3: /* square centre */ printf(" "); break; } } printf("\n"); } printf("\n"); sfree(dsf); } printf("%d retries needed for %d successes\n", fail_counter, tries); return 0; } #endif puzzles-r9872/dominosa.c0000644000175300017530000013712712132232554014414 0ustar simonsimon/* * dominosa.c: Domino jigsaw puzzle. Aim to place one of every * possible domino within a rectangle in such a way that the number * on each square matches the provided clue. */ /* * TODO: * * - improve solver so as to use more interesting forms of * deduction * * * rule out a domino placement if it would divide an unfilled * region such that at least one resulting region had an odd * area * + use b.f.s. to determine the area of an unfilled region * + a square is unfilled iff it has at least two possible * placements, and two adjacent unfilled squares are part * of the same region iff the domino placement joining * them is possible * * * perhaps set analysis * + look at all unclaimed squares containing a given number * + for each one, find the set of possible numbers that it * can connect to (i.e. each neighbouring tile such that * the placement between it and that neighbour has not yet * been ruled out) * + now proceed similarly to Solo set analysis: try to find * a subset of the squares such that the union of their * possible numbers is the same size as the subset. If so, * rule out those possible numbers for all other squares. * * important wrinkle: the double dominoes complicate * matters. Connecting a number to itself uses up _two_ * of the unclaimed squares containing a number. Thus, * when finding the initial subset we must never * include two adjacent squares; and also, when ruling * things out after finding the subset, we must be * careful that we don't rule out precisely the domino * placement that was _included_ in our set! */ #include #include #include #include #include #include #include "puzzles.h" /* nth triangular number */ #define TRI(n) ( (n) * ((n) + 1) / 2 ) /* number of dominoes for value n */ #define DCOUNT(n) TRI((n)+1) /* map a pair of numbers to a unique domino index from 0 upwards. */ #define DINDEX(n1,n2) ( TRI(max(n1,n2)) + min(n1,n2) ) #define FLASH_TIME 0.13F enum { COL_BACKGROUND, COL_TEXT, COL_DOMINO, COL_DOMINOCLASH, COL_DOMINOTEXT, COL_EDGE, NCOLOURS }; struct game_params { int n; int unique; }; struct game_numbers { int refcount; int *numbers; /* h x w */ }; #define EDGE_L 0x100 #define EDGE_R 0x200 #define EDGE_T 0x400 #define EDGE_B 0x800 struct game_state { game_params params; int w, h; struct game_numbers *numbers; int *grid; unsigned short *edges; /* h x w */ int completed, cheated; }; static game_params *default_params(void) { game_params *ret = snew(game_params); ret->n = 6; ret->unique = TRUE; return ret; } static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; int n; char buf[80]; switch (i) { case 0: n = 3; break; case 1: n = 4; break; case 2: n = 5; break; case 3: n = 6; break; case 4: n = 7; break; case 5: n = 8; break; case 6: n = 9; break; default: return FALSE; } sprintf(buf, "Up to double-%d", n); *name = dupstr(buf); *params = ret = snew(game_params); ret->n = n; ret->unique = TRUE; 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->n = atoi(string); while (*string && isdigit((unsigned char)*string)) string++; if (*string == 'a') params->unique = FALSE; } static char *encode_params(const game_params *params, int full) { char buf[80]; sprintf(buf, "%d", params->n); if (full && !params->unique) strcat(buf, "a"); return dupstr(buf); } static config_item *game_configure(const game_params *params) { config_item *ret; char buf[80]; ret = snewn(3, config_item); ret[0].name = "Maximum number on dominoes"; ret[0].type = C_STRING; sprintf(buf, "%d", params->n); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Ensure unique solution"; ret[1].type = C_BOOLEAN; ret[1].sval = NULL; ret[1].ival = params->unique; ret[2].name = NULL; ret[2].type = C_END; ret[2].sval = NULL; ret[2].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->n = atoi(cfg[0].sval); ret->unique = cfg[1].ival; return ret; } static char *validate_params(const game_params *params, int full) { if (params->n < 1) return "Maximum face number must be at least one"; return NULL; } /* ---------------------------------------------------------------------- * Solver. */ static int find_overlaps(int w, int h, int placement, int *set) { int x, y, n; n = 0; /* number of returned placements */ x = placement / 2; y = x / w; x %= w; if (placement & 1) { /* * Horizontal domino, indexed by its left end. */ if (x > 0) set[n++] = placement-2; /* horizontal domino to the left */ if (y > 0) set[n++] = placement-2*w-1;/* vertical domino above left side */ if (y+1 < h) set[n++] = placement-1; /* vertical domino below left side */ if (x+2 < w) set[n++] = placement+2; /* horizontal domino to the right */ if (y > 0) set[n++] = placement-2*w+2-1;/* vertical domino above right side */ if (y+1 < h) set[n++] = placement+2-1; /* vertical domino below right side */ } else { /* * Vertical domino, indexed by its top end. */ if (y > 0) set[n++] = placement-2*w; /* vertical domino above */ if (x > 0) set[n++] = placement-2+1; /* horizontal domino left of top */ if (x+1 < w) set[n++] = placement+1; /* horizontal domino right of top */ if (y+2 < h) set[n++] = placement+2*w; /* vertical domino below */ if (x > 0) set[n++] = placement-2+2*w+1;/* horizontal domino left of bottom */ if (x+1 < w) set[n++] = placement+2*w+1;/* horizontal domino right of bottom */ } return n; } /* * Returns 0, 1 or 2 for number of solutions. 2 means `any number * more than one', or more accurately `we were unable to prove * there was only one'. * * Outputs in a `placements' array, indexed the same way as the one * within this function (see below); entries in there are <0 for a * placement ruled out, 0 for an uncertain placement, and 1 for a * definite one. */ static int solver(int w, int h, int n, int *grid, int *output) { int wh = w*h, dc = DCOUNT(n); int *placements, *heads; int i, j, x, y, ret; /* * This array has one entry for every possible domino * placement. Vertical placements are indexed by their top * half, at (y*w+x)*2; horizontal placements are indexed by * their left half at (y*w+x)*2+1. * * This array is used to link domino placements together into * linked lists, so that we can track all the possible * placements of each different domino. It's also used as a * quick means of looking up an individual placement to see * whether we still think it's possible. Actual values stored * in this array are -2 (placement not possible at all), -1 * (end of list), or the array index of the next item. * * Oh, and -3 for `not even valid', used for array indices * which don't even represent a plausible placement. */ placements = snewn(2*wh, int); for (i = 0; i < 2*wh; i++) placements[i] = -3; /* not even valid */ /* * This array has one entry for every domino, and it is an * index into `placements' denoting the head of the placement * list for that domino. */ heads = snewn(dc, int); for (i = 0; i < dc; i++) heads[i] = -1; /* * Set up the initial possibility lists by scanning the grid. */ for (y = 0; y < h-1; y++) for (x = 0; x < w; x++) { int di = DINDEX(grid[y*w+x], grid[(y+1)*w+x]); placements[(y*w+x)*2] = heads[di]; heads[di] = (y*w+x)*2; } for (y = 0; y < h; y++) for (x = 0; x < w-1; x++) { int di = DINDEX(grid[y*w+x], grid[y*w+(x+1)]); placements[(y*w+x)*2+1] = heads[di]; heads[di] = (y*w+x)*2+1; } #ifdef SOLVER_DIAGNOSTICS printf("before solver:\n"); for (i = 0; i <= n; i++) for (j = 0; j <= i; j++) { int k, m; m = 0; printf("%2d [%d %d]:", DINDEX(i, j), i, j); for (k = heads[DINDEX(i,j)]; k >= 0; k = placements[k]) printf(" %3d [%d,%d,%c]", k, k/2%w, k/2/w, k%2?'h':'v'); printf("\n"); } #endif while (1) { int done_something = FALSE; /* * For each domino, look at its possible placements, and * for each placement consider the placements (of any * domino) it overlaps. Any placement overlapped by all * placements of this domino can be ruled out. * * Each domino placement overlaps only six others, so we * need not do serious set theory to work this out. */ for (i = 0; i < dc; i++) { int permset[6], permlen = 0, p; if (heads[i] == -1) { /* no placement for this domino */ ret = 0; /* therefore puzzle is impossible */ goto done; } for (j = heads[i]; j >= 0; j = placements[j]) { assert(placements[j] != -2); if (j == heads[i]) { permlen = find_overlaps(w, h, j, permset); } else { int tempset[6], templen, m, n, k; templen = find_overlaps(w, h, j, tempset); /* * Pathetically primitive set intersection * algorithm, which I'm only getting away with * because I know my sets are bounded by a very * small size. */ for (m = n = 0; m < permlen; m++) { for (k = 0; k < templen; k++) if (tempset[k] == permset[m]) break; if (k < templen) permset[n++] = permset[m]; } permlen = n; } } for (p = 0; p < permlen; p++) { j = permset[p]; if (placements[j] != -2) { int p1, p2, di; done_something = TRUE; /* * Rule out this placement. First find what * domino it is... */ p1 = j / 2; p2 = (j & 1) ? p1 + 1 : p1 + w; di = DINDEX(grid[p1], grid[p2]); #ifdef SOLVER_DIAGNOSTICS printf("considering domino %d: ruling out placement %d" " for %d\n", i, j, di); #endif /* * ... then walk that domino's placement list, * removing this placement when we find it. */ if (heads[di] == j) heads[di] = placements[j]; else { int k = heads[di]; while (placements[k] != -1 && placements[k] != j) k = placements[k]; assert(placements[k] == j); placements[k] = placements[j]; } placements[j] = -2; } } } /* * For each square, look at the available placements * involving that square. If all of them are for the same * domino, then rule out any placements for that domino * _not_ involving this square. */ for (i = 0; i < wh; i++) { int list[4], k, n, adi; x = i % w; y = i / w; j = 0; if (x > 0) list[j++] = 2*(i-1)+1; if (x+1 < w) list[j++] = 2*i+1; if (y > 0) list[j++] = 2*(i-w); if (y+1 < h) list[j++] = 2*i; for (n = k = 0; k < j; k++) if (placements[list[k]] >= -1) list[n++] = list[k]; adi = -1; for (j = 0; j < n; j++) { int p1, p2, di; k = list[j]; p1 = k / 2; p2 = (k & 1) ? p1 + 1 : p1 + w; di = DINDEX(grid[p1], grid[p2]); if (adi == -1) adi = di; if (adi != di) break; } if (j == n) { int nn; assert(adi >= 0); /* * We've found something. All viable placements * involving this square are for domino `adi'. If * the current placement list for that domino is * longer than n, reduce it to precisely this * placement list and we've done something. */ nn = 0; for (k = heads[adi]; k >= 0; k = placements[k]) nn++; if (nn > n) { done_something = TRUE; #ifdef SOLVER_DIAGNOSTICS printf("considering square %d,%d: reducing placements " "of domino %d\n", x, y, adi); #endif /* * Set all other placements on the list to * impossible. */ k = heads[adi]; while (k >= 0) { int tmp = placements[k]; placements[k] = -2; k = tmp; } /* * Set up the new list. */ heads[adi] = list[0]; for (k = 0; k < n; k++) placements[list[k]] = (k+1 == n ? -1 : list[k+1]); } } } if (!done_something) break; } #ifdef SOLVER_DIAGNOSTICS printf("after solver:\n"); for (i = 0; i <= n; i++) for (j = 0; j <= i; j++) { int k, m; m = 0; printf("%2d [%d %d]:", DINDEX(i, j), i, j); for (k = heads[DINDEX(i,j)]; k >= 0; k = placements[k]) printf(" %3d [%d,%d,%c]", k, k/2%w, k/2/w, k%2?'h':'v'); printf("\n"); } #endif ret = 1; for (i = 0; i < wh*2; i++) { if (placements[i] == -2) { if (output) output[i] = -1; /* ruled out */ } else if (placements[i] != -3) { int p1, p2, di; p1 = i / 2; p2 = (i & 1) ? p1 + 1 : p1 + w; di = DINDEX(grid[p1], grid[p2]); if (i == heads[di] && placements[i] == -1) { if (output) output[i] = 1; /* certain */ } else { if (output) output[i] = 0; /* uncertain */ ret = 2; } } } done: /* * Free working data. */ sfree(placements); sfree(heads); return ret; } /* ---------------------------------------------------------------------- * End of solver code. */ static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { int n = params->n, w = n+2, h = n+1, wh = w*h; int *grid, *grid2, *list; int i, j, k, len; char *ret; /* * Allocate space in which to lay the grid out. */ grid = snewn(wh, int); grid2 = snewn(wh, int); list = snewn(2*wh, int); /* * I haven't been able to think of any particularly clever * techniques for generating instances of Dominosa with a * unique solution. Many of the deductions used in this puzzle * are based on information involving half the grid at a time * (`of all the 6s, exactly one is next to a 3'), so a strategy * of partially solving the grid and then perturbing the place * where the solver got stuck seems particularly likely to * accidentally destroy the information which the solver had * used in getting that far. (Contrast with, say, Mines, in * which most deductions are local so this is an excellent * strategy.) * * Therefore I resort to the basest of brute force methods: * generate a random grid, see if it's solvable, throw it away * and try again if not. My only concession to sophistication * and cleverness is to at least _try_ not to generate obvious * 2x2 ambiguous sections (see comment below in the domino- * flipping section). * * During tests performed on 2005-07-15, I found that the brute * force approach without that tweak had to throw away about 87 * grids on average (at the default n=6) before finding a * unique one, or a staggering 379 at n=9; good job the * generator and solver are fast! When I added the * ambiguous-section avoidance, those numbers came down to 19 * and 26 respectively, which is a lot more sensible. */ do { domino_layout_prealloc(w, h, rs, grid, grid2, list); /* * Now we have a complete layout covering the whole * rectangle with dominoes. So shuffle the actual domino * values and fill the rectangle with numbers. */ k = 0; for (i = 0; i <= params->n; i++) for (j = 0; j <= i; j++) { list[k++] = i; list[k++] = j; } shuffle(list, k/2, 2*sizeof(*list), rs); j = 0; for (i = 0; i < wh; i++) if (grid[i] > i) { /* Optionally flip the domino round. */ int flip = -1; if (params->unique) { int t1, t2; /* * If we're after a unique solution, we can do * something here to improve the chances. If * we're placing a domino so that it forms a * 2x2 rectangle with one we've already placed, * and if that domino and this one share a * number, we can try not to put them so that * the identical numbers are diagonally * separated, because that automatically causes * non-uniqueness: * * +---+ +-+-+ * |2 3| |2|3| * +---+ -> | | | * |4 2| |4|2| * +---+ +-+-+ */ t1 = i; t2 = grid[i]; if (t2 == t1 + w) { /* this domino is vertical */ if (t1 % w > 0 &&/* and not on the left hand edge */ grid[t1-1] == t2-1 &&/* alongside one to left */ (grid2[t1-1] == list[j] || /* and has a number */ grid2[t1-1] == list[j+1] || /* in common */ grid2[t2-1] == list[j] || grid2[t2-1] == list[j+1])) { if (grid2[t1-1] == list[j] || grid2[t2-1] == list[j+1]) flip = 0; else flip = 1; } } else { /* this domino is horizontal */ if (t1 / w > 0 &&/* and not on the top edge */ grid[t1-w] == t2-w &&/* alongside one above */ (grid2[t1-w] == list[j] || /* and has a number */ grid2[t1-w] == list[j+1] || /* in common */ grid2[t2-w] == list[j] || grid2[t2-w] == list[j+1])) { if (grid2[t1-w] == list[j] || grid2[t2-w] == list[j+1]) flip = 0; else flip = 1; } } } if (flip < 0) flip = random_upto(rs, 2); grid2[i] = list[j + flip]; grid2[grid[i]] = list[j + 1 - flip]; j += 2; } assert(j == k); } while (params->unique && solver(w, h, n, grid2, NULL) > 1); #ifdef GENERATION_DIAGNOSTICS for (j = 0; j < h; j++) { for (i = 0; i < w; i++) { putchar('0' + grid2[j*w+i]); } putchar('\n'); } putchar('\n'); #endif /* * Encode the resulting game state. * * Our encoding is a string of digits. Any number greater than * 9 is represented by a decimal integer within square * brackets. We know there are n+2 of every number (it's paired * with each number from 0 to n inclusive, and one of those is * itself so that adds another occurrence), so we can work out * the string length in advance. */ /* * To work out the total length of the decimal encodings of all * the numbers from 0 to n inclusive: * - every number has a units digit; total is n+1. * - all numbers above 9 have a tens digit; total is max(n+1-10,0). * - all numbers above 99 have a hundreds digit; total is max(n+1-100,0). * - and so on. */ len = n+1; for (i = 10; i <= n; i *= 10) len += max(n + 1 - i, 0); /* Now add two square brackets for each number above 9. */ len += 2 * max(n + 1 - 10, 0); /* And multiply by n+2 for the repeated occurrences of each number. */ len *= n+2; /* * Now actually encode the string. */ ret = snewn(len+1, char); j = 0; for (i = 0; i < wh; i++) { k = grid2[i]; if (k < 10) ret[j++] = '0' + k; else j += sprintf(ret+j, "[%d]", k); assert(j <= len); } assert(j == len); ret[j] = '\0'; /* * Encode the solved state as an aux_info. */ { char *auxinfo = snewn(wh+1, char); for (i = 0; i < wh; i++) { int v = grid[i]; auxinfo[i] = (v == i+1 ? 'L' : v == i-1 ? 'R' : v == i+w ? 'T' : v == i-w ? 'B' : '.'); } auxinfo[wh] = '\0'; *aux = auxinfo; } sfree(list); sfree(grid2); sfree(grid); return ret; } static char *validate_desc(const game_params *params, const char *desc) { int n = params->n, w = n+2, h = n+1, wh = w*h; int *occurrences; int i, j; char *ret; ret = NULL; occurrences = snewn(n+1, int); for (i = 0; i <= n; i++) occurrences[i] = 0; for (i = 0; i < wh; i++) { if (!*desc) { ret = ret ? ret : "Game description is too short"; } else { if (*desc >= '0' && *desc <= '9') j = *desc++ - '0'; else if (*desc == '[') { desc++; j = atoi(desc); while (*desc && isdigit((unsigned char)*desc)) desc++; if (*desc != ']') ret = ret ? ret : "Missing ']' in game description"; else desc++; } else { j = -1; ret = ret ? ret : "Invalid syntax in game description"; } if (j < 0 || j > n) ret = ret ? ret : "Number out of range in game description"; else occurrences[j]++; } } if (*desc) ret = ret ? ret : "Game description is too long"; if (!ret) { for (i = 0; i <= n; i++) if (occurrences[i] != n+2) ret = "Incorrect number balance in game description"; } sfree(occurrences); return ret; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { int n = params->n, w = n+2, h = n+1, wh = w*h; game_state *state = snew(game_state); int i, j; state->params = *params; state->w = w; state->h = h; state->grid = snewn(wh, int); for (i = 0; i < wh; i++) state->grid[i] = i; state->edges = snewn(wh, unsigned short); for (i = 0; i < wh; i++) state->edges[i] = 0; state->numbers = snew(struct game_numbers); state->numbers->refcount = 1; state->numbers->numbers = snewn(wh, int); for (i = 0; i < wh; i++) { assert(*desc); if (*desc >= '0' && *desc <= '9') j = *desc++ - '0'; else { assert(*desc == '['); desc++; j = atoi(desc); while (*desc && isdigit((unsigned char)*desc)) desc++; assert(*desc == ']'); desc++; } assert(j >= 0 && j <= n); state->numbers->numbers[i] = j; } state->completed = state->cheated = FALSE; return state; } static game_state *dup_game(const game_state *state) { int n = state->params.n, w = n+2, h = n+1, wh = w*h; game_state *ret = snew(game_state); ret->params = state->params; ret->w = state->w; ret->h = state->h; ret->grid = snewn(wh, int); memcpy(ret->grid, state->grid, wh * sizeof(int)); ret->edges = snewn(wh, unsigned short); memcpy(ret->edges, state->edges, wh * sizeof(unsigned short)); ret->numbers = state->numbers; ret->numbers->refcount++; ret->completed = state->completed; ret->cheated = state->cheated; return ret; } static void free_game(game_state *state) { sfree(state->grid); sfree(state->edges); if (--state->numbers->refcount <= 0) { sfree(state->numbers->numbers); sfree(state->numbers); } sfree(state); } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { int n = state->params.n, w = n+2, h = n+1, wh = w*h; int *placements; char *ret; int retlen, retsize; int i, v; char buf[80]; int extra; if (aux) { retsize = 256; ret = snewn(retsize, char); retlen = sprintf(ret, "S"); for (i = 0; i < wh; i++) { if (aux[i] == 'L') extra = sprintf(buf, ";D%d,%d", i, i+1); else if (aux[i] == 'T') extra = sprintf(buf, ";D%d,%d", i, i+w); else continue; if (retlen + extra + 1 >= retsize) { retsize = retlen + extra + 256; ret = sresize(ret, retsize, char); } strcpy(ret + retlen, buf); retlen += extra; } } else { placements = snewn(wh*2, int); for (i = 0; i < wh*2; i++) placements[i] = -3; solver(w, h, n, state->numbers->numbers, placements); /* * First make a pass putting in edges for -1, then make a pass * putting in dominoes for +1. */ retsize = 256; ret = snewn(retsize, char); retlen = sprintf(ret, "S"); for (v = -1; v <= +1; v += 2) for (i = 0; i < wh*2; i++) if (placements[i] == v) { int p1 = i / 2; int p2 = (i & 1) ? p1+1 : p1+w; extra = sprintf(buf, ";%c%d,%d", (int)(v==-1 ? 'E' : 'D'), p1, p2); if (retlen + extra + 1 >= retsize) { retsize = retlen + extra + 256; ret = sresize(ret, retsize, char); } strcpy(ret + retlen, buf); retlen += extra; } sfree(placements); } return ret; } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { return NULL; } struct game_ui { int cur_x, cur_y, cur_visible; }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->cur_x = ui->cur_y = 0; ui->cur_visible = 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) { if (!oldstate->completed && newstate->completed) ui->cur_visible = 0; } #define PREFERRED_TILESIZE 32 #define TILESIZE (ds->tilesize) #define BORDER (TILESIZE * 3 / 4) #define DOMINO_GUTTER (TILESIZE / 16) #define DOMINO_RADIUS (TILESIZE / 8) #define DOMINO_COFFSET (DOMINO_GUTTER + DOMINO_RADIUS) #define CURSOR_RADIUS (TILESIZE / 4) #define COORD(x) ( (x) * TILESIZE + BORDER ) #define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 ) struct game_drawstate { int started; int w, h, tilesize; unsigned long *visible; }; 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; char buf[80]; /* * A left-click between two numbers toggles a domino covering * them. A right-click toggles an edge. */ if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { int tx = FROMCOORD(x), ty = FROMCOORD(y), t = ty*w+tx; int dx, dy; int d1, d2; if (tx < 0 || tx >= w || ty < 0 || ty >= h) return NULL; /* * Now we know which square the click was in, decide which * edge of the square it was closest to. */ dx = 2 * (x - COORD(tx)) - TILESIZE; dy = 2 * (y - COORD(ty)) - TILESIZE; if (abs(dx) > abs(dy) && dx < 0 && tx > 0) d1 = t - 1, d2 = t; /* clicked in right side of domino */ else if (abs(dx) > abs(dy) && dx > 0 && tx+1 < w) d1 = t, d2 = t + 1; /* clicked in left side of domino */ else if (abs(dy) > abs(dx) && dy < 0 && ty > 0) d1 = t - w, d2 = t; /* clicked in bottom half of domino */ else if (abs(dy) > abs(dx) && dy > 0 && ty+1 < h) d1 = t, d2 = t + w; /* clicked in top half of domino */ else return NULL; /* * We can't mark an edge next to any domino. */ if (button == RIGHT_BUTTON && (state->grid[d1] != d1 || state->grid[d2] != d2)) return NULL; ui->cur_visible = 0; sprintf(buf, "%c%d,%d", (int)(button == RIGHT_BUTTON ? 'E' : 'D'), d1, d2); return dupstr(buf); } else if (IS_CURSOR_MOVE(button)) { ui->cur_visible = 1; move_cursor(button, &ui->cur_x, &ui->cur_y, 2*w-1, 2*h-1, 0); return ""; } else if (IS_CURSOR_SELECT(button)) { int d1, d2; if (!((ui->cur_x ^ ui->cur_y) & 1)) return NULL; /* must have exactly one dimension odd */ d1 = (ui->cur_y / 2) * w + (ui->cur_x / 2); d2 = ((ui->cur_y+1) / 2) * w + ((ui->cur_x+1) / 2); /* * We can't mark an edge next to any domino. */ if (button == CURSOR_SELECT2 && (state->grid[d1] != d1 || state->grid[d2] != d2)) return NULL; sprintf(buf, "%c%d,%d", (int)(button == CURSOR_SELECT2 ? 'E' : 'D'), d1, d2); return dupstr(buf); } return NULL; } static game_state *execute_move(const game_state *state, const char *move) { int n = state->params.n, w = n+2, h = n+1, wh = w*h; int d1, d2, d3, p; game_state *ret = dup_game(state); while (*move) { if (move[0] == 'S') { int i; ret->cheated = TRUE; /* * Clear the existing edges and domino placements. We * expect the S to be followed by other commands. */ for (i = 0; i < wh; i++) { ret->grid[i] = i; ret->edges[i] = 0; } move++; } else if (move[0] == 'D' && sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 && d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2) { /* * Toggle domino presence between d1 and d2. */ if (ret->grid[d1] == d2) { assert(ret->grid[d2] == d1); ret->grid[d1] = d1; ret->grid[d2] = d2; } else { /* * Erase any dominoes that might overlap the new one. */ d3 = ret->grid[d1]; if (d3 != d1) ret->grid[d3] = d3; d3 = ret->grid[d2]; if (d3 != d2) ret->grid[d3] = d3; /* * Place the new one. */ ret->grid[d1] = d2; ret->grid[d2] = d1; /* * Destroy any edges lurking around it. */ if (ret->edges[d1] & EDGE_L) { assert(d1 - 1 >= 0); ret->edges[d1 - 1] &= ~EDGE_R; } if (ret->edges[d1] & EDGE_R) { assert(d1 + 1 < wh); ret->edges[d1 + 1] &= ~EDGE_L; } if (ret->edges[d1] & EDGE_T) { assert(d1 - w >= 0); ret->edges[d1 - w] &= ~EDGE_B; } if (ret->edges[d1] & EDGE_B) { assert(d1 + 1 < wh); ret->edges[d1 + w] &= ~EDGE_T; } ret->edges[d1] = 0; if (ret->edges[d2] & EDGE_L) { assert(d2 - 1 >= 0); ret->edges[d2 - 1] &= ~EDGE_R; } if (ret->edges[d2] & EDGE_R) { assert(d2 + 1 < wh); ret->edges[d2 + 1] &= ~EDGE_L; } if (ret->edges[d2] & EDGE_T) { assert(d2 - w >= 0); ret->edges[d2 - w] &= ~EDGE_B; } if (ret->edges[d2] & EDGE_B) { assert(d2 + 1 < wh); ret->edges[d2 + w] &= ~EDGE_T; } ret->edges[d2] = 0; } move += p+1; } else if (move[0] == 'E' && sscanf(move+1, "%d,%d%n", &d1, &d2, &p) == 2 && d1 >= 0 && d1 < wh && d2 >= 0 && d2 < wh && d1 < d2 && ret->grid[d1] == d1 && ret->grid[d2] == d2) { /* * Toggle edge presence between d1 and d2. */ if (d2 == d1 + 1) { ret->edges[d1] ^= EDGE_R; ret->edges[d2] ^= EDGE_L; } else { ret->edges[d1] ^= EDGE_B; ret->edges[d2] ^= EDGE_T; } move += p+1; } else { free_game(ret); return NULL; } if (*move) { if (*move != ';') { free_game(ret); return NULL; } move++; } } /* * After modifying the grid, check completion. */ if (!ret->completed) { int i, ok = 0; unsigned char *used = snewn(TRI(n+1), unsigned char); memset(used, 0, TRI(n+1)); for (i = 0; i < wh; i++) if (ret->grid[i] > i) { int n1, n2, di; n1 = ret->numbers->numbers[i]; n2 = ret->numbers->numbers[ret->grid[i]]; di = DINDEX(n1, n2); assert(di >= 0 && di < TRI(n+1)); if (!used[di]) { used[di] = 1; ok++; } } sfree(used); if (ok == DCOUNT(n)) ret->completed = TRUE; } return ret; } /* ---------------------------------------------------------------------- * Drawing routines. */ static void game_compute_size(const game_params *params, int tilesize, int *x, int *y) { int n = params->n, w = n+2, h = n+1; /* Ick: fake up `ds->tilesize' for macro expansion purposes */ struct { int tilesize; } ads, *ds = &ads; ads.tilesize = tilesize; *x = w * TILESIZE + 2*BORDER; *y = h * TILESIZE + 2*BORDER; } 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_TEXT * 3 + 0] = 0.0F; ret[COL_TEXT * 3 + 1] = 0.0F; ret[COL_TEXT * 3 + 2] = 0.0F; ret[COL_DOMINO * 3 + 0] = 0.0F; ret[COL_DOMINO * 3 + 1] = 0.0F; ret[COL_DOMINO * 3 + 2] = 0.0F; ret[COL_DOMINOCLASH * 3 + 0] = 0.5F; ret[COL_DOMINOCLASH * 3 + 1] = 0.0F; ret[COL_DOMINOCLASH * 3 + 2] = 0.0F; ret[COL_DOMINOTEXT * 3 + 0] = 1.0F; ret[COL_DOMINOTEXT * 3 + 1] = 1.0F; ret[COL_DOMINOTEXT * 3 + 2] = 1.0F; ret[COL_EDGE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 2 / 3; ret[COL_EDGE * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 2 / 3; ret[COL_EDGE * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 2 / 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); int i; ds->started = FALSE; ds->w = state->w; ds->h = state->h; ds->visible = snewn(ds->w * ds->h, unsigned long); ds->tilesize = 0; /* not decided yet */ for (i = 0; i < ds->w * ds->h; i++) ds->visible[i] = 0xFFFF; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->visible); sfree(ds); } enum { TYPE_L, TYPE_R, TYPE_T, TYPE_B, TYPE_BLANK, TYPE_MASK = 0x0F }; /* These flags must be disjoint with: * the above enum (TYPE_*) [0x000 -- 0x00F] * EDGE_* [0x100 -- 0xF00] * and must fit into an unsigned long (32 bits). */ #define DF_FLASH 0x40 #define DF_CLASH 0x80 #define DF_CURSOR 0x01000 #define DF_CURSOR_USEFUL 0x02000 #define DF_CURSOR_XBASE 0x10000 #define DF_CURSOR_XMASK 0x30000 #define DF_CURSOR_YBASE 0x40000 #define DF_CURSOR_YMASK 0xC0000 #define CEDGE_OFF (TILESIZE / 8) #define IS_EMPTY(s,x,y) ((s)->grid[(y)*(s)->w+(x)] == ((y)*(s)->w+(x))) static void draw_tile(drawing *dr, game_drawstate *ds, const game_state *state, int x, int y, int type) { int w = state->w /*, h = state->h */; int cx = COORD(x), cy = COORD(y); int nc; char str[80]; int flags; clip(dr, cx, cy, TILESIZE, TILESIZE); draw_rect(dr, cx, cy, TILESIZE, TILESIZE, COL_BACKGROUND); flags = type &~ TYPE_MASK; type &= TYPE_MASK; if (type != TYPE_BLANK) { int i, bg; /* * Draw one end of a domino. This is composed of: * * - two filled circles (rounded corners) * - two rectangles * - a slight shift in the number */ if (flags & DF_CLASH) bg = COL_DOMINOCLASH; else bg = COL_DOMINO; nc = COL_DOMINOTEXT; if (flags & DF_FLASH) { int tmp = nc; nc = bg; bg = tmp; } if (type == TYPE_L || type == TYPE_T) draw_circle(dr, cx+DOMINO_COFFSET, cy+DOMINO_COFFSET, DOMINO_RADIUS, bg, bg); if (type == TYPE_R || type == TYPE_T) draw_circle(dr, cx+TILESIZE-1-DOMINO_COFFSET, cy+DOMINO_COFFSET, DOMINO_RADIUS, bg, bg); if (type == TYPE_L || type == TYPE_B) draw_circle(dr, cx+DOMINO_COFFSET, cy+TILESIZE-1-DOMINO_COFFSET, DOMINO_RADIUS, bg, bg); if (type == TYPE_R || type == TYPE_B) draw_circle(dr, cx+TILESIZE-1-DOMINO_COFFSET, cy+TILESIZE-1-DOMINO_COFFSET, DOMINO_RADIUS, bg, bg); for (i = 0; i < 2; i++) { int x1, y1, x2, y2; x1 = cx + (i ? DOMINO_GUTTER : DOMINO_COFFSET); y1 = cy + (i ? DOMINO_COFFSET : DOMINO_GUTTER); x2 = cx + TILESIZE-1 - (i ? DOMINO_GUTTER : DOMINO_COFFSET); y2 = cy + TILESIZE-1 - (i ? DOMINO_COFFSET : DOMINO_GUTTER); if (type == TYPE_L) x2 = cx + TILESIZE + TILESIZE/16; else if (type == TYPE_R) x1 = cx - TILESIZE/16; else if (type == TYPE_T) y2 = cy + TILESIZE + TILESIZE/16; else if (type == TYPE_B) y1 = cy - TILESIZE/16; draw_rect(dr, x1, y1, x2-x1+1, y2-y1+1, bg); } } else { if (flags & EDGE_T) draw_rect(dr, cx+DOMINO_GUTTER, cy, TILESIZE-2*DOMINO_GUTTER, 1, COL_EDGE); if (flags & EDGE_B) draw_rect(dr, cx+DOMINO_GUTTER, cy+TILESIZE-1, TILESIZE-2*DOMINO_GUTTER, 1, COL_EDGE); if (flags & EDGE_L) draw_rect(dr, cx, cy+DOMINO_GUTTER, 1, TILESIZE-2*DOMINO_GUTTER, COL_EDGE); if (flags & EDGE_R) draw_rect(dr, cx+TILESIZE-1, cy+DOMINO_GUTTER, 1, TILESIZE-2*DOMINO_GUTTER, COL_EDGE); nc = COL_TEXT; } if (flags & DF_CURSOR) { int curx = ((flags & DF_CURSOR_XMASK) / DF_CURSOR_XBASE) & 3; int cury = ((flags & DF_CURSOR_YMASK) / DF_CURSOR_YBASE) & 3; int ox = cx + curx*TILESIZE/2; int oy = cy + cury*TILESIZE/2; draw_rect_corners(dr, ox, oy, CURSOR_RADIUS, nc); if (flags & DF_CURSOR_USEFUL) draw_rect_corners(dr, ox, oy, CURSOR_RADIUS+1, nc); } sprintf(str, "%d", state->numbers->numbers[y*w+x]); draw_text(dr, cx+TILESIZE/2, cy+TILESIZE/2, FONT_VARIABLE, TILESIZE/2, ALIGN_HCENTRE | ALIGN_VCENTRE, nc, str); draw_update(dr, cx, cy, TILESIZE, TILESIZE); unclip(dr); } 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 n = state->params.n, w = state->w, h = state->h, wh = w*h; int x, y, i; unsigned char *used; if (!ds->started) { int pw, ph; game_compute_size(&state->params, TILESIZE, &pw, &ph); draw_rect(dr, 0, 0, pw, ph, COL_BACKGROUND); draw_update(dr, 0, 0, pw, ph); ds->started = TRUE; } /* * See how many dominoes of each type there are, so we can * highlight clashes in red. */ used = snewn(TRI(n+1), unsigned char); memset(used, 0, TRI(n+1)); for (i = 0; i < wh; i++) if (state->grid[i] > i) { int n1, n2, di; n1 = state->numbers->numbers[i]; n2 = state->numbers->numbers[state->grid[i]]; di = DINDEX(n1, n2); assert(di >= 0 && di < TRI(n+1)); if (used[di] < 2) used[di]++; } for (y = 0; y < h; y++) for (x = 0; x < w; x++) { int n = y*w+x; int n1, n2, di; unsigned long c; if (state->grid[n] == n-1) c = TYPE_R; else if (state->grid[n] == n+1) c = TYPE_L; else if (state->grid[n] == n-w) c = TYPE_B; else if (state->grid[n] == n+w) c = TYPE_T; else c = TYPE_BLANK; if (c != TYPE_BLANK) { n1 = state->numbers->numbers[n]; n2 = state->numbers->numbers[state->grid[n]]; di = DINDEX(n1, n2); if (used[di] > 1) c |= DF_CLASH; /* highlight a clash */ } else { c |= state->edges[n]; } if (flashtime != 0) c |= DF_FLASH; /* we're flashing */ if (ui->cur_visible) { unsigned curx = (unsigned)(ui->cur_x - (2*x-1)); unsigned cury = (unsigned)(ui->cur_y - (2*y-1)); if (curx < 3 && cury < 3) { c |= (DF_CURSOR | (curx * DF_CURSOR_XBASE) | (cury * DF_CURSOR_YBASE)); if ((ui->cur_x ^ ui->cur_y) & 1) c |= DF_CURSOR_USEFUL; } } if (ds->visible[n] != c) { draw_tile(dr, ds, state, x, y, c); ds->visible[n] = c; } } sfree(used); } 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 int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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) { int pw, ph; /* * I'll use 6mm squares by default. */ game_compute_size(params, 600, &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->w, h = state->h; int c, x, y; /* Ick: fake up `ds->tilesize' for macro expansion purposes */ game_drawstate ads, *ds = &ads; game_set_size(dr, ds, NULL, tilesize); c = print_mono_colour(dr, 1); assert(c == COL_BACKGROUND); c = print_mono_colour(dr, 0); assert(c == COL_TEXT); c = print_mono_colour(dr, 0); assert(c == COL_DOMINO); c = print_mono_colour(dr, 0); assert(c == COL_DOMINOCLASH); c = print_mono_colour(dr, 1); assert(c == COL_DOMINOTEXT); c = print_mono_colour(dr, 0); assert(c == COL_EDGE); for (y = 0; y < h; y++) for (x = 0; x < w; x++) { int n = y*w+x; unsigned long c; if (state->grid[n] == n-1) c = TYPE_R; else if (state->grid[n] == n+1) c = TYPE_L; else if (state->grid[n] == n-w) c = TYPE_B; else if (state->grid[n] == n+w) c = TYPE_T; else c = TYPE_BLANK; draw_tile(dr, ds, state, x, y, c); } } #ifdef COMBINED #define thegame dominosa #endif const struct game thegame = { "Dominosa", "games.dominosa", "dominosa", default_params, game_fetch_preset, 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, FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, decode_ui, 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_status, TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, 0, /* flags */ }; /* vim: set shiftwidth=4 :set textwidth=80: */ puzzles-r9872/drawing.c0000644000175300017530000002166511400233062014225 0ustar simonsimon/* * drawing.c: Intermediary between the drawing interface as * presented to the back end, and that implemented by the front * end. * * Mostly just looks up calls in a vtable and passes them through * unchanged. However, on the printing side it tracks print colours * so the front end API doesn't have to. * * FIXME: * * - I'd _like_ to do automatic draw_updates, but it's a pain for * draw_text in particular. I'd have to invent a front end API * which retrieved the text bounds. * + that might allow me to do the alignment centrally as well? * * perhaps not, because PS can't return this information, * so there would have to be a special case for it. * + however, that at least doesn't stand in the way of using * the text bounds for draw_update, because PS doesn't need * draw_update since it's printing-only. Any _interactive_ * drawing API couldn't get away with refusing to tell you * what parts of the screen a text draw had covered, because * you would inevitably need to erase it later on. */ #include #include #include #include #include #include "puzzles.h" struct print_colour { int hatch; int hatch_when; /* 0=never 1=only-in-b&w 2=always */ float r, g, b; float grey; }; struct drawing { const drawing_api *api; void *handle; struct print_colour *colours; int ncolours, coloursize; float scale; /* `me' is only used in status_bar(), so print-oriented instances of * this may set it to NULL. */ midend *me; char *laststatus; }; drawing *drawing_new(const drawing_api *api, midend *me, void *handle) { drawing *dr = snew(drawing); dr->api = api; dr->handle = handle; dr->colours = NULL; dr->ncolours = dr->coloursize = 0; dr->scale = 1.0F; dr->me = me; dr->laststatus = NULL; return dr; } void drawing_free(drawing *dr) { sfree(dr->laststatus); sfree(dr->colours); sfree(dr); } void draw_text(drawing *dr, int x, int y, int fonttype, int fontsize, int align, int colour, char *text) { dr->api->draw_text(dr->handle, x, y, fonttype, fontsize, align, colour, text); } void draw_rect(drawing *dr, int x, int y, int w, int h, int colour) { dr->api->draw_rect(dr->handle, x, y, w, h, colour); } void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour) { dr->api->draw_line(dr->handle, x1, y1, x2, y2, colour); } void draw_thick_line(drawing *dr, float thickness, float x1, float y1, float x2, float y2, int colour) { if (dr->api->draw_thick_line) { dr->api->draw_thick_line(dr->handle, thickness, x1, y1, x2, y2, colour); } else { /* We'll fake it up with a filled polygon. The tweak to the * thickness empirically compensates for rounding errors, because * polygon rendering uses integer coordinates. */ float len = sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)); float tvhatx = (x2 - x1)/len * (thickness/2 - 0.2); float tvhaty = (y2 - y1)/len * (thickness/2 - 0.2); int p[8]; p[0] = x1 - tvhaty; p[1] = y1 + tvhatx; p[2] = x2 - tvhaty; p[3] = y2 + tvhatx; p[4] = x2 + tvhaty; p[5] = y2 - tvhatx; p[6] = x1 + tvhaty; p[7] = y1 - tvhatx; dr->api->draw_polygon(dr->handle, p, 4, colour, colour); } } void draw_polygon(drawing *dr, int *coords, int npoints, int fillcolour, int outlinecolour) { dr->api->draw_polygon(dr->handle, coords, npoints, fillcolour, outlinecolour); } void draw_circle(drawing *dr, int cx, int cy, int radius, int fillcolour, int outlinecolour) { dr->api->draw_circle(dr->handle, cx, cy, radius, fillcolour, outlinecolour); } void draw_update(drawing *dr, int x, int y, int w, int h) { if (dr->api->draw_update) dr->api->draw_update(dr->handle, x, y, w, h); } void clip(drawing *dr, int x, int y, int w, int h) { dr->api->clip(dr->handle, x, y, w, h); } void unclip(drawing *dr) { dr->api->unclip(dr->handle); } void start_draw(drawing *dr) { dr->api->start_draw(dr->handle); } void end_draw(drawing *dr) { dr->api->end_draw(dr->handle); } char *text_fallback(drawing *dr, const char *const *strings, int nstrings) { int i; /* * If the drawing implementation provides one of these, use it. */ if (dr && dr->api->text_fallback) return dr->api->text_fallback(dr->handle, strings, nstrings); /* * Otherwise, do the simple thing and just pick the first string * that fits in plain ASCII. It will then need no translation * out of UTF-8. */ for (i = 0; i < nstrings; i++) { const char *p; for (p = strings[i]; *p; p++) if (*p & 0x80) break; if (!*p) return dupstr(strings[i]); } /* * The caller was responsible for making sure _some_ string in * the list was in plain ASCII. */ assert(!"Should never get here"); return NULL; /* placate optimiser */ } void status_bar(drawing *dr, char *text) { char *rewritten; if (!dr->api->status_bar) return; assert(dr->me); rewritten = midend_rewrite_statusbar(dr->me, text); if (!dr->laststatus || strcmp(rewritten, dr->laststatus)) { dr->api->status_bar(dr->handle, rewritten); sfree(dr->laststatus); dr->laststatus = rewritten; } else { sfree(rewritten); } } blitter *blitter_new(drawing *dr, int w, int h) { return dr->api->blitter_new(dr->handle, w, h); } void blitter_free(drawing *dr, blitter *bl) { dr->api->blitter_free(dr->handle, bl); } void blitter_save(drawing *dr, blitter *bl, int x, int y) { dr->api->blitter_save(dr->handle, bl, x, y); } void blitter_load(drawing *dr, blitter *bl, int x, int y) { dr->api->blitter_load(dr->handle, bl, x, y); } void print_begin_doc(drawing *dr, int pages) { dr->api->begin_doc(dr->handle, pages); } void print_begin_page(drawing *dr, int number) { dr->api->begin_page(dr->handle, number); } void print_begin_puzzle(drawing *dr, float xm, float xc, float ym, float yc, int pw, int ph, float wmm, float scale) { dr->scale = scale; dr->ncolours = 0; dr->api->begin_puzzle(dr->handle, xm, xc, ym, yc, pw, ph, wmm); } void print_end_puzzle(drawing *dr) { dr->api->end_puzzle(dr->handle); dr->scale = 1.0F; } void print_end_page(drawing *dr, int number) { dr->api->end_page(dr->handle, number); } void print_end_doc(drawing *dr) { dr->api->end_doc(dr->handle); } void print_get_colour(drawing *dr, int colour, int printing_in_colour, int *hatch, float *r, float *g, float *b) { assert(colour >= 0 && colour < dr->ncolours); if (dr->colours[colour].hatch_when == 2 || (dr->colours[colour].hatch_when == 1 && !printing_in_colour)) { *hatch = dr->colours[colour].hatch; } else { *hatch = -1; if (printing_in_colour) { *r = dr->colours[colour].r; *g = dr->colours[colour].g; *b = dr->colours[colour].b; } else { *r = *g = *b = dr->colours[colour].grey; } } } static int print_generic_colour(drawing *dr, float r, float g, float b, float grey, int hatch, int hatch_when) { if (dr->ncolours >= dr->coloursize) { dr->coloursize = dr->ncolours + 16; dr->colours = sresize(dr->colours, dr->coloursize, struct print_colour); } dr->colours[dr->ncolours].hatch = hatch; dr->colours[dr->ncolours].hatch_when = hatch_when; dr->colours[dr->ncolours].r = r; dr->colours[dr->ncolours].g = g; dr->colours[dr->ncolours].b = b; dr->colours[dr->ncolours].grey = grey; return dr->ncolours++; } int print_mono_colour(drawing *dr, int grey) { return print_generic_colour(dr, grey, grey, grey, grey, -1, 0); } int print_grey_colour(drawing *dr, float grey) { return print_generic_colour(dr, grey, grey, grey, grey, -1, 0); } int print_hatched_colour(drawing *dr, int hatch) { return print_generic_colour(dr, 0, 0, 0, 0, hatch, 2); } int print_rgb_mono_colour(drawing *dr, float r, float g, float b, int grey) { return print_generic_colour(dr, r, g, b, grey, -1, 0); } int print_rgb_grey_colour(drawing *dr, float r, float g, float b, float grey) { return print_generic_colour(dr, r, g, b, grey, -1, 0); } int print_rgb_hatched_colour(drawing *dr, float r, float g, float b, int hatch) { return print_generic_colour(dr, r, g, b, 0, hatch, 1); } void print_line_width(drawing *dr, int width) { /* * I don't think it's entirely sensible to have line widths be * entirely relative to the puzzle size; there is a point * beyond which lines are just _stupidly_ thick. On the other * hand, absolute line widths aren't particularly nice either * because they start to feel a bit feeble at really large * scales. * * My experimental answer is to scale line widths as the * _square root_ of the main puzzle scale. Double the puzzle * size, and the line width multiplies by 1.4. */ dr->api->line_width(dr->handle, (float)sqrt(dr->scale) * width); } void print_line_dotted(drawing *dr, int dotted) { dr->api->line_dotted(dr->handle, dotted); } puzzles-r9872/dsf.c0000644000175300017530000001216611315630163013352 0ustar simonsimon/* * dsf.c: some functions to handle a disjoint set forest, * which is a data structure useful in any solver which has to * worry about avoiding closed loops. */ #include #include #include "puzzles.h" /*void print_dsf(int *dsf, int size) { int *printed_elements = snewn(size, int); int *equal_elements = snewn(size, int); int *inverse_elements = snewn(size, int); int printed_count = 0, equal_count, inverse_count; int i, n, inverse; memset(printed_elements, -1, sizeof(int) * size); while (1) { equal_count = 0; inverse_count = 0; for (i = 0; i < size; ++i) { if (!memchr(printed_elements, i, sizeof(int) * size)) break; } if (i == size) goto done; i = dsf_canonify(dsf, i); for (n = 0; n < size; ++n) { if (edsf_canonify(dsf, n, &inverse) == i) { if (inverse) inverse_elements[inverse_count++] = n; else equal_elements[equal_count++] = n; } } for (n = 0; n < equal_count; ++n) { fprintf(stderr, "%d ", equal_elements[n]); printed_elements[printed_count++] = equal_elements[n]; } if (inverse_count) { fprintf(stderr, "!= "); for (n = 0; n < inverse_count; ++n) { fprintf(stderr, "%d ", inverse_elements[n]); printed_elements[printed_count++] = inverse_elements[n]; } } fprintf(stderr, "\n"); } done: sfree(printed_elements); sfree(equal_elements); sfree(inverse_elements); }*/ void dsf_init(int *dsf, int size) { int i; for (i = 0; i < size; i++) dsf[i] = 6; /* Bottom bit of each element of this array stores whether that * element is opposite to its parent, which starts off as * false. Second bit of each element stores whether that element * is the root of its tree or not. If it's not the root, the * remaining 30 bits are the parent, otherwise the remaining 30 * bits are the number of elements in the tree. */ } int *snew_dsf(int size) { int *ret; ret = snewn(size, int); dsf_init(ret, size); /*print_dsf(ret, size); */ return ret; } int dsf_canonify(int *dsf, int index) { return edsf_canonify(dsf, index, NULL); } void dsf_merge(int *dsf, int v1, int v2) { edsf_merge(dsf, v1, v2, FALSE); } int dsf_size(int *dsf, int index) { return dsf[dsf_canonify(dsf, index)] >> 2; } int edsf_canonify(int *dsf, int index, int *inverse_return) { int start_index = index, canonical_index; int inverse = 0; /* fprintf(stderr, "dsf = %p\n", dsf); */ /* fprintf(stderr, "Canonify %2d\n", index); */ assert(index >= 0); /* Find the index of the canonical element of the 'equivalence class' of * which start_index is a member, and figure out whether start_index is the * same as or inverse to that. */ while ((dsf[index] & 2) == 0) { inverse ^= (dsf[index] & 1); index = dsf[index] >> 2; /* fprintf(stderr, "index = %2d, ", index); */ /* fprintf(stderr, "inverse = %d\n", inverse); */ } canonical_index = index; if (inverse_return) *inverse_return = inverse; /* Update every member of this 'equivalence class' to point directly at the * canonical member. */ index = start_index; while (index != canonical_index) { int nextindex = dsf[index] >> 2; int nextinverse = inverse ^ (dsf[index] & 1); dsf[index] = (canonical_index << 2) | inverse; inverse = nextinverse; index = nextindex; } assert(inverse == 0); /* fprintf(stderr, "Return %2d\n", index); */ return index; } void edsf_merge(int *dsf, int v1, int v2, int inverse) { int i1, i2; /* fprintf(stderr, "dsf = %p\n", dsf); */ /* fprintf(stderr, "Merge [%2d,%2d], %d\n", v1, v2, inverse); */ v1 = edsf_canonify(dsf, v1, &i1); assert(dsf[v1] & 2); inverse ^= i1; v2 = edsf_canonify(dsf, v2, &i2); assert(dsf[v2] & 2); inverse ^= i2; /* fprintf(stderr, "Doing [%2d,%2d], %d\n", v1, v2, inverse); */ if (v1 == v2) assert(!inverse); else { assert(inverse == 0 || inverse == 1); /* * We always make the smaller of v1 and v2 the new canonical * element. This ensures that the canonical element of any * class in this structure is always the first element in * it. 'Keen' depends critically on this property. * * (Jonas Koelker previously had this code choosing which * way round to connect the trees by examining the sizes of * the classes being merged, so that the root of the * larger-sized class became the new root. This gives better * asymptotic performance, but I've changed it to do it this * way because I like having a deterministic canonical * element.) */ if (v1 > v2) { int v3 = v1; v1 = v2; v2 = v3; } dsf[v1] += (dsf[v2] >> 2) << 2; dsf[v2] = (v1 << 2) | !!inverse; } v2 = edsf_canonify(dsf, v2, &i2); assert(v2 == v1); assert(i2 == inverse); /* fprintf(stderr, "dsf[%2d] = %2d\n", v2, dsf[v2]); */ } puzzles-r9872/emcc.c0000644000175300017530000006266312130244545013515 0ustar simonsimon/* * emcc.c: the C component of an Emscripten-based web/Javascript front * end for Puzzles. * * The Javascript parts of this system live in emcclib.js 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. */ /* * Further thoughts on possible enhancements: * * - I think it might be feasible to have these JS puzzles permit * loading and saving games in disk files. Saving would be done by * constructing a data: URI encapsulating the save file, and then * telling the browser to visit that URI with the effect that it * would naturally pop up a 'where would you like to save this' * dialog box. Loading, more or less similarly, might be feasible * by using the DOM File API to ask the user to select a file and * permit us to see its contents. * * - I should think about whether these webified puzzles can support * touchscreen-based tablet browsers (assuming there are any that * can cope with the reasonably modern JS and run it fast enough to * be worthwhile). * * - think about making use of localStorage. It might be useful to * let the user save games into there as an alternative to disk * files - disk files are all very well for getting the save right * out of your browser to (e.g.) email to me as a bug report, but * for just resuming a game you were in the middle of, you'd * probably rather have a nice simple 'quick save' and 'quick load' * button pair. Also, that might be a useful place to store * preferences, if I ever get round to writing a preferences UI. * * - some CSS to make the button bar and configuration dialogs a * little less ugly would probably not go amiss. * * - this is a downright silly idea, but it does occur to me that if * I were to write a PDF output driver for the Puzzles printing * API, then I might be able to implement a sort of 'printing' * feature in this front end, using data: URIs again. (Ask the user * exactly what they want printed, then construct an appropriate * PDF and embed it in a gigantic data: URI. Then they can print * that using whatever they normally use to print PDFs!) */ #include #include #include "puzzles.h" /* * Extern references to Javascript functions provided in emcclib.js. */ extern void js_debug(const char *); extern void js_error_box(const char *message); extern void js_remove_type_dropdown(void); extern void js_remove_solve_button(void); extern void js_add_preset(const char *name); extern int js_get_selected_preset(void); extern void js_select_preset(int n); extern void js_get_date_64(unsigned *p); extern void js_update_permalinks(const char *desc, const char *seed); extern void js_enable_undo_redo(int undo, int redo); extern void js_activate_timer(); extern void js_deactivate_timer(); extern void js_canvas_start_draw(void); extern void js_canvas_draw_update(int x, int y, int w, int h); extern void js_canvas_end_draw(void); extern void js_canvas_draw_rect(int x, int y, int w, int h, const char *colour); extern void js_canvas_clip_rect(int x, int y, int w, int h); extern void js_canvas_unclip(void); extern void js_canvas_draw_line(float x1, float y1, float x2, float y2, int width, const char *colour); extern void js_canvas_draw_poly(int *points, int npoints, const char *fillcolour, const char *outlinecolour); extern void js_canvas_draw_circle(int x, int y, int r, const char *fillcolour, const char *outlinecolour); extern int js_canvas_find_font_midpoint(int height, const char *fontptr); extern void js_canvas_draw_text(int x, int y, int halign, const char *colptr, const char *fontptr, const char *text); extern int js_canvas_new_blitter(int w, int h); extern void js_canvas_free_blitter(int id); extern void js_canvas_copy_to_blitter(int id, int x, int y, int w, int h); extern void js_canvas_copy_from_blitter(int id, int x, int y, int w, int h); extern void js_canvas_make_statusbar(void); extern void js_canvas_set_statusbar(const char *text); extern void js_canvas_set_size(int w, int h); extern void js_dialog_init(const char *title); extern void js_dialog_string(int i, const char *title, const char *initvalue); extern void js_dialog_choices(int i, const char *title, const char *choicelist, int initvalue); extern void js_dialog_boolean(int i, const char *title, int initvalue); extern void js_dialog_launch(void); extern void js_dialog_cleanup(void); extern void js_focus_canvas(void); /* * Call JS to get the date, and use that to initialise our random * number generator to invent the first game seed. */ void get_random_seed(void **randseed, int *randseedsize) { unsigned *ret = snewn(2, unsigned); js_get_date_64(ret); *randseed = ret; *randseedsize = 2*sizeof(unsigned); } /* * Fatal error, called in cases of complete despair such as when * malloc() has returned NULL. */ void fatal(char *fmt, ...) { char buf[512]; va_list ap; strcpy(buf, "puzzle fatal error: "); va_start(ap, fmt); vsnprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), fmt, ap); va_end(ap); js_error_box(buf); } void debug_printf(char *fmt, ...) { char buf[512]; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); js_debug(buf); } /* * Helper function that makes it easy to test strings that might be * NULL. */ int strnullcmp(const char *a, const char *b) { if (a == NULL || b == NULL) return a != NULL ? +1 : b != NULL ? -1 : 0; return strcmp(a, b); } /* * HTMLish names for the colours allocated by the puzzle. */ char **colour_strings; int ncolours; /* * The global midend object. */ midend *me; /* ---------------------------------------------------------------------- * Timing functions. */ int timer_active = FALSE; void deactivate_timer(frontend *fe) { js_deactivate_timer(); timer_active = FALSE; } void activate_timer(frontend *fe) { if (!timer_active) { js_activate_timer(); timer_active = TRUE; } } void timer_callback(double tplus) { if (timer_active) midend_timer(me, tplus); } /* ---------------------------------------------------------------------- * Helper functions to resize the canvas, and variables to remember * its size for other functions (e.g. trimming blitter rectangles). */ static int canvas_w, canvas_h; /* Called when we resize as a result of changing puzzle settings */ static void resize(void) { int w, h; w = h = INT_MAX; midend_size(me, &w, &h, FALSE); js_canvas_set_size(w, h); canvas_w = w; canvas_h = h; } /* Called from JS when the user uses the resize handle */ void resize_puzzle(int w, int h) { midend_size(me, &w, &h, TRUE); if (canvas_w != w || canvas_h != h) { js_canvas_set_size(w, h); canvas_w = w; canvas_h = h; midend_force_redraw(me); } } /* Called from JS when the user uses the restore button */ void restore_puzzle_size(int w, int h) { midend_reset_tilesize(me); resize(); midend_force_redraw(me); } /* * HTML doesn't give us a default frontend colour of its own, so we * just make up a lightish grey ourselves. */ void frontend_default_colour(frontend *fe, float *output) { output[0] = output[1] = output[2] = 0.9F; } /* * Helper function called from all over the place to ensure the undo * and redo buttons get properly enabled and disabled after every move * or undo or new-game event. */ static void update_undo_redo(void) { js_enable_undo_redo(midend_can_undo(me), midend_can_redo(me)); } /* * Mouse event handlers called from JS. */ void mousedown(int x, int y, int button) { button = (button == 0 ? LEFT_BUTTON : button == 1 ? MIDDLE_BUTTON : RIGHT_BUTTON); midend_process_key(me, x, y, button); update_undo_redo(); } void mouseup(int x, int y, int button) { button = (button == 0 ? LEFT_RELEASE : button == 1 ? MIDDLE_RELEASE : RIGHT_RELEASE); midend_process_key(me, x, y, button); update_undo_redo(); } void mousemove(int x, int y, int buttons) { int button = (buttons & 2 ? MIDDLE_DRAG : buttons & 4 ? RIGHT_DRAG : LEFT_DRAG); midend_process_key(me, x, y, button); update_undo_redo(); } /* * Keyboard handler called from JS. */ void key(int keycode, int charcode, const char *key, const char *chr, int shift, int ctrl) { int keyevent = -1; if (!strnullcmp(key, "Backspace") || !strnullcmp(key, "Del") || keycode == 8 || keycode == 46) { keyevent = 127; /* Backspace / Delete */ } else if (!strnullcmp(key, "Enter") || keycode == 13) { keyevent = 13; /* return */ } else if (!strnullcmp(key, "Left") || keycode == 37) { keyevent = CURSOR_LEFT; } else if (!strnullcmp(key, "Up") || keycode == 38) { keyevent = CURSOR_UP; } else if (!strnullcmp(key, "Right") || keycode == 39) { keyevent = CURSOR_RIGHT; } else if (!strnullcmp(key, "Down") || keycode == 40) { keyevent = CURSOR_DOWN; } else if (!strnullcmp(key, "End") || keycode == 35) { /* * We interpret Home, End, PgUp and PgDn as numeric keypad * controls regardless of whether they're the ones on the * numeric keypad (since we can't tell). The effect of * this should only be that the non-numeric-pad versions * of those keys generate directions in 8-way movement * puzzles like Cube and Inertia. */ keyevent = MOD_NUM_KEYPAD | '1'; } else if (!strnullcmp(key, "PageDown") || keycode==34) { keyevent = MOD_NUM_KEYPAD | '3'; } else if (!strnullcmp(key, "Home") || keycode==36) { keyevent = MOD_NUM_KEYPAD | '7'; } else if (!strnullcmp(key, "PageUp") || keycode==33) { keyevent = MOD_NUM_KEYPAD | '9'; } else if (chr && chr[0] && !chr[1]) { keyevent = chr[0] & 0xFF; } else if (keycode >= 96 && keycode < 106) { keyevent = MOD_NUM_KEYPAD | ('0' + keycode - 96); } else if (keycode >= 65 && keycode <= 90) { keyevent = keycode + (shift ? 0 : 32); } else if (keycode >= 48 && keycode <= 57) { keyevent = keycode; } if (keyevent >= 0) { if (shift && keyevent >= 0x100) keyevent |= MOD_SHFT; if (ctrl) { if (keyevent >= 0x100) keyevent |= MOD_CTRL; else keyevent &= 0x1F; } midend_process_key(me, 0, 0, keyevent); update_undo_redo(); } } /* * Helper function called from several places to update the permalinks * whenever a new game is created. */ static void update_permalinks(void) { char *desc, *seed; desc = midend_get_game_id(me); seed = midend_get_random_seed(me); js_update_permalinks(desc, seed); sfree(desc); sfree(seed); } /* * Callback from the midend when the game ids change, so we can update * the permalinks. */ static void ids_changed(void *ignored) { update_permalinks(); } /* ---------------------------------------------------------------------- * Implementation of the drawing API by calling Javascript canvas * drawing functions. (Well, half of it; the other half is on the JS * side.) */ static void js_start_draw(void *handle) { js_canvas_start_draw(); } static void js_clip(void *handle, int x, int y, int w, int h) { js_canvas_clip_rect(x, y, w, h); } static void js_unclip(void *handle) { js_canvas_unclip(); } static void js_draw_text(void *handle, int x, int y, int fonttype, int fontsize, int align, int colour, char *text) { char fontstyle[80]; int halign; sprintf(fontstyle, "%dpx %s", fontsize, fonttype == FONT_FIXED ? "monospace" : "sans-serif"); if (align & ALIGN_VCENTRE) y += js_canvas_find_font_midpoint(fontsize, fontstyle); if (align & ALIGN_HCENTRE) halign = 1; else if (align & ALIGN_HRIGHT) halign = 2; else halign = 0; js_canvas_draw_text(x, y, halign, colour_strings[colour], fontstyle, text); } static void js_draw_rect(void *handle, int x, int y, int w, int h, int colour) { js_canvas_draw_rect(x, y, w, h, colour_strings[colour]); } static void js_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour) { js_canvas_draw_line(x1, y1, x2, y2, 1, colour_strings[colour]); } static void js_draw_thick_line(void *handle, float thickness, float x1, float y1, float x2, float y2, int colour) { js_canvas_draw_line(x1, y1, x2, y2, thickness, colour_strings[colour]); } static void js_draw_poly(void *handle, int *coords, int npoints, int fillcolour, int outlinecolour) { js_canvas_draw_poly(coords, npoints, fillcolour >= 0 ? colour_strings[fillcolour] : NULL, colour_strings[outlinecolour]); } static void js_draw_circle(void *handle, int cx, int cy, int radius, int fillcolour, int outlinecolour) { js_canvas_draw_circle(cx, cy, radius, fillcolour >= 0 ? colour_strings[fillcolour] : NULL, colour_strings[outlinecolour]); } struct blitter { int id; /* allocated on the js side */ int w, h; /* easier to retain here */ }; static blitter *js_blitter_new(void *handle, int w, int h) { blitter *bl = snew(blitter); bl->w = w; bl->h = h; bl->id = js_canvas_new_blitter(w, h); return bl; } static void js_blitter_free(void *handle, blitter *bl) { js_canvas_free_blitter(bl->id); sfree(bl); } static void trim_rect(int *x, int *y, int *w, int *h) { int x0, x1, y0, y1; /* * Reduce the size of the copied rectangle to stop it going * outside the bounds of the canvas. */ /* Transform from x,y,w,h form into coordinates of all edges */ x0 = *x; y0 = *y; x1 = *x + *w; y1 = *y + *h; /* Clip each coordinate at both extremes of the canvas */ x0 = (x0 < 0 ? 0 : x0 > canvas_w ? canvas_w : x0); x1 = (x1 < 0 ? 0 : x1 > canvas_w ? canvas_w : x1); y0 = (y0 < 0 ? 0 : y0 > canvas_h ? canvas_h : y0); y1 = (y1 < 0 ? 0 : y1 > canvas_h ? canvas_h : y1); /* Transform back into x,y,w,h to return */ *x = x0; *y = y0; *w = x1 - x0; *h = y1 - y0; } static void js_blitter_save(void *handle, blitter *bl, int x, int y) { int w = bl->w, h = bl->h; trim_rect(&x, &y, &w, &h); if (w > 0 && h > 0) js_canvas_copy_to_blitter(bl->id, x, y, w, h); } static void js_blitter_load(void *handle, blitter *bl, int x, int y) { int w = bl->w, h = bl->h; trim_rect(&x, &y, &w, &h); if (w > 0 && h > 0) js_canvas_copy_from_blitter(bl->id, x, y, w, h); } static void js_draw_update(void *handle, int x, int y, int w, int h) { trim_rect(&x, &y, &w, &h); if (w > 0 && h > 0) js_canvas_draw_update(x, y, w, h); } static void js_end_draw(void *handle) { js_canvas_end_draw(); } static void js_status_bar(void *handle, char *text) { js_canvas_set_statusbar(text); } static char *js_text_fallback(void *handle, const char *const *strings, int nstrings) { return dupstr(strings[0]); /* Emscripten has no trouble with UTF-8 */ } const struct drawing_api js_drawing = { js_draw_text, js_draw_rect, js_draw_line, js_draw_poly, js_draw_circle, js_draw_update, js_clip, js_unclip, js_start_draw, js_end_draw, js_status_bar, js_blitter_new, js_blitter_free, js_blitter_save, js_blitter_load, NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */ NULL, NULL, /* line_width, line_dotted */ js_text_fallback, js_draw_thick_line, }; /* ---------------------------------------------------------------------- * Presets and game-configuration dialog support. */ static game_params **presets; static int npresets; int have_presets_dropdown; void select_appropriate_preset(void) { if (have_presets_dropdown) { int preset = midend_which_preset(me); js_select_preset(preset < 0 ? -1 : preset); } } static config_item *cfg = NULL; static int cfg_which; /* * Set up a dialog box. This is pretty easy on the C side; most of the * work is done in JS. */ static void cfg_start(int which) { char *title; int i; cfg = midend_get_config(me, which, &title); cfg_which = which; js_dialog_init(title); sfree(title); for (i = 0; cfg[i].type != C_END; i++) { switch (cfg[i].type) { case C_STRING: js_dialog_string(i, cfg[i].name, cfg[i].sval); break; case C_BOOLEAN: js_dialog_boolean(i, cfg[i].name, cfg[i].ival); break; case C_CHOICES: js_dialog_choices(i, cfg[i].name, cfg[i].sval, cfg[i].ival); break; } } js_dialog_launch(); } /* * Callbacks from JS when the OK button is clicked, to return the * final state of each control. */ void dlg_return_sval(int index, const char *val) { sfree(cfg[index].sval); cfg[index].sval = dupstr(val); } void dlg_return_ival(int index, int val) { cfg[index].ival = val; } /* * Called when the user clicks OK or Cancel. use_results will be TRUE * or FALSE respectively, in those cases. We terminate the dialog box, * unless the user selected an invalid combination of parameters. */ static void cfg_end(int use_results) { if (use_results) { /* * User hit OK. */ char *err = midend_set_config(me, cfg_which, cfg); if (err) { /* * The settings were unacceptable, so leave the config box * open for the user to adjust them and try again. */ js_error_box(err); } else { /* * New settings are fine; start a new game and close the * dialog. */ select_appropriate_preset(); midend_new_game(me); resize(); midend_redraw(me); free_cfg(cfg); js_dialog_cleanup(); } } else { /* * User hit Cancel. Close the dialog, but also we must still * reselect the right element of the dropdown list. * * (Because: imagine you have a preset selected, and then you * select Custom from the list, but change your mind and hit * Esc. The Custom option will now still be selected in the * list, whereas obviously it should show the preset you still * _actually_ have selected. Worse still, it'll be the visible * rather than invisible Custom option - see the comment in * js_add_preset in emcclib.js - so you won't even be able to * select Custom without a faffy workaround.) */ select_appropriate_preset(); free_cfg(cfg); js_dialog_cleanup(); } } /* ---------------------------------------------------------------------- * Called from JS when a command is given to the puzzle by clicking a * button or control of some sort. */ void command(int n) { switch (n) { case 0: /* specific game ID */ cfg_start(CFG_DESC); break; case 1: /* random game seed */ cfg_start(CFG_SEED); break; case 2: /* game parameter dropdown changed */ { int i = js_get_selected_preset(); if (i < 0) { /* * The user selected 'Custom', so launch the config * box. */ if (thegame.can_configure) /* (double-check just in case) */ cfg_start(CFG_SETTINGS); } else { /* * The user selected a preset, so just switch straight * to that. */ assert(i < npresets); midend_set_params(me, presets[i]); midend_new_game(me); resize(); midend_redraw(me); update_undo_redo(); js_focus_canvas(); select_appropriate_preset(); /* sort out Custom/Customise */ } } break; case 3: /* OK clicked in a config box */ cfg_end(TRUE); update_undo_redo(); break; case 4: /* Cancel clicked in a config box */ cfg_end(FALSE); update_undo_redo(); break; case 5: /* New Game */ midend_process_key(me, 0, 0, 'n'); update_undo_redo(); js_focus_canvas(); break; case 6: /* Restart */ midend_restart_game(me); update_undo_redo(); js_focus_canvas(); break; case 7: /* Undo */ midend_process_key(me, 0, 0, 'u'); update_undo_redo(); js_focus_canvas(); break; case 8: /* Redo */ midend_process_key(me, 0, 0, 'r'); update_undo_redo(); js_focus_canvas(); break; case 9: /* Solve */ if (thegame.can_solve) { char *msg = midend_solve(me); if (msg) js_error_box(msg); } update_undo_redo(); js_focus_canvas(); break; } } /* ---------------------------------------------------------------------- * Setup function called at page load time. It's called main() because * that's the most convenient thing in Emscripten, but it's not main() * in the usual sense of bounding the program's entire execution. * Instead, this function returns once the initial puzzle is set up * and working, and everything thereafter happens by means of JS event * handlers sending us callbacks. */ int main(int argc, char **argv) { char *param_err; float *colours; int i; /* * Instantiate a midend. */ me = midend_new(NULL, &thegame, &js_drawing, NULL); /* * Chuck in the HTML fragment ID if we have one (trimming the * leading # off the front first). If that's invalid, we retain * the error message and will display it at the end, after setting * up a random puzzle as usual. */ if (argc > 1 && argv[1][0] == '#' && argv[1][1] != '\0') param_err = midend_game_id(me, argv[1] + 1); else param_err = NULL; /* * Create either a random game or the specified one, and set the * canvas size appropriately. */ midend_new_game(me); resize(); /* * Create a status bar, if needed. */ if (midend_wants_statusbar(me)) js_canvas_make_statusbar(); /* * Set up the game-type dropdown with presets and/or the Custom * option. */ npresets = midend_num_presets(me); if (npresets == 0) { /* * This puzzle doesn't have selectable game types at all. * Completely remove the drop-down list from the page. */ js_remove_type_dropdown(); have_presets_dropdown = FALSE; } else { int preset; presets = snewn(npresets, game_params *); for (i = 0; i < npresets; i++) { char *name; midend_fetch_preset(me, i, &name, &presets[i]); js_add_preset(name); } if (thegame.can_configure) js_add_preset(NULL); /* the 'Custom' entry in the dropdown */ have_presets_dropdown = TRUE; /* * Now ensure the appropriate element of the presets menu * starts off selected, in case it isn't the first one in the * list (e.g. Slant). */ select_appropriate_preset(); } /* * Remove the Solve button if the game doesn't support it. */ if (!thegame.can_solve) js_remove_solve_button(); /* * Retrieve the game's colours, and convert them into #abcdef type * hex ID strings. */ colours = midend_colours(me, &ncolours); colour_strings = snewn(ncolours, char *); for (i = 0; i < ncolours; i++) { char col[40]; sprintf(col, "#%02x%02x%02x", (unsigned)(0.5 + 255 * colours[i*3+0]), (unsigned)(0.5 + 255 * colours[i*3+1]), (unsigned)(0.5 + 255 * colours[i*3+2])); colour_strings[i] = dupstr(col); } /* * Request notification when the game ids change (e.g. if the user * presses 'n', and also when Mines supersedes its game * description), so that we can proactively update the permalink. */ midend_request_id_changes(me, ids_changed, NULL); /* * Draw the puzzle's initial state, and set up the permalinks and * undo/redo greying out. */ midend_redraw(me); update_permalinks(); update_undo_redo(); /* * If we were given an erroneous game ID in argv[1], now's the * time to put up the error box about it, after we've fully set up * a random puzzle. Then when the user clicks 'ok', we have a * puzzle for them. */ if (param_err) js_error_box(param_err); /* * Done. Return to JS, and await callbacks! */ return 0; } puzzles-r9872/fifteen.c0000644000175300017530000005263512132232554014223 0ustar simonsimon/* * fifteen.c: standard 15-puzzle. */ #include #include #include #include #include #include #include "puzzles.h" #define PREFERRED_TILE_SIZE 48 #define TILE_SIZE (ds->tilesize) #define BORDER (TILE_SIZE / 2) #define HIGHLIGHT_WIDTH (TILE_SIZE / 20) #define COORD(x) ( (x) * TILE_SIZE + BORDER ) #define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 ) #define ANIM_TIME 0.13F #define FLASH_FRAME 0.13F #define X(state, i) ( (i) % (state)->w ) #define Y(state, i) ( (i) / (state)->w ) #define C(state, x, y) ( (y) * (state)->w + (x) ) enum { COL_BACKGROUND, COL_TEXT, COL_HIGHLIGHT, COL_LOWLIGHT, NCOLOURS }; struct game_params { int w, h; }; struct game_state { int w, h, n; int *tiles; int gap_pos; int completed; int used_solve; /* used to suppress completion flash */ int movecount; }; static game_params *default_params(void) { game_params *ret = snew(game_params); ret->w = ret->h = 4; return ret; } static int game_fetch_preset(int i, char **name, game_params **params) { if (i == 0) { *params = default_params(); *name = dupstr("4x4"); return TRUE; } 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 *ret, char const *string) { ret->w = ret->h = atoi(string); while (*string && isdigit((unsigned char)*string)) string++; if (*string == 'x') { string++; ret->h = atoi(string); } } static char *encode_params(const game_params *params, int 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].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = NULL; ret[2].type = C_END; ret[2].sval = NULL; ret[2].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); return ret; } static char *validate_params(const game_params *params, int full) { if (params->w < 2 || params->h < 2) return "Width and height must both be at least two"; return NULL; } static int perm_parity(int *perm, int n) { int i, j, ret; ret = 0; for (i = 0; i < n-1; i++) for (j = i+1; j < n; j++) if (perm[i] > perm[j]) ret = !ret; return ret; } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { int gap, n, i, x; int x1, x2, p1, p2, parity; int *tiles, *used; char *ret; int retlen; n = params->w * params->h; tiles = snewn(n, int); used = snewn(n, int); for (i = 0; i < n; i++) { tiles[i] = -1; used[i] = FALSE; } gap = random_upto(rs, n); tiles[gap] = 0; used[0] = TRUE; /* * Place everything else except the last two tiles. */ for (x = 0, i = n-1; i > 2; i--) { int k = random_upto(rs, i); int j; for (j = 0; j < n; j++) if (!used[j] && (k-- == 0)) break; assert(j < n && !used[j]); used[j] = TRUE; while (tiles[x] >= 0) x++; assert(x < n); tiles[x] = j; } /* * Find the last two locations, and the last two pieces. */ while (tiles[x] >= 0) x++; assert(x < n); x1 = x; x++; while (tiles[x] >= 0) x++; assert(x < n); x2 = x; for (i = 0; i < n; i++) if (!used[i]) break; p1 = i; for (i = p1+1; i < n; i++) if (!used[i]) break; p2 = i; /* * Determine the required parity of the overall permutation. * This is the XOR of: * * - The chessboard parity ((x^y)&1) of the gap square. The * bottom right counts as even. * * - The parity of n. (The target permutation is 1,...,n-1,0 * rather than 0,...,n-1; this is a cyclic permutation of * the starting point and hence is odd iff n is even.) */ parity = ((X(params, gap) - (params->w-1)) ^ (Y(params, gap) - (params->h-1)) ^ (n+1)) & 1; /* * Try the last two tiles one way round. If that fails, swap * them. */ tiles[x1] = p1; tiles[x2] = p2; if (perm_parity(tiles, n) != parity) { tiles[x1] = p2; tiles[x2] = p1; assert(perm_parity(tiles, n) == parity); } /* * Now construct the game description, by describing the tile * array as a simple sequence of comma-separated integers. */ ret = NULL; retlen = 0; for (i = 0; i < n; i++) { char buf[80]; int k; k = sprintf(buf, "%d,", tiles[i]); ret = sresize(ret, retlen + k + 1, char); strcpy(ret + retlen, buf); retlen += k; } ret[retlen-1] = '\0'; /* delete last comma */ sfree(tiles); sfree(used); return ret; } static char *validate_desc(const game_params *params, const char *desc) { const char *p; char *err; int i, area; int *used; area = params->w * params->h; p = desc; err = NULL; used = snewn(area, int); for (i = 0; i < area; i++) used[i] = FALSE; for (i = 0; i < area; i++) { const char *q = p; int n; if (*p < '0' || *p > '9') { err = "Not enough numbers in string"; goto leave; } while (*p >= '0' && *p <= '9') p++; if (i < area-1 && *p != ',') { err = "Expected comma after number"; goto leave; } else if (i == area-1 && *p) { err = "Excess junk at end of string"; goto leave; } n = atoi(q); if (n < 0 || n >= area) { err = "Number out of range"; goto leave; } if (used[n]) { err = "Number used twice"; goto leave; } used[n] = TRUE; if (*p) p++; /* eat comma */ } leave: sfree(used); return err; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { game_state *state = snew(game_state); int i; const char *p; state->w = params->w; state->h = params->h; state->n = params->w * params->h; state->tiles = snewn(state->n, int); state->gap_pos = 0; p = desc; i = 0; for (i = 0; i < state->n; i++) { assert(*p); state->tiles[i] = atoi(p); if (state->tiles[i] == 0) state->gap_pos = i; while (*p && *p != ',') p++; if (*p) p++; /* eat comma */ } assert(!*p); assert(state->tiles[state->gap_pos] == 0); state->completed = state->movecount = 0; state->used_solve = FALSE; return state; } static game_state *dup_game(const game_state *state) { game_state *ret = snew(game_state); ret->w = state->w; ret->h = state->h; ret->n = state->n; ret->tiles = snewn(state->w * state->h, int); memcpy(ret->tiles, state->tiles, state->w * state->h * sizeof(int)); ret->gap_pos = state->gap_pos; ret->completed = state->completed; ret->movecount = state->movecount; ret->used_solve = state->used_solve; return ret; } static void free_game(game_state *state) { sfree(state->tiles); sfree(state); } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { return dupstr("S"); } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { char *ret, *p, buf[80]; int x, y, col, maxlen; /* * First work out how many characters we need to display each * number. */ col = sprintf(buf, "%d", state->n-1); /* * Now we know the exact total size of the grid we're going to * produce: it's got h rows, each containing w lots of col, w-1 * spaces and a trailing newline. */ maxlen = state->h * state->w * (col+1); ret = snewn(maxlen+1, char); p = ret; for (y = 0; y < state->h; y++) { for (x = 0; x < state->w; x++) { int v = state->tiles[state->w*y+x]; if (v == 0) sprintf(buf, "%*s", col, ""); else sprintf(buf, "%*d", col, v); memcpy(p, buf, col); p += col; if (x+1 == state->w) *p++ = '\n'; else *p++ = ' '; } } assert(p - ret == maxlen); *p = '\0'; return ret; } 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 started; int w, h, bgcolour; int *tiles; int tilesize; }; static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { int gx, gy, dx, dy; char buf[80]; button &= ~MOD_MASK; gx = X(state, state->gap_pos); gy = Y(state, state->gap_pos); if (button == CURSOR_RIGHT && gx > 0) dx = gx - 1, dy = gy; else if (button == CURSOR_LEFT && gx < state->w-1) dx = gx + 1, dy = gy; else if (button == CURSOR_DOWN && gy > 0) dy = gy - 1, dx = gx; else if (button == CURSOR_UP && gy < state->h-1) dy = gy + 1, dx = gx; else if (button == LEFT_BUTTON) { dx = FROMCOORD(x); dy = FROMCOORD(y); if (dx < 0 || dx >= state->w || dy < 0 || dy >= state->h) return NULL; /* out of bounds */ /* * Any click location should be equal to the gap location * in _precisely_ one coordinate. */ if ((dx == gx && dy == gy) || (dx != gx && dy != gy)) return NULL; } else return NULL; /* no move */ sprintf(buf, "M%d,%d", dx, dy); return dupstr(buf); } static game_state *execute_move(const game_state *from, const char *move) { int gx, gy, dx, dy, ux, uy, up, p; game_state *ret; if (!strcmp(move, "S")) { int i; ret = dup_game(from); /* * Simply replace the grid with a solved one. For this game, * this isn't a useful operation for actually telling the user * what they should have done, but it is useful for * conveniently being able to get hold of a clean state from * which to practise manoeuvres. */ for (i = 0; i < ret->n; i++) ret->tiles[i] = (i+1) % ret->n; ret->gap_pos = ret->n-1; ret->used_solve = TRUE; ret->completed = ret->movecount = 1; return ret; } gx = X(from, from->gap_pos); gy = Y(from, from->gap_pos); if (move[0] != 'M' || sscanf(move+1, "%d,%d", &dx, &dy) != 2 || (dx == gx && dy == gy) || (dx != gx && dy != gy) || dx < 0 || dx >= from->w || dy < 0 || dy >= from->h) return NULL; /* * Find the unit displacement from the original gap * position towards this one. */ ux = (dx < gx ? -1 : dx > gx ? +1 : 0); uy = (dy < gy ? -1 : dy > gy ? +1 : 0); up = C(from, ux, uy); ret = dup_game(from); ret->gap_pos = C(from, dx, dy); assert(ret->gap_pos >= 0 && ret->gap_pos < ret->n); ret->tiles[ret->gap_pos] = 0; for (p = from->gap_pos; p != ret->gap_pos; p += up) { assert(p >= 0 && p < from->n); ret->tiles[p] = from->tiles[p + up]; ret->movecount++; } /* * See if the game has been completed. */ if (!ret->completed) { ret->completed = ret->movecount; for (p = 0; p < ret->n; p++) if (ret->tiles[p] != (p < ret->n-1 ? p+1 : 0)) ret->completed = 0; } 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 = TILE_SIZE * params->w + 2 * BORDER; *y = TILE_SIZE * params->h + 2 * BORDER; } 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); for (i = 0; i < 3; i++) ret[COL_TEXT * 3 + i] = 0.0; *ncolours = NCOLOURS; return ret; } static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { struct game_drawstate *ds = snew(struct game_drawstate); int i; ds->started = FALSE; ds->w = state->w; ds->h = state->h; ds->bgcolour = COL_BACKGROUND; ds->tiles = snewn(ds->w*ds->h, int); ds->tilesize = 0; /* haven't decided yet */ for (i = 0; i < ds->w*ds->h; i++) ds->tiles[i] = -1; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->tiles); sfree(ds); } static void draw_tile(drawing *dr, game_drawstate *ds, const game_state *state, int x, int y, int tile, int flash_colour) { if (tile == 0) { draw_rect(dr, x, y, TILE_SIZE, TILE_SIZE, flash_colour); } else { int coords[6]; char str[40]; coords[0] = x + TILE_SIZE - 1; coords[1] = y + TILE_SIZE - 1; coords[2] = x + TILE_SIZE - 1; coords[3] = y; coords[4] = x; coords[5] = y + TILE_SIZE - 1; draw_polygon(dr, coords, 3, COL_LOWLIGHT, COL_LOWLIGHT); coords[0] = x; coords[1] = y; draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT); draw_rect(dr, x + HIGHLIGHT_WIDTH, y + HIGHLIGHT_WIDTH, TILE_SIZE - 2*HIGHLIGHT_WIDTH, TILE_SIZE - 2*HIGHLIGHT_WIDTH, flash_colour); sprintf(str, "%d", tile); draw_text(dr, x + TILE_SIZE/2, y + TILE_SIZE/2, FONT_VARIABLE, TILE_SIZE/3, ALIGN_VCENTRE | ALIGN_HCENTRE, COL_TEXT, str); } draw_update(dr, x, y, TILE_SIZE, TILE_SIZE); } 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 i, pass, bgcolour; if (flashtime > 0) { int frame = (int)(flashtime / FLASH_FRAME); bgcolour = (frame % 2 ? COL_LOWLIGHT : COL_HIGHLIGHT); } else bgcolour = COL_BACKGROUND; if (!ds->started) { int coords[10]; draw_rect(dr, 0, 0, TILE_SIZE * state->w + 2 * BORDER, TILE_SIZE * state->h + 2 * BORDER, COL_BACKGROUND); draw_update(dr, 0, 0, TILE_SIZE * state->w + 2 * BORDER, TILE_SIZE * state->h + 2 * BORDER); /* * Recessed area containing the whole puzzle. */ coords[0] = COORD(state->w) + HIGHLIGHT_WIDTH - 1; coords[1] = COORD(state->h) + HIGHLIGHT_WIDTH - 1; coords[2] = COORD(state->w) + HIGHLIGHT_WIDTH - 1; coords[3] = COORD(0) - HIGHLIGHT_WIDTH; coords[4] = coords[2] - TILE_SIZE; coords[5] = coords[3] + TILE_SIZE; coords[8] = COORD(0) - HIGHLIGHT_WIDTH; coords[9] = COORD(state->h) + HIGHLIGHT_WIDTH - 1; coords[6] = coords[8] + TILE_SIZE; coords[7] = coords[9] - TILE_SIZE; draw_polygon(dr, coords, 5, COL_HIGHLIGHT, COL_HIGHLIGHT); coords[1] = COORD(0) - HIGHLIGHT_WIDTH; coords[0] = COORD(0) - HIGHLIGHT_WIDTH; draw_polygon(dr, coords, 5, COL_LOWLIGHT, COL_LOWLIGHT); ds->started = TRUE; } /* * Now draw each tile. We do this in two passes to make * animation easy. */ for (pass = 0; pass < 2; pass++) { for (i = 0; i < state->n; i++) { int t, t0; /* * Figure out what should be displayed at this * location. It's either a simple tile, or it's a * transition between two tiles (in which case we say * -1 because it must always be drawn). */ if (oldstate && oldstate->tiles[i] != state->tiles[i]) t = -1; else t = state->tiles[i]; t0 = t; if (ds->bgcolour != bgcolour || /* always redraw when flashing */ ds->tiles[i] != t || ds->tiles[i] == -1 || t == -1) { int x, y; /* * Figure out what to _actually_ draw, and where to * draw it. */ if (t == -1) { int x0, y0, x1, y1; int j; /* * On the first pass, just blank the tile. */ if (pass == 0) { x = COORD(X(state, i)); y = COORD(Y(state, i)); t = 0; } else { float c; t = state->tiles[i]; /* * Don't bother moving the gap; just don't * draw it. */ if (t == 0) continue; /* * Find the coordinates of this tile in the old and * new states. */ x1 = COORD(X(state, i)); y1 = COORD(Y(state, i)); for (j = 0; j < oldstate->n; j++) if (oldstate->tiles[j] == state->tiles[i]) break; assert(j < oldstate->n); x0 = COORD(X(state, j)); y0 = COORD(Y(state, j)); c = (animtime / ANIM_TIME); if (c < 0.0F) c = 0.0F; if (c > 1.0F) c = 1.0F; x = x0 + (int)(c * (x1 - x0)); y = y0 + (int)(c * (y1 - y0)); } } else { if (pass == 0) continue; x = COORD(X(state, i)); y = COORD(Y(state, i)); } draw_tile(dr, ds, state, x, y, t, bgcolour); } ds->tiles[i] = t0; } } ds->bgcolour = bgcolour; /* * Update the status bar. */ { char statusbuf[256]; /* * Don't show the new status until we're also showing the * new _state_ - after the game animation is complete. */ if (oldstate) state = oldstate; if (state->used_solve) sprintf(statusbuf, "Moves since auto-solve: %d", state->movecount - state->completed); else sprintf(statusbuf, "%sMoves: %d", (state->completed ? "COMPLETED! " : ""), (state->completed ? state->completed : state->movecount)); status_bar(dr, statusbuf); } } static float game_anim_length(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui) { return ANIM_TIME; } static float game_flash_length(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui) { if (!oldstate->completed && newstate->completed && !oldstate->used_solve && !newstate->used_solve) return 2 * FLASH_FRAME; else return 0.0F; } static int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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 fifteen #endif const struct game thegame = { "Fifteen", "games.fifteen", "fifteen", default_params, game_fetch_preset, 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, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, FALSE, FALSE, game_print_size, game_print, TRUE, /* wants_statusbar */ FALSE, game_timing_state, 0, /* flags */ }; puzzles-r9872/filling.c0000644000175300017530000014470112132232554014223 0ustar simonsimon/* -*- tab-width: 8; indent-tabs-mode: t -*- * filling.c: An implementation of the Nikoli game fillomino. * Copyright (C) 2007 Jonas Klker. See LICENSE for the license. */ /* TODO: * * - use a typedef instead of int for numbers on the board * + replace int with something else (signed short?) * - the type should be signed (for -board[i] and -SENTINEL) * - the type should be somewhat big: board[i] = i * - Using shorts gives us 181x181 puzzles as upper bound. * * - make a somewhat more clever solver * + enable "ghost regions" of size > 1 * - one can put an upper bound on the size of a ghost region * by considering the board size and summing present hints. * + for each square, for i=1..n, what is the distance to a region * containing i? How full is the region? How is this useful? * * - in board generation, after having merged regions such that no * more merges are necessary, try splitting (big) regions. * + it seems that smaller regions make for better puzzles; see * for instance the 7x7 puzzle in this file (grep for 7x7:). * * - symmetric hints (solo-style) * + right now that means including _many_ hints, and the puzzles * won't look any nicer. Not worth it (at the moment). * * - make the solver do recursion/backtracking. * + This is for user-submitted puzzles, not for puzzle * generation (on the other hand, never say never). * * - prove that only w=h=2 needs a special case * * - solo-like pencil marks? * * - a user says that the difficulty is unevenly distributed. * + partition into levels? Will they be non-crap? * * - Allow square contents > 9? * + I could use letters for digits (solo does this), but * letters don't have numeric significance (normal people hate * base36), which is relevant here (much more than in solo). * + [click, 1, 0, enter] => [10 in clicked square]? * + How much information is needed to solve? Does one need to * know the algorithm by which the largest number is set? * * - eliminate puzzle instances with done chunks (1's in particular)? * + that's what the qsort call is all about. * + the 1's don't bother me that much. * + but this takes a LONG time (not always possible)? * - this may be affected by solver (lack of) quality. * - weed them out by construction instead of post-cons check * + but that interleaves make_board and new_game_desc: you * have to alternate between changing the board and * changing the hint set (instead of just creating the * board once, then changing the hint set once -> done). * * - use binary search when discovering the minimal sovable point * + profile to show a need (but when the solver gets slower...) * + 7x9 @ .011s, 9x13 @ .075s, 17x13 @ .661s (all avg with n=100) * + but the hints are independent, not linear, so... what? */ #include #include #include #include #include #include #include #include "puzzles.h" static unsigned char verbose; static void printv(char *fmt, ...) { #ifndef PALM if (verbose) { va_list va; va_start(va, fmt); vprintf(fmt, va); va_end(va); } #endif } /***************************************************************************** * GAME CONFIGURATION AND PARAMETERS * *****************************************************************************/ struct game_params { int h, w; }; struct shared_state { struct game_params params; int *clues; int refcnt; }; struct game_state { int *board; struct shared_state *shared; int completed, cheated; }; static const struct game_params filling_defaults[3] = {{7, 9}, {9, 13}, {13, 17}}; static game_params *default_params(void) { game_params *ret = snew(game_params); *ret = filling_defaults[1]; /* struct copy */ return ret; } static int game_fetch_preset(int i, char **name, game_params **params) { char buf[64]; if (i < 0 || i >= lenof(filling_defaults)) return FALSE; *params = snew(game_params); **params = filling_defaults[i]; /* struct copy */ sprintf(buf, "%dx%d", filling_defaults[i].h, filling_defaults[i].w); *name = dupstr(buf); 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; /* struct copy */ return ret; } static void decode_params(game_params *ret, char const *string) { ret->w = ret->h = atoi(string); while (*string && isdigit((unsigned char) *string)) ++string; if (*string == 'x') ret->h = atoi(++string); } static char *encode_params(const game_params *params, int full) { char buf[64]; sprintf(buf, "%dx%d", params->w, params->h); return dupstr(buf); } static config_item *game_configure(const game_params *params) { config_item *ret; char buf[64]; ret = snewn(3, config_item); ret[0].name = "Width"; ret[0].type = C_STRING; sprintf(buf, "%d", params->w); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = NULL; ret[2].type = C_END; ret[2].sval = NULL; ret[2].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); return ret; } static char *validate_params(const game_params *params, int full) { if (params->w < 1) return "Width must be at least one"; if (params->h < 1) return "Height must be at least one"; return NULL; } /***************************************************************************** * STRINGIFICATION OF GAME STATE * *****************************************************************************/ #define EMPTY 0 /* Example of plaintext rendering: * +---+---+---+---+---+---+---+ * | 6 | | | 2 | | | 2 | * +---+---+---+---+---+---+---+ * | | 3 | | 6 | | 3 | | * +---+---+---+---+---+---+---+ * | 3 | | | | | | 1 | * +---+---+---+---+---+---+---+ * | | 2 | 3 | | 4 | 2 | | * +---+---+---+---+---+---+---+ * | 2 | | | | | | 3 | * +---+---+---+---+---+---+---+ * | | 5 | | 1 | | 4 | | * +---+---+---+---+---+---+---+ * | 4 | | | 3 | | | 3 | * +---+---+---+---+---+---+---+ * * This puzzle instance is taken from the nikoli website * Encoded (unsolved and solved), the strings are these: * 7x7:6002002030603030000010230420200000305010404003003 * 7x7:6662232336663232331311235422255544325413434443313 */ static char *board_to_string(int *board, int w, int h) { const int sz = w * h; const int chw = (4*w + 2); /* +2 for trailing '+' and '\n' */ const int chh = (2*h + 1); /* +1: n fence segments, n+1 posts */ const int chlen = chw * chh; char *repr = snewn(chlen + 1, char); int i; assert(board); /* build the first line ("^(\+---){n}\+$") */ for (i = 0; i < w; ++i) { repr[4*i + 0] = '+'; repr[4*i + 1] = '-'; repr[4*i + 2] = '-'; repr[4*i + 3] = '-'; } repr[4*i + 0] = '+'; repr[4*i + 1] = '\n'; /* ... and copy it onto the odd-numbered lines */ for (i = 0; i < h; ++i) memcpy(repr + (2*i + 2) * chw, repr, chw); /* build the second line ("^(\|\t){n}\|$") */ for (i = 0; i < w; ++i) { repr[chw + 4*i + 0] = '|'; repr[chw + 4*i + 1] = ' '; repr[chw + 4*i + 2] = ' '; repr[chw + 4*i + 3] = ' '; } repr[chw + 4*i + 0] = '|'; repr[chw + 4*i + 1] = '\n'; /* ... and copy it onto the even-numbered lines */ for (i = 1; i < h; ++i) memcpy(repr + (2*i + 1) * chw, repr + chw, chw); /* fill in the numbers */ for (i = 0; i < sz; ++i) { const int x = i % w; const int y = i / w; if (board[i] == EMPTY) continue; repr[chw*(2*y + 1) + (4*x + 2)] = board[i] + '0'; } repr[chlen] = '\0'; return repr; } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { const int w = state->shared->params.w; const int h = state->shared->params.h; return board_to_string(state->board, w, h); } /***************************************************************************** * GAME GENERATION AND SOLVER * *****************************************************************************/ static const int dx[4] = {-1, 1, 0, 0}; static const int dy[4] = {0, 0, -1, 1}; struct solver_state { int *dsf; int *board; int *connected; int nempty; }; static void print_board(int *board, int w, int h) { if (verbose) { char *repr = board_to_string(board, w, h); printv("%s\n", repr); free(repr); } } static game_state *new_game(midend *, const game_params *, const char *); static void free_game(game_state *); #define SENTINEL sz /* generate a random valid board; uses validate_board. */ static void make_board(int *board, int w, int h, random_state *rs) { int *dsf; const unsigned int sz = w * h; /* w=h=2 is a special case which requires a number > max(w, h) */ /* TODO prove that this is the case ONLY for w=h=2. */ const int maxsize = min(max(max(w, h), 3), 9); /* Note that if 1 in {w, h} then it's impossible to have a region * of size > w*h, so the special case only affects w=h=2. */ int nboards = 0; int i; assert(w >= 1); assert(h >= 1); assert(board); dsf = snew_dsf(sz); /* implicit dsf_init */ /* I abuse the board variable: when generating the puzzle, it * contains a shuffled list of numbers {0, ..., nsq-1}. */ for (i = 0; i < (int)sz; ++i) board[i] = i; while (1) { int change; ++nboards; shuffle(board, sz, sizeof (int), rs); /* while the board can in principle be fixed */ do { change = FALSE; for (i = 0; i < (int)sz; ++i) { int a = SENTINEL; int b = SENTINEL; int c = SENTINEL; const int aa = dsf_canonify(dsf, board[i]); int cc = sz; int j; for (j = 0; j < 4; ++j) { const int x = (board[i] % w) + dx[j]; const int y = (board[i] / w) + dy[j]; int bb; if (x < 0 || x >= w || y < 0 || y >= h) continue; bb = dsf_canonify(dsf, w*y + x); if (aa == bb) continue; else if (dsf_size(dsf, aa) == dsf_size(dsf, bb)) { a = aa; b = bb; c = cc; } else if (cc == sz) c = cc = bb; } if (a != SENTINEL) { a = dsf_canonify(dsf, a); assert(a != dsf_canonify(dsf, b)); if (c != sz) assert(a != dsf_canonify(dsf, c)); dsf_merge(dsf, a, c == sz? b: c); /* if repair impossible; make a new board */ if (dsf_size(dsf, a) > maxsize) goto retry; change = TRUE; } } } while (change); for (i = 0; i < (int)sz; ++i) board[i] = dsf_size(dsf, i); sfree(dsf); printv("returning board number %d\n", nboards); return; retry: dsf_init(dsf, sz); } assert(FALSE); /* unreachable */ } static int rhofree(int *hop, int start) { int turtle = start, rabbit = hop[start]; while (rabbit != turtle) { /* find a cycle */ turtle = hop[turtle]; rabbit = hop[hop[rabbit]]; } do { /* check that start is in the cycle */ rabbit = hop[rabbit]; if (start == rabbit) return 1; } while (rabbit != turtle); return 0; } static void merge(int *dsf, int *connected, int a, int b) { int c; assert(dsf); assert(connected); assert(rhofree(connected, a)); assert(rhofree(connected, b)); a = dsf_canonify(dsf, a); b = dsf_canonify(dsf, b); if (a == b) return; dsf_merge(dsf, a, b); c = connected[a]; connected[a] = connected[b]; connected[b] = c; assert(rhofree(connected, a)); assert(rhofree(connected, b)); } static void *memdup(const void *ptr, size_t len, size_t esz) { void *dup = smalloc(len * esz); assert(ptr); memcpy(dup, ptr, len * esz); return dup; } static void expand(struct solver_state *s, int w, int h, int t, int f) { int j; assert(s); assert(s->board[t] == EMPTY); /* expand to empty square */ assert(s->board[f] != EMPTY); /* expand from non-empty square */ printv( "learn: expanding %d from (%d, %d) into (%d, %d)\n", s->board[f], f % w, f / w, t % w, t / w); s->board[t] = s->board[f]; for (j = 0; j < 4; ++j) { const int x = (t % w) + dx[j]; const int y = (t / w) + dy[j]; const int idx = w*y + x; if (x < 0 || x >= w || y < 0 || y >= h) continue; if (s->board[idx] != s->board[t]) continue; merge(s->dsf, s->connected, t, idx); } --s->nempty; } static void clear_count(int *board, int sz) { int i; for (i = 0; i < sz; ++i) { if (board[i] >= 0) continue; else if (board[i] == -SENTINEL) board[i] = EMPTY; else board[i] = -board[i]; } } static void flood_count(int *board, int w, int h, int i, int n, int *c) { const int sz = w * h; int k; if (board[i] == EMPTY) board[i] = -SENTINEL; else if (board[i] == n) board[i] = -board[i]; else return; if (--*c == 0) return; for (k = 0; k < 4; ++k) { const int x = (i % w) + dx[k]; const int y = (i / w) + dy[k]; const int idx = w*y + x; if (x < 0 || x >= w || y < 0 || y >= h) continue; flood_count(board, w, h, idx, n, c); if (*c == 0) return; } } static int check_capacity(int *board, int w, int h, int i) { int n = board[i]; flood_count(board, w, h, i, board[i], &n); clear_count(board, w * h); return n == 0; } static int expandsize(const int *board, int *dsf, int w, int h, int i, int n) { int j; int nhits = 0; int hits[4]; int size = 1; for (j = 0; j < 4; ++j) { const int x = (i % w) + dx[j]; const int y = (i / w) + dy[j]; const int idx = w*y + x; int root; int m; if (x < 0 || x >= w || y < 0 || y >= h) continue; if (board[idx] != n) continue; root = dsf_canonify(dsf, idx); for (m = 0; m < nhits && root != hits[m]; ++m); if (m < nhits) continue; printv("\t (%d, %d) contrib %d to size\n", x, y, dsf[root] >> 2); size += dsf_size(dsf, root); assert(dsf_size(dsf, root) >= 1); hits[nhits++] = root; } return size; } /* * +---+---+---+---+---+---+---+ * | 6 | | | 2 | | | 2 | * +---+---+---+---+---+---+---+ * | | 3 | | 6 | | 3 | | * +---+---+---+---+---+---+---+ * | 3 | | | | | | 1 | * +---+---+---+---+---+---+---+ * | | 2 | 3 | | 4 | 2 | | * +---+---+---+---+---+---+---+ * | 2 | | | | | | 3 | * +---+---+---+---+---+---+---+ * | | 5 | | 1 | | 4 | | * +---+---+---+---+---+---+---+ * | 4 | | | 3 | | | 3 | * +---+---+---+---+---+---+---+ */ /* Solving techniques: * * CONNECTED COMPONENT FORCED EXPANSION (too big): * When a CC can only be expanded in one direction, because all the * other ones would make the CC too big. * +---+---+---+---+---+ * | 2 | 2 | | 2 | _ | * +---+---+---+---+---+ * * CONNECTED COMPONENT FORCED EXPANSION (too small): * When a CC must include a particular square, because otherwise there * would not be enough room to complete it. This includes squares not * adjacent to the CC through learn_critical_square. * +---+---+ * | 2 | _ | * +---+---+ * * DROPPING IN A ONE: * When an empty square has no neighbouring empty squares and only a 1 * will go into the square (or other CCs would be too big). * +---+---+---+ * | 2 | 2 | _ | * +---+---+---+ * * TODO: generalise DROPPING IN A ONE: find the size of the CC of * empty squares and a list of all adjacent numbers. See if only one * number in {1, ..., size} u {all adjacent numbers} is possible. * Probably this is only effective for a CC size < n for some n (4?) * * TODO: backtracking. */ static void filled_square(struct solver_state *s, int w, int h, int i) { int j; for (j = 0; j < 4; ++j) { const int x = (i % w) + dx[j]; const int y = (i / w) + dy[j]; const int idx = w*y + x; if (x < 0 || x >= w || y < 0 || y >= h) continue; if (s->board[i] == s->board[idx]) merge(s->dsf, s->connected, i, idx); } } static void init_solver_state(struct solver_state *s, int w, int h) { const int sz = w * h; int i; assert(s); s->nempty = 0; for (i = 0; i < sz; ++i) s->connected[i] = i; for (i = 0; i < sz; ++i) if (s->board[i] == EMPTY) ++s->nempty; else filled_square(s, w, h, i); } static int learn_expand_or_one(struct solver_state *s, int w, int h) { const int sz = w * h; int i; int learn = FALSE; assert(s); for (i = 0; i < sz; ++i) { int j; int one = TRUE; if (s->board[i] != EMPTY) continue; for (j = 0; j < 4; ++j) { const int x = (i % w) + dx[j]; const int y = (i / w) + dy[j]; const int idx = w*y + x; if (x < 0 || x >= w || y < 0 || y >= h) continue; if (s->board[idx] == EMPTY) { one = FALSE; continue; } if (one && (s->board[idx] == 1 || (s->board[idx] >= expandsize(s->board, s->dsf, w, h, i, s->board[idx])))) one = FALSE; assert(s->board[i] == EMPTY); s->board[i] = -SENTINEL; if (check_capacity(s->board, w, h, idx)) continue; assert(s->board[i] == EMPTY); printv("learn: expanding in one\n"); expand(s, w, h, i, idx); learn = TRUE; break; } if (j == 4 && one) { printv("learn: one at (%d, %d)\n", i % w, i / w); assert(s->board[i] == EMPTY); s->board[i] = 1; assert(s->nempty); --s->nempty; learn = TRUE; } } return learn; } static int learn_blocked_expansion(struct solver_state *s, int w, int h) { const int sz = w * h; int i; int learn = FALSE; assert(s); /* for every connected component */ for (i = 0; i < sz; ++i) { int exp = SENTINEL; int j; if (s->board[i] == EMPTY) continue; j = dsf_canonify(s->dsf, i); /* (but only for each connected component) */ if (i != j) continue; /* (and not if it's already complete) */ if (dsf_size(s->dsf, j) == s->board[j]) continue; /* for each square j _in_ the connected component */ do { int k; printv(" looking at (%d, %d)\n", j % w, j / w); /* for each neighbouring square (idx) */ for (k = 0; k < 4; ++k) { const int x = (j % w) + dx[k]; const int y = (j / w) + dy[k]; const int idx = w*y + x; int size; /* int l; int nhits = 0; int hits[4]; */ if (x < 0 || x >= w || y < 0 || y >= h) continue; if (s->board[idx] != EMPTY) continue; if (exp == idx) continue; printv("\ttrying to expand onto (%d, %d)\n", x, y); /* find out the would-be size of the new connected * component if we actually expanded into idx */ /* size = 1; for (l = 0; l < 4; ++l) { const int lx = x + dx[l]; const int ly = y + dy[l]; const int idxl = w*ly + lx; int root; int m; if (lx < 0 || lx >= w || ly < 0 || ly >= h) continue; if (board[idxl] != board[j]) continue; root = dsf_canonify(dsf, idxl); for (m = 0; m < nhits && root != hits[m]; ++m); if (m != nhits) continue; // printv("\t (%d, %d) contributed %d to size\n", lx, ly, dsf[root] >> 2); size += dsf_size(dsf, root); assert(dsf_size(dsf, root) >= 1); hits[nhits++] = root; } */ size = expandsize(s->board, s->dsf, w, h, idx, s->board[j]); /* ... and see if that size is too big, or if we * have other expansion candidates. Otherwise * remember the (so far) only candidate. */ printv("\tthat would give a size of %d\n", size); if (size > s->board[j]) continue; /* printv("\tnow knowing %d expansions\n", nexpand + 1); */ if (exp != SENTINEL) goto next_i; assert(exp != idx); exp = idx; } j = s->connected[j]; /* next square in the same CC */ assert(s->board[i] == s->board[j]); } while (j != i); /* end: for each square j _in_ the connected component */ if (exp == SENTINEL) continue; printv("learning to expand\n"); expand(s, w, h, exp, i); learn = TRUE; next_i: ; } /* end: for each connected component */ return learn; } static int learn_critical_square(struct solver_state *s, int w, int h) { const int sz = w * h; int i; int learn = FALSE; assert(s); /* for each connected component */ for (i = 0; i < sz; ++i) { int j; if (s->board[i] == EMPTY) continue; if (i != dsf_canonify(s->dsf, i)) continue; if (dsf_size(s->dsf, i) == s->board[i]) continue; assert(s->board[i] != 1); /* for each empty square */ for (j = 0; j < sz; ++j) { if (s->board[j] != EMPTY) continue; s->board[j] = -SENTINEL; if (check_capacity(s->board, w, h, i)) continue; /* if not expanding s->board[i] to s->board[j] implies * that s->board[i] can't reach its full size, ... */ assert(s->nempty); printv( "learn: ds %d at (%d, %d) blocking (%d, %d)\n", s->board[i], j % w, j / w, i % w, i / w); --s->nempty; s->board[j] = s->board[i]; filled_square(s, w, h, j); learn = TRUE; } } return learn; } static int solver(const int *orig, int w, int h, char **solution) { const int sz = w * h; struct solver_state ss; ss.board = memdup(orig, sz, sizeof (int)); ss.dsf = snew_dsf(sz); /* eqv classes: connected components */ ss.connected = snewn(sz, int); /* connected[n] := n.next; */ /* cyclic disjoint singly linked lists, same partitioning as dsf. * The lists lets you iterate over a partition given any member */ printv("trying to solve this:\n"); print_board(ss.board, w, h); init_solver_state(&ss, w, h); do { if (learn_blocked_expansion(&ss, w, h)) continue; if (learn_expand_or_one(&ss, w, h)) continue; if (learn_critical_square(&ss, w, h)) continue; break; } while (ss.nempty); printv("best guess:\n"); print_board(ss.board, w, h); if (solution) { int i; *solution = snewn(sz + 2, char); **solution = 's'; for (i = 0; i < sz; ++i) (*solution)[i + 1] = ss.board[i] + '0'; (*solution)[sz + 1] = '\0'; /* We don't need the \0 for execute_move (the only user) * I'm just being printf-friendly in case I wanna print */ } sfree(ss.dsf); sfree(ss.board); sfree(ss.connected); return !ss.nempty; } static int *make_dsf(int *dsf, int *board, const int w, const int h) { const int sz = w * h; int i; if (!dsf) dsf = snew_dsf(w * h); else dsf_init(dsf, w * h); for (i = 0; i < sz; ++i) { int j; for (j = 0; j < 4; ++j) { const int x = (i % w) + dx[j]; const int y = (i / w) + dy[j]; const int k = w*y + x; if (x < 0 || x >= w || y < 0 || y >= h) continue; if (board[i] == board[k]) dsf_merge(dsf, i, k); } } return dsf; } /* static int filled(int *board, int *randomize, int k, int n) { int i; if (board == NULL) return FALSE; if (randomize == NULL) return FALSE; if (k > n) return FALSE; for (i = 0; i < k; ++i) if (board[randomize[i]] == 0) return FALSE; for (; i < n; ++i) if (board[randomize[i]] != 0) return FALSE; return TRUE; } */ static int *g_board; static int compare(const void *pa, const void *pb) { if (!g_board) return 0; return g_board[*(const int *)pb] - g_board[*(const int *)pa]; } static void minimize_clue_set(int *board, int w, int h, int *randomize) { const int sz = w * h; int i; int *board_cp = snewn(sz, int); memcpy(board_cp, board, sz * sizeof (int)); /* since more clues only helps and never hurts, one pass will do * just fine: if we can remove clue n with k clues of index > n, * we could have removed clue n with >= k clues of index > n. * So an additional pass wouldn't do anything [use induction]. */ for (i = 0; i < sz; ++i) { if (board[randomize[i]] == EMPTY) continue; board[randomize[i]] = EMPTY; /* (rot.) symmetry tends to include _way_ too many hints */ /* board[sz - randomize[i] - 1] = EMPTY; */ if (!solver(board, w, h, NULL)) { board[randomize[i]] = board_cp[randomize[i]]; /* board[sz - randomize[i] - 1] = board_cp[sz - randomize[i] - 1]; */ } } sfree(board_cp); } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { const int w = params->w; const int h = params->h; const int sz = w * h; int *board = snewn(sz, int); int *randomize = snewn(sz, int); char *game_description = snewn(sz + 1, char); int i; for (i = 0; i < sz; ++i) { board[i] = EMPTY; randomize[i] = i; } make_board(board, w, h, rs); g_board = board; qsort(randomize, sz, sizeof (int), compare); minimize_clue_set(board, w, h, randomize); for (i = 0; i < sz; ++i) { assert(board[i] >= 0); assert(board[i] < 10); game_description[i] = board[i] + '0'; } game_description[sz] = '\0'; /* solver(board, w, h, aux); print_board(board, w, h); */ sfree(randomize); sfree(board); return game_description; } static char *validate_desc(const game_params *params, const char *desc) { int i; const int sz = params->w * params->h; const char m = '0' + max(max(params->w, params->h), 3); printv("desc = '%s'; sz = %d\n", desc, sz); for (i = 0; desc[i] && i < sz; ++i) if (!isdigit((unsigned char) *desc)) return "non-digit in string"; else if (desc[i] > m) return "too large digit in string"; if (desc[i]) return "string too long"; else if (i < sz) return "string too short"; return NULL; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { game_state *state = snew(game_state); int sz = params->w * params->h; int i; state->cheated = state->completed = FALSE; state->shared = snew(struct shared_state); state->shared->refcnt = 1; state->shared->params = *params; /* struct copy */ state->shared->clues = snewn(sz, int); for (i = 0; i < sz; ++i) state->shared->clues[i] = desc[i] - '0'; state->board = memdup(state->shared->clues, sz, sizeof (int)); return state; } static game_state *dup_game(const game_state *state) { const int sz = state->shared->params.w * state->shared->params.h; game_state *ret = snew(game_state); ret->board = memdup(state->board, sz, sizeof (int)); ret->shared = state->shared; ret->cheated = state->cheated; ret->completed = state->completed; ++ret->shared->refcnt; return ret; } static void free_game(game_state *state) { assert(state); sfree(state->board); if (--state->shared->refcnt == 0) { sfree(state->shared->clues); sfree(state->shared); } sfree(state); } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { if (aux == NULL) { const int w = state->shared->params.w; const int h = state->shared->params.h; char *new_aux; if (!solver(state->board, w, h, &new_aux)) *error = "Sorry, I couldn't find a solution"; return new_aux; } return dupstr(aux); } /***************************************************************************** * USER INTERFACE STATE AND ACTION * *****************************************************************************/ struct game_ui { int *sel; /* w*h highlighted squares, or NULL */ int cur_x, cur_y, cur_visible; }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->sel = NULL; ui->cur_x = ui->cur_y = ui->cur_visible = 0; return ui; } static void free_ui(game_ui *ui) { if (ui->sel) sfree(ui->sel); 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) { /* Clear any selection */ if (ui->sel) { sfree(ui->sel); ui->sel = NULL; } } #define PREFERRED_TILE_SIZE 32 #define TILE_SIZE (ds->tilesize) #define BORDER (TILE_SIZE / 2) #define BORDER_WIDTH (max(TILE_SIZE / 32, 1)) struct game_drawstate { struct game_params params; int tilesize; int started; int *v, *flags; int *dsf_scratch, *border_scratch; }; static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { const int w = state->shared->params.w; const int h = state->shared->params.h; const int tx = (x + TILE_SIZE - BORDER) / TILE_SIZE - 1; const int ty = (y + TILE_SIZE - BORDER) / TILE_SIZE - 1; char *move = NULL; int i; assert(ui); assert(ds); button &= ~MOD_MASK; if (button == LEFT_BUTTON || button == LEFT_DRAG) { /* A left-click anywhere will clear the current selection. */ if (button == LEFT_BUTTON) { if (ui->sel) { sfree(ui->sel); ui->sel = NULL; } } if (tx >= 0 && tx < w && ty >= 0 && ty < h) { if (!ui->sel) { ui->sel = snewn(w*h, int); memset(ui->sel, 0, w*h*sizeof(int)); } if (!state->shared->clues[w*ty+tx]) ui->sel[w*ty+tx] = 1; } ui->cur_visible = 0; return ""; /* redraw */ } if (IS_CURSOR_MOVE(button)) { ui->cur_visible = 1; move_cursor(button, &ui->cur_x, &ui->cur_y, w, h, 0); return ""; } if (IS_CURSOR_SELECT(button)) { if (!ui->cur_visible) { ui->cur_visible = 1; return ""; } if (!ui->sel) { ui->sel = snewn(w*h, int); memset(ui->sel, 0, w*h*sizeof(int)); } if (state->shared->clues[w*ui->cur_y + ui->cur_x] == 0) ui->sel[w*ui->cur_y + ui->cur_x] ^= 1; return ""; } switch (button) { case ' ': case '\r': case '\n': case '\b': button = 0; break; default: if (button < '0' || button > '9') return NULL; button -= '0'; if (button > (w == 2 && h == 2? 3: max(w, h))) return NULL; } for (i = 0; i < w*h; i++) { char buf[32]; if ((ui->sel && ui->sel[i]) || (!ui->sel && ui->cur_visible && (w*ui->cur_y+ui->cur_x) == i)) { if (state->shared->clues[i] != 0) continue; /* in case cursor is on clue */ if (state->board[i] != button) { sprintf(buf, "%s%d", move ? "," : "", i); if (move) { move = srealloc(move, strlen(move)+strlen(buf)+1); strcat(move, buf); } else { move = smalloc(strlen(buf)+1); strcpy(move, buf); } } } } if (move) { char buf[32]; sprintf(buf, "_%d", button); move = srealloc(move, strlen(move)+strlen(buf)+1); strcat(move, buf); } if (!ui->sel) return move ? move : NULL; sfree(ui->sel); ui->sel = NULL; /* Need to update UI at least, as we cleared the selection */ return move ? move : ""; } static game_state *execute_move(const game_state *state, const char *move) { game_state *new_state = NULL; const int sz = state->shared->params.w * state->shared->params.h; if (*move == 's') { int i = 0; new_state = dup_game(state); for (++move; i < sz; ++i) new_state->board[i] = move[i] - '0'; new_state->cheated = TRUE; } else { int value; char *endptr, *delim = strchr(move, '_'); if (!delim) goto err; value = strtol(delim+1, &endptr, 0); if (*endptr || endptr == delim+1) goto err; if (value < 0 || value > 9) goto err; new_state = dup_game(state); while (*move) { const int i = strtol(move, &endptr, 0); if (endptr == move) goto err; if (i < 0 || i >= sz) goto err; new_state->board[i] = value; if (*endptr == '_') break; if (*endptr != ',') goto err; move = endptr + 1; } } /* * Check for completion. */ if (!new_state->completed) { const int w = new_state->shared->params.w; const int h = new_state->shared->params.h; const int sz = w * h; int *dsf = make_dsf(NULL, new_state->board, w, h); int i; for (i = 0; i < sz && new_state->board[i] == dsf_size(dsf, i); ++i); sfree(dsf); if (i == sz) new_state->completed = TRUE; } return new_state; err: if (new_state) free_game(new_state); return NULL; } /* ---------------------------------------------------------------------- * Drawing routines. */ #define FLASH_TIME 0.4F #define COL_CLUE COL_GRID enum { COL_BACKGROUND, COL_GRID, COL_HIGHLIGHT, COL_CORRECT, COL_ERROR, COL_USER, COL_CURSOR, NCOLOURS }; static void game_compute_size(const game_params *params, int tilesize, int *x, int *y) { *x = (params->w + 1) * tilesize; *y = (params->h + 1) * 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); 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_HIGHLIGHT * 3 + 0] = 0.85F * ret[COL_BACKGROUND * 3 + 0]; ret[COL_HIGHLIGHT * 3 + 1] = 0.85F * ret[COL_BACKGROUND * 3 + 1]; ret[COL_HIGHLIGHT * 3 + 2] = 0.85F * ret[COL_BACKGROUND * 3 + 2]; ret[COL_CORRECT * 3 + 0] = 0.9F * ret[COL_BACKGROUND * 3 + 0]; ret[COL_CORRECT * 3 + 1] = 0.9F * ret[COL_BACKGROUND * 3 + 1]; ret[COL_CORRECT * 3 + 2] = 0.9F * ret[COL_BACKGROUND * 3 + 2]; ret[COL_CURSOR * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0]; ret[COL_CURSOR * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1]; ret[COL_CURSOR * 3 + 2] = 0.5F * ret[COL_BACKGROUND * 3 + 2]; ret[COL_ERROR * 3 + 0] = 1.0F; ret[COL_ERROR * 3 + 1] = 0.85F * ret[COL_BACKGROUND * 3 + 1]; ret[COL_ERROR * 3 + 2] = 0.85F * ret[COL_BACKGROUND * 3 + 2]; 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; *ncolours = NCOLOURS; return ret; } static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { struct game_drawstate *ds = snew(struct game_drawstate); int i; ds->tilesize = PREFERRED_TILE_SIZE; ds->started = 0; ds->params = state->shared->params; ds->v = snewn(ds->params.w * ds->params.h, int); ds->flags = snewn(ds->params.w * ds->params.h, int); for (i = 0; i < ds->params.w * ds->params.h; i++) ds->v[i] = ds->flags[i] = -1; ds->border_scratch = snewn(ds->params.w * ds->params.h, int); ds->dsf_scratch = NULL; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->v); sfree(ds->flags); sfree(ds->border_scratch); sfree(ds->dsf_scratch); sfree(ds); } #define BORDER_U 0x001 #define BORDER_D 0x002 #define BORDER_L 0x004 #define BORDER_R 0x008 #define BORDER_UR 0x010 #define BORDER_DR 0x020 #define BORDER_UL 0x040 #define BORDER_DL 0x080 #define HIGH_BG 0x100 #define CORRECT_BG 0x200 #define ERROR_BG 0x400 #define USER_COL 0x800 #define CURSOR_SQ 0x1000 static void draw_square(drawing *dr, game_drawstate *ds, int x, int y, int n, int flags) { assert(dr); assert(ds); /* * Clip to the grid square. */ clip(dr, BORDER + x*TILE_SIZE, BORDER + y*TILE_SIZE, TILE_SIZE, TILE_SIZE); /* * Clear the square. */ draw_rect(dr, BORDER + x*TILE_SIZE, BORDER + y*TILE_SIZE, TILE_SIZE, TILE_SIZE, (flags & HIGH_BG ? COL_HIGHLIGHT : flags & ERROR_BG ? COL_ERROR : flags & CORRECT_BG ? COL_CORRECT : COL_BACKGROUND)); /* * Draw the grid lines. */ draw_line(dr, BORDER + x*TILE_SIZE, BORDER + y*TILE_SIZE, BORDER + (x+1)*TILE_SIZE, BORDER + y*TILE_SIZE, COL_GRID); draw_line(dr, BORDER + x*TILE_SIZE, BORDER + y*TILE_SIZE, BORDER + x*TILE_SIZE, BORDER + (y+1)*TILE_SIZE, COL_GRID); /* * Draw the number. */ if (n) { char buf[2]; buf[0] = n + '0'; buf[1] = '\0'; draw_text(dr, (x + 1) * TILE_SIZE, (y + 1) * TILE_SIZE, FONT_VARIABLE, TILE_SIZE / 2, ALIGN_VCENTRE | ALIGN_HCENTRE, flags & USER_COL ? COL_USER : COL_CLUE, buf); } /* * Draw bold lines around the borders. */ if (flags & BORDER_L) draw_rect(dr, BORDER + x*TILE_SIZE + 1, BORDER + y*TILE_SIZE + 1, BORDER_WIDTH, TILE_SIZE - 1, COL_GRID); if (flags & BORDER_U) draw_rect(dr, BORDER + x*TILE_SIZE + 1, BORDER + y*TILE_SIZE + 1, TILE_SIZE - 1, BORDER_WIDTH, COL_GRID); if (flags & BORDER_R) draw_rect(dr, BORDER + (x+1)*TILE_SIZE - BORDER_WIDTH, BORDER + y*TILE_SIZE + 1, BORDER_WIDTH, TILE_SIZE - 1, COL_GRID); if (flags & BORDER_D) draw_rect(dr, BORDER + x*TILE_SIZE + 1, BORDER + (y+1)*TILE_SIZE - BORDER_WIDTH, TILE_SIZE - 1, BORDER_WIDTH, COL_GRID); if (flags & BORDER_UL) draw_rect(dr, BORDER + x*TILE_SIZE + 1, BORDER + y*TILE_SIZE + 1, BORDER_WIDTH, BORDER_WIDTH, COL_GRID); if (flags & BORDER_UR) draw_rect(dr, BORDER + (x+1)*TILE_SIZE - BORDER_WIDTH, BORDER + y*TILE_SIZE + 1, BORDER_WIDTH, BORDER_WIDTH, COL_GRID); if (flags & BORDER_DL) draw_rect(dr, BORDER + x*TILE_SIZE + 1, BORDER + (y+1)*TILE_SIZE - BORDER_WIDTH, BORDER_WIDTH, BORDER_WIDTH, COL_GRID); if (flags & BORDER_DR) draw_rect(dr, BORDER + (x+1)*TILE_SIZE - BORDER_WIDTH, BORDER + (y+1)*TILE_SIZE - BORDER_WIDTH, BORDER_WIDTH, BORDER_WIDTH, COL_GRID); if (flags & CURSOR_SQ) { int coff = TILE_SIZE/8; draw_rect_outline(dr, BORDER + x*TILE_SIZE + coff, BORDER + y*TILE_SIZE + coff, TILE_SIZE - coff*2, TILE_SIZE - coff*2, COL_CURSOR); } unclip(dr); draw_update(dr, BORDER + x*TILE_SIZE, BORDER + y*TILE_SIZE, TILE_SIZE, TILE_SIZE); } static void draw_grid(drawing *dr, game_drawstate *ds, const game_state *state, const game_ui *ui, int flashy, int borders, int shading) { const int w = state->shared->params.w; const int h = state->shared->params.h; int x; int y; /* * Build a dsf for the board in its current state, to use for * highlights and hints. */ ds->dsf_scratch = make_dsf(ds->dsf_scratch, state->board, w, h); /* * Work out where we're putting borders between the cells. */ for (y = 0; y < w*h; y++) ds->border_scratch[y] = 0; for (y = 0; y < h; y++) for (x = 0; x < w; x++) { int dx, dy; int v1, s1, v2, s2; for (dx = 0; dx <= 1; dx++) { int border = FALSE; dy = 1 - dx; if (x+dx >= w || y+dy >= h) continue; v1 = state->board[y*w+x]; v2 = state->board[(y+dy)*w+(x+dx)]; s1 = dsf_size(ds->dsf_scratch, y*w+x); s2 = dsf_size(ds->dsf_scratch, (y+dy)*w+(x+dx)); /* * We only ever draw a border between two cells if * they don't have the same contents. */ if (v1 != v2) { /* * But in that situation, we don't always draw * a border. We do if the two cells both * contain actual numbers... */ if (v1 && v2) border = TRUE; /* * ... or if at least one of them is a * completed or overfull omino. */ if (v1 && s1 >= v1) border = TRUE; if (v2 && s2 >= v2) border = TRUE; } if (border) ds->border_scratch[y*w+x] |= (dx ? 1 : 2); } } /* * Actually do the drawing. */ for (y = 0; y < h; ++y) for (x = 0; x < w; ++x) { /* * Determine what we need to draw in this square. */ int i = y*w+x, v = state->board[i]; int flags = 0; if (flashy || !shading) { /* clear all background flags */ } else if (ui && ui->sel && ui->sel[i]) { flags |= HIGH_BG; } else if (v) { int size = dsf_size(ds->dsf_scratch, i); if (size == v) flags |= CORRECT_BG; else if (size > v) flags |= ERROR_BG; else { int rt = dsf_canonify(ds->dsf_scratch, i), j; for (j = 0; j < w*h; ++j) { int k; if (dsf_canonify(ds->dsf_scratch, j) != rt) continue; for (k = 0; k < 4; ++k) { const int xx = j % w + dx[k], yy = j / w + dy[k]; if (xx >= 0 && xx < w && yy >= 0 && yy < h && state->board[yy*w + xx] == EMPTY) goto noflag; } } flags |= ERROR_BG; noflag: ; } } if (ui && ui->cur_visible && x == ui->cur_x && y == ui->cur_y) flags |= CURSOR_SQ; /* * Borders at the very edges of the grid are * independent of the `borders' flag. */ if (x == 0) flags |= BORDER_L; if (y == 0) flags |= BORDER_U; if (x == w-1) flags |= BORDER_R; if (y == h-1) flags |= BORDER_D; if (borders) { if (x == 0 || (ds->border_scratch[y*w+(x-1)] & 1)) flags |= BORDER_L; if (y == 0 || (ds->border_scratch[(y-1)*w+x] & 2)) flags |= BORDER_U; if (x == w-1 || (ds->border_scratch[y*w+x] & 1)) flags |= BORDER_R; if (y == h-1 || (ds->border_scratch[y*w+x] & 2)) flags |= BORDER_D; if (y > 0 && x > 0 && (ds->border_scratch[(y-1)*w+(x-1)])) flags |= BORDER_UL; if (y > 0 && x < w-1 && ((ds->border_scratch[(y-1)*w+x] & 1) || (ds->border_scratch[(y-1)*w+(x+1)] & 2))) flags |= BORDER_UR; if (y < h-1 && x > 0 && ((ds->border_scratch[y*w+(x-1)] & 2) || (ds->border_scratch[(y+1)*w+(x-1)] & 1))) flags |= BORDER_DL; if (y < h-1 && x < w-1 && ((ds->border_scratch[y*w+(x+1)] & 2) || (ds->border_scratch[(y+1)*w+x] & 1))) flags |= BORDER_DR; } if (!state->shared->clues[y*w+x]) flags |= USER_COL; if (ds->v[y*w+x] != v || ds->flags[y*w+x] != flags) { draw_square(dr, ds, x, y, v, flags); ds->v[y*w+x] = v; ds->flags[y*w+x] = flags; } } } 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) { const int w = state->shared->params.w; const int h = state->shared->params.h; const int flashy = flashtime > 0 && (flashtime <= FLASH_TIME/3 || flashtime >= FLASH_TIME*2/3); 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, w*TILE_SIZE + 2*BORDER, h*TILE_SIZE + 2*BORDER, COL_BACKGROUND); /* * Smaller black rectangle which is the main grid. */ draw_rect(dr, BORDER - BORDER_WIDTH, BORDER - BORDER_WIDTH, w*TILE_SIZE + 2*BORDER_WIDTH + 1, h*TILE_SIZE + 2*BORDER_WIDTH + 1, COL_GRID); draw_update(dr, 0, 0, w*TILE_SIZE + 2*BORDER, h*TILE_SIZE + 2*BORDER); ds->started = TRUE; } draw_grid(dr, ds, state, ui, flashy, TRUE, TRUE); } 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) { assert(oldstate); assert(newstate); assert(newstate->shared); assert(oldstate->shared == newstate->shared); if (!oldstate->completed && newstate->completed && !oldstate->cheated && !newstate->cheated) return FLASH_TIME; return 0.0F; } static int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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) { int pw, ph; /* * I'll use 6mm squares by default. */ game_compute_size(params, 600, &pw, &ph); *x = pw / 100.0F; *y = ph / 100.0F; } static void game_print(drawing *dr, const game_state *state, int tilesize) { const int w = state->shared->params.w; const int h = state->shared->params.h; int c, i, borders; /* Ick: fake up `ds->tilesize' for macro expansion purposes */ game_drawstate *ds = game_new_drawstate(dr, state); game_set_size(dr, ds, NULL, tilesize); c = print_mono_colour(dr, 1); assert(c == COL_BACKGROUND); c = print_mono_colour(dr, 0); assert(c == COL_GRID); c = print_mono_colour(dr, 1); assert(c == COL_HIGHLIGHT); c = print_mono_colour(dr, 1); assert(c == COL_CORRECT); c = print_mono_colour(dr, 1); assert(c == COL_ERROR); c = print_mono_colour(dr, 0); assert(c == COL_USER); /* * Border. */ draw_rect(dr, BORDER - BORDER_WIDTH, BORDER - BORDER_WIDTH, w*TILE_SIZE + 2*BORDER_WIDTH + 1, h*TILE_SIZE + 2*BORDER_WIDTH + 1, COL_GRID); /* * We'll draw borders between the ominoes iff the grid is not * pristine. So scan it to see if it is. */ borders = FALSE; for (i = 0; i < w*h; i++) if (state->board[i] && !state->shared->clues[i]) borders = TRUE; /* * Draw grid. */ print_line_width(dr, TILE_SIZE / 64); draw_grid(dr, ds, state, NULL, FALSE, borders, FALSE); /* * Clean up. */ game_free_drawstate(dr, ds); } #ifdef COMBINED #define thegame filling #endif const struct game thegame = { "Filling", "games.filling", "filling", default_params, game_fetch_preset, 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, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, REQUIRE_NUMPAD, /* flags */ }; #ifdef STANDALONE_SOLVER /* solver? hah! */ int main(int argc, char **argv) { while (*++argv) { game_params *params; game_state *state; char *par; char *desc; for (par = desc = *argv; *desc != '\0' && *desc != ':'; ++desc); if (*desc == '\0') { fprintf(stderr, "bad puzzle id: %s", par); continue; } *desc++ = '\0'; params = snew(game_params); decode_params(params, par); state = new_game(NULL, params, desc); if (solver(state->board, params->w, params->h, NULL)) printf("%s:%s: solvable\n", par, desc); else printf("%s:%s: not solvable\n", par, desc); } return 0; } #endif /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/flip.c0000644000175300017530000010626512132232554013534 0ustar simonsimon/* * flip.c: Puzzle involving lighting up all the squares on a grid, * where each click toggles an overlapping set of lights. */ #include #include #include #include #include #include #include "puzzles.h" #include "tree234.h" enum { COL_BACKGROUND, COL_WRONG, COL_RIGHT, COL_GRID, COL_DIAG, COL_HINT, COL_CURSOR, NCOLOURS }; #define PREFERRED_TILE_SIZE 48 #define TILE_SIZE (ds->tilesize) #define BORDER (TILE_SIZE / 2) #define COORD(x) ( (x) * TILE_SIZE + BORDER ) #define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 ) #define ANIM_TIME 0.25F #define FLASH_FRAME 0.07F /* * Possible ways to decide which lights are toggled by each click. * Essentially, each of these describes a means of inventing a * matrix over GF(2). */ enum { CROSSES, RANDOM }; struct game_params { int w, h; int matrix_type; }; /* * This structure is shared between all the game_states describing * a particular game, so it's reference-counted. */ struct matrix { int refcount; unsigned char *matrix; /* array of (w*h) by (w*h) */ }; struct game_state { int w, h; int moves, completed, cheated, hints_active; unsigned char *grid; /* array of w*h */ struct matrix *matrix; }; static game_params *default_params(void) { game_params *ret = snew(game_params); ret->w = ret->h = 5; ret->matrix_type = CROSSES; return ret; } static const struct game_params flip_presets[] = { {3, 3, CROSSES}, {4, 4, CROSSES}, {5, 5, CROSSES}, {3, 3, RANDOM}, {4, 4, RANDOM}, {5, 5, RANDOM}, }; static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char str[80]; if (i < 0 || i >= lenof(flip_presets)) return FALSE; ret = snew(game_params); *ret = flip_presets[i]; sprintf(str, "%dx%d %s", ret->w, ret->h, ret->matrix_type == CROSSES ? "Crosses" : "Random"); *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 *ret, char const *string) { ret->w = ret->h = atoi(string); while (*string && isdigit((unsigned char)*string)) string++; if (*string == 'x') { string++; ret->h = atoi(string); while (*string && isdigit((unsigned char)*string)) string++; } if (*string == 'r') { string++; ret->matrix_type = RANDOM; } else if (*string == 'c') { string++; ret->matrix_type = CROSSES; } } static char *encode_params(const game_params *params, int full) { char data[256]; sprintf(data, "%dx%d%s", params->w, params->h, !full ? "" : params->matrix_type == CROSSES ? "c" : "r"); return dupstr(data); } static config_item *game_configure(const game_params *params) { config_item *ret = snewn(4, config_item); char buf[80]; ret[0].name = "Width"; ret[0].type = C_STRING; sprintf(buf, "%d", params->w); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Shape type"; ret[2].type = C_CHOICES; ret[2].sval = ":Crosses:Random"; ret[2].ival = params->matrix_type; ret[3].name = NULL; ret[3].type = C_END; ret[3].sval = NULL; ret[3].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); ret->matrix_type = cfg[2].ival; return ret; } static char *validate_params(const game_params *params, int full) { if (params->w <= 0 || params->h <= 0) return "Width and height must both be greater than zero"; return NULL; } static char *encode_bitmap(unsigned char *bmp, int len) { int slen = (len + 3) / 4; char *ret; int i; ret = snewn(slen + 1, char); for (i = 0; i < slen; i++) { int j, v; v = 0; for (j = 0; j < 4; j++) if (i*4+j < len && bmp[i*4+j]) v |= 8 >> j; ret[i] = "0123456789abcdef"[v]; } ret[slen] = '\0'; return ret; } static void decode_bitmap(unsigned char *bmp, int len, const char *hex) { int slen = (len + 3) / 4; int i; for (i = 0; i < slen; i++) { int j, v, c = hex[i]; if (c >= '0' && c <= '9') v = c - '0'; else if (c >= 'A' && c <= 'F') v = c - 'A' + 10; else if (c >= 'a' && c <= 'f') v = c - 'a' + 10; else v = 0; /* shouldn't happen */ for (j = 0; j < 4; j++) { if (i*4+j < len) { if (v & (8 >> j)) bmp[i*4+j] = 1; else bmp[i*4+j] = 0; } } } } /* * Structure used during random matrix generation, and a compare * function to permit storage in a tree234. */ struct sq { int cx, cy; /* coords of click square */ int x, y; /* coords of output square */ /* * Number of click squares which currently affect this output * square. */ int coverage; /* * Number of output squares currently affected by this click * square. */ int ominosize; }; #define SORT(field) do { \ if (a->field < b->field) \ return -1; \ else if (a->field > b->field) \ return +1; \ } while (0) /* * Compare function for choosing the next square to add. We must * sort by coverage, then by omino size, then everything else. */ static int sqcmp_pick(void *av, void *bv) { struct sq *a = (struct sq *)av; struct sq *b = (struct sq *)bv; SORT(coverage); SORT(ominosize); SORT(cy); SORT(cx); SORT(y); SORT(x); return 0; } /* * Compare function for adjusting the coverage figures after a * change. We sort first by coverage and output square, then by * everything else. */ static int sqcmp_cov(void *av, void *bv) { struct sq *a = (struct sq *)av; struct sq *b = (struct sq *)bv; SORT(coverage); SORT(y); SORT(x); SORT(ominosize); SORT(cy); SORT(cx); return 0; } /* * Compare function for adjusting the omino sizes after a change. * We sort first by omino size and input square, then by everything * else. */ static int sqcmp_osize(void *av, void *bv) { struct sq *a = (struct sq *)av; struct sq *b = (struct sq *)bv; SORT(ominosize); SORT(cy); SORT(cx); SORT(coverage); SORT(y); SORT(x); return 0; } static void addsq(tree234 *t, int w, int h, int cx, int cy, int x, int y, unsigned char *matrix) { int wh = w * h; struct sq *sq; int i; if (x < 0 || x >= w || y < 0 || y >= h) return; if (abs(x-cx) > 1 || abs(y-cy) > 1) return; if (matrix[(cy*w+cx) * wh + y*w+x]) return; sq = snew(struct sq); sq->cx = cx; sq->cy = cy; sq->x = x; sq->y = y; sq->coverage = sq->ominosize = 0; for (i = 0; i < wh; i++) { if (matrix[i * wh + y*w+x]) sq->coverage++; if (matrix[(cy*w+cx) * wh + i]) sq->ominosize++; } if (add234(t, sq) != sq) sfree(sq); /* already there */ } static void addneighbours(tree234 *t, int w, int h, int cx, int cy, int x, int y, unsigned char *matrix) { addsq(t, w, h, cx, cy, x-1, y, matrix); addsq(t, w, h, cx, cy, x+1, y, matrix); addsq(t, w, h, cx, cy, x, y-1, matrix); addsq(t, w, h, cx, cy, x, y+1, matrix); } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { int w = params->w, h = params->h, wh = w * h; int i, j; unsigned char *matrix, *grid; char *mbmp, *gbmp, *ret; matrix = snewn(wh * wh, unsigned char); grid = snewn(wh, unsigned char); /* * First set up the matrix. */ switch (params->matrix_type) { case CROSSES: for (i = 0; i < wh; i++) { int ix = i % w, iy = i / w; for (j = 0; j < wh; j++) { int jx = j % w, jy = j / w; if (abs(jx - ix) + abs(jy - iy) <= 1) matrix[i*wh+j] = 1; else matrix[i*wh+j] = 0; } } break; case RANDOM: while (1) { tree234 *pick, *cov, *osize; int limit; pick = newtree234(sqcmp_pick); cov = newtree234(sqcmp_cov); osize = newtree234(sqcmp_osize); memset(matrix, 0, wh * wh); for (i = 0; i < wh; i++) { matrix[i*wh+i] = 1; } for (i = 0; i < wh; i++) { int ix = i % w, iy = i / w; addneighbours(pick, w, h, ix, iy, ix, iy, matrix); addneighbours(cov, w, h, ix, iy, ix, iy, matrix); addneighbours(osize, w, h, ix, iy, ix, iy, matrix); } /* * Repeatedly choose a square to add to the matrix, * until we have enough. I'll arbitrarily choose our * limit to be the same as the total number of set bits * in the crosses matrix. */ limit = 4*wh - 2*(w+h); /* centre squares already present */ while (limit-- > 0) { struct sq *sq, *sq2, sqlocal; int k; /* * Find the lowest element in the pick tree. */ sq = index234(pick, 0); /* * Find the highest element with the same coverage * and omino size, by setting all other elements to * lots. */ sqlocal = *sq; sqlocal.cx = sqlocal.cy = sqlocal.x = sqlocal.y = wh; sq = findrelpos234(pick, &sqlocal, NULL, REL234_LT, &k); assert(sq != 0); /* * Pick at random from all elements up to k of the * pick tree. */ k = random_upto(rs, k+1); sq = delpos234(pick, k); del234(cov, sq); del234(osize, sq); /* * Add this square to the matrix. */ matrix[(sq->cy * w + sq->cx) * wh + (sq->y * w + sq->x)] = 1; /* * Correct the matrix coverage field of any sq * which points at this output square. */ sqlocal = *sq; sqlocal.cx = sqlocal.cy = sqlocal.ominosize = -1; while ((sq2 = findrel234(cov, &sqlocal, NULL, REL234_GT)) != NULL && sq2->coverage == sq->coverage && sq2->x == sq->x && sq2->y == sq->y) { del234(pick, sq2); del234(cov, sq2); del234(osize, sq2); sq2->coverage++; add234(pick, sq2); add234(cov, sq2); add234(osize, sq2); } /* * Correct the omino size field of any sq which * points at this input square. */ sqlocal = *sq; sqlocal.x = sqlocal.y = sqlocal.coverage = -1; while ((sq2 = findrel234(osize, &sqlocal, NULL, REL234_GT)) != NULL && sq2->ominosize == sq->ominosize && sq2->cx == sq->cx && sq2->cy == sq->cy) { del234(pick, sq2); del234(cov, sq2); del234(osize, sq2); sq2->ominosize++; add234(pick, sq2); add234(cov, sq2); add234(osize, sq2); } /* * The sq we actually picked out of the tree is * finished with; but its neighbours now need to * appear. */ addneighbours(pick, w,h, sq->cx,sq->cy, sq->x,sq->y, matrix); addneighbours(cov, w,h, sq->cx,sq->cy, sq->x,sq->y, matrix); addneighbours(osize, w,h, sq->cx,sq->cy, sq->x,sq->y, matrix); sfree(sq); } /* * Free all remaining sq structures. */ { struct sq *sq; while ((sq = delpos234(pick, 0)) != NULL) sfree(sq); } freetree234(pick); freetree234(cov); freetree234(osize); /* * Finally, check to see if any two matrix rows are * exactly identical. If so, this is not an acceptable * matrix, and we give up and go round again. * * I haven't been immediately able to think of a * plausible means of algorithmically avoiding this * situation (by, say, making a small perturbation to * an offending matrix), so for the moment I'm just * going to deal with it by throwing the whole thing * away. I suspect this will lead to scalability * problems (since most of the things happening in * these matrices are local, the chance of _some_ * neighbourhood having two identical regions will * increase with the grid area), but so far this puzzle * seems to be really hard at large sizes so I'm not * massively worried yet. Anyone needs this done * better, they're welcome to submit a patch. */ for (i = 0; i < wh; i++) { for (j = 0; j < wh; j++) if (i != j && !memcmp(matrix + i * wh, matrix + j * wh, wh)) break; if (j < wh) break; } if (i == wh) break; /* no matches found */ } break; } /* * Now invent a random initial set of lights. * * At first glance it looks as if it might be quite difficult * to choose equiprobably from all soluble light sets. After * all, soluble light sets are those in the image space of the * transformation matrix; so first we'd have to identify that * space and its dimension, then pick a random coordinate for * each basis vector and recombine. Lot of fiddly matrix * algebra there. * * However, vector spaces are nicely orthogonal and relieve us * of all that difficulty. For every point in the image space, * there are precisely as many points in the input space that * map to it as there are elements in the kernel of the * transformation matrix (because adding any kernel element to * the input does not change the output, and because any two * inputs mapping to the same output must differ by an element * of the kernel because that's what the kernel _is_); and * these cosets are all disjoint (obviously, since no input * point can map to more than one output point) and cover the * whole space (equally obviously, because no input point can * map to fewer than one output point!). * * So the input space contains the same number of points for * each point in the output space; thus, we can simply choose * equiprobably from elements of the _input_ space, and filter * the result through the transformation matrix in the obvious * way, and we thereby guarantee to choose equiprobably from * all the output points. Phew! */ while (1) { memset(grid, 0, wh); for (i = 0; i < wh; i++) { int v = random_upto(rs, 2); if (v) { for (j = 0; j < wh; j++) grid[j] ^= matrix[i*wh+j]; } } /* * Ensure we don't have the starting state already! */ for (i = 0; i < wh; i++) if (grid[i]) break; if (i < wh) break; } /* * Now encode the matrix and the starting grid as a game * description. We'll do this by concatenating two great big * hex bitmaps. */ mbmp = encode_bitmap(matrix, wh*wh); gbmp = encode_bitmap(grid, wh); ret = snewn(strlen(mbmp) + strlen(gbmp) + 2, char); sprintf(ret, "%s,%s", mbmp, gbmp); sfree(mbmp); sfree(gbmp); sfree(matrix); sfree(grid); return ret; } static char *validate_desc(const game_params *params, const char *desc) { int w = params->w, h = params->h, wh = w * h; int mlen = (wh*wh+3)/4, glen = (wh+3)/4; if (strspn(desc, "0123456789abcdefABCDEF") != mlen) return "Matrix description is wrong length"; if (desc[mlen] != ',') return "Expected comma after matrix description"; if (strspn(desc+mlen+1, "0123456789abcdefABCDEF") != glen) return "Grid description is wrong length"; if (desc[mlen+1+glen]) return "Unexpected data after grid description"; return NULL; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { int w = params->w, h = params->h, wh = w * h; int mlen = (wh*wh+3)/4; game_state *state = snew(game_state); state->w = w; state->h = h; state->completed = FALSE; state->cheated = FALSE; state->hints_active = FALSE; state->moves = 0; state->matrix = snew(struct matrix); state->matrix->refcount = 1; state->matrix->matrix = snewn(wh*wh, unsigned char); decode_bitmap(state->matrix->matrix, wh*wh, desc); state->grid = snewn(wh, unsigned char); decode_bitmap(state->grid, wh, desc + mlen + 1); return state; } static game_state *dup_game(const game_state *state) { game_state *ret = snew(game_state); ret->w = state->w; ret->h = state->h; ret->completed = state->completed; ret->cheated = state->cheated; ret->hints_active = state->hints_active; ret->moves = state->moves; ret->matrix = state->matrix; state->matrix->refcount++; ret->grid = snewn(ret->w * ret->h, unsigned char); memcpy(ret->grid, state->grid, ret->w * ret->h); return ret; } static void free_game(game_state *state) { sfree(state->grid); if (--state->matrix->refcount <= 0) { sfree(state->matrix->matrix); sfree(state->matrix); } sfree(state); } static void rowxor(unsigned char *row1, unsigned char *row2, int len) { int i; for (i = 0; i < len; i++) row1[i] ^= row2[i]; } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { int w = state->w, h = state->h, wh = w * h; unsigned char *equations, *solution, *shortest; int *und, nund; int rowsdone, colsdone; int i, j, k, len, bestlen; char *ret; /* * Set up a list of simultaneous equations. Each one is of * length (wh+1) and has wh coefficients followed by a value. */ equations = snewn((wh + 1) * wh, unsigned char); for (i = 0; i < wh; i++) { for (j = 0; j < wh; j++) equations[i * (wh+1) + j] = currstate->matrix->matrix[j*wh+i]; equations[i * (wh+1) + wh] = currstate->grid[i] & 1; } /* * Perform Gaussian elimination over GF(2). */ rowsdone = colsdone = 0; nund = 0; und = snewn(wh, int); do { /* * Find the leftmost column which has a 1 in it somewhere * outside the first `rowsdone' rows. */ j = -1; for (i = colsdone; i < wh; i++) { for (j = rowsdone; j < wh; j++) if (equations[j * (wh+1) + i]) break; if (j < wh) break; /* found one */ /* * This is a column which will not have an equation * controlling it. Mark it as undetermined. */ und[nund++] = i; } /* * If there wasn't one, then we've finished: all remaining * equations are of the form 0 = constant. Check to see if * any of them wants 0 to be equal to 1; this is the * condition which indicates an insoluble problem * (therefore _hopefully_ one typed in by a user!). */ if (i == wh) { for (j = rowsdone; j < wh; j++) if (equations[j * (wh+1) + wh]) { *error = "No solution exists for this position"; sfree(equations); sfree(und); return NULL; } break; } /* * We've found a 1. It's in column i, and the topmost 1 in * that column is in row j. Do a row-XOR to move it up to * the topmost row if it isn't already there. */ assert(j != -1); if (j > rowsdone) rowxor(equations + rowsdone*(wh+1), equations + j*(wh+1), wh+1); /* * Do row-XORs to eliminate that 1 from all rows below the * topmost row. */ for (j = rowsdone + 1; j < wh; j++) if (equations[j*(wh+1) + i]) rowxor(equations + j*(wh+1), equations + rowsdone*(wh+1), wh+1); /* * Mark this row and column as done. */ rowsdone++; colsdone = i+1; /* * If we've done all the rows, terminate. */ } while (rowsdone < wh); /* * If we reach here, we have the ability to produce a solution. * So we go through _all_ possible solutions (each * corresponding to a set of arbitrary choices of those * components not directly determined by an equation), and pick * one requiring the smallest number of flips. */ solution = snewn(wh, unsigned char); shortest = snewn(wh, unsigned char); memset(solution, 0, wh); bestlen = wh + 1; while (1) { /* * Find a solution based on the current values of the * undetermined variables. */ for (j = rowsdone; j-- ;) { int v; /* * Find the leftmost set bit in this equation. */ for (i = 0; i < wh; i++) if (equations[j * (wh+1) + i]) break; assert(i < wh); /* there must have been one! */ /* * Compute this variable using the rest. */ v = equations[j * (wh+1) + wh]; for (k = i+1; k < wh; k++) if (equations[j * (wh+1) + k]) v ^= solution[k]; solution[i] = v; } /* * Compare this solution to the current best one, and * replace the best one if this one is shorter. */ len = 0; for (i = 0; i < wh; i++) if (solution[i]) len++; if (len < bestlen) { bestlen = len; memcpy(shortest, solution, wh); } /* * Now increment the binary number given by the * undetermined variables: turn all 1s into 0s until we see * a 0, at which point we turn it into a 1. */ for (i = 0; i < nund; i++) { solution[und[i]] = !solution[und[i]]; if (solution[und[i]]) break; } /* * If we didn't find a 0 at any point, we have wrapped * round and are back at the start, i.e. we have enumerated * all solutions. */ if (i == nund) break; } /* * We have a solution. Produce a move string encoding the * solution. */ ret = snewn(wh + 2, char); ret[0] = 'S'; for (i = 0; i < wh; i++) ret[i+1] = shortest[i] ? '1' : '0'; ret[wh+1] = '\0'; sfree(shortest); sfree(solution); sfree(equations); sfree(und); return ret; } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { return NULL; } struct game_ui { int cx, cy, cdraw; }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->cx = ui->cy = ui->cdraw = 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) { } struct game_drawstate { int w, h, started; unsigned char *tiles; int tilesize; }; 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; char buf[80], *nullret = NULL; if (button == LEFT_BUTTON || IS_CURSOR_SELECT(button)) { int tx, ty; if (button == LEFT_BUTTON) { tx = FROMCOORD(x), ty = FROMCOORD(y); ui->cdraw = 0; } else { tx = ui->cx; ty = ui->cy; ui->cdraw = 1; } nullret = ""; if (tx >= 0 && tx < w && ty >= 0 && ty < h) { /* * It's just possible that a manually entered game ID * will have at least one square do nothing whatsoever. * If so, we avoid encoding a move at all. */ int i = ty*w+tx, j, makemove = FALSE; for (j = 0; j < wh; j++) { if (state->matrix->matrix[i*wh+j]) makemove = TRUE; } if (makemove) { sprintf(buf, "M%d,%d", tx, ty); return dupstr(buf); } else { return NULL; } } } else if (IS_CURSOR_MOVE(button)) { int dx = 0, dy = 0; switch (button) { case CURSOR_UP: dy = -1; break; case CURSOR_DOWN: dy = 1; break; case CURSOR_RIGHT: dx = 1; break; case CURSOR_LEFT: dx = -1; break; default: assert(!"shouldn't get here"); } ui->cx += dx; ui->cy += dy; ui->cx = min(max(ui->cx, 0), state->w - 1); ui->cy = min(max(ui->cy, 0), state->h - 1); ui->cdraw = 1; nullret = ""; } return nullret; } static game_state *execute_move(const game_state *from, const char *move) { int w = from->w, h = from->h, wh = w * h; game_state *ret; int x, y; if (move[0] == 'S' && strlen(move) == wh+1) { int i; ret = dup_game(from); ret->hints_active = TRUE; ret->cheated = TRUE; for (i = 0; i < wh; i++) { ret->grid[i] &= ~2; if (move[i+1] != '0') ret->grid[i] |= 2; } return ret; } else if (move[0] == 'M' && sscanf(move+1, "%d,%d", &x, &y) == 2 && x >= 0 && x < w && y >= 0 && y < h) { int i, j, done; ret = dup_game(from); if (!ret->completed) ret->moves++; i = y * w + x; done = TRUE; for (j = 0; j < wh; j++) { ret->grid[j] ^= ret->matrix->matrix[i*wh+j]; if (ret->grid[j] & 1) done = FALSE; } ret->grid[i] ^= 2; /* toggle hint */ if (done) { ret->completed = TRUE; ret->hints_active = FALSE; } return ret; } else return NULL; /* can't parse move string */ } /* ---------------------------------------------------------------------- * 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 = TILE_SIZE * params->w + 2 * BORDER; *y = TILE_SIZE * params->h + 2 * BORDER; } 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_WRONG * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] / 3; ret[COL_WRONG * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] / 3; ret[COL_WRONG * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] / 3; ret[COL_RIGHT * 3 + 0] = 1.0F; ret[COL_RIGHT * 3 + 1] = 1.0F; ret[COL_RIGHT * 3 + 2] = 1.0F; ret[COL_GRID * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] / 1.5F; ret[COL_GRID * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] / 1.5F; ret[COL_GRID * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] / 1.5F; ret[COL_DIAG * 3 + 0] = ret[COL_GRID * 3 + 0]; ret[COL_DIAG * 3 + 1] = ret[COL_GRID * 3 + 1]; ret[COL_DIAG * 3 + 2] = ret[COL_GRID * 3 + 2]; ret[COL_HINT * 3 + 0] = 1.0F; ret[COL_HINT * 3 + 1] = 0.0F; ret[COL_HINT * 3 + 2] = 0.0F; ret[COL_CURSOR * 3 + 0] = 0.8F; ret[COL_CURSOR * 3 + 1] = 0.0F; ret[COL_CURSOR * 3 + 2] = 0.0F; *ncolours = NCOLOURS; return ret; } static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { struct game_drawstate *ds = snew(struct game_drawstate); int i; ds->started = FALSE; ds->w = state->w; ds->h = state->h; ds->tiles = snewn(ds->w*ds->h, unsigned char); ds->tilesize = 0; /* haven't decided yet */ for (i = 0; i < ds->w*ds->h; i++) ds->tiles[i] = -1; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->tiles); sfree(ds); } static void draw_tile(drawing *dr, game_drawstate *ds, const game_state *state, int x, int y, int tile, int anim, float animtime) { int w = ds->w, h = ds->h, wh = w * h; int bx = x * TILE_SIZE + BORDER, by = y * TILE_SIZE + BORDER; int i, j, dcol = (tile & 4) ? COL_CURSOR : COL_DIAG; clip(dr, bx+1, by+1, TILE_SIZE-1, TILE_SIZE-1); draw_rect(dr, bx+1, by+1, TILE_SIZE-1, TILE_SIZE-1, anim ? COL_BACKGROUND : tile & 1 ? COL_WRONG : COL_RIGHT); if (anim) { /* * Draw a polygon indicating that the square is diagonally * flipping over. */ int coords[8], colour; coords[0] = bx + TILE_SIZE; coords[1] = by; coords[2] = bx + (int)((float)TILE_SIZE * animtime); coords[3] = by + (int)((float)TILE_SIZE * animtime); coords[4] = bx; coords[5] = by + TILE_SIZE; coords[6] = bx + TILE_SIZE - (int)((float)TILE_SIZE * animtime); coords[7] = by + TILE_SIZE - (int)((float)TILE_SIZE * animtime); colour = (tile & 1 ? COL_WRONG : COL_RIGHT); if (animtime < 0.5) colour = COL_WRONG + COL_RIGHT - colour; draw_polygon(dr, coords, 4, colour, COL_GRID); } /* * Draw a little diagram in the tile which indicates which * surrounding tiles flip when this one is clicked. */ for (i = 0; i < h; i++) for (j = 0; j < w; j++) if (state->matrix->matrix[(y*w+x)*wh + i*w+j]) { int ox = j - x, oy = i - y; int td = TILE_SIZE / 16; int cx = (bx + TILE_SIZE/2) + (2 * ox - 1) * td; int cy = (by + TILE_SIZE/2) + (2 * oy - 1) * td; if (ox == 0 && oy == 0) draw_rect(dr, cx, cy, 2*td+1, 2*td+1, dcol); else { draw_line(dr, cx, cy, cx+2*td, cy, dcol); draw_line(dr, cx, cy+2*td, cx+2*td, cy+2*td, dcol); draw_line(dr, cx, cy, cx, cy+2*td, dcol); draw_line(dr, cx+2*td, cy, cx+2*td, cy+2*td, dcol); } } /* * Draw a hint rectangle if required. */ if (tile & 2) { int x1 = bx + TILE_SIZE / 20, x2 = bx + TILE_SIZE - TILE_SIZE / 20; int y1 = by + TILE_SIZE / 20, y2 = by + TILE_SIZE - TILE_SIZE / 20; int i = 3; while (i--) { draw_line(dr, x1, y1, x2, y1, COL_HINT); draw_line(dr, x1, y2, x2, y2, COL_HINT); draw_line(dr, x1, y1, x1, y2, COL_HINT); draw_line(dr, x2, y1, x2, y2, COL_HINT); x1++, y1++, x2--, y2--; } } unclip(dr); draw_update(dr, bx+1, by+1, TILE_SIZE-1, TILE_SIZE-1); } 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 = ds->w, h = ds->h, wh = w * h; int i, flashframe; if (!ds->started) { draw_rect(dr, 0, 0, TILE_SIZE * w + 2 * BORDER, TILE_SIZE * h + 2 * BORDER, COL_BACKGROUND); /* * Draw the grid lines. */ for (i = 0; i <= w; i++) draw_line(dr, i * TILE_SIZE + BORDER, BORDER, i * TILE_SIZE + BORDER, h * TILE_SIZE + BORDER, COL_GRID); for (i = 0; i <= h; i++) draw_line(dr, BORDER, i * TILE_SIZE + BORDER, w * TILE_SIZE + BORDER, i * TILE_SIZE + BORDER, COL_GRID); draw_update(dr, 0, 0, TILE_SIZE * w + 2 * BORDER, TILE_SIZE * h + 2 * BORDER); ds->started = TRUE; } if (flashtime) flashframe = (int)(flashtime / FLASH_FRAME); else flashframe = -1; animtime /= ANIM_TIME; /* scale it so it goes from 0 to 1 */ for (i = 0; i < wh; i++) { int x = i % w, y = i / w; int fx, fy, fd; int v = state->grid[i]; int vv; if (flashframe >= 0) { fx = (w+1)/2 - min(x+1, w-x); fy = (h+1)/2 - min(y+1, h-y); fd = max(fx, fy); if (fd == flashframe) v |= 1; else if (fd == flashframe - 1) v &= ~1; } if (!state->hints_active) v &= ~2; if (ui->cdraw && ui->cx == x && ui->cy == y) v |= 4; if (oldstate && ((state->grid[i] ^ oldstate->grid[i]) &~ 2)) vv = 255; /* means `animated' */ else vv = v; if (ds->tiles[i] == 255 || vv == 255 || ds->tiles[i] != vv) { draw_tile(dr, ds, state, x, y, v, vv == 255, animtime); ds->tiles[i] = vv; } } { char buf[256]; sprintf(buf, "%sMoves: %d", (state->completed ? (state->cheated ? "Auto-solved. " : "COMPLETED! ") : (state->cheated ? "Auto-solver used. " : "")), state->moves); status_bar(dr, buf); } } static float game_anim_length(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui) { return ANIM_TIME; } 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_FRAME * (max((newstate->w+1)/2, (newstate->h+1)/2)+1); return 0.0F; } static int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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 flip #endif const struct game thegame = { "Flip", "games.flip", "flip", default_params, game_fetch_preset, 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, FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, decode_ui, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, FALSE, FALSE, game_print_size, game_print, TRUE, /* wants_statusbar */ FALSE, game_timing_state, 0, /* flags */ }; puzzles-r9872/galaxies.c0000644000175300017530000034545712132232554014407 0ustar simonsimon/* * galaxies.c: implementation of 'Tentai Show' from Nikoli, * also sometimes called 'Spiral Galaxies'. * * Notes: * * Grid is stored as size (2n-1), holding edges as well as spaces * (and thus vertices too, at edge intersections). * * Any dot will thus be positioned at one of our grid points, * which saves any faffing with half-of-a-square stuff. * * Edges have on/off state; obviously the actual edges of the * board are fixed to on, and everything else starts as off. * * TTD: * Cleverer solver * Think about how to display remote groups of tiles? * * Bugs: * * Notable puzzle IDs: * * Nikoli's example [web site has wrong highlighting] * (at http://www.nikoli.co.jp/en/puzzles/astronomical_show/): * 5x5:eBbbMlaBbOEnf * * The 'spiral galaxies puzzles are NP-complete' paper * (at http://www.stetson.edu/~efriedma/papers/spiral.pdf): * 7x7:chpgdqqqoezdddki * * Puzzle competition pdf examples * (at http://www.puzzleratings.org/Yurekli2006puz.pdf): * 6x6:EDbaMucCohbrecEi * 10x10:beFbufEEzowDlxldibMHezBQzCdcFzjlci * 13x13:dCemIHFFkJajjgDfdbdBzdzEgjccoPOcztHjBczLDjczqktJjmpreivvNcggFi * */ #include #include #include #include #include #include #include "puzzles.h" #ifdef DEBUGGING #define solvep debug #else int solver_show_working; #define solvep(x) do { if (solver_show_working) { printf x; } } while(0) #endif #ifdef STANDALONE_PICTURE_GENERATOR /* * Dirty hack to enable the generator to construct a game ID which * solves to a specified black-and-white bitmap. We define a global * variable here which gives the desired colour of each square, and * we arrange that the grid generator never merges squares of * different colours. * * The bitmap as stored here is a simple int array (at these sizes * it isn't worth doing fiddly bit-packing). picture[y*w+x] is 1 * iff the pixel at (x,y) is intended to be black. * * (It might be nice to be able to specify some pixels as * don't-care, to give the generator more leeway. But that might be * fiddly.) */ static int *picture; #endif enum { COL_BACKGROUND, COL_WHITEBG, COL_BLACKBG, COL_WHITEDOT, COL_BLACKDOT, COL_GRID, COL_EDGE, COL_ARROW, COL_CURSOR, NCOLOURS }; #define DIFFLIST(A) \ A(NORMAL,Normal,n) \ A(UNREASONABLE,Unreasonable,u) #define ENUM(upper,title,lower) DIFF_ ## upper, #define TITLE(upper,title,lower) #title, #define ENCODE(upper,title,lower) #lower #define CONFIG(upper,title,lower) ":" #title enum { DIFFLIST(ENUM) DIFF_IMPOSSIBLE, DIFF_AMBIGUOUS, DIFF_UNFINISHED, DIFF_MAX }; static char const *const galaxies_diffnames[] = { DIFFLIST(TITLE) "Impossible", "Ambiguous", "Unfinished" }; static char const galaxies_diffchars[] = DIFFLIST(ENCODE); #define DIFFCONFIG DIFFLIST(CONFIG) struct game_params { /* X and Y is the area of the board as seen by * the user, not the (2n+1) area the game uses. */ int w, h, diff; }; enum { s_tile, s_edge, s_vertex }; #define F_DOT 1 /* there's a dot here */ #define F_EDGE_SET 2 /* the edge is set */ #define F_TILE_ASSOC 4 /* this tile is associated with a dot. */ #define F_DOT_BLACK 8 /* (ui only) dot is black. */ #define F_MARK 16 /* scratch flag */ #define F_REACHABLE 32 #define F_SCRATCH 64 #define F_MULTIPLE 128 #define F_DOT_HOLD 256 #define F_GOOD 512 typedef struct space { int x, y; /* its position */ int type; unsigned int flags; int dotx, doty; /* if flags & F_TILE_ASSOC */ int nassoc; /* if flags & F_DOT */ } space; #define INGRID(s,x,y) ((x) >= 0 && (y) >= 0 && \ (x) < (state)->sx && (y) < (state)->sy) #define INUI(s,x,y) ((x) > 0 && (y) > 0 && \ (x) < ((state)->sx-1) && (y) < ((state)->sy-1)) #define GRID(s,g,x,y) ((s)->g[((y)*(s)->sx)+(x)]) #define SPACE(s,x,y) GRID(s,grid,x,y) struct game_state { int w, h; /* size from params */ int sx, sy; /* allocated size, (2x-1)*(2y-1) */ space *grid; int completed, used_solve; int ndots; space **dots; midend *me; /* to call supersede_game_desc */ int cdiff; /* difficulty of current puzzle (for status bar), or -1 if stale. */ }; /* ---------------------------------------------------------- * Game parameters and presets */ /* make up some sensible default sizes */ #define DEFAULT_PRESET 0 static const game_params galaxies_presets[] = { { 7, 7, DIFF_NORMAL }, { 7, 7, DIFF_UNREASONABLE }, { 10, 10, DIFF_NORMAL }, { 15, 15, DIFF_NORMAL }, }; static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char buf[80]; if (i < 0 || i >= lenof(galaxies_presets)) return FALSE; ret = snew(game_params); *ret = galaxies_presets[i]; /* structure copy */ sprintf(buf, "%dx%d %s", ret->w, ret->h, galaxies_diffnames[ret->diff]); if (name) *name = dupstr(buf); *params = ret; return TRUE; } static game_params *default_params(void) { game_params *ret; game_fetch_preset(DEFAULT_PRESET, NULL, &ret); 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 void decode_params(game_params *params, char const *string) { params->h = params->w = atoi(string); params->diff = DIFF_NORMAL; while (*string && isdigit((unsigned char)*string)) string++; if (*string == 'x') { string++; params->h = atoi(string); while (*string && isdigit((unsigned char)*string)) string++; } if (*string == 'd') { int i; string++; for (i = 0; i <= DIFF_UNREASONABLE; i++) if (*string == galaxies_diffchars[i]) params->diff = i; if (*string) string++; } } static char *encode_params(const game_params *params, int full) { char str[80]; sprintf(str, "%dx%d", params->w, params->h); if (full) sprintf(str + strlen(str), "d%c", galaxies_diffchars[params->diff]); return dupstr(str); } 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].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Difficulty"; ret[2].type = C_CHOICES; ret[2].sval = DIFFCONFIG; ret[2].ival = params->diff; ret[3].name = NULL; ret[3].type = C_END; ret[3].sval = NULL; ret[3].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); ret->diff = cfg[2].ival; return ret; } static char *validate_params(const game_params *params, int full) { if (params->w < 3 || params->h < 3) return "Width and height must both be at least 3"; /* * This shouldn't be able to happen at all, since decode_params * and custom_params will never generate anything that isn't * within range. */ assert(params->diff <= DIFF_UNREASONABLE); return NULL; } /* ---------------------------------------------------------- * Game utility functions. */ static void add_dot(space *space) { assert(!(space->flags & F_DOT)); space->flags |= F_DOT; space->nassoc = 0; } static void remove_dot(space *space) { assert(space->flags & F_DOT); space->flags &= ~F_DOT; } static void remove_assoc(const game_state *state, space *tile) { if (tile->flags & F_TILE_ASSOC) { SPACE(state, tile->dotx, tile->doty).nassoc--; tile->flags &= ~F_TILE_ASSOC; tile->dotx = -1; tile->doty = -1; } } static void add_assoc(const game_state *state, space *tile, space *dot) { remove_assoc(state, tile); #ifdef STANDALONE_PICTURE_GENERATOR if (picture) assert(!picture[(tile->y/2) * state->w + (tile->x/2)] == !(dot->flags & F_DOT_BLACK)); #endif tile->flags |= F_TILE_ASSOC; tile->dotx = dot->x; tile->doty = dot->y; dot->nassoc++; /*debug(("add_assoc sp %d %d --> dot %d,%d, new nassoc %d.\n", tile->x, tile->y, dot->x, dot->y, dot->nassoc));*/ } static struct space *sp2dot(const game_state *state, int x, int y) { struct space *sp = &SPACE(state, x, y); if (!(sp->flags & F_TILE_ASSOC)) return NULL; return &SPACE(state, sp->dotx, sp->doty); } #define IS_VERTICAL_EDGE(x) ((x % 2) == 0) static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { int maxlen = (state->sx+1)*state->sy, x, y; char *ret, *p; space *sp; ret = snewn(maxlen+1, char); p = ret; for (y = 0; y < state->sy; y++) { for (x = 0; x < state->sx; x++) { sp = &SPACE(state, x, y); if (sp->flags & F_DOT) *p++ = 'o'; #if 0 else if (sp->flags & (F_REACHABLE|F_MULTIPLE|F_MARK)) *p++ = (sp->flags & F_MULTIPLE) ? 'M' : (sp->flags & F_REACHABLE) ? 'R' : 'X'; #endif else { switch (sp->type) { case s_tile: if (sp->flags & F_TILE_ASSOC) { space *dot = sp2dot(state, sp->x, sp->y); if (dot && dot->flags & F_DOT) *p++ = (dot->flags & F_DOT_BLACK) ? 'B' : 'W'; else *p++ = '?'; /* association with not-a-dot. */ } else *p++ = ' '; break; case s_vertex: *p++ = '+'; break; case s_edge: if (sp->flags & F_EDGE_SET) *p++ = (IS_VERTICAL_EDGE(x)) ? '|' : '-'; else *p++ = ' '; break; default: assert(!"shouldn't get here!"); } } } *p++ = '\n'; } assert(p - ret == maxlen); *p = '\0'; return ret; } static void dbg_state(const game_state *state) { #ifdef DEBUGGING char *temp = game_text_format(state); debug(("%s\n", temp)); sfree(temp); #endif } /* Space-enumeration callbacks should all return 1 for 'progress made', * -1 for 'impossible', and 0 otherwise. */ typedef int (*space_cb)(game_state *state, space *sp, void *ctx); #define IMPOSSIBLE_QUITS 1 static int foreach_sub(game_state *state, space_cb cb, unsigned int f, void *ctx, int startx, int starty) { int x, y, progress = 0, impossible = 0, ret; space *sp; for (y = starty; y < state->sy; y += 2) { sp = &SPACE(state, startx, y); for (x = startx; x < state->sx; x += 2) { ret = cb(state, sp, ctx); if (ret == -1) { if (f & IMPOSSIBLE_QUITS) return -1; impossible = -1; } else if (ret == 1) { progress = 1; } sp += 2; } } return impossible ? -1 : progress; } static int foreach_tile(game_state *state, space_cb cb, unsigned int f, void *ctx) { return foreach_sub(state, cb, f, ctx, 1, 1); } static int foreach_edge(game_state *state, space_cb cb, unsigned int f, void *ctx) { int ret1, ret2; ret1 = foreach_sub(state, cb, f, ctx, 0, 1); ret2 = foreach_sub(state, cb, f, ctx, 1, 0); if (ret1 == -1 || ret2 == -1) return -1; return (ret1 || ret2) ? 1 : 0; } #if 0 static int foreach_vertex(game_state *state, space_cb cb, unsigned int f, void *ctx) { return foreach_sub(state, cb, f, ctx, 0, 0); } #endif #if 0 static int is_same_assoc(game_state *state, int x1, int y1, int x2, int y2) { struct space *s1, *s2; if (!INGRID(state, x1, y1) || !INGRID(state, x2, y2)) return 0; s1 = &SPACE(state, x1, y1); s2 = &SPACE(state, x2, y2); assert(s1->type == s_tile && s2->type == s_tile); if ((s1->flags & F_TILE_ASSOC) && (s2->flags & F_TILE_ASSOC) && s1->dotx == s2->dotx && s1->doty == s2->doty) return 1; return 0; /* 0 if not same or not both associated. */ } #endif #if 0 static int edges_into_vertex(game_state *state, int x, int y) { int dx, dy, nx, ny, count = 0; assert(SPACE(state, x, y).type == s_vertex); for (dx = -1; dx <= 1; dx++) { for (dy = -1; dy <= 1; dy++) { if (dx != 0 && dy != 0) continue; if (dx == 0 && dy == 0) continue; nx = x+dx; ny = y+dy; if (!INGRID(state, nx, ny)) continue; assert(SPACE(state, nx, ny).type == s_edge); if (SPACE(state, nx, ny).flags & F_EDGE_SET) count++; } } return count; } #endif static struct space *space_opposite_dot(struct game_state *state, struct space *sp, struct space *dot) { int dx, dy, tx, ty; space *sp2; dx = sp->x - dot->x; dy = sp->y - dot->y; tx = dot->x - dx; ty = dot->y - dy; if (!INGRID(state, tx, ty)) return NULL; sp2 = &SPACE(state, tx, ty); assert(sp2->type == sp->type); return sp2; } static struct space *tile_opposite(struct game_state *state, struct space *sp) { struct space *dot; assert(sp->flags & F_TILE_ASSOC); dot = &SPACE(state, sp->dotx, sp->doty); return space_opposite_dot(state, sp, dot); } static int dotfortile(game_state *state, space *tile, space *dot) { space *tile_opp = space_opposite_dot(state, tile, dot); if (!tile_opp) return 0; /* opposite would be off grid */ if (tile_opp->flags & F_TILE_ASSOC && (tile_opp->dotx != dot->x || tile_opp->doty != dot->y)) return 0; /* opposite already associated with diff. dot */ return 1; } static void adjacencies(struct game_state *state, struct space *sp, struct space **a1s, struct space **a2s) { int dxs[4] = {-1, 1, 0, 0}, dys[4] = {0, 0, -1, 1}; int n, x, y; /* this function needs optimising. */ for (n = 0; n < 4; n++) { x = sp->x+dxs[n]; y = sp->y+dys[n]; if (INGRID(state, x, y)) { a1s[n] = &SPACE(state, x, y); x += dxs[n]; y += dys[n]; if (INGRID(state, x, y)) a2s[n] = &SPACE(state, x, y); else a2s[n] = NULL; } else { a1s[n] = a2s[n] = NULL; } } } static int outline_tile_fordot(game_state *state, space *tile, int mark) { struct space *tadj[4], *eadj[4]; int i, didsth = 0, edge, same; assert(tile->type == s_tile); adjacencies(state, tile, eadj, tadj); for (i = 0; i < 4; i++) { if (!eadj[i]) continue; edge = (eadj[i]->flags & F_EDGE_SET) ? 1 : 0; if (tadj[i]) { if (!(tile->flags & F_TILE_ASSOC)) same = (tadj[i]->flags & F_TILE_ASSOC) ? 0 : 1; else same = ((tadj[i]->flags & F_TILE_ASSOC) && tile->dotx == tadj[i]->dotx && tile->doty == tadj[i]->doty) ? 1 : 0; } else same = 0; if (!edge && !same) { if (mark) eadj[i]->flags |= F_EDGE_SET; didsth = 1; } else if (edge && same) { if (mark) eadj[i]->flags &= ~F_EDGE_SET; didsth = 1; } } return didsth; } static void tiles_from_edge(struct game_state *state, struct space *sp, struct space **ts) { int xs[2], ys[2]; if (IS_VERTICAL_EDGE(sp->x)) { xs[0] = sp->x-1; ys[0] = sp->y; xs[1] = sp->x+1; ys[1] = sp->y; } else { xs[0] = sp->x; ys[0] = sp->y-1; xs[1] = sp->x; ys[1] = sp->y+1; } ts[0] = INGRID(state, xs[0], ys[0]) ? &SPACE(state, xs[0], ys[0]) : NULL; ts[1] = INGRID(state, xs[1], ys[1]) ? &SPACE(state, xs[1], ys[1]) : NULL; } /* Returns a move string for use by 'solve', including the initial * 'S' if issolve is true. */ static char *diff_game(const game_state *src, const game_state *dest, int issolve) { int movelen = 0, movesize = 256, x, y, len; char *move = snewn(movesize, char), buf[80], *sep = ""; char achar = issolve ? 'a' : 'A'; space *sps, *spd; assert(src->sx == dest->sx && src->sy == dest->sy); if (issolve) { move[movelen++] = 'S'; sep = ";"; } move[movelen] = '\0'; for (x = 0; x < src->sx; x++) { for (y = 0; y < src->sy; y++) { sps = &SPACE(src, x, y); spd = &SPACE(dest, x, y); assert(sps->type == spd->type); len = 0; if (sps->type == s_tile) { if ((sps->flags & F_TILE_ASSOC) && (spd->flags & F_TILE_ASSOC)) { if (sps->dotx != spd->dotx || sps->doty != spd->doty) /* Both associated; change association, if different */ len = sprintf(buf, "%s%c%d,%d,%d,%d", sep, (int)achar, x, y, spd->dotx, spd->doty); } else if (sps->flags & F_TILE_ASSOC) /* Only src associated; remove. */ len = sprintf(buf, "%sU%d,%d", sep, x, y); else if (spd->flags & F_TILE_ASSOC) /* Only dest associated; add. */ len = sprintf(buf, "%s%c%d,%d,%d,%d", sep, (int)achar, x, y, spd->dotx, spd->doty); } else if (sps->type == s_edge) { if ((sps->flags & F_EDGE_SET) != (spd->flags & F_EDGE_SET)) /* edge flags are different; flip them. */ len = sprintf(buf, "%sE%d,%d", sep, x, y); } if (len) { if (movelen + len >= movesize) { movesize = movelen + len + 256; move = sresize(move, movesize, char); } strcpy(move + movelen, buf); movelen += len; sep = ";"; } } } debug(("diff_game src then dest:\n")); dbg_state(src); dbg_state(dest); debug(("diff string %s\n", move)); return move; } /* Returns 1 if a dot here would not be too close to any other dots * (and would avoid other game furniture). */ static int dot_is_possible(game_state *state, space *sp, int allow_assoc) { int bx = 0, by = 0, dx, dy; space *adj; #ifdef STANDALONE_PICTURE_GENERATOR int col = -1; #endif switch (sp->type) { case s_tile: bx = by = 1; break; case s_edge: if (IS_VERTICAL_EDGE(sp->x)) { bx = 2; by = 1; } else { bx = 1; by = 2; } break; case s_vertex: bx = by = 2; break; } for (dx = -bx; dx <= bx; dx++) { for (dy = -by; dy <= by; dy++) { if (!INGRID(state, sp->x+dx, sp->y+dy)) continue; adj = &SPACE(state, sp->x+dx, sp->y+dy); #ifdef STANDALONE_PICTURE_GENERATOR /* * Check that all the squares we're looking at have the * same colour. */ if (picture) { if (adj->type == s_tile) { int c = picture[(adj->y / 2) * state->w + (adj->x / 2)]; if (col < 0) col = c; if (c != col) return 0; /* colour mismatch */ } } #endif if (!allow_assoc && (adj->flags & F_TILE_ASSOC)) return 0; if (dx != 0 || dy != 0) { /* Other than our own square, no dots nearby. */ if (adj->flags & (F_DOT)) return 0; } /* We don't want edges within our rectangle * (but don't care about edges on the edge) */ if (abs(dx) < bx && abs(dy) < by && adj->flags & F_EDGE_SET) return 0; } } return 1; } /* ---------------------------------------------------------- * Game generation, structure creation, and descriptions. */ static game_state *blank_game(int w, int h) { game_state *state = snew(game_state); int x, y; state->w = w; state->h = h; state->sx = (w*2)+1; state->sy = (h*2)+1; state->grid = snewn(state->sx * state->sy, struct space); state->completed = state->used_solve = 0; for (x = 0; x < state->sx; x++) { for (y = 0; y < state->sy; y++) { struct space *sp = &SPACE(state, x, y); memset(sp, 0, sizeof(struct space)); sp->x = x; sp->y = y; if ((x % 2) == 0 && (y % 2) == 0) sp->type = s_vertex; else if ((x % 2) == 0 || (y % 2) == 0) { sp->type = s_edge; if (x == 0 || y == 0 || x == state->sx-1 || y == state->sy-1) sp->flags |= F_EDGE_SET; } else sp->type = s_tile; } } state->ndots = 0; state->dots = NULL; state->me = NULL; /* filled in by new_game. */ state->cdiff = -1; return state; } static void game_update_dots(game_state *state) { int i, n, sz = state->sx * state->sy; if (state->dots) sfree(state->dots); state->ndots = 0; for (i = 0; i < sz; i++) { if (state->grid[i].flags & F_DOT) state->ndots++; } state->dots = snewn(state->ndots, space *); n = 0; for (i = 0; i < sz; i++) { if (state->grid[i].flags & F_DOT) state->dots[n++] = &state->grid[i]; } } static void clear_game(game_state *state, int cleardots) { int x, y; /* don't erase edge flags around outline! */ for (x = 1; x < state->sx-1; x++) { for (y = 1; y < state->sy-1; y++) { if (cleardots) SPACE(state, x, y).flags = 0; else SPACE(state, x, y).flags &= (F_DOT|F_DOT_BLACK); } } if (cleardots) game_update_dots(state); } static game_state *dup_game(const game_state *state) { game_state *ret = blank_game(state->w, state->h); ret->completed = state->completed; ret->used_solve = state->used_solve; memcpy(ret->grid, state->grid, ret->sx*ret->sy*sizeof(struct space)); game_update_dots(ret); ret->me = state->me; ret->cdiff = state->cdiff; return ret; } static void free_game(game_state *state) { if (state->dots) sfree(state->dots); sfree(state->grid); sfree(state); } /* Game description is a sequence of letters representing the number * of spaces (a = 0, y = 24) before the next dot; a-y for a white dot, * and A-Y for a black dot. 'z' is 25 spaces (and no dot). * * I know it's a bitch to generate by hand, so we provide * an edit mode. */ static char *encode_game(game_state *state) { char *desc, *p; int run, x, y, area; unsigned int f; area = (state->sx-2) * (state->sy-2); desc = snewn(area, char); p = desc; run = 0; for (y = 1; y < state->sy-1; y++) { for (x = 1; x < state->sx-1; x++) { f = SPACE(state, x, y).flags; /* a/A is 0 spaces between, b/B is 1 space, ... * y/Y is 24 spaces, za/zA is 25 spaces, ... * It's easier to count from 0 because we then * don't have to special-case the top left-hand corner * (which could be a dot with 0 spaces before it). */ if (!(f & F_DOT)) run++; else { while (run > 24) { *p++ = 'z'; run -= 25; } *p++ = ((f & F_DOT_BLACK) ? 'A' : 'a') + run; run = 0; } } } assert(p - desc < area); *p++ = '\0'; desc = sresize(desc, p - desc, char); return desc; } struct movedot { int op; space *olddot, *newdot; }; enum { MD_CHECK, MD_MOVE }; static int movedot_cb(game_state *state, space *tile, void *vctx) { struct movedot *md = (struct movedot *)vctx; space *newopp = NULL; assert(tile->type == s_tile); assert(md->olddot && md->newdot); if (!(tile->flags & F_TILE_ASSOC)) return 0; if (tile->dotx != md->olddot->x || tile->doty != md->olddot->y) return 0; newopp = space_opposite_dot(state, tile, md->newdot); switch (md->op) { case MD_CHECK: /* If the tile is associated with the old dot, check its * opposite wrt the _new_ dot is empty or same assoc. */ if (!newopp) return -1; /* no new opposite */ if (newopp->flags & F_TILE_ASSOC) { if (newopp->dotx != md->olddot->x || newopp->doty != md->olddot->y) return -1; /* associated, but wrong dot. */ } #ifdef STANDALONE_PICTURE_GENERATOR if (picture) { /* * Reject if either tile and the dot don't match in colour. */ if (!(picture[(tile->y/2) * state->w + (tile->x/2)]) ^ !(md->newdot->flags & F_DOT_BLACK)) return -1; if (!(picture[(newopp->y/2) * state->w + (newopp->x/2)]) ^ !(md->newdot->flags & F_DOT_BLACK)) return -1; } #endif break; case MD_MOVE: /* Move dot associations: anything that was associated * with the old dot, and its opposite wrt the new dot, * become associated with the new dot. */ assert(newopp); debug(("Associating %d,%d and %d,%d with new dot %d,%d.\n", tile->x, tile->y, newopp->x, newopp->y, md->newdot->x, md->newdot->y)); add_assoc(state, tile, md->newdot); add_assoc(state, newopp, md->newdot); return 1; /* we did something! */ } return 0; } /* For the given dot, first see if we could expand it into all the given * extra spaces (by checking for empty spaces on the far side), and then * see if we can move the dot to shift the CoG to include the new spaces. */ static int dot_expand_or_move(game_state *state, space *dot, space **toadd, int nadd) { space *tileopp; int i, ret, nnew, cx, cy; struct movedot md; debug(("dot_expand_or_move: %d tiles for dot %d,%d\n", nadd, dot->x, dot->y)); for (i = 0; i < nadd; i++) debug(("dot_expand_or_move: dot %d,%d\n", toadd[i]->x, toadd[i]->y)); assert(dot->flags & F_DOT); #ifdef STANDALONE_PICTURE_GENERATOR if (picture) { /* * Reject the expansion totally if any of the new tiles are * the wrong colour. */ for (i = 0; i < nadd; i++) { if (!(picture[(toadd[i]->y/2) * state->w + (toadd[i]->x/2)]) ^ !(dot->flags & F_DOT_BLACK)) return 0; } } #endif /* First off, could we just expand the current dot's tile to cover * the space(s) passed in and their opposites? */ for (i = 0; i < nadd; i++) { tileopp = space_opposite_dot(state, toadd[i], dot); if (!tileopp) goto noexpand; if (tileopp->flags & F_TILE_ASSOC) goto noexpand; #ifdef STANDALONE_PICTURE_GENERATOR if (picture) { /* * The opposite tiles have to be the right colour as well. */ if (!(picture[(tileopp->y/2) * state->w + (tileopp->x/2)]) ^ !(dot->flags & F_DOT_BLACK)) goto noexpand; } #endif } /* OK, all spaces have valid empty opposites: associate spaces and * opposites with our dot. */ for (i = 0; i < nadd; i++) { tileopp = space_opposite_dot(state, toadd[i], dot); add_assoc(state, toadd[i], dot); add_assoc(state, tileopp, dot); debug(("Added associations %d,%d and %d,%d --> %d,%d\n", toadd[i]->x, toadd[i]->y, tileopp->x, tileopp->y, dot->x, dot->y)); dbg_state(state); } return 1; noexpand: /* Otherwise, try to move dot so as to encompass given spaces: */ /* first, calculate the 'centre of gravity' of the new dot. */ nnew = dot->nassoc + nadd; /* number of tiles assoc. with new dot. */ cx = dot->x * dot->nassoc; cy = dot->y * dot->nassoc; for (i = 0; i < nadd; i++) { cx += toadd[i]->x; cy += toadd[i]->y; } /* If the CoG isn't a whole number, it's not possible. */ if ((cx % nnew) != 0 || (cy % nnew) != 0) { debug(("Unable to move dot %d,%d, CoG not whole number.\n", dot->x, dot->y)); return 0; } cx /= nnew; cy /= nnew; /* Check whether all spaces in the old tile would have a good * opposite wrt the new dot. */ md.olddot = dot; md.newdot = &SPACE(state, cx, cy); md.op = MD_CHECK; ret = foreach_tile(state, movedot_cb, IMPOSSIBLE_QUITS, &md); if (ret == -1) { debug(("Unable to move dot %d,%d, new dot not symmetrical.\n", dot->x, dot->y)); return 0; } /* Also check whether all spaces we're adding would have a good * opposite wrt the new dot. */ for (i = 0; i < nadd; i++) { tileopp = space_opposite_dot(state, toadd[i], md.newdot); if (tileopp && (tileopp->flags & F_TILE_ASSOC) && (tileopp->dotx != dot->x || tileopp->doty != dot->y)) { tileopp = NULL; } if (!tileopp) { debug(("Unable to move dot %d,%d, new dot not symmetrical.\n", dot->x, dot->y)); return 0; } #ifdef STANDALONE_PICTURE_GENERATOR if (picture) { if (!(picture[(tileopp->y/2) * state->w + (tileopp->x/2)]) ^ !(dot->flags & F_DOT_BLACK)) return 0; } #endif } /* If we've got here, we're ok. First, associate all of 'toadd' * with the _old_ dot (so they'll get fixed up, with their opposites, * in the next step). */ for (i = 0; i < nadd; i++) { debug(("Associating to-add %d,%d with old dot %d,%d.\n", toadd[i]->x, toadd[i]->y, dot->x, dot->y)); add_assoc(state, toadd[i], dot); } /* Finally, move the dot and fix up all the old associations. */ debug(("Moving dot at %d,%d to %d,%d\n", dot->x, dot->y, md.newdot->x, md.newdot->y)); { #ifdef STANDALONE_PICTURE_GENERATOR int f = dot->flags & F_DOT_BLACK; #endif remove_dot(dot); add_dot(md.newdot); #ifdef STANDALONE_PICTURE_GENERATOR md.newdot->flags |= f; #endif } md.op = MD_MOVE; ret = foreach_tile(state, movedot_cb, 0, &md); assert(ret == 1); dbg_state(state); return 1; } /* Hard-code to a max. of 2x2 squares, for speed (less malloc) */ #define MAX_TOADD 4 #define MAX_OUTSIDE 8 #define MAX_TILE_PERC 20 static int generate_try_block(game_state *state, random_state *rs, int x1, int y1, int x2, int y2) { int x, y, nadd = 0, nout = 0, i, maxsz; space *sp, *toadd[MAX_TOADD], *outside[MAX_OUTSIDE], *dot; if (!INGRID(state, x1, y1) || !INGRID(state, x2, y2)) return 0; /* We limit the maximum size of tiles to be ~2*sqrt(area); so, * a 5x5 grid shouldn't have anything >10 tiles, a 20x20 grid * nothing >40 tiles. */ maxsz = (int)sqrt((double)(state->w * state->h)) * 2; debug(("generate_try_block, maxsz %d\n", maxsz)); /* Make a static list of the spaces; if any space is already * associated then quit immediately. */ for (x = x1; x <= x2; x += 2) { for (y = y1; y <= y2; y += 2) { assert(nadd < MAX_TOADD); sp = &SPACE(state, x, y); assert(sp->type == s_tile); if (sp->flags & F_TILE_ASSOC) return 0; toadd[nadd++] = sp; } } /* Make a list of the spaces outside of our block, and shuffle it. */ #define OUTSIDE(x, y) do { \ if (INGRID(state, (x), (y))) { \ assert(nout < MAX_OUTSIDE); \ outside[nout++] = &SPACE(state, (x), (y)); \ } \ } while(0) for (x = x1; x <= x2; x += 2) { OUTSIDE(x, y1-2); OUTSIDE(x, y2+2); } for (y = y1; y <= y2; y += 2) { OUTSIDE(x1-2, y); OUTSIDE(x2+2, y); } shuffle(outside, nout, sizeof(space *), rs); for (i = 0; i < nout; i++) { if (!(outside[i]->flags & F_TILE_ASSOC)) continue; dot = &SPACE(state, outside[i]->dotx, outside[i]->doty); if (dot->nassoc >= maxsz) { debug(("Not adding to dot %d,%d, large enough (%d) already.\n", dot->x, dot->y, dot->nassoc)); continue; } if (dot_expand_or_move(state, dot, toadd, nadd)) return 1; } return 0; } #ifdef STANDALONE_SOLVER int maxtries; #define MAXTRIES maxtries #else #define MAXTRIES 50 #endif static int solver_obvious_dot(game_state *state,space *dot); #define GP_DOTS 1 static void generate_pass(game_state *state, random_state *rs, int *scratch, int perc, unsigned int flags) { int sz = state->sx*state->sy, nspc, i, ret; shuffle(scratch, sz, sizeof(int), rs); /* This bug took me a, er, little while to track down. On PalmOS, * which has 16-bit signed ints, puzzles over about 9x9 started * failing to generate because the nspc calculation would start * to overflow, causing the dots not to be filled in properly. */ nspc = (int)(((long)perc * (long)sz) / 100L); debug(("generate_pass: %d%% (%d of %dx%d) squares, flags 0x%x\n", perc, nspc, state->sx, state->sy, flags)); for (i = 0; i < nspc; i++) { space *sp = &state->grid[scratch[i]]; int x1 = sp->x, y1 = sp->y, x2 = sp->x, y2 = sp->y; if (sp->type == s_edge) { if (IS_VERTICAL_EDGE(sp->x)) { x1--; x2++; } else { y1--; y2++; } } if (sp->type != s_vertex) { /* heuristic; expanding from vertices tends to generate lots of * too-big regions of tiles. */ if (generate_try_block(state, rs, x1, y1, x2, y2)) continue; /* we expanded successfully. */ } if (!(flags & GP_DOTS)) continue; if ((sp->type == s_edge) && (i % 2)) { debug(("Omitting edge %d,%d as half-of.\n", sp->x, sp->y)); continue; } /* If we've got here we might want to put a dot down. Check * if we can, and add one if so. */ if (dot_is_possible(state, sp, 0)) { add_dot(sp); #ifdef STANDALONE_PICTURE_GENERATOR if (picture) { if (picture[(sp->y/2) * state->w + (sp->x/2)]) sp->flags |= F_DOT_BLACK; } #endif ret = solver_obvious_dot(state, sp); assert(ret != -1); debug(("Added dot (and obvious associations) at %d,%d\n", sp->x, sp->y)); dbg_state(state); } } dbg_state(state); } static int check_complete(const game_state *state, int *dsf, int *colours); static int solver_state(game_state *state, int maxdiff); static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { game_state *state = blank_game(params->w, params->h), *copy; char *desc; int *scratch, sz = state->sx*state->sy, i; int diff, ntries = 0, cc; /* Random list of squares to try and process, one-by-one. */ scratch = snewn(sz, int); for (i = 0; i < sz; i++) scratch[i] = i; generate: clear_game(state, 1); ntries++; /* generate_pass(state, rs, scratch, 10, GP_DOTS); */ /* generate_pass(state, rs, scratch, 100, 0); */ generate_pass(state, rs, scratch, 100, GP_DOTS); game_update_dots(state); #ifdef DEBUGGING { char *tmp = encode_game(state); debug(("new_game_desc state %dx%d:%s\n", params->w, params->h, tmp)); sfree(tmp); } #endif for (i = 0; i < state->sx*state->sy; i++) if (state->grid[i].type == s_tile) outline_tile_fordot(state, &state->grid[i], TRUE); cc = check_complete(state, NULL, NULL); assert(cc); copy = dup_game(state); clear_game(copy, 0); dbg_state(copy); diff = solver_state(copy, params->diff); free_game(copy); assert(diff != DIFF_IMPOSSIBLE); if (diff != params->diff) { /* * We'll grudgingly accept a too-easy puzzle, but we must * _not_ permit a too-hard one (one which the solver * couldn't handle at all). */ if (diff > params->diff || ntries < MAXTRIES) goto generate; } #ifdef STANDALONE_PICTURE_GENERATOR /* * Postprocessing pass to prevent excessive numbers of adjacent * singletons. Iterate over all edges in random shuffled order; * for each edge that separates two regions, investigate * whether removing that edge and merging the regions would * still yield a valid and soluble puzzle. (The two regions * must also be the same colour, of course.) If so, do it. * * This postprocessing pass is slow (due to repeated solver * invocations), and seems to be unnecessary during normal * unconstrained game generation. However, when generating a * game under colour constraints, excessive singletons seem to * turn up more often, so it's worth doing this. */ { int *posns, nposns; int i, j, newdiff; game_state *copy2; nposns = params->w * (params->h+1) + params->h * (params->w+1); posns = snewn(nposns, int); for (i = j = 0; i < state->sx*state->sy; i++) if (state->grid[i].type == s_edge) posns[j++] = i; assert(j == nposns); shuffle(posns, nposns, sizeof(*posns), rs); for (i = 0; i < nposns; i++) { int x, y, x0, y0, x1, y1, cx, cy, cn, cx0, cy0, cx1, cy1, tx, ty; space *s0, *s1, *ts, *d0, *d1, *dn; int ok; /* Coordinates of edge space */ x = posns[i] % state->sx; y = posns[i] / state->sx; /* Coordinates of square spaces on either side of edge */ x0 = ((x+1) & ~1) - 1; /* round down to next odd number */ y0 = ((y+1) & ~1) - 1; x1 = 2*x-x0; /* and reflect about x to get x1 */ y1 = 2*y-y0; if (!INGRID(state, x0, y0) || !INGRID(state, x1, y1)) continue; /* outermost edge of grid */ s0 = &SPACE(state, x0, y0); s1 = &SPACE(state, x1, y1); assert(s0->type == s_tile && s1->type == s_tile); if (s0->dotx == s1->dotx && s0->doty == s1->doty) continue; /* tiles _already_ owned by same dot */ d0 = &SPACE(state, s0->dotx, s0->doty); d1 = &SPACE(state, s1->dotx, s1->doty); if ((d0->flags ^ d1->flags) & F_DOT_BLACK) continue; /* different colours: cannot merge */ /* * Work out where the centre of gravity of the new * region would be. */ cx = d0->nassoc * d0->x + d1->nassoc * d1->x; cy = d0->nassoc * d0->y + d1->nassoc * d1->y; cn = d0->nassoc + d1->nassoc; if (cx % cn || cy % cn) continue; /* CoG not at integer coordinates */ cx /= cn; cy /= cn; assert(INUI(state, cx, cy)); /* * Ensure that the CoG would actually be _in_ the new * region, by verifying that all its surrounding tiles * belong to one or other of our two dots. */ cx0 = ((cx+1) & ~1) - 1; /* round down to next odd number */ cy0 = ((cy+1) & ~1) - 1; cx1 = 2*cx-cx0; /* and reflect about cx to get cx1 */ cy1 = 2*cy-cy0; ok = TRUE; for (ty = cy0; ty <= cy1; ty += 2) for (tx = cx0; tx <= cx1; tx += 2) { ts = &SPACE(state, tx, ty); assert(ts->type == s_tile); if ((ts->dotx != d0->x || ts->doty != d0->y) && (ts->dotx != d1->x || ts->doty != d1->y)) ok = FALSE; } if (!ok) continue; /* * Verify that for every tile in either source region, * that tile's image in the new CoG is also in one of * the two source regions. */ for (ty = 1; ty < state->sy; ty += 2) { for (tx = 1; tx < state->sx; tx += 2) { int tx1, ty1; ts = &SPACE(state, tx, ty); assert(ts->type == s_tile); if ((ts->dotx != d0->x || ts->doty != d0->y) && (ts->dotx != d1->x || ts->doty != d1->y)) continue; /* not part of these tiles anyway */ tx1 = 2*cx-tx; ty1 = 2*cy-ty; if (!INGRID(state, tx1, ty1)) { ok = FALSE; break; } ts = &SPACE(state, cx+cx-tx, cy+cy-ty); if ((ts->dotx != d0->x || ts->doty != d0->y) && (ts->dotx != d1->x || ts->doty != d1->y)) { ok = FALSE; break; } } if (!ok) break; } if (!ok) continue; /* * Now we're clear to attempt the merge. We take a copy * of the game state first, so we can revert it easily * if the resulting puzzle turns out to have become * insoluble. */ copy2 = dup_game(state); remove_dot(d0); remove_dot(d1); dn = &SPACE(state, cx, cy); add_dot(dn); dn->flags |= (d0->flags & F_DOT_BLACK); for (ty = 1; ty < state->sy; ty += 2) { for (tx = 1; tx < state->sx; tx += 2) { ts = &SPACE(state, tx, ty); assert(ts->type == s_tile); if ((ts->dotx != d0->x || ts->doty != d0->y) && (ts->dotx != d1->x || ts->doty != d1->y)) continue; /* not part of these tiles anyway */ add_assoc(state, ts, dn); } } copy = dup_game(state); clear_game(copy, 0); dbg_state(copy); newdiff = solver_state(copy, params->diff); free_game(copy); if (diff == newdiff) { /* Still just as soluble. Let the merge stand. */ free_game(copy2); } else { /* Became insoluble. Revert. */ free_game(state); state = copy2; } } sfree(posns); } #endif desc = encode_game(state); #ifndef STANDALONE_SOLVER debug(("new_game_desc generated: \n")); dbg_state(state); #endif free_game(state); sfree(scratch); return desc; } static int solver_obvious(game_state *state); static int dots_too_close(game_state *state) { /* Quick-and-dirty check, using half the solver: * solver_obvious will only fail if the dots are * too close together, so dot-proximity associations * overlap. */ game_state *tmp = dup_game(state); int ret = solver_obvious(tmp); free_game(tmp); return (ret == -1) ? 1 : 0; } static game_state *load_game(const game_params *params, const char *desc, char **why_r) { game_state *state = blank_game(params->w, params->h); char *why = NULL; int i, x, y, n; unsigned int df; i = 0; while (*desc) { n = *desc++; if (n == 'z') { i += 25; continue; } if (n >= 'a' && n <= 'y') { i += n - 'a'; df = 0; } else if (n >= 'A' && n <= 'Y') { i += n - 'A'; df = F_DOT_BLACK; } else { why = "Invalid characters in game description"; goto fail; } /* if we got here we incremented i and have a dot to add. */ y = (i / (state->sx-2)) + 1; x = (i % (state->sx-2)) + 1; if (!INUI(state, x, y)) { why = "Too much data to fit in grid"; goto fail; } add_dot(&SPACE(state, x, y)); SPACE(state, x, y).flags |= df; i++; } game_update_dots(state); if (dots_too_close(state)) { why = "Dots too close together"; goto fail; } return state; fail: free_game(state); if (why_r) *why_r = why; return NULL; } static char *validate_desc(const game_params *params, const char *desc) { char *why = NULL; game_state *dummy = load_game(params, desc, &why); if (dummy) { free_game(dummy); assert(!why); } else assert(why); return why; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { game_state *state = load_game(params, desc, NULL); if (!state) { assert("Unable to load ?validated game."); return NULL; } #ifdef EDITOR state->me = me; #endif return state; } /* ---------------------------------------------------------- * Solver and all its little wizards. */ int solver_recurse_depth; typedef struct solver_ctx { game_state *state; int sz; /* state->sx * state->sy */ space **scratch; /* size sz */ } solver_ctx; static solver_ctx *new_solver(game_state *state) { solver_ctx *sctx = snew(solver_ctx); sctx->state = state; sctx->sz = state->sx*state->sy; sctx->scratch = snewn(sctx->sz, space *); return sctx; } static void free_solver(solver_ctx *sctx) { sfree(sctx->scratch); sfree(sctx); } /* Solver ideas so far: * * For any empty space, work out how many dots it could associate * with: * it needs line-of-sight * it needs an empty space on the far side * any adjacent lines need corresponding line possibilities. */ /* The solver_ctx should keep a list of dot positions, for quicker looping. * * Solver techniques, in order of difficulty: * obvious adjacency to dots * transferring tiles to opposite side * transferring lines to opposite side * one possible dot for a given tile based on opposite availability * tile with 3 definite edges next to an associated tile must associate with same dot. * * one possible dot for a given tile based on line-of-sight */ static int solver_add_assoc(game_state *state, space *tile, int dx, int dy, const char *why) { space *dot, *tile_opp; dot = &SPACE(state, dx, dy); tile_opp = space_opposite_dot(state, tile, dot); assert(tile->type == s_tile); if (tile->flags & F_TILE_ASSOC) { if ((tile->dotx != dx) || (tile->doty != dy)) { solvep(("%*sSet %d,%d --> %d,%d (%s) impossible; " "already --> %d,%d.\n", solver_recurse_depth*4, "", tile->x, tile->y, dx, dy, why, tile->dotx, tile->doty)); return -1; } return 0; /* no-op */ } if (!tile_opp) { solvep(("%*s%d,%d --> %d,%d impossible, no opposite tile.\n", solver_recurse_depth*4, "", tile->x, tile->y, dx, dy)); return -1; } if (tile_opp->flags & F_TILE_ASSOC && (tile_opp->dotx != dx || tile_opp->doty != dy)) { solvep(("%*sSet %d,%d --> %d,%d (%s) impossible; " "opposite already --> %d,%d.\n", solver_recurse_depth*4, "", tile->x, tile->y, dx, dy, why, tile_opp->dotx, tile_opp->doty)); return -1; } add_assoc(state, tile, dot); add_assoc(state, tile_opp, dot); solvep(("%*sSetting %d,%d --> %d,%d (%s).\n", solver_recurse_depth*4, "", tile->x, tile->y,dx, dy, why)); solvep(("%*sSetting %d,%d --> %d,%d (%s, opposite).\n", solver_recurse_depth*4, "", tile_opp->x, tile_opp->y, dx, dy, why)); return 1; } static int solver_obvious_dot(game_state *state, space *dot) { int dx, dy, ret, didsth = 0; space *tile; debug(("%*ssolver_obvious_dot for %d,%d.\n", solver_recurse_depth*4, "", dot->x, dot->y)); assert(dot->flags & F_DOT); for (dx = -1; dx <= 1; dx++) { for (dy = -1; dy <= 1; dy++) { if (!INGRID(state, dot->x+dx, dot->y+dy)) continue; tile = &SPACE(state, dot->x+dx, dot->y+dy); if (tile->type == s_tile) { ret = solver_add_assoc(state, tile, dot->x, dot->y, "next to dot"); if (ret < 0) return -1; if (ret > 0) didsth = 1; } } } return didsth; } static int solver_obvious(game_state *state) { int i, didsth = 0, ret; debug(("%*ssolver_obvious.\n", solver_recurse_depth*4, "")); for (i = 0; i < state->ndots; i++) { ret = solver_obvious_dot(state, state->dots[i]); if (ret < 0) return -1; if (ret > 0) didsth = 1; } return didsth; } static int solver_lines_opposite_cb(game_state *state, space *edge, void *ctx) { int didsth = 0, n, dx, dy; space *tiles[2], *tile_opp, *edge_opp; assert(edge->type == s_edge); tiles_from_edge(state, edge, tiles); /* if tiles[0] && tiles[1] && they're both associated * and they're both associated with different dots, * ensure the line is set. */ if (!(edge->flags & F_EDGE_SET) && tiles[0] && tiles[1] && (tiles[0]->flags & F_TILE_ASSOC) && (tiles[1]->flags & F_TILE_ASSOC) && (tiles[0]->dotx != tiles[1]->dotx || tiles[0]->doty != tiles[1]->doty)) { /* No edge, but the two adjacent tiles are both * associated with different dots; add the edge. */ solvep(("%*sSetting edge %d,%d - tiles different dots.\n", solver_recurse_depth*4, "", edge->x, edge->y)); edge->flags |= F_EDGE_SET; didsth = 1; } if (!(edge->flags & F_EDGE_SET)) return didsth; for (n = 0; n < 2; n++) { if (!tiles[n]) continue; assert(tiles[n]->type == s_tile); if (!(tiles[n]->flags & F_TILE_ASSOC)) continue; tile_opp = tile_opposite(state, tiles[n]); if (!tile_opp) { solvep(("%*simpossible: edge %d,%d has assoc. tile %d,%d" " with no opposite.\n", solver_recurse_depth*4, "", edge->x, edge->y, tiles[n]->x, tiles[n]->y)); /* edge of tile has no opposite edge (off grid?); * this is impossible. */ return -1; } dx = tiles[n]->x - edge->x; dy = tiles[n]->y - edge->y; assert(INGRID(state, tile_opp->x+dx, tile_opp->y+dy)); edge_opp = &SPACE(state, tile_opp->x+dx, tile_opp->y+dy); if (!(edge_opp->flags & F_EDGE_SET)) { solvep(("%*sSetting edge %d,%d as opposite %d,%d\n", solver_recurse_depth*4, "", tile_opp->x-dx, tile_opp->y-dy, edge->x, edge->y)); edge_opp->flags |= F_EDGE_SET; didsth = 1; } } return didsth; } static int solver_spaces_oneposs_cb(game_state *state, space *tile, void *ctx) { int n, eset, ret; struct space *edgeadj[4], *tileadj[4]; int dotx, doty; assert(tile->type == s_tile); if (tile->flags & F_TILE_ASSOC) return 0; adjacencies(state, tile, edgeadj, tileadj); /* Empty tile. If each edge is either set, or associated with * the same dot, we must also associate with dot. */ eset = 0; dotx = -1; doty = -1; for (n = 0; n < 4; n++) { assert(edgeadj[n]); assert(edgeadj[n]->type == s_edge); if (edgeadj[n]->flags & F_EDGE_SET) { eset++; } else { assert(tileadj[n]); assert(tileadj[n]->type == s_tile); /* If an adjacent tile is empty we can't make any deductions.*/ if (!(tileadj[n]->flags & F_TILE_ASSOC)) return 0; /* If an adjacent tile is assoc. with a different dot * we can't make any deductions. */ if (dotx != -1 && doty != -1 && (tileadj[n]->dotx != dotx || tileadj[n]->doty != doty)) return 0; dotx = tileadj[n]->dotx; doty = tileadj[n]->doty; } } if (eset == 4) { solvep(("%*simpossible: empty tile %d,%d has 4 edges\n", solver_recurse_depth*4, "", tile->x, tile->y)); return -1; } assert(dotx != -1 && doty != -1); ret = solver_add_assoc(state, tile, dotx, doty, "rest are edges"); if (ret == -1) return -1; assert(ret != 0); /* really should have done something. */ return 1; } /* Improved algorithm for tracking line-of-sight from dots, and not spaces. * * The solver_ctx already stores a list of dots: the algorithm proceeds by * expanding outwards from each dot in turn, expanding first to the boundary * of its currently-connected tile and then to all empty tiles that could see * it. Empty tiles will be flagged with a 'can see dot ' sticker. * * Expansion will happen by (symmetrically opposite) pairs of squares; if * a square hasn't an opposite number there's no point trying to expand through * it. Empty tiles will therefore also be tagged in pairs. * * If an empty tile already has a 'can see dot ' tag from a previous dot, * it (and its partner) gets untagged (or, rather, a 'can see two dots' tag) * because we're looking for single-dot possibilities. * * Once we've gone through all the dots, any which still have a 'can see dot' * tag get associated with that dot (because it must have been the only one); * any without any tag (i.e. that could see _no_ dots) cause an impossibility * marked. * * The expansion will happen each time with a stored list of (space *) pairs, * rather than a mark-and-sweep idea; that's horrifically inefficient. * * expansion algorithm: * * * allocate list of (space *) the size of s->sx*s->sy. * * allocate second grid for (flags, dotx, doty) size of sx*sy. * * clear second grid (flags = 0, all dotx and doty = 0) * flags: F_REACHABLE, F_MULTIPLE * * * * for each dot, start with one pair of tiles that are associated with it -- * * vertex --> (dx+1, dy+1), (dx-1, dy-1) * * edge --> (adj1, adj2) * * tile --> (tile, tile) ??? * * mark that pair of tiles with F_MARK, clear all other F_MARKs. * * add two tiles to start of list. * * set start = 0, end = next = 2 * * from (start to end-1, step 2) { * * we have two tiles (t1, t2), opposites wrt our dot. * * for each (at1) sensible adjacent tile to t1 (i.e. not past an edge): * * work out at2 as the opposite to at1 * * assert at1 and at2 have the same F_MARK values. * * if at1 & F_MARK ignore it (we've been there on a previous sweep) * * if either are associated with a different dot * * mark both with F_MARK (so we ignore them later) * * otherwise (assoc. with our dot, or empty): * * mark both with F_MARK * * add their space * values to the end of the list, set next += 2. * } * * if (end == next) * * we didn't add any new squares; exit the loop. * else * * set start = next+1, end = next. go round again * * We've finished expanding from the dot. Now, for each square we have * in our list (--> each square with F_MARK): * * if the tile is empty: * * if F_REACHABLE was already set * * set F_MULTIPLE * * otherwise * * set F_REACHABLE, set dotx and doty to our dot. * * Then, continue the whole thing for each dot in turn. * * Once we've done for each dot, go through the entire grid looking for * empty tiles: for each empty tile: * if F_REACHABLE and not F_MULTIPLE, set that dot (and its double) * if !F_REACHABLE, return as impossible. * */ /* Returns 1 if this tile is either already associated with this dot, * or blank. */ static int solver_expand_checkdot(space *tile, space *dot) { if (!(tile->flags & F_TILE_ASSOC)) return 1; if (tile->dotx == dot->x && tile->doty == dot->y) return 1; return 0; } static void solver_expand_fromdot(game_state *state, space *dot, solver_ctx *sctx) { int i, j, x, y, start, end, next; space *sp; /* Clear the grid of the (space) flags we'll use. */ /* This is well optimised; analysis showed that: for (i = 0; i < sctx->sz; i++) state->grid[i].flags &= ~F_MARK; took up ~85% of the total function time! */ for (y = 1; y < state->sy; y += 2) { sp = &SPACE(state, 1, y); for (x = 1; x < state->sx; x += 2, sp += 2) sp->flags &= ~F_MARK; } /* Seed the list of marked squares with two that must be associated * with our dot (possibly the same space) */ if (dot->type == s_tile) { sctx->scratch[0] = sctx->scratch[1] = dot; } else if (dot->type == s_edge) { tiles_from_edge(state, dot, sctx->scratch); } else if (dot->type == s_vertex) { /* pick two of the opposite ones arbitrarily. */ sctx->scratch[0] = &SPACE(state, dot->x-1, dot->y-1); sctx->scratch[1] = &SPACE(state, dot->x+1, dot->y+1); } assert(sctx->scratch[0]->flags & F_TILE_ASSOC); assert(sctx->scratch[1]->flags & F_TILE_ASSOC); sctx->scratch[0]->flags |= F_MARK; sctx->scratch[1]->flags |= F_MARK; debug(("%*sexpand from dot %d,%d seeded with %d,%d and %d,%d.\n", solver_recurse_depth*4, "", dot->x, dot->y, sctx->scratch[0]->x, sctx->scratch[0]->y, sctx->scratch[1]->x, sctx->scratch[1]->y)); start = 0; end = 2; next = 2; expand: debug(("%*sexpand: start %d, end %d, next %d\n", solver_recurse_depth*4, "", start, end, next)); for (i = start; i < end; i += 2) { space *t1 = sctx->scratch[i]/*, *t2 = sctx->scratch[i+1]*/; space *edges[4], *tileadj[4], *tileadj2; adjacencies(state, t1, edges, tileadj); for (j = 0; j < 4; j++) { assert(edges[j]); if (edges[j]->flags & F_EDGE_SET) continue; assert(tileadj[j]); if (tileadj[j]->flags & F_MARK) continue; /* seen before. */ /* We have a tile adjacent to t1; find its opposite. */ tileadj2 = space_opposite_dot(state, tileadj[j], dot); if (!tileadj2) { debug(("%*sMarking %d,%d, no opposite.\n", solver_recurse_depth*4, "", tileadj[j]->x, tileadj[j]->y)); tileadj[j]->flags |= F_MARK; continue; /* no opposite, so mark for next time. */ } /* If the tile had an opposite we should have either seen both of * these, or neither of these, before. */ assert(!(tileadj2->flags & F_MARK)); if (solver_expand_checkdot(tileadj[j], dot) && solver_expand_checkdot(tileadj2, dot)) { /* Both tiles could associate with this dot; add them to * our list. */ debug(("%*sAdding %d,%d and %d,%d to possibles list.\n", solver_recurse_depth*4, "", tileadj[j]->x, tileadj[j]->y, tileadj2->x, tileadj2->y)); sctx->scratch[next++] = tileadj[j]; sctx->scratch[next++] = tileadj2; } /* Either way, we've seen these tiles already so mark them. */ debug(("%*sMarking %d,%d and %d,%d.\n", solver_recurse_depth*4, "", tileadj[j]->x, tileadj[j]->y, tileadj2->x, tileadj2->y)); tileadj[j]->flags |= F_MARK; tileadj2->flags |= F_MARK; } } if (next > end) { /* We added more squares; go back and try again. */ start = end; end = next; goto expand; } /* We've expanded as far as we can go. Now we update the main flags * on all tiles we've expanded into -- if they were empty, we have * found possible associations for this dot. */ for (i = 0; i < end; i++) { if (sctx->scratch[i]->flags & F_TILE_ASSOC) continue; if (sctx->scratch[i]->flags & F_REACHABLE) { /* This is (at least) the second dot this tile could * associate with. */ debug(("%*sempty tile %d,%d could assoc. other dot %d,%d\n", solver_recurse_depth*4, "", sctx->scratch[i]->x, sctx->scratch[i]->y, dot->x, dot->y)); sctx->scratch[i]->flags |= F_MULTIPLE; } else { /* This is the first (possibly only) dot. */ debug(("%*sempty tile %d,%d could assoc. 1st dot %d,%d\n", solver_recurse_depth*4, "", sctx->scratch[i]->x, sctx->scratch[i]->y, dot->x, dot->y)); sctx->scratch[i]->flags |= F_REACHABLE; sctx->scratch[i]->dotx = dot->x; sctx->scratch[i]->doty = dot->y; } } dbg_state(state); } static int solver_expand_postcb(game_state *state, space *tile, void *ctx) { assert(tile->type == s_tile); if (tile->flags & F_TILE_ASSOC) return 0; if (!(tile->flags & F_REACHABLE)) { solvep(("%*simpossible: space (%d,%d) can reach no dots.\n", solver_recurse_depth*4, "", tile->x, tile->y)); return -1; } if (tile->flags & F_MULTIPLE) return 0; return solver_add_assoc(state, tile, tile->dotx, tile->doty, "single possible dot after expansion"); } static int solver_expand_dots(game_state *state, solver_ctx *sctx) { int i; for (i = 0; i < sctx->sz; i++) state->grid[i].flags &= ~(F_REACHABLE|F_MULTIPLE); for (i = 0; i < state->ndots; i++) solver_expand_fromdot(state, state->dots[i], sctx); return foreach_tile(state, solver_expand_postcb, IMPOSSIBLE_QUITS, sctx); } struct recurse_ctx { space *best; int bestn; }; static int solver_recurse_cb(game_state *state, space *tile, void *ctx) { struct recurse_ctx *rctx = (struct recurse_ctx *)ctx; int i, n = 0; assert(tile->type == s_tile); if (tile->flags & F_TILE_ASSOC) return 0; /* We're unassociated: count up all the dots we could associate with. */ for (i = 0; i < state->ndots; i++) { if (dotfortile(state, tile, state->dots[i])) n++; } if (n > rctx->bestn) { rctx->bestn = n; rctx->best = tile; } return 0; } static int solver_state(game_state *state, int maxdiff); #define MAXRECURSE 5 static int solver_recurse(game_state *state, int maxdiff) { int diff = DIFF_IMPOSSIBLE, ret, n, gsz = state->sx * state->sy; space *ingrid, *outgrid = NULL, *bestopp; struct recurse_ctx rctx; if (solver_recurse_depth >= MAXRECURSE) { solvep(("Limiting recursion to %d, returning.", MAXRECURSE)); return DIFF_UNFINISHED; } /* Work out the cell to recurse on; go through all unassociated tiles * and find which one has the most possible dots it could associate * with. */ rctx.best = NULL; rctx.bestn = 0; foreach_tile(state, solver_recurse_cb, 0, &rctx); if (rctx.bestn == 0) return DIFF_IMPOSSIBLE; /* or assert? */ assert(rctx.best); solvep(("%*sRecursing around %d,%d, with %d possible dots.\n", solver_recurse_depth*4, "", rctx.best->x, rctx.best->y, rctx.bestn)); #ifdef STANDALONE_SOLVER solver_recurse_depth++; #endif ingrid = snewn(gsz, struct space); memcpy(ingrid, state->grid, gsz * sizeof(struct space)); for (n = 0; n < state->ndots; n++) { memcpy(state->grid, ingrid, gsz * sizeof(struct space)); if (!dotfortile(state, rctx.best, state->dots[n])) continue; /* set cell (temporarily) pointing to that dot. */ solver_add_assoc(state, rctx.best, state->dots[n]->x, state->dots[n]->y, "Attempting for recursion"); ret = solver_state(state, maxdiff); if (diff == DIFF_IMPOSSIBLE && ret != DIFF_IMPOSSIBLE) { /* we found our first solved grid; copy it away. */ assert(!outgrid); outgrid = snewn(gsz, struct space); memcpy(outgrid, state->grid, gsz * sizeof(struct space)); } /* reset cell back to unassociated. */ bestopp = tile_opposite(state, rctx.best); assert(bestopp && bestopp->flags & F_TILE_ASSOC); remove_assoc(state, rctx.best); remove_assoc(state, bestopp); if (ret == DIFF_AMBIGUOUS || ret == DIFF_UNFINISHED) diff = ret; else if (ret == DIFF_IMPOSSIBLE) /* no change */; else { /* precisely one solution */ if (diff == DIFF_IMPOSSIBLE) diff = DIFF_UNREASONABLE; else diff = DIFF_AMBIGUOUS; } /* if we've found >1 solution, or ran out of recursion, * give up immediately. */ if (diff == DIFF_AMBIGUOUS || diff == DIFF_UNFINISHED) break; } #ifdef STANDALONE_SOLVER solver_recurse_depth--; #endif if (outgrid) { /* we found (at least one) soln; copy it back to state */ memcpy(state->grid, outgrid, gsz * sizeof(struct space)); sfree(outgrid); } sfree(ingrid); return diff; } static int solver_state(game_state *state, int maxdiff) { solver_ctx *sctx = new_solver(state); int ret, diff = DIFF_NORMAL; #ifdef STANDALONE_PICTURE_GENERATOR /* hack, hack: set picture to NULL during solving so that add_assoc * won't complain when we attempt recursive guessing and guess wrong */ int *savepic = picture; picture = NULL; #endif ret = solver_obvious(state); if (ret < 0) { diff = DIFF_IMPOSSIBLE; goto got_result; } #define CHECKRET(d) do { \ if (ret < 0) { diff = DIFF_IMPOSSIBLE; goto got_result; } \ if (ret > 0) { diff = max(diff, (d)); goto cont; } \ } while(0) while (1) { cont: ret = foreach_edge(state, solver_lines_opposite_cb, IMPOSSIBLE_QUITS, sctx); CHECKRET(DIFF_NORMAL); ret = foreach_tile(state, solver_spaces_oneposs_cb, IMPOSSIBLE_QUITS, sctx); CHECKRET(DIFF_NORMAL); ret = solver_expand_dots(state, sctx); CHECKRET(DIFF_NORMAL); if (maxdiff <= DIFF_NORMAL) break; /* harder still? */ /* if we reach here, we've made no deductions, so we terminate. */ break; } if (check_complete(state, NULL, NULL)) goto got_result; diff = (maxdiff >= DIFF_UNREASONABLE) ? solver_recurse(state, maxdiff) : DIFF_UNFINISHED; got_result: free_solver(sctx); #ifndef STANDALONE_SOLVER debug(("solver_state ends, diff %s:\n", galaxies_diffnames[diff])); dbg_state(state); #endif #ifdef STANDALONE_PICTURE_GENERATOR picture = savepic; #endif return diff; } #ifndef EDITOR static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { game_state *tosolve; char *ret; int i; int diff; tosolve = dup_game(currstate); diff = solver_state(tosolve, DIFF_UNREASONABLE); if (diff != DIFF_UNFINISHED && diff != DIFF_IMPOSSIBLE) { debug(("solve_game solved with current state.\n")); goto solved; } free_game(tosolve); tosolve = dup_game(state); diff = solver_state(tosolve, DIFF_UNREASONABLE); if (diff != DIFF_UNFINISHED && diff != DIFF_IMPOSSIBLE) { debug(("solve_game solved with original state.\n")); goto solved; } free_game(tosolve); return NULL; solved: /* * Clear tile associations: the solution will only include the * edges. */ for (i = 0; i < tosolve->sx*tosolve->sy; i++) tosolve->grid[i].flags &= ~F_TILE_ASSOC; ret = diff_game(currstate, tosolve, 1); free_game(tosolve); return ret; } #endif /* ---------------------------------------------------------- * User interface. */ struct game_ui { int dragging; int dx, dy; /* pixel coords of drag pos. */ int dotx, doty; /* grid coords of dot we're dragging from. */ int srcx, srcy; /* grid coords of drag start */ int cur_x, cur_y, cur_visible; }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->dragging = FALSE; ui->cur_x = ui->cur_y = 1; ui->cur_visible = 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) { } #define FLASH_TIME 0.15F #define PREFERRED_TILE_SIZE 32 #define TILE_SIZE (ds->tilesize) #define DOT_SIZE (TILE_SIZE / 4) #define EDGE_THICKNESS (max(TILE_SIZE / 16, 2)) #define BORDER TILE_SIZE #define COORD(x) ( (x) * TILE_SIZE + BORDER ) #define SCOORD(x) ( ((x) * TILE_SIZE)/2 + BORDER ) #define FROMCOORD(x) ( ((x) - BORDER) / TILE_SIZE ) #define DRAW_WIDTH (BORDER * 2 + ds->w * TILE_SIZE) #define DRAW_HEIGHT (BORDER * 2 + ds->h * TILE_SIZE) #define CURSOR_SIZE DOT_SIZE struct game_drawstate { int started; int w, h; int tilesize; unsigned long *grid; int *dx, *dy; blitter *bl; int dragging, dragx, dragy; int *colour_scratch; int cx, cy, cur_visible; blitter *cur_bl; }; #define CORNER_TOLERANCE 0.15F #define CENTRE_TOLERANCE 0.15F /* * Round FP coordinates to the centre of the nearest edge. */ #ifndef EDITOR static void coord_round_to_edge(float x, float y, int *xr, int *yr) { float xs, ys, xv, yv, dx, dy; /* * Find the nearest square-centre. */ xs = (float)floor(x) + 0.5F; ys = (float)floor(y) + 0.5F; /* * Find the nearest grid vertex. */ xv = (float)floor(x + 0.5F); yv = (float)floor(y + 0.5F); /* * Determine whether the horizontal or vertical edge from that * vertex alongside that square is closer to us, by comparing * distances from the square cente. */ dx = (float)fabs(x - xs); dy = (float)fabs(y - ys); if (dx > dy) { /* Vertical edge: x-coord of corner, * y-coord of square centre. */ *xr = 2 * (int)xv; *yr = 1 + 2 * (int)floor(ys); } else { /* Horizontal edge: x-coord of square centre, * y-coord of corner. */ *xr = 1 + 2 * (int)floor(xs); *yr = 2 * (int)yv; } } #endif #ifdef EDITOR static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { char buf[80]; int px, py; struct space *sp; px = 2*FROMCOORD((float)x) + 0.5; py = 2*FROMCOORD((float)y) + 0.5; state->cdiff = -1; if (button == 'C' || button == 'c') return dupstr("C"); if (button == 'S' || button == 's') { char *ret; game_state *tmp = dup_game(state); state->cdiff = solver_state(tmp, DIFF_UNREASONABLE-1); ret = diff_game(state, tmp, 0); free_game(tmp); return ret; } if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { if (!INUI(state, px, py)) return NULL; sp = &SPACE(state, px, py); if (!dot_is_possible(state, sp, 1)) return NULL; sprintf(buf, "%c%d,%d", (char)((button == LEFT_BUTTON) ? 'D' : 'd'), px, py); return dupstr(buf); } return NULL; } #else static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { /* UI operations (play mode): * * Toggle edge (set/unset) (left-click on edge) * Associate space with dot (left-drag from dot) * Unassociate space (left-drag from space off grid) * Autofill lines around shape? (right-click?) * * (edit mode; will clear all lines/associations) * * Add or remove dot (left-click) */ char buf[80]; const char *sep = ""; int px, py; struct space *sp, *dot; buf[0] = '\0'; if (button == 'H' || button == 'h') { char *ret; game_state *tmp = dup_game(state); solver_obvious(tmp); ret = diff_game(state, tmp, 0); free_game(tmp); return ret; } if (button == LEFT_BUTTON) { ui->cur_visible = 0; coord_round_to_edge(FROMCOORD((float)x), FROMCOORD((float)y), &px, &py); if (!INUI(state, px, py)) return NULL; sp = &SPACE(state, px, py); assert(sp->type == s_edge); { sprintf(buf, "E%d,%d", px, py); return dupstr(buf); } } else if (button == RIGHT_BUTTON) { int px1, py1; ui->cur_visible = 0; px = (int)(2*FROMCOORD((float)x) + 0.5); py = (int)(2*FROMCOORD((float)y) + 0.5); dot = NULL; /* * If there's a dot anywhere nearby, we pick up an arrow * pointing at that dot. */ for (py1 = py-1; py1 <= py+1; py1++) for (px1 = px-1; px1 <= px+1; px1++) { if (px1 >= 0 && px1 < state->sx && py1 >= 0 && py1 < state->sy && x >= SCOORD(px1-1) && x < SCOORD(px1+1) && y >= SCOORD(py1-1) && y < SCOORD(py1+1) && SPACE(state, px1, py1).flags & F_DOT) { /* * Found a dot. Begin a drag from it. */ dot = &SPACE(state, px1, py1); ui->srcx = px1; ui->srcy = py1; goto done; /* multi-level break */ } } /* * Otherwise, find the nearest _square_, and pick up the * same arrow as it's got on it, if any. */ if (!dot) { px = 2*FROMCOORD(x+TILE_SIZE) - 1; py = 2*FROMCOORD(y+TILE_SIZE) - 1; if (px >= 0 && px < state->sx && py >= 0 && py < state->sy) { sp = &SPACE(state, px, py); if (sp->flags & F_TILE_ASSOC) { dot = &SPACE(state, sp->dotx, sp->doty); ui->srcx = px; ui->srcy = py; } } } done: /* * Now, if we've managed to find a dot, begin a drag. */ if (dot) { ui->dragging = TRUE; ui->dx = x; ui->dy = y; ui->dotx = dot->x; ui->doty = dot->y; return ""; } } else if (button == RIGHT_DRAG && ui->dragging) { /* just move the drag coords. */ ui->dx = x; ui->dy = y; return ""; } else if (button == RIGHT_RELEASE && ui->dragging) { ui->dragging = FALSE; /* * Drags are always targeted at a single square. */ px = 2*FROMCOORD(x+TILE_SIZE) - 1; py = 2*FROMCOORD(y+TILE_SIZE) - 1; /* * Dragging an arrow on to the same square it started from * is a null move; just update the ui and finish. */ if (px == ui->srcx && py == ui->srcy) return ""; /* * Otherwise, we remove the arrow from its starting * square if we didn't start from a dot... */ if ((ui->srcx != ui->dotx || ui->srcy != ui->doty) && SPACE(state, ui->srcx, ui->srcy).flags & F_TILE_ASSOC) { sprintf(buf + strlen(buf), "%sU%d,%d", sep, ui->srcx, ui->srcy); sep = ";"; } /* * ... and if the square we're moving it _to_ is valid, we * add one there instead. */ if (INUI(state, px, py)) { sp = &SPACE(state, px, py); if (!(sp->flags & F_DOT) && !(sp->flags & F_TILE_ASSOC)) sprintf(buf + strlen(buf), "%sA%d,%d,%d,%d", sep, px, py, ui->dotx, ui->doty); } if (buf[0]) return dupstr(buf); else return ""; } else if (IS_CURSOR_MOVE(button)) { move_cursor(button, &ui->cur_x, &ui->cur_y, state->sx-1, state->sy-1, 0); if (ui->cur_x < 1) ui->cur_x = 1; if (ui->cur_y < 1) ui->cur_y = 1; ui->cur_visible = 1; if (ui->dragging) { ui->dx = SCOORD(ui->cur_x); ui->dy = SCOORD(ui->cur_y); } return ""; } else if (IS_CURSOR_SELECT(button)) { if (!ui->cur_visible) { ui->cur_visible = 1; return ""; } sp = &SPACE(state, ui->cur_x, ui->cur_y); if (ui->dragging) { ui->dragging = FALSE; if ((ui->srcx != ui->dotx || ui->srcy != ui->doty) && SPACE(state, ui->srcx, ui->srcy).flags & F_TILE_ASSOC) { sprintf(buf, "%sU%d,%d", sep, ui->srcx, ui->srcy); sep = ";"; } if (sp->type == s_tile && !(sp->flags & F_DOT) && !(sp->flags & F_TILE_ASSOC)) { sprintf(buf + strlen(buf), "%sA%d,%d,%d,%d", sep, ui->cur_x, ui->cur_y, ui->dotx, ui->doty); } return dupstr(buf); } else if (sp->flags & F_DOT) { ui->dragging = TRUE; ui->dx = SCOORD(ui->cur_x); ui->dy = SCOORD(ui->cur_y); ui->dotx = ui->srcx = ui->cur_x; ui->doty = ui->srcy = ui->cur_y; return ""; } else if (sp->flags & F_TILE_ASSOC) { assert(sp->type == s_tile); ui->dragging = TRUE; ui->dx = SCOORD(ui->cur_x); ui->dy = SCOORD(ui->cur_y); ui->dotx = sp->dotx; ui->doty = sp->doty; ui->srcx = ui->cur_x; ui->srcy = ui->cur_y; return ""; } else if (sp->type == s_edge) { sprintf(buf, "E%d,%d", ui->cur_x, ui->cur_y); return dupstr(buf); } } return NULL; } #endif static int check_complete(const game_state *state, int *dsf, int *colours) { int w = state->w, h = state->h; int x, y, i, ret; int free_dsf; struct sqdata { int minx, miny, maxx, maxy; int cx, cy; int valid, colour; } *sqdata; if (!dsf) { dsf = snew_dsf(w*h); free_dsf = TRUE; } else { dsf_init(dsf, w*h); free_dsf = FALSE; } /* * During actual game play, completion checking is done on the * basis of the edges rather than the square associations. So * first we must go through the grid figuring out the connected * components into which the edges divide it. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) { if (y+1 < h && !(SPACE(state, 2*x+1, 2*y+2).flags & F_EDGE_SET)) dsf_merge(dsf, y*w+x, (y+1)*w+x); if (x+1 < w && !(SPACE(state, 2*x+2, 2*y+1).flags & F_EDGE_SET)) dsf_merge(dsf, y*w+x, y*w+(x+1)); } /* * That gives us our connected components. Now, for each * component, decide whether it's _valid_. A valid component is * one which: * * - is 180-degree rotationally symmetric * - has a dot at its centre of symmetry * - has no other dots anywhere within it (including on its * boundary) * - contains no internal edges (i.e. edges separating two * squares which are both part of the component). */ /* * First, go through the grid finding the bounding box of each * component. */ sqdata = snewn(w*h, struct sqdata); for (i = 0; i < w*h; i++) { sqdata[i].minx = w+1; sqdata[i].miny = h+1; sqdata[i].maxx = sqdata[i].maxy = -1; sqdata[i].valid = FALSE; } for (y = 0; y < h; y++) for (x = 0; x < w; x++) { i = dsf_canonify(dsf, y*w+x); if (sqdata[i].minx > x) sqdata[i].minx = x; if (sqdata[i].maxx < x) sqdata[i].maxx = x; if (sqdata[i].miny > y) sqdata[i].miny = y; if (sqdata[i].maxy < y) sqdata[i].maxy = y; sqdata[i].valid = TRUE; } /* * Now we're in a position to loop over each actual component * and figure out where its centre of symmetry has to be if * it's anywhere. */ for (i = 0; i < w*h; i++) if (sqdata[i].valid) { int cx, cy; cx = sqdata[i].cx = sqdata[i].minx + sqdata[i].maxx + 1; cy = sqdata[i].cy = sqdata[i].miny + sqdata[i].maxy + 1; if (!(SPACE(state, sqdata[i].cx, sqdata[i].cy).flags & F_DOT)) sqdata[i].valid = FALSE; /* no dot at centre of symmetry */ if (dsf_canonify(dsf, (cy-1)/2*w+(cx-1)/2) != i || dsf_canonify(dsf, (cy)/2*w+(cx-1)/2) != i || dsf_canonify(dsf, (cy-1)/2*w+(cx)/2) != i || dsf_canonify(dsf, (cy)/2*w+(cx)/2) != i) sqdata[i].valid = FALSE; /* dot at cx,cy isn't ours */ if (SPACE(state, sqdata[i].cx, sqdata[i].cy).flags & F_DOT_BLACK) sqdata[i].colour = 2; else sqdata[i].colour = 1; } /* * Now we loop over the whole grid again, this time finding * extraneous dots (any dot which wholly or partially overlaps * a square and is not at the centre of symmetry of that * square's component disqualifies the component from validity) * and extraneous edges (any edge separating two squares * belonging to the same component also disqualifies that * component). */ for (y = 1; y < state->sy-1; y++) for (x = 1; x < state->sx-1; x++) { space *sp = &SPACE(state, x, y); if (sp->flags & F_DOT) { /* * There's a dot here. Use it to disqualify any * component which deserves it. */ int cx, cy; for (cy = (y-1) >> 1; cy <= y >> 1; cy++) for (cx = (x-1) >> 1; cx <= x >> 1; cx++) { i = dsf_canonify(dsf, cy*w+cx); if (x != sqdata[i].cx || y != sqdata[i].cy) sqdata[i].valid = FALSE; } } if (sp->flags & F_EDGE_SET) { /* * There's an edge here. Use it to disqualify a * component if necessary. */ int cx1 = (x-1) >> 1, cx2 = x >> 1; int cy1 = (y-1) >> 1, cy2 = y >> 1; assert((cx1==cx2) ^ (cy1==cy2)); i = dsf_canonify(dsf, cy1*w+cx1); if (i == dsf_canonify(dsf, cy2*w+cx2)) sqdata[i].valid = FALSE; } } /* * And finally we test rotational symmetry: for each square in * the grid, find which component it's in, test that that * component also has a square in the symmetric position, and * disqualify it if it doesn't. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) { int x2, y2; i = dsf_canonify(dsf, y*w+x); x2 = sqdata[i].cx - 1 - x; y2 = sqdata[i].cy - 1 - y; if (i != dsf_canonify(dsf, y2*w+x2)) sqdata[i].valid = FALSE; } /* * That's it. We now have all the connected components marked * as valid or not valid. So now we return a `colours' array if * we were asked for one, and also we return an overall * true/false value depending on whether _every_ square in the * grid is part of a valid component. */ ret = TRUE; for (i = 0; i < w*h; i++) { int ci = dsf_canonify(dsf, i); int thisok = sqdata[ci].valid; if (colours) colours[i] = thisok ? sqdata[ci].colour : 0; ret = ret && thisok; } sfree(sqdata); if (free_dsf) sfree(dsf); return ret; } static game_state *execute_move(const game_state *state, const char *move) { int x, y, ax, ay, n, dx, dy; game_state *ret = dup_game(state); struct space *sp, *dot; debug(("%s\n", move)); while (*move) { char c = *move; if (c == 'E' || c == 'U' || c == 'M' #ifdef EDITOR || c == 'D' || c == 'd' #endif ) { move++; if (sscanf(move, "%d,%d%n", &x, &y, &n) != 2 || !INUI(state, x, y)) goto badmove; sp = &SPACE(ret, x, y); #ifdef EDITOR if (c == 'D' || c == 'd') { unsigned int currf, newf, maskf; if (!dot_is_possible(state, sp, 1)) goto badmove; newf = F_DOT | (c == 'd' ? F_DOT_BLACK : 0); currf = GRID(ret, grid, x, y).flags; maskf = F_DOT | F_DOT_BLACK; /* if we clicked 'white dot': * white --> empty, empty --> white, black --> white. * if we clicker 'black dot': * black --> empty, empty --> black, white --> black. */ if (currf & maskf) { sp->flags &= ~maskf; if ((currf & maskf) != newf) sp->flags |= newf; } else sp->flags |= newf; sp->nassoc = 0; /* edit-mode disallows associations. */ game_update_dots(ret); } else #endif if (c == 'E') { if (sp->type != s_edge) goto badmove; sp->flags ^= F_EDGE_SET; } else if (c == 'U') { if (sp->type != s_tile || !(sp->flags & F_TILE_ASSOC)) goto badmove; remove_assoc(ret, sp); } else if (c == 'M') { if (!(sp->flags & F_DOT)) goto badmove; sp->flags ^= F_DOT_HOLD; } move += n; } else if (c == 'A' || c == 'a') { move++; if (sscanf(move, "%d,%d,%d,%d%n", &x, &y, &ax, &ay, &n) != 4 || x < 1 || y < 1 || x >= (state->sx-1) || y >= (state->sy-1) || ax < 1 || ay < 1 || ax >= (state->sx-1) || ay >= (state->sy-1)) goto badmove; dot = &GRID(ret, grid, ax, ay); if (!(dot->flags & F_DOT))goto badmove; if (dot->flags & F_DOT_HOLD) goto badmove; for (dx = -1; dx <= 1; dx++) { for (dy = -1; dy <= 1; dy++) { sp = &GRID(ret, grid, x+dx, y+dy); if (sp->type != s_tile) continue; if (sp->flags & F_TILE_ASSOC) { space *dot = &SPACE(state, sp->dotx, sp->doty); if (dot->flags & F_DOT_HOLD) continue; } add_assoc(state, sp, dot); } } move += n; #ifdef EDITOR } else if (c == 'C') { move++; clear_game(ret, 1); #endif } else if (c == 'S') { move++; ret->used_solve = 1; } else goto badmove; if (*move == ';') move++; else if (*move) goto badmove; } if (check_complete(ret, NULL, NULL)) ret->completed = 1; return ret; badmove: free_game(ret); return NULL; } /* ---------------------------------------------------------------------- * Drawing routines. */ /* Lines will be much smaller size than squares; say, 1/8 the size? * * Need a 'top-left corner of location XxY' to take this into account; * alternaticaly, that could give the middle of that location, and the * drawing code would just know the expected dimensions. * * We also need something to take a click and work out what it was * we were interested in. Clicking on vertices is required because * we may want to drag from them, for example. */ static void game_compute_size(const game_params *params, int sz, int *x, int *y) { struct { int tilesize, w, h; } ads, *ds = &ads; ds->tilesize = sz; ds->w = params->w; ds->h = params->h; *x = DRAW_WIDTH; *y = DRAW_HEIGHT; } static void game_set_size(drawing *dr, game_drawstate *ds, const game_params *params, int sz) { ds->tilesize = sz; assert(TILE_SIZE > 0); assert(!ds->bl); ds->bl = blitter_new(dr, TILE_SIZE, TILE_SIZE); assert(!ds->cur_bl); ds->cur_bl = blitter_new(dr, TILE_SIZE, TILE_SIZE); } static float *game_colours(frontend *fe, int *ncolours) { float *ret = snewn(3 * NCOLOURS, float); int i; /* * We call game_mkhighlight to ensure the background colour * isn't completely white. We don't actually use the high- and * lowlight colours it generates. */ game_mkhighlight(fe, ret, COL_BACKGROUND, COL_WHITEBG, COL_BLACKBG); for (i = 0; i < 3; i++) { /* * Currently, white dots and white-background squares are * both pure white. */ ret[COL_WHITEDOT * 3 + i] = 1.0F; ret[COL_WHITEBG * 3 + i] = 1.0F; /* * But black-background squares are a dark grey, whereas * black dots are really black. */ ret[COL_BLACKDOT * 3 + i] = 0.0F; ret[COL_BLACKBG * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.3F; /* * In unfilled squares, we draw a faint gridwork. */ ret[COL_GRID * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.8F; /* * Edges and arrows are filled in in pure black. */ ret[COL_EDGE * 3 + i] = 0.0F; ret[COL_ARROW * 3 + i] = 0.0F; } #ifdef EDITOR /* tinge the edit background to bluey */ ret[COL_BACKGROUND * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.8F; ret[COL_BACKGROUND * 3 + 1] = ret[COL_BACKGROUND * 3 + 0] * 0.8F; ret[COL_BACKGROUND * 3 + 2] = min(ret[COL_BACKGROUND * 3 + 0] * 1.4F, 1.0F); #endif ret[COL_CURSOR * 3 + 0] = min(ret[COL_BACKGROUND * 3 + 0] * 1.4F, 1.0F); ret[COL_CURSOR * 3 + 1] = ret[COL_BACKGROUND * 3 + 0] * 0.8F; ret[COL_CURSOR * 3 + 2] = ret[COL_BACKGROUND * 3 + 0] * 0.8F; *ncolours = NCOLOURS; return ret; } static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { struct game_drawstate *ds = snew(struct game_drawstate); int i; ds->started = 0; ds->w = state->w; ds->h = state->h; ds->grid = snewn(ds->w*ds->h, unsigned long); for (i = 0; i < ds->w*ds->h; i++) ds->grid[i] = 0xFFFFFFFFUL; ds->dx = snewn(ds->w*ds->h, int); ds->dy = snewn(ds->w*ds->h, int); ds->bl = NULL; ds->dragging = FALSE; ds->dragx = ds->dragy = 0; ds->colour_scratch = snewn(ds->w * ds->h, int); ds->cur_bl = NULL; ds->cx = ds->cy = 0; ds->cur_visible = 0; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { if (ds->cur_bl) blitter_free(dr, ds->cur_bl); sfree(ds->colour_scratch); if (ds->bl) blitter_free(dr, ds->bl); sfree(ds->dx); sfree(ds->dy); sfree(ds->grid); sfree(ds); } #define DRAW_EDGE_L 0x0001 #define DRAW_EDGE_R 0x0002 #define DRAW_EDGE_U 0x0004 #define DRAW_EDGE_D 0x0008 #define DRAW_CORNER_UL 0x0010 #define DRAW_CORNER_UR 0x0020 #define DRAW_CORNER_DL 0x0040 #define DRAW_CORNER_DR 0x0080 #define DRAW_WHITE 0x0100 #define DRAW_BLACK 0x0200 #define DRAW_ARROW 0x0400 #define DRAW_CURSOR 0x0800 #define DOT_SHIFT_C 12 #define DOT_SHIFT_M 2 #define DOT_WHITE 1UL #define DOT_BLACK 2UL /* * Draw an arrow centred on (cx,cy), pointing in the direction * (ddx,ddy). (I.e. pointing at the point (cx+ddx, cy+ddy). */ static void draw_arrow(drawing *dr, game_drawstate *ds, int cx, int cy, int ddx, int ddy, int col) { float vlen = (float)sqrt(ddx*ddx+ddy*ddy); float xdx = ddx/vlen, xdy = ddy/vlen; float ydx = -xdy, ydy = xdx; int e1x = cx + (int)(xdx*TILE_SIZE/3), e1y = cy + (int)(xdy*TILE_SIZE/3); int e2x = cx - (int)(xdx*TILE_SIZE/3), e2y = cy - (int)(xdy*TILE_SIZE/3); int adx = (int)((ydx-xdx)*TILE_SIZE/8), ady = (int)((ydy-xdy)*TILE_SIZE/8); int adx2 = (int)((-ydx-xdx)*TILE_SIZE/8), ady2 = (int)((-ydy-xdy)*TILE_SIZE/8); draw_line(dr, e1x, e1y, e2x, e2y, col); draw_line(dr, e1x, e1y, e1x+adx, e1y+ady, col); draw_line(dr, e1x, e1y, e1x+adx2, e1y+ady2, col); } static void draw_square(drawing *dr, game_drawstate *ds, int x, int y, unsigned long flags, int ddx, int ddy) { int lx = COORD(x), ly = COORD(y); int dx, dy; int gridcol; clip(dr, lx, ly, TILE_SIZE, TILE_SIZE); /* * Draw the tile background. */ draw_rect(dr, lx, ly, TILE_SIZE, TILE_SIZE, (flags & DRAW_WHITE ? COL_WHITEBG : flags & DRAW_BLACK ? COL_BLACKBG : COL_BACKGROUND)); /* * Draw the grid. */ gridcol = (flags & DRAW_BLACK ? COL_BLACKDOT : COL_GRID); draw_rect(dr, lx, ly, 1, TILE_SIZE, gridcol); draw_rect(dr, lx, ly, TILE_SIZE, 1, gridcol); /* * Draw the arrow, if present, or the cursor, if here. */ if (flags & DRAW_ARROW) draw_arrow(dr, ds, lx + TILE_SIZE/2, ly + TILE_SIZE/2, ddx, ddy, (flags & DRAW_CURSOR) ? COL_CURSOR : COL_ARROW); else if (flags & DRAW_CURSOR) draw_rect_outline(dr, lx + TILE_SIZE/2 - CURSOR_SIZE, ly + TILE_SIZE/2 - CURSOR_SIZE, 2*CURSOR_SIZE+1, 2*CURSOR_SIZE+1, COL_CURSOR); /* * Draw the edges. */ if (flags & DRAW_EDGE_L) draw_rect(dr, lx, ly, EDGE_THICKNESS, TILE_SIZE, COL_EDGE); if (flags & DRAW_EDGE_R) draw_rect(dr, lx + TILE_SIZE - EDGE_THICKNESS + 1, ly, EDGE_THICKNESS - 1, TILE_SIZE, COL_EDGE); if (flags & DRAW_EDGE_U) draw_rect(dr, lx, ly, TILE_SIZE, EDGE_THICKNESS, COL_EDGE); if (flags & DRAW_EDGE_D) draw_rect(dr, lx, ly + TILE_SIZE - EDGE_THICKNESS + 1, TILE_SIZE, EDGE_THICKNESS - 1, COL_EDGE); if (flags & DRAW_CORNER_UL) draw_rect(dr, lx, ly, EDGE_THICKNESS, EDGE_THICKNESS, COL_EDGE); if (flags & DRAW_CORNER_UR) draw_rect(dr, lx + TILE_SIZE - EDGE_THICKNESS + 1, ly, EDGE_THICKNESS - 1, EDGE_THICKNESS, COL_EDGE); if (flags & DRAW_CORNER_DL) draw_rect(dr, lx, ly + TILE_SIZE - EDGE_THICKNESS + 1, EDGE_THICKNESS, EDGE_THICKNESS - 1, COL_EDGE); if (flags & DRAW_CORNER_DR) draw_rect(dr, lx + TILE_SIZE - EDGE_THICKNESS + 1, ly + TILE_SIZE - EDGE_THICKNESS + 1, EDGE_THICKNESS - 1, EDGE_THICKNESS - 1, COL_EDGE); /* * Draw the dots. */ for (dy = 0; dy < 3; dy++) for (dx = 0; dx < 3; dx++) { int dotval = (flags >> (DOT_SHIFT_C + DOT_SHIFT_M*(dy*3+dx))); dotval &= (1 << DOT_SHIFT_M)-1; if (dotval) draw_circle(dr, lx+dx*TILE_SIZE/2, ly+dy*TILE_SIZE/2, DOT_SIZE, (dotval == 1 ? COL_WHITEDOT : COL_BLACKDOT), COL_BLACKDOT); } unclip(dr); draw_update(dr, lx, ly, TILE_SIZE, TILE_SIZE); } 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 = ds->w, h = ds->h; int x, y, flashing = FALSE; if (flashtime > 0) { int frame = (int)(flashtime / FLASH_TIME); flashing = (frame % 2 == 0); } if (ds->dragging) { assert(ds->bl); blitter_load(dr, ds->bl, ds->dragx, ds->dragy); draw_update(dr, ds->dragx, ds->dragy, TILE_SIZE, TILE_SIZE); ds->dragging = FALSE; } if (ds->cur_visible) { assert(ds->cur_bl); blitter_load(dr, ds->cur_bl, ds->cx, ds->cy); draw_update(dr, ds->cx, ds->cy, CURSOR_SIZE*2+1, CURSOR_SIZE*2+1); ds->cur_visible = FALSE; } if (!ds->started) { draw_rect(dr, 0, 0, DRAW_WIDTH, DRAW_HEIGHT, COL_BACKGROUND); draw_rect(dr, BORDER - EDGE_THICKNESS + 1, BORDER - EDGE_THICKNESS + 1, w*TILE_SIZE + EDGE_THICKNESS*2 - 1, h*TILE_SIZE + EDGE_THICKNESS*2 - 1, COL_EDGE); draw_update(dr, 0, 0, DRAW_WIDTH, DRAW_HEIGHT); ds->started = TRUE; } check_complete(state, NULL, ds->colour_scratch); for (y = 0; y < h; y++) for (x = 0; x < w; x++) { unsigned long flags = 0; int ddx = 0, ddy = 0; space *sp; int dx, dy; /* * Set up the flags for this square. Firstly, see if we * have edges. */ if (SPACE(state, x*2, y*2+1).flags & F_EDGE_SET) flags |= DRAW_EDGE_L; if (SPACE(state, x*2+2, y*2+1).flags & F_EDGE_SET) flags |= DRAW_EDGE_R; if (SPACE(state, x*2+1, y*2).flags & F_EDGE_SET) flags |= DRAW_EDGE_U; if (SPACE(state, x*2+1, y*2+2).flags & F_EDGE_SET) flags |= DRAW_EDGE_D; /* * Also, mark corners of neighbouring edges. */ if ((x > 0 && SPACE(state, x*2-1, y*2).flags & F_EDGE_SET) || (y > 0 && SPACE(state, x*2, y*2-1).flags & F_EDGE_SET)) flags |= DRAW_CORNER_UL; if ((x+1 < w && SPACE(state, x*2+3, y*2).flags & F_EDGE_SET) || (y > 0 && SPACE(state, x*2+2, y*2-1).flags & F_EDGE_SET)) flags |= DRAW_CORNER_UR; if ((x > 0 && SPACE(state, x*2-1, y*2+2).flags & F_EDGE_SET) || (y+1 < h && SPACE(state, x*2, y*2+3).flags & F_EDGE_SET)) flags |= DRAW_CORNER_DL; if ((x+1 < w && SPACE(state, x*2+3, y*2+2).flags & F_EDGE_SET) || (y+1 < h && SPACE(state, x*2+2, y*2+3).flags & F_EDGE_SET)) flags |= DRAW_CORNER_DR; /* * If this square is part of a valid region, paint it * that region's colour. Exception: if we're flashing, * everything goes briefly back to background colour. */ sp = &SPACE(state, x*2+1, y*2+1); if (ds->colour_scratch[y*w+x] && !flashing) { flags |= (ds->colour_scratch[y*w+x] == 2 ? DRAW_BLACK : DRAW_WHITE); } /* * If this square is associated with a dot but it isn't * part of a valid region, draw an arrow in it pointing * in the direction of that dot. * * Exception: if this is the source point of an active * drag, we don't draw the arrow. */ if ((sp->flags & F_TILE_ASSOC) && !ds->colour_scratch[y*w+x]) { if (ui->dragging && ui->srcx == x*2+1 && ui->srcy == y*2+1) { /* don't do it */ } else if (sp->doty != y*2+1 || sp->dotx != x*2+1) { flags |= DRAW_ARROW; ddy = sp->doty - (y*2+1); ddx = sp->dotx - (x*2+1); } } /* * Now go through the nine possible places we could * have dots. */ for (dy = 0; dy < 3; dy++) for (dx = 0; dx < 3; dx++) { sp = &SPACE(state, x*2+dx, y*2+dy); if (sp->flags & F_DOT) { unsigned long dotval = (sp->flags & F_DOT_BLACK ? DOT_BLACK : DOT_WHITE); flags |= dotval << (DOT_SHIFT_C + DOT_SHIFT_M*(dy*3+dx)); } } /* * Now work out if we have to draw a cursor for this square; * cursors-on-lines are taken care of below. */ if (ui->cur_visible && ui->cur_x == x*2+1 && ui->cur_y == y*2+1 && !(SPACE(state, x*2+1, y*2+1).flags & F_DOT)) flags |= DRAW_CURSOR; /* * Now we have everything we're going to need. Draw the * square. */ if (ds->grid[y*w+x] != flags || ds->dx[y*w+x] != ddx || ds->dy[y*w+x] != ddy) { draw_square(dr, ds, x, y, flags, ddx, ddy); ds->grid[y*w+x] = flags; ds->dx[y*w+x] = ddx; ds->dy[y*w+x] = ddy; } } /* * Draw a cursor. This secondary blitter is much less invasive than trying * to fix up all of the rest of the code with sufficient flags to be able to * display this sensibly. */ if (ui->cur_visible) { space *sp = &SPACE(state, ui->cur_x, ui->cur_y); ds->cur_visible = TRUE; ds->cx = SCOORD(ui->cur_x) - CURSOR_SIZE; ds->cy = SCOORD(ui->cur_y) - CURSOR_SIZE; blitter_save(dr, ds->cur_bl, ds->cx, ds->cy); if (sp->flags & F_DOT) { /* draw a red dot (over the top of whatever would be there already) */ draw_circle(dr, SCOORD(ui->cur_x), SCOORD(ui->cur_y), DOT_SIZE, COL_CURSOR, COL_BLACKDOT); } else if (sp->type != s_tile) { /* draw an edge/vertex square; tile cursors are dealt with above. */ int dx = (ui->cur_x % 2) ? CURSOR_SIZE : CURSOR_SIZE/3; int dy = (ui->cur_y % 2) ? CURSOR_SIZE : CURSOR_SIZE/3; int x1 = SCOORD(ui->cur_x)-dx, y1 = SCOORD(ui->cur_y)-dy; int xs = dx*2+1, ys = dy*2+1; draw_rect(dr, x1, y1, xs, ys, COL_CURSOR); } draw_update(dr, ds->cx, ds->cy, CURSOR_SIZE*2+1, CURSOR_SIZE*2+1); } if (ui->dragging) { ds->dragging = TRUE; ds->dragx = ui->dx - TILE_SIZE/2; ds->dragy = ui->dy - TILE_SIZE/2; blitter_save(dr, ds->bl, ds->dragx, ds->dragy); draw_arrow(dr, ds, ui->dx, ui->dy, SCOORD(ui->dotx) - ui->dx, SCOORD(ui->doty) - ui->dy, COL_ARROW); } #ifdef EDITOR { char buf[256]; if (state->cdiff != -1) sprintf(buf, "Puzzle is %s.", galaxies_diffnames[state->cdiff]); else buf[0] = '\0'; status_bar(dr, buf); } #endif } 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) && !(newstate->used_solve)) return 3 * FLASH_TIME; else return 0.0F; } static int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int game_timing_state(const game_state *state, game_ui *ui) { return TRUE; } #ifndef EDITOR static void game_print_size(const game_params *params, float *x, float *y) { int pw, ph; /* * 8mm squares by default. (There isn't all that much detail * that needs to go in each square.) */ game_compute_size(params, 800, &pw, &ph); *x = pw / 100.0F; *y = ph / 100.0F; } static void game_print(drawing *dr, const game_state *state, int sz) { int w = state->w, h = state->h; int white, black, blackish; int x, y, i, j; int *colours, *dsf; int *coords = NULL; int ncoords = 0, coordsize = 0; /* Ick: fake up `ds->tilesize' for macro expansion purposes */ game_drawstate ads, *ds = &ads; ds->tilesize = sz; white = print_mono_colour(dr, 1); black = print_mono_colour(dr, 0); blackish = print_hatched_colour(dr, HATCH_X); /* * Get the completion information. */ dsf = snewn(w * h, int); colours = snewn(w * h, int); check_complete(state, dsf, colours); /* * Draw the grid. */ print_line_width(dr, TILE_SIZE / 64); for (x = 1; x < w; x++) draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), black); for (y = 1; y < h; y++) draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), black); /* * Shade the completed regions. Just in case any particular * printing platform deals badly with adjacent * similarly-hatched regions, we'll fill each one as a single * polygon. */ for (i = 0; i < w*h; i++) { j = dsf_canonify(dsf, i); if (colours[j] != 0) { int dx, dy, t; /* * This is the first square we've run into belonging to * this polyomino, which means an edge of the polyomino * is certain to be to our left. (After we finish * tracing round it, we'll set the colours[] entry to * zero to prevent accidentally doing it again.) */ x = i % w; y = i / w; dx = -1; dy = 0; ncoords = 0; while (1) { /* * We are currently sitting on square (x,y), which * we know to be in our polyomino, and we also know * that (x+dx,y+dy) is not. The way I visualise * this is that we're standing to the right of a * boundary line, stretching our left arm out to * point to the exterior square on the far side. */ /* * First, check if we've gone round the entire * polyomino. */ if (ncoords > 0 && (x == i%w && y == i/w && dx == -1 && dy == 0)) break; /* * Add to our coordinate list the coordinate * backwards and to the left of where we are. */ if (ncoords + 2 > coordsize) { coordsize = (ncoords * 3 / 2) + 64; coords = sresize(coords, coordsize, int); } coords[ncoords++] = COORD((2*x+1 + dx + dy) / 2); coords[ncoords++] = COORD((2*y+1 + dy - dx) / 2); /* * Follow the edge round. If the square directly in * front of us is not part of the polyomino, we * turn right; if it is and so is the square in * front of (x+dx,y+dy), we turn left; otherwise we * go straight on. */ if (x-dy < 0 || x-dy >= w || y+dx < 0 || y+dx >= h || dsf_canonify(dsf, (y+dx)*w+(x-dy)) != j) { /* Turn right. */ t = dx; dx = -dy; dy = t; } else if (x+dx-dy >= 0 && x+dx-dy < w && y+dy+dx >= 0 && y+dy+dx < h && dsf_canonify(dsf, (y+dy+dx)*w+(x+dx-dy)) == j) { /* Turn left. */ x += dx; y += dy; t = dx; dx = dy; dy = -t; x -= dx; y -= dy; } else { /* Straight on. */ x -= dy; y += dx; } } /* * Now we have our polygon complete, so fill it. */ draw_polygon(dr, coords, ncoords/2, colours[j] == 2 ? blackish : -1, black); /* * And mark this polyomino as done. */ colours[j] = 0; } } /* * Draw the edges. */ for (y = 0; y <= h; y++) for (x = 0; x <= w; x++) { if (x < w && SPACE(state, x*2+1, y*2).flags & F_EDGE_SET) draw_rect(dr, COORD(x)-EDGE_THICKNESS, COORD(y)-EDGE_THICKNESS, EDGE_THICKNESS * 2 + TILE_SIZE, EDGE_THICKNESS * 2, black); if (y < h && SPACE(state, x*2, y*2+1).flags & F_EDGE_SET) draw_rect(dr, COORD(x)-EDGE_THICKNESS, COORD(y)-EDGE_THICKNESS, EDGE_THICKNESS * 2, EDGE_THICKNESS * 2 + TILE_SIZE, black); } /* * Draw the dots. */ for (y = 0; y <= 2*h; y++) for (x = 0; x <= 2*w; x++) if (SPACE(state, x, y).flags & F_DOT) { draw_circle(dr, (int)COORD(x/2.0), (int)COORD(y/2.0), DOT_SIZE, (SPACE(state, x, y).flags & F_DOT_BLACK ? black : white), black); } sfree(dsf); sfree(colours); sfree(coords); } #endif #ifdef COMBINED #define thegame galaxies #endif const struct game thegame = { "Galaxies", "games.galaxies", "galaxies", default_params, game_fetch_preset, 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, #ifdef EDITOR FALSE, NULL, #else TRUE, solve_game, #endif TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, decode_ui, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, #ifdef EDITOR FALSE, FALSE, NULL, NULL, TRUE, /* wants_statusbar */ #else TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ #endif FALSE, game_timing_state, REQUIRE_RBUTTON, /* flags */ }; #ifdef STANDALONE_SOLVER const char *quis; #include static void usage_exit(const char *msg) { if (msg) fprintf(stderr, "%s: %s\n", quis, msg); fprintf(stderr, "Usage: %s [--seed SEED] --soak | [game_id [game_id ...]]\n", quis); exit(1); } static void dump_state(game_state *state) { char *temp = game_text_format(state); printf("%s\n", temp); sfree(temp); } static int gen(game_params *p, random_state *rs, int debug) { char *desc; int diff; game_state *state; #ifndef DEBUGGING solver_show_working = debug; #endif printf("Generating a %dx%d %s puzzle.\n", p->w, p->h, galaxies_diffnames[p->diff]); desc = new_game_desc(p, rs, NULL, 0); state = new_game(NULL, p, desc); dump_state(state); diff = solver_state(state, DIFF_UNREASONABLE); printf("Generated %s game %dx%d:%s\n", galaxies_diffnames[diff], p->w, p->h, desc); dump_state(state); free_game(state); sfree(desc); return diff; } static void soak(game_params *p, random_state *rs) { time_t tt_start, tt_now, tt_last; char *desc; game_state *st; int diff, n = 0, i, diffs[DIFF_MAX], ndots = 0, nspaces = 0; #ifndef DEBUGGING solver_show_working = 0; #endif tt_start = tt_now = time(NULL); for (i = 0; i < DIFF_MAX; i++) diffs[i] = 0; maxtries = 1; printf("Soak-generating a %dx%d grid, max. diff %s.\n", p->w, p->h, galaxies_diffnames[p->diff]); printf(" ["); for (i = 0; i < DIFF_MAX; i++) printf("%s%s", (i == 0) ? "" : ", ", galaxies_diffnames[i]); printf("]\n"); while (1) { desc = new_game_desc(p, rs, NULL, 0); st = new_game(NULL, p, desc); diff = solver_state(st, p->diff); nspaces += st->w*st->h; for (i = 0; i < st->sx*st->sy; i++) if (st->grid[i].flags & F_DOT) ndots++; free_game(st); sfree(desc); diffs[diff]++; n++; tt_last = time(NULL); if (tt_last > tt_now) { tt_now = tt_last; printf("%d total, %3.1f/s, [", n, (double)n / ((double)tt_now - tt_start)); for (i = 0; i < DIFF_MAX; i++) printf("%s%.1f%%", (i == 0) ? "" : ", ", 100.0 * ((double)diffs[i] / (double)n)); printf("], %.1f%% dots\n", 100.0 * ((double)ndots / (double)nspaces)); } } } int main(int argc, char **argv) { game_params *p; char *id = NULL, *desc, *err; game_state *s; int diff, do_soak = 0, verbose = 0; random_state *rs; time_t seed = time(NULL); quis = argv[0]; while (--argc > 0) { char *p = *++argv; if (!strcmp(p, "-v")) { verbose = 1; } else if (!strcmp(p, "--seed")) { if (argc == 0) usage_exit("--seed needs an argument"); seed = (time_t)atoi(*++argv); argc--; } else if (!strcmp(p, "--soak")) { do_soak = 1; } else if (*p == '-') { usage_exit("unrecognised option"); } else { id = p; } } maxtries = 50; p = default_params(); rs = random_new((void*)&seed, sizeof(time_t)); if (do_soak) { if (!id) usage_exit("need one argument for --soak"); decode_params(p, *argv); soak(p, rs); return 0; } if (!id) { while (1) { p->w = random_upto(rs, 15) + 3; p->h = random_upto(rs, 15) + 3; p->diff = random_upto(rs, DIFF_UNREASONABLE); diff = gen(p, rs, 0); } return 0; } desc = strchr(id, ':'); if (!desc) { decode_params(p, id); gen(p, rs, verbose); } else { #ifndef DEBUGGING solver_show_working = 1; #endif *desc++ = '\0'; decode_params(p, id); err = validate_desc(p, desc); if (err) { fprintf(stderr, "%s: %s\n", argv[0], err); exit(1); } s = new_game(NULL, p, desc); diff = solver_state(s, DIFF_UNREASONABLE); dump_state(s); printf("Puzzle is %s.\n", galaxies_diffnames[diff]); free_game(s); } free_params(p); return 0; } #endif #ifdef STANDALONE_PICTURE_GENERATOR /* * Main program for the standalone picture generator. To use it, * simply provide it with an XBM-format bitmap file (note XBM, not * XPM) on standard input, and it will output a game ID in return. * For example: * * $ ./galaxiespicture < badly-drawn-cat.xbm * 11x11:eloMBLzFeEzLNMWifhaWYdDbixCymBbBMLoDdewGg * * If you want a puzzle with a non-standard difficulty level, pass * a partial parameters string as a command-line argument (e.g. * `./galaxiespicture du < foo.xbm', where `du' is the same suffix * which if it appeared in a random-seed game ID would set the * difficulty level to Unreasonable). However, be aware that if the * generator fails to produce an adequately difficult puzzle too * many times then it will give up and return an easier one (just * as it does during normal GUI play). To be sure you really have * the difficulty you asked for, use galaxiessolver to * double-check. * * (Perhaps I ought to include an option to make this standalone * generator carry on looping until it really does get the right * difficulty. Hmmm.) */ #include int main(int argc, char **argv) { game_params *par; char *params, *desc; random_state *rs; time_t seed = time(NULL); char buf[4096]; int i; int x, y; par = default_params(); if (argc > 1) decode_params(par, argv[1]); /* get difficulty */ par->w = par->h = -1; /* * Now read an XBM file from standard input. This is simple and * hacky and will do very little error detection, so don't feed * it bogus data. */ picture = NULL; x = y = 0; while (fgets(buf, sizeof(buf), stdin)) { buf[strcspn(buf, "\r\n")] = '\0'; if (!strncmp(buf, "#define", 7)) { /* * Lines starting `#define' give the width and height. */ char *num = buf + strlen(buf); char *symend; while (num > buf && isdigit((unsigned char)num[-1])) num--; symend = num; while (symend > buf && isspace((unsigned char)symend[-1])) symend--; if (symend-5 >= buf && !strncmp(symend-5, "width", 5)) par->w = atoi(num); else if (symend-6 >= buf && !strncmp(symend-6, "height", 6)) par->h = atoi(num); } else { /* * Otherwise, break the string up into words and take * any word of the form `0x' plus hex digits to be a * byte. */ char *p, *wordstart; if (!picture) { if (par->w < 0 || par->h < 0) { printf("failed to read width and height\n"); return 1; } picture = snewn(par->w * par->h, int); for (i = 0; i < par->w * par->h; i++) picture[i] = -1; } p = buf; while (*p) { while (*p && (*p == ',' || isspace((unsigned char)*p))) p++; wordstart = p; while (*p && !(*p == ',' || *p == '}' || isspace((unsigned char)*p))) p++; if (*p) *p++ = '\0'; if (wordstart[0] == '0' && (wordstart[1] == 'x' || wordstart[1] == 'X') && !wordstart[2 + strspn(wordstart+2, "0123456789abcdefABCDEF")]) { unsigned long byte = strtoul(wordstart+2, NULL, 16); for (i = 0; i < 8; i++) { int bit = (byte >> i) & 1; if (y < par->h && x < par->w) picture[y * par->w + x] = bit; x++; } if (x >= par->w) { x = 0; y++; } } } } } for (i = 0; i < par->w * par->h; i++) if (picture[i] < 0) { fprintf(stderr, "failed to read enough bitmap data\n"); return 1; } rs = random_new((void*)&seed, sizeof(time_t)); desc = new_game_desc(par, rs, NULL, FALSE); params = encode_params(par, FALSE); printf("%s:%s\n", params, desc); sfree(desc); sfree(params); free_params(par); random_free(rs); return 0; } #endif /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/grid.c0000644000175300017530000031550512143232260013522 0ustar simonsimon/* * (c) Lambros Lambrou 2008 * * Code for working with general grids, which can be any planar graph * with faces, edges and vertices (dots). Includes generators for a few * types of grid, including square, hexagonal, triangular and others. */ #include #include #include #include #include #include #include #include "puzzles.h" #include "tree234.h" #include "grid.h" #include "penrose.h" /* Debugging options */ /* #define DEBUG_GRID */ /* ---------------------------------------------------------------------- * Deallocate or dereference a grid */ void grid_free(grid *g) { assert(g->refcount); g->refcount--; if (g->refcount == 0) { int i; for (i = 0; i < g->num_faces; i++) { sfree(g->faces[i].dots); sfree(g->faces[i].edges); } for (i = 0; i < g->num_dots; i++) { sfree(g->dots[i].faces); sfree(g->dots[i].edges); } sfree(g->faces); sfree(g->edges); sfree(g->dots); sfree(g); } } /* Used by the other grid generators. Create a brand new grid with nothing * initialised (all lists are NULL) */ static grid *grid_empty(void) { grid *g = snew(grid); g->faces = NULL; g->edges = NULL; g->dots = NULL; g->num_faces = g->num_edges = g->num_dots = 0; g->refcount = 1; g->lowest_x = g->lowest_y = g->highest_x = g->highest_y = 0; return g; } /* Helper function to calculate perpendicular distance from * a point P to a line AB. A and B mustn't be equal here. * * Well-known formula for area A of a triangle: * / 1 1 1 \ * 2A = determinant of matrix | px ax bx | * \ py ay by / * * Also well-known: 2A = base * height * = perpendicular distance * line-length. * * Combining gives: distance = determinant / line-length(a,b) */ static double point_line_distance(long px, long py, long ax, long ay, long bx, long by) { long det = ax*by - bx*ay + bx*py - px*by + px*ay - ax*py; double len; det = max(det, -det); len = sqrt(SQ(ax - bx) + SQ(ay - by)); return det / len; } /* Determine nearest edge to where the user clicked. * (x, y) is the clicked location, converted to grid coordinates. * Returns the nearest edge, or NULL if no edge is reasonably * near the position. * * Just judging edges by perpendicular distance is not quite right - * the edge might be "off to one side". So we insist that the triangle * with (x,y) has acute angles at the edge's dots. * * edge1 * *---------*------ * | * | *(x,y) * edge2 | * | edge2 is OK, but edge1 is not, even though * | edge1 is perpendicularly closer to (x,y) * * * */ grid_edge *grid_nearest_edge(grid *g, int x, int y) { grid_edge *best_edge; double best_distance = 0; int i; best_edge = NULL; for (i = 0; i < g->num_edges; i++) { grid_edge *e = &g->edges[i]; long e2; /* squared length of edge */ long a2, b2; /* squared lengths of other sides */ double dist; /* See if edge e is eligible - the triangle must have acute angles * at the edge's dots. * Pythagoras formula h^2 = a^2 + b^2 detects right-angles, * so detect acute angles by testing for h^2 < a^2 + b^2 */ e2 = SQ((long)e->dot1->x - (long)e->dot2->x) + SQ((long)e->dot1->y - (long)e->dot2->y); a2 = SQ((long)e->dot1->x - (long)x) + SQ((long)e->dot1->y - (long)y); b2 = SQ((long)e->dot2->x - (long)x) + SQ((long)e->dot2->y - (long)y); if (a2 >= e2 + b2) continue; if (b2 >= e2 + a2) continue; /* e is eligible so far. Now check the edge is reasonably close * to where the user clicked. Don't want to toggle an edge if the * click was way off the grid. * There is room for experimentation here. We could check the * perpendicular distance is within a certain fraction of the length * of the edge. That amounts to testing a rectangular region around * the edge. * Alternatively, we could check that the angle at the point is obtuse. * That would amount to testing a circular region with the edge as * diameter. */ dist = point_line_distance((long)x, (long)y, (long)e->dot1->x, (long)e->dot1->y, (long)e->dot2->x, (long)e->dot2->y); /* Is dist more than half edge length ? */ if (4 * SQ(dist) > e2) continue; if (best_edge == NULL || dist < best_distance) { best_edge = e; best_distance = dist; } } return best_edge; } /* ---------------------------------------------------------------------- * Grid generation */ #ifdef SVG_GRID #define SVG_DOTS 1 #define SVG_EDGES 2 #define SVG_FACES 4 #define FACE_COLOUR "red" #define EDGE_COLOUR "blue" #define DOT_COLOUR "black" static void grid_output_svg(FILE *fp, grid *g, int which) { int i, j; fprintf(fp,"\ \n\ \n\ \n\ \n\n"); if (which & SVG_FACES) { fprintf(fp, "\n"); for (i = 0; i < g->num_faces; i++) { grid_face *f = g->faces + i; fprintf(fp, "order; j++) { grid_dot *d = f->dots[j]; fprintf(fp, "%s%d,%d", (j == 0) ? "" : " ", d->x, d->y); } fprintf(fp, "\" style=\"fill: %s; fill-opacity: 0.2; stroke: %s\" />\n", FACE_COLOUR, FACE_COLOUR); } fprintf(fp, "\n"); } if (which & SVG_EDGES) { fprintf(fp, "\n"); for (i = 0; i < g->num_edges; i++) { grid_edge *e = g->edges + i; grid_dot *d1 = e->dot1, *d2 = e->dot2; fprintf(fp, "\n", d1->x, d1->y, d2->x, d2->y, EDGE_COLOUR); } fprintf(fp, "\n"); } if (which & SVG_DOTS) { fprintf(fp, "\n"); for (i = 0; i < g->num_dots; i++) { grid_dot *d = g->dots + i; fprintf(fp, "", d->x, d->y, g->tilesize/20, g->tilesize/20, DOT_COLOUR); } fprintf(fp, "\n"); } fprintf(fp, "\n"); } #endif #ifdef SVG_GRID #include static void grid_try_svg(grid *g, int which) { char *svg = getenv("PUZZLES_SVG_GRID"); if (svg) { FILE *svgf = fopen(svg, "w"); if (svgf) { grid_output_svg(svgf, g, which); fclose(svgf); } else { fprintf(stderr, "Unable to open file `%s': %s", svg, strerror(errno)); } } } #endif /* Show the basic grid information, before doing grid_make_consistent */ static void grid_debug_basic(grid *g) { /* TODO: Maybe we should generate an SVG image of the dots and lines * of the grid here, before grid_make_consistent. * Would help with debugging grid generation. */ #ifdef DEBUG_GRID int i; printf("--- Basic Grid Data ---\n"); for (i = 0; i < g->num_faces; i++) { grid_face *f = g->faces + i; printf("Face %d: dots[", i); int j; for (j = 0; j < f->order; j++) { grid_dot *d = f->dots[j]; printf("%s%d", j ? "," : "", (int)(d - g->dots)); } printf("]\n"); } #endif #ifdef SVG_GRID grid_try_svg(g, SVG_FACES); #endif } /* Show the derived grid information, computed by grid_make_consistent */ static void grid_debug_derived(grid *g) { #ifdef DEBUG_GRID /* edges */ int i; printf("--- Derived Grid Data ---\n"); for (i = 0; i < g->num_edges; i++) { grid_edge *e = g->edges + i; printf("Edge %d: dots[%d,%d] faces[%d,%d]\n", i, (int)(e->dot1 - g->dots), (int)(e->dot2 - g->dots), e->face1 ? (int)(e->face1 - g->faces) : -1, e->face2 ? (int)(e->face2 - g->faces) : -1); } /* faces */ for (i = 0; i < g->num_faces; i++) { grid_face *f = g->faces + i; int j; printf("Face %d: faces[", i); for (j = 0; j < f->order; j++) { grid_edge *e = f->edges[j]; grid_face *f2 = (e->face1 == f) ? e->face2 : e->face1; printf("%s%d", j ? "," : "", f2 ? (int)(f2 - g->faces) : -1); } printf("]\n"); } /* dots */ for (i = 0; i < g->num_dots; i++) { grid_dot *d = g->dots + i; int j; printf("Dot %d: dots[", i); for (j = 0; j < d->order; j++) { grid_edge *e = d->edges[j]; grid_dot *d2 = (e->dot1 == d) ? e->dot2 : e->dot1; printf("%s%d", j ? "," : "", (int)(d2 - g->dots)); } printf("] faces["); for (j = 0; j < d->order; j++) { grid_face *f = d->faces[j]; printf("%s%d", j ? "," : "", f ? (int)(f - g->faces) : -1); } printf("]\n"); } #endif #ifdef SVG_GRID grid_try_svg(g, SVG_DOTS | SVG_EDGES | SVG_FACES); #endif } /* Helper function for building incomplete-edges list in * grid_make_consistent() */ static int grid_edge_bydots_cmpfn(void *v1, void *v2) { grid_edge *a = v1; grid_edge *b = v2; grid_dot *da, *db; /* Pointer subtraction is valid here, because all dots point into the * same dot-list (g->dots). * Edges are not "normalised" - the 2 dots could be stored in any order, * so we need to take this into account when comparing edges. */ /* Compare first dots */ da = (a->dot1 < a->dot2) ? a->dot1 : a->dot2; db = (b->dot1 < b->dot2) ? b->dot1 : b->dot2; if (da != db) return db - da; /* Compare last dots */ da = (a->dot1 < a->dot2) ? a->dot2 : a->dot1; db = (b->dot1 < b->dot2) ? b->dot2 : b->dot1; if (da != db) return db - da; return 0; } /* * 'Vigorously trim' a grid, by which I mean deleting any isolated or * uninteresting faces. By which, in turn, I mean: ensure that the * grid is composed solely of faces adjacent to at least one * 'landlocked' dot (i.e. one not in contact with the infinite * exterior face), and that all those dots are in a single connected * component. * * This function operates on, and returns, a grid satisfying the * preconditions to grid_make_consistent() rather than the * postconditions. (So call it first.) */ static void grid_trim_vigorously(grid *g) { int *dotpairs, *faces, *dots; int *dsf; int i, j, k, size, newfaces, newdots; /* * First construct a matrix in which each ordered pair of dots is * mapped to the index of the face in which those dots occur in * that order. */ dotpairs = snewn(g->num_dots * g->num_dots, int); for (i = 0; i < g->num_dots; i++) for (j = 0; j < g->num_dots; j++) dotpairs[i*g->num_dots+j] = -1; for (i = 0; i < g->num_faces; i++) { grid_face *f = g->faces + i; int dot0 = f->dots[f->order-1] - g->dots; for (j = 0; j < f->order; j++) { int dot1 = f->dots[j] - g->dots; dotpairs[dot0 * g->num_dots + dot1] = i; dot0 = dot1; } } /* * Now we can identify landlocked dots: they're the ones all of * whose edges have a mirror-image counterpart in this matrix. */ dots = snewn(g->num_dots, int); for (i = 0; i < g->num_dots; i++) { dots[i] = TRUE; for (j = 0; j < g->num_dots; j++) { if ((dotpairs[i*g->num_dots+j] >= 0) ^ (dotpairs[j*g->num_dots+i] >= 0)) dots[i] = FALSE; /* non-duplicated edge: coastal dot */ } } /* * Now identify connected pairs of landlocked dots, and form a dsf * unifying them. */ dsf = snew_dsf(g->num_dots); for (i = 0; i < g->num_dots; i++) for (j = 0; j < i; j++) if (dots[i] && dots[j] && dotpairs[i*g->num_dots+j] >= 0 && dotpairs[j*g->num_dots+i] >= 0) dsf_merge(dsf, i, j); /* * Now look for the largest component. */ size = 0; j = -1; for (i = 0; i < g->num_dots; i++) { int newsize; if (dots[i] && dsf_canonify(dsf, i) == i && (newsize = dsf_size(dsf, i)) > size) { j = i; size = newsize; } } /* * Work out which faces we're going to keep (precisely those with * at least one dot in the same connected component as j) and * which dots (those required by any face we're keeping). * * At this point we reuse the 'dots' array to indicate the dots * we're keeping, rather than the ones that are landlocked. */ faces = snewn(g->num_faces, int); for (i = 0; i < g->num_faces; i++) faces[i] = 0; for (i = 0; i < g->num_dots; i++) dots[i] = 0; for (i = 0; i < g->num_faces; i++) { grid_face *f = g->faces + i; int keep = FALSE; for (k = 0; k < f->order; k++) if (dsf_canonify(dsf, f->dots[k] - g->dots) == j) keep = TRUE; if (keep) { faces[i] = TRUE; for (k = 0; k < f->order; k++) dots[f->dots[k]-g->dots] = TRUE; } } /* * Work out the new indices of those faces and dots, when we * compact the arrays containing them. */ for (i = newfaces = 0; i < g->num_faces; i++) faces[i] = (faces[i] ? newfaces++ : -1); for (i = newdots = 0; i < g->num_dots; i++) dots[i] = (dots[i] ? newdots++ : -1); /* * Free the dynamically allocated 'dots' pointer lists in faces * we're going to discard. */ for (i = 0; i < g->num_faces; i++) if (faces[i] < 0) sfree(g->faces[i].dots); /* * Go through and compact the arrays. */ for (i = 0; i < g->num_dots; i++) if (dots[i] >= 0) { grid_dot *dnew = g->dots + dots[i], *dold = g->dots + i; *dnew = *dold; /* structure copy */ } for (i = 0; i < g->num_faces; i++) if (faces[i] >= 0) { grid_face *fnew = g->faces + faces[i], *fold = g->faces + i; *fnew = *fold; /* structure copy */ for (j = 0; j < fnew->order; j++) { /* * Reindex the dots in this face. */ k = fnew->dots[j] - g->dots; fnew->dots[j] = g->dots + dots[k]; } } g->num_faces = newfaces; g->num_dots = newdots; sfree(dotpairs); sfree(dsf); sfree(dots); sfree(faces); } /* Input: grid has its dots and faces initialised: * - dots have (optionally) x and y coordinates, but no edges or faces * (pointers are NULL). * - edges not initialised at all * - faces initialised and know which dots they have (but no edges yet). The * dots around each face are assumed to be clockwise. * * Output: grid is complete and valid with all relationships defined. */ static void grid_make_consistent(grid *g) { int i; tree234 *incomplete_edges; grid_edge *next_new_edge; /* Where new edge will go into g->edges */ grid_debug_basic(g); /* ====== Stage 1 ====== * Generate edges */ /* We know how many dots and faces there are, so we can find the exact * number of edges from Euler's polyhedral formula: F + V = E + 2 . * We use "-1", not "-2" here, because Euler's formula includes the * infinite face, which we don't count. */ g->num_edges = g->num_faces + g->num_dots - 1; g->edges = snewn(g->num_edges, grid_edge); next_new_edge = g->edges; /* Iterate over faces, and over each face's dots, generating edges as we * go. As we find each new edge, we can immediately fill in the edge's * dots, but only one of the edge's faces. Later on in the iteration, we * will find the same edge again (unless it's on the border), but we will * know the other face. * For efficiency, maintain a list of the incomplete edges, sorted by * their dots. */ incomplete_edges = newtree234(grid_edge_bydots_cmpfn); for (i = 0; i < g->num_faces; i++) { grid_face *f = g->faces + i; int j; for (j = 0; j < f->order; j++) { grid_edge e; /* fake edge for searching */ grid_edge *edge_found; int j2 = j + 1; if (j2 == f->order) j2 = 0; e.dot1 = f->dots[j]; e.dot2 = f->dots[j2]; /* Use del234 instead of find234, because we always want to * remove the edge if found */ edge_found = del234(incomplete_edges, &e); if (edge_found) { /* This edge already added, so fill out missing face. * Edge is already removed from incomplete_edges. */ edge_found->face2 = f; } else { assert(next_new_edge - g->edges < g->num_edges); next_new_edge->dot1 = e.dot1; next_new_edge->dot2 = e.dot2; next_new_edge->face1 = f; next_new_edge->face2 = NULL; /* potentially infinite face */ add234(incomplete_edges, next_new_edge); ++next_new_edge; } } } freetree234(incomplete_edges); /* ====== Stage 2 ====== * For each face, build its edge list. */ /* Allocate space for each edge list. Can do this, because each face's * edge-list is the same size as its dot-list. */ for (i = 0; i < g->num_faces; i++) { grid_face *f = g->faces + i; int j; f->edges = snewn(f->order, grid_edge*); /* Preload with NULLs, to help detect potential bugs. */ for (j = 0; j < f->order; j++) f->edges[j] = NULL; } /* Iterate over each edge, and over both its faces. Add this edge to * the face's edge-list, after finding where it should go in the * sequence. */ for (i = 0; i < g->num_edges; i++) { grid_edge *e = g->edges + i; int j; for (j = 0; j < 2; j++) { grid_face *f = j ? e->face2 : e->face1; int k, k2; if (f == NULL) continue; /* Find one of the dots around the face */ for (k = 0; k < f->order; k++) { if (f->dots[k] == e->dot1) break; /* found dot1 */ } assert(k != f->order); /* Must find the dot around this face */ /* Labelling scheme: as we walk clockwise around the face, * starting at dot0 (f->dots[0]), we hit: * (dot0), edge0, dot1, edge1, dot2,... * * 0 * 0-----1 * | * |1 * | * 3-----2 * 2 * * Therefore, edgeK joins dotK and dot{K+1} */ /* Around this face, either the next dot or the previous dot * must be e->dot2. Otherwise the edge is wrong. */ k2 = k + 1; if (k2 == f->order) k2 = 0; if (f->dots[k2] == e->dot2) { /* dot1(k) and dot2(k2) go clockwise around this face, so add * this edge at position k (see diagram). */ assert(f->edges[k] == NULL); f->edges[k] = e; continue; } /* Try previous dot */ k2 = k - 1; if (k2 == -1) k2 = f->order - 1; if (f->dots[k2] == e->dot2) { /* dot1(k) and dot2(k2) go anticlockwise around this face. */ assert(f->edges[k2] == NULL); f->edges[k2] = e; continue; } assert(!"Grid broken: bad edge-face relationship"); } } /* ====== Stage 3 ====== * For each dot, build its edge-list and face-list. */ /* We don't know how many edges/faces go around each dot, so we can't * allocate the right space for these lists. Pre-compute the sizes by * iterating over each edge and recording a tally against each dot. */ for (i = 0; i < g->num_dots; i++) { g->dots[i].order = 0; } for (i = 0; i < g->num_edges; i++) { grid_edge *e = g->edges + i; ++(e->dot1->order); ++(e->dot2->order); } /* Now we have the sizes, pre-allocate the edge and face lists. */ for (i = 0; i < g->num_dots; i++) { grid_dot *d = g->dots + i; int j; assert(d->order >= 2); /* sanity check */ d->edges = snewn(d->order, grid_edge*); d->faces = snewn(d->order, grid_face*); for (j = 0; j < d->order; j++) { d->edges[j] = NULL; d->faces[j] = NULL; } } /* For each dot, need to find a face that touches it, so we can seed * the edge-face-edge-face process around each dot. */ for (i = 0; i < g->num_faces; i++) { grid_face *f = g->faces + i; int j; for (j = 0; j < f->order; j++) { grid_dot *d = f->dots[j]; d->faces[0] = f; } } /* Each dot now has a face in its first slot. Generate the remaining * faces and edges around the dot, by searching both clockwise and * anticlockwise from the first face. Need to do both directions, * because of the possibility of hitting the infinite face, which * blocks progress. But there's only one such face, so we will * succeed in finding every edge and face this way. */ for (i = 0; i < g->num_dots; i++) { grid_dot *d = g->dots + i; int current_face1 = 0; /* ascends clockwise */ int current_face2 = 0; /* descends anticlockwise */ /* Labelling scheme: as we walk clockwise around the dot, starting * at face0 (d->faces[0]), we hit: * (face0), edge0, face1, edge1, face2,... * * 0 * | * 0 | 1 * | * -----d-----1 * | * | 2 * | * 2 * * So, for example, face1 should be joined to edge0 and edge1, * and those edges should appear in an anticlockwise sense around * that face (see diagram). */ /* clockwise search */ while (TRUE) { grid_face *f = d->faces[current_face1]; grid_edge *e; int j; assert(f != NULL); /* find dot around this face */ for (j = 0; j < f->order; j++) { if (f->dots[j] == d) break; } assert(j != f->order); /* must find dot */ /* Around f, required edge is anticlockwise from the dot. See * the other labelling scheme higher up, for why we subtract 1 * from j. */ j--; if (j == -1) j = f->order - 1; e = f->edges[j]; d->edges[current_face1] = e; /* set edge */ current_face1++; if (current_face1 == d->order) break; else { /* set face */ d->faces[current_face1] = (e->face1 == f) ? e->face2 : e->face1; if (d->faces[current_face1] == NULL) break; /* cannot progress beyond infinite face */ } } /* If the clockwise search made it all the way round, don't need to * bother with the anticlockwise search. */ if (current_face1 == d->order) continue; /* this dot is complete, move on to next dot */ /* anticlockwise search */ while (TRUE) { grid_face *f = d->faces[current_face2]; grid_edge *e; int j; assert(f != NULL); /* find dot around this face */ for (j = 0; j < f->order; j++) { if (f->dots[j] == d) break; } assert(j != f->order); /* must find dot */ /* Around f, required edge is clockwise from the dot. */ e = f->edges[j]; current_face2--; if (current_face2 == -1) current_face2 = d->order - 1; d->edges[current_face2] = e; /* set edge */ /* set face */ if (current_face2 == current_face1) break; d->faces[current_face2] = (e->face1 == f) ? e->face2 : e->face1; /* There's only 1 infinite face, so we must get all the way * to current_face1 before we hit it. */ assert(d->faces[current_face2]); } } /* ====== Stage 4 ====== * Compute other grid settings */ /* Bounding rectangle */ for (i = 0; i < g->num_dots; i++) { grid_dot *d = g->dots + i; if (i == 0) { g->lowest_x = g->highest_x = d->x; g->lowest_y = g->highest_y = d->y; } else { g->lowest_x = min(g->lowest_x, d->x); g->highest_x = max(g->highest_x, d->x); g->lowest_y = min(g->lowest_y, d->y); g->highest_y = max(g->highest_y, d->y); } } grid_debug_derived(g); } /* Helpers for making grid-generation easier. These functions are only * intended for use during grid generation. */ /* Comparison function for the (tree234) sorted dot list */ static int grid_point_cmp_fn(void *v1, void *v2) { grid_dot *p1 = v1; grid_dot *p2 = v2; if (p1->y != p2->y) return p2->y - p1->y; else return p2->x - p1->x; } /* Add a new face to the grid, with its dot list allocated. * Assumes there's enough space allocated for the new face in grid->faces */ static void grid_face_add_new(grid *g, int face_size) { int i; grid_face *new_face = g->faces + g->num_faces; new_face->order = face_size; new_face->dots = snewn(face_size, grid_dot*); for (i = 0; i < face_size; i++) new_face->dots[i] = NULL; new_face->edges = NULL; new_face->has_incentre = FALSE; g->num_faces++; } /* Assumes dot list has enough space */ static grid_dot *grid_dot_add_new(grid *g, int x, int y) { grid_dot *new_dot = g->dots + g->num_dots; new_dot->order = 0; new_dot->edges = NULL; new_dot->faces = NULL; new_dot->x = x; new_dot->y = y; g->num_dots++; return new_dot; } /* Retrieve a dot with these (x,y) coordinates. Either return an existing dot * in the dot_list, or add a new dot to the grid (and the dot_list) and * return that. * Assumes g->dots has enough capacity allocated */ static grid_dot *grid_get_dot(grid *g, tree234 *dot_list, int x, int y) { grid_dot test, *ret; test.order = 0; test.edges = NULL; test.faces = NULL; test.x = x; test.y = y; ret = find234(dot_list, &test, NULL); if (ret) return ret; ret = grid_dot_add_new(g, x, y); add234(dot_list, ret); return ret; } /* Sets the last face of the grid to include this dot, at this position * around the face. Assumes num_faces is at least 1 (a new face has * previously been added, with the required number of dots allocated) */ static void grid_face_set_dot(grid *g, grid_dot *d, int position) { grid_face *last_face = g->faces + g->num_faces - 1; last_face->dots[position] = d; } /* * Helper routines for grid_find_incentre. */ static int solve_2x2_matrix(double mx[4], double vin[2], double vout[2]) { double inv[4]; double det; det = (mx[0]*mx[3] - mx[1]*mx[2]); if (det == 0) return FALSE; inv[0] = mx[3] / det; inv[1] = -mx[1] / det; inv[2] = -mx[2] / det; inv[3] = mx[0] / det; vout[0] = inv[0]*vin[0] + inv[1]*vin[1]; vout[1] = inv[2]*vin[0] + inv[3]*vin[1]; return TRUE; } static int solve_3x3_matrix(double mx[9], double vin[3], double vout[3]) { double inv[9]; double det; det = (mx[0]*mx[4]*mx[8] + mx[1]*mx[5]*mx[6] + mx[2]*mx[3]*mx[7] - mx[0]*mx[5]*mx[7] - mx[1]*mx[3]*mx[8] - mx[2]*mx[4]*mx[6]); if (det == 0) return FALSE; inv[0] = (mx[4]*mx[8] - mx[5]*mx[7]) / det; inv[1] = (mx[2]*mx[7] - mx[1]*mx[8]) / det; inv[2] = (mx[1]*mx[5] - mx[2]*mx[4]) / det; inv[3] = (mx[5]*mx[6] - mx[3]*mx[8]) / det; inv[4] = (mx[0]*mx[8] - mx[2]*mx[6]) / det; inv[5] = (mx[2]*mx[3] - mx[0]*mx[5]) / det; inv[6] = (mx[3]*mx[7] - mx[4]*mx[6]) / det; inv[7] = (mx[1]*mx[6] - mx[0]*mx[7]) / det; inv[8] = (mx[0]*mx[4] - mx[1]*mx[3]) / det; vout[0] = inv[0]*vin[0] + inv[1]*vin[1] + inv[2]*vin[2]; vout[1] = inv[3]*vin[0] + inv[4]*vin[1] + inv[5]*vin[2]; vout[2] = inv[6]*vin[0] + inv[7]*vin[1] + inv[8]*vin[2]; return TRUE; } void grid_find_incentre(grid_face *f) { double xbest, ybest, bestdist; int i, j, k, m; grid_dot *edgedot1[3], *edgedot2[3]; grid_dot *dots[3]; int nedges, ndots; if (f->has_incentre) return; /* * Find the point in the polygon with the maximum distance to any * edge or corner. * * Such a point must exist which is in contact with at least three * edges and/or vertices. (Proof: if it's only in contact with two * edges and/or vertices, it can't even be at a _local_ maximum - * any such circle can always be expanded in some direction.) So * we iterate through all 3-subsets of the combined set of edges * and vertices; for each subset we generate one or two candidate * points that might be the incentre, and then we vet each one to * see if it's inside the polygon and what its maximum radius is. * * (There's one case which this algorithm will get noticeably * wrong, and that's when a continuum of equally good answers * exists due to parallel edges. Consider a long thin rectangle, * for instance, or a parallelogram. This algorithm will pick a * point near one end, and choose the end arbitrarily; obviously a * nicer point to choose would be in the centre. To fix this I * would have to introduce a special-case system which detected * parallel edges in advance, set aside all candidate points * generated using both edges in a parallel pair, and generated * some additional candidate points half way between them. Also, * of course, I'd have to cope with rounding error making such a * point look worse than one of its endpoints. So I haven't done * this for the moment, and will cross it if necessary when I come * to it.) * * We don't actually iterate literally over _edges_, in the sense * of grid_edge structures. Instead, we fill in edgedot1[] and * edgedot2[] with a pair of dots adjacent in the face's list of * vertices. This ensures that we get the edges in consistent * orientation, which we could not do from the grid structure * alone. (A moment's consideration of an order-3 vertex should * make it clear that if a notional arrow was written on each * edge, _at least one_ of the three faces bordering that vertex * would have to have the two arrows tip-to-tip or tail-to-tail * rather than tip-to-tail.) */ nedges = ndots = 0; bestdist = 0; xbest = ybest = 0; for (i = 0; i+2 < 2*f->order; i++) { if (i < f->order) { edgedot1[nedges] = f->dots[i]; edgedot2[nedges++] = f->dots[(i+1)%f->order]; } else dots[ndots++] = f->dots[i - f->order]; for (j = i+1; j+1 < 2*f->order; j++) { if (j < f->order) { edgedot1[nedges] = f->dots[j]; edgedot2[nedges++] = f->dots[(j+1)%f->order]; } else dots[ndots++] = f->dots[j - f->order]; for (k = j+1; k < 2*f->order; k++) { double cx[2], cy[2]; /* candidate positions */ int cn = 0; /* number of candidates */ if (k < f->order) { edgedot1[nedges] = f->dots[k]; edgedot2[nedges++] = f->dots[(k+1)%f->order]; } else dots[ndots++] = f->dots[k - f->order]; /* * Find a point, or pair of points, equidistant from * all the specified edges and/or vertices. */ if (nedges == 3) { /* * Three edges. This is a linear matrix equation: * each row of the matrix represents the fact that * the point (x,y) we seek is at distance r from * that edge, and we solve three of those * simultaneously to obtain x,y,r. (We ignore r.) */ double matrix[9], vector[3], vector2[3]; int m; for (m = 0; m < 3; m++) { int x1 = edgedot1[m]->x, x2 = edgedot2[m]->x; int y1 = edgedot1[m]->y, y2 = edgedot2[m]->y; int dx = x2-x1, dy = y2-y1; /* * ((x,y) - (x1,y1)) . (dy,-dx) = r |(dx,dy)| * * => x dy - y dx - r |(dx,dy)| = (x1 dy - y1 dx) */ matrix[3*m+0] = dy; matrix[3*m+1] = -dx; matrix[3*m+2] = -sqrt((double)dx*dx+(double)dy*dy); vector[m] = (double)x1*dy - (double)y1*dx; } if (solve_3x3_matrix(matrix, vector, vector2)) { cx[cn] = vector2[0]; cy[cn] = vector2[1]; cn++; } } else if (nedges == 2) { /* * Two edges and a dot. This will end up in a * quadratic equation. * * First, look at the two edges. Having our point * be some distance r from both of them gives rise * to a pair of linear equations in x,y,r of the * form * * (x-x1) dy - (y-y1) dx = r sqrt(dx^2+dy^2) * * We eliminate r between those equations to give * us a single linear equation in x,y describing * the locus of points equidistant from both lines * - i.e. the angle bisector. * * We then choose one of x,y to be a parameter t, * and derive linear formulae for x,y,r in terms * of t. This enables us to write down the * circular equation (x-xd)^2+(y-yd)^2=r^2 as a * quadratic in t; solving that and substituting * in for x,y gives us two candidate points. */ double eqs[2][4]; /* a,b,c,d : ax+by+cr=d */ double eq[3]; /* a,b,c: ax+by=c */ double xt[2], yt[2], rt[2]; /* a,b: {x,y,r}=at+b */ double q[3]; /* a,b,c: at^2+bt+c=0 */ double disc; /* Find equations of the two input lines. */ for (m = 0; m < 2; m++) { int x1 = edgedot1[m]->x, x2 = edgedot2[m]->x; int y1 = edgedot1[m]->y, y2 = edgedot2[m]->y; int dx = x2-x1, dy = y2-y1; eqs[m][0] = dy; eqs[m][1] = -dx; eqs[m][2] = -sqrt(dx*dx+dy*dy); eqs[m][3] = x1*dy - y1*dx; } /* Derive the angle bisector by eliminating r. */ eq[0] = eqs[0][0]*eqs[1][2] - eqs[1][0]*eqs[0][2]; eq[1] = eqs[0][1]*eqs[1][2] - eqs[1][1]*eqs[0][2]; eq[2] = eqs[0][3]*eqs[1][2] - eqs[1][3]*eqs[0][2]; /* Parametrise x and y in terms of some t. */ if (abs(eq[0]) < abs(eq[1])) { /* Parameter is x. */ xt[0] = 1; xt[1] = 0; yt[0] = -eq[0]/eq[1]; yt[1] = eq[2]/eq[1]; } else { /* Parameter is y. */ yt[0] = 1; yt[1] = 0; xt[0] = -eq[1]/eq[0]; xt[1] = eq[2]/eq[0]; } /* Find a linear representation of r using eqs[0]. */ rt[0] = -(eqs[0][0]*xt[0] + eqs[0][1]*yt[0])/eqs[0][2]; rt[1] = (eqs[0][3] - eqs[0][0]*xt[1] - eqs[0][1]*yt[1])/eqs[0][2]; /* Construct the quadratic equation. */ q[0] = -rt[0]*rt[0]; q[1] = -2*rt[0]*rt[1]; q[2] = -rt[1]*rt[1]; q[0] += xt[0]*xt[0]; q[1] += 2*xt[0]*(xt[1]-dots[0]->x); q[2] += (xt[1]-dots[0]->x)*(xt[1]-dots[0]->x); q[0] += yt[0]*yt[0]; q[1] += 2*yt[0]*(yt[1]-dots[0]->y); q[2] += (yt[1]-dots[0]->y)*(yt[1]-dots[0]->y); /* And solve it. */ disc = q[1]*q[1] - 4*q[0]*q[2]; if (disc >= 0) { double t; disc = sqrt(disc); t = (-q[1] + disc) / (2*q[0]); cx[cn] = xt[0]*t + xt[1]; cy[cn] = yt[0]*t + yt[1]; cn++; t = (-q[1] - disc) / (2*q[0]); cx[cn] = xt[0]*t + xt[1]; cy[cn] = yt[0]*t + yt[1]; cn++; } } else if (nedges == 1) { /* * Two dots and an edge. This one's another * quadratic equation. * * The point we want must lie on the perpendicular * bisector of the two dots; that much is obvious. * So we can construct a parametrisation of that * bisecting line, giving linear formulae for x,y * in terms of t. We can also express the distance * from the edge as such a linear formula. * * Then we set that equal to the radius of the * circle passing through the two points, which is * a Pythagoras exercise; that gives rise to a * quadratic in t, which we solve. */ double xt[2], yt[2], rt[2]; /* a,b: {x,y,r}=at+b */ double q[3]; /* a,b,c: at^2+bt+c=0 */ double disc; double halfsep; /* Find parametric formulae for x,y. */ { int x1 = dots[0]->x, x2 = dots[1]->x; int y1 = dots[0]->y, y2 = dots[1]->y; int dx = x2-x1, dy = y2-y1; double d = sqrt((double)dx*dx + (double)dy*dy); xt[1] = (x1+x2)/2.0; yt[1] = (y1+y2)/2.0; /* It's convenient if we have t at standard scale. */ xt[0] = -dy/d; yt[0] = dx/d; /* Also note down half the separation between * the dots, for use in computing the circle radius. */ halfsep = 0.5*d; } /* Find a parametric formula for r. */ { int x1 = edgedot1[0]->x, x2 = edgedot2[0]->x; int y1 = edgedot1[0]->y, y2 = edgedot2[0]->y; int dx = x2-x1, dy = y2-y1; double d = sqrt((double)dx*dx + (double)dy*dy); rt[0] = (xt[0]*dy - yt[0]*dx) / d; rt[1] = ((xt[1]-x1)*dy - (yt[1]-y1)*dx) / d; } /* Construct the quadratic equation. */ q[0] = rt[0]*rt[0]; q[1] = 2*rt[0]*rt[1]; q[2] = rt[1]*rt[1]; q[0] -= 1; q[2] -= halfsep*halfsep; /* And solve it. */ disc = q[1]*q[1] - 4*q[0]*q[2]; if (disc >= 0) { double t; disc = sqrt(disc); t = (-q[1] + disc) / (2*q[0]); cx[cn] = xt[0]*t + xt[1]; cy[cn] = yt[0]*t + yt[1]; cn++; t = (-q[1] - disc) / (2*q[0]); cx[cn] = xt[0]*t + xt[1]; cy[cn] = yt[0]*t + yt[1]; cn++; } } else if (nedges == 0) { /* * Three dots. This is another linear matrix * equation, this time with each row of the matrix * representing the perpendicular bisector between * two of the points. Of course we only need two * such lines to find their intersection, so we * need only solve a 2x2 matrix equation. */ double matrix[4], vector[2], vector2[2]; int m; for (m = 0; m < 2; m++) { int x1 = dots[m]->x, x2 = dots[m+1]->x; int y1 = dots[m]->y, y2 = dots[m+1]->y; int dx = x2-x1, dy = y2-y1; /* * ((x,y) - (x1,y1)) . (dx,dy) = 1/2 |(dx,dy)|^2 * * => 2x dx + 2y dy = dx^2+dy^2 + (2 x1 dx + 2 y1 dy) */ matrix[2*m+0] = 2*dx; matrix[2*m+1] = 2*dy; vector[m] = ((double)dx*dx + (double)dy*dy + 2.0*x1*dx + 2.0*y1*dy); } if (solve_2x2_matrix(matrix, vector, vector2)) { cx[cn] = vector2[0]; cy[cn] = vector2[1]; cn++; } } /* * Now go through our candidate points and see if any * of them are better than what we've got so far. */ for (m = 0; m < cn; m++) { double x = cx[m], y = cy[m]; /* * First, disqualify the point if it's not inside * the polygon, which we work out by counting the * edges to the right of the point. (For * tiebreaking purposes when edges start or end on * our y-coordinate or go right through it, we * consider our point to be offset by a small * _positive_ epsilon in both the x- and * y-direction.) */ int e, in = 0; for (e = 0; e < f->order; e++) { int xs = f->edges[e]->dot1->x; int xe = f->edges[e]->dot2->x; int ys = f->edges[e]->dot1->y; int ye = f->edges[e]->dot2->y; if ((y >= ys && y < ye) || (y >= ye && y < ys)) { /* * The line goes past our y-position. Now we need * to know if its x-coordinate when it does so is * to our right. * * The x-coordinate in question is mathematically * (y - ys) * (xe - xs) / (ye - ys), and we want * to know whether (x - xs) >= that. Of course we * avoid the division, so we can work in integers; * to do this we must multiply both sides of the * inequality by ye - ys, which means we must * first check that's not negative. */ int num = xe - xs, denom = ye - ys; if (denom < 0) { num = -num; denom = -denom; } if ((x - xs) * denom >= (y - ys) * num) in ^= 1; } } if (in) { #ifdef HUGE_VAL double mindist = HUGE_VAL; #else #ifdef DBL_MAX double mindist = DBL_MAX; #else #error No way to get maximum floating-point number. #endif #endif int e, d; /* * This point is inside the polygon, so now we check * its minimum distance to every edge and corner. * First the corners ... */ for (d = 0; d < f->order; d++) { int xp = f->dots[d]->x; int yp = f->dots[d]->y; double dx = x - xp, dy = y - yp; double dist = dx*dx + dy*dy; if (mindist > dist) mindist = dist; } /* * ... and now also check the perpendicular distance * to every edge, if the perpendicular lies between * the edge's endpoints. */ for (e = 0; e < f->order; e++) { int xs = f->edges[e]->dot1->x; int xe = f->edges[e]->dot2->x; int ys = f->edges[e]->dot1->y; int ye = f->edges[e]->dot2->y; /* * If s and e are our endpoints, and p our * candidate circle centre, the foot of a * perpendicular from p to the line se lies * between s and e if and only if (p-s).(e-s) lies * strictly between 0 and (e-s).(e-s). */ int edx = xe - xs, edy = ye - ys; double pdx = x - xs, pdy = y - ys; double pde = pdx * edx + pdy * edy; long ede = (long)edx * edx + (long)edy * edy; if (0 < pde && pde < ede) { /* * Yes, the nearest point on this edge is * closer than either endpoint, so we must * take it into account by measuring the * perpendicular distance to the edge and * checking its square against mindist. */ double pdre = pdx * edy - pdy * edx; double sqlen = pdre * pdre / ede; if (mindist > sqlen) mindist = sqlen; } } /* * Right. Now we know the biggest circle around this * point, so we can check it against bestdist. */ if (bestdist < mindist) { bestdist = mindist; xbest = x; ybest = y; } } } if (k < f->order) nedges--; else ndots--; } if (j < f->order) nedges--; else ndots--; } if (i < f->order) nedges--; else ndots--; } assert(bestdist > 0); f->has_incentre = TRUE; f->ix = xbest + 0.5; /* round to nearest */ f->iy = ybest + 0.5; } /* ------ Generate various types of grid ------ */ /* General method is to generate faces, by calculating their dot coordinates. * As new faces are added, we keep track of all the dots so we can tell when * a new face reuses an existing dot. For example, two squares touching at an * edge would generate six unique dots: four dots from the first face, then * two additional dots for the second face, because we detect the other two * dots have already been taken up. This list is stored in a tree234 * called "points". No extra memory-allocation needed here - we store the * actual grid_dot* pointers, which all point into the g->dots list. * For this reason, we have to calculate coordinates in such a way as to * eliminate any rounding errors, so we can detect when a dot on one * face precisely lands on a dot of a different face. No floating-point * arithmetic here! */ #define SQUARE_TILESIZE 20 static void grid_size_square(int width, int height, int *tilesize, int *xextent, int *yextent) { int a = SQUARE_TILESIZE; *tilesize = a; *xextent = width * a; *yextent = height * a; } static grid *grid_new_square(int width, int height, const char *desc) { int x, y; /* Side length */ int a = SQUARE_TILESIZE; /* Upper bounds - don't have to be exact */ int max_faces = width * height; int max_dots = (width + 1) * (height + 1); tree234 *points; grid *g = grid_empty(); g->tilesize = a; g->faces = snewn(max_faces, grid_face); g->dots = snewn(max_dots, grid_dot); points = newtree234(grid_point_cmp_fn); /* generate square faces */ for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { grid_dot *d; /* face position */ int px = a * x; int py = a * y; grid_face_add_new(g, 4); d = grid_get_dot(g, points, px, py); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + a, py); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px + a, py + a); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px, py + a); grid_face_set_dot(g, d, 3); } } freetree234(points); assert(g->num_faces <= max_faces); assert(g->num_dots <= max_dots); grid_make_consistent(g); return g; } #define HONEY_TILESIZE 45 /* Vector for side of hexagon - ratio is close to sqrt(3) */ #define HONEY_A 15 #define HONEY_B 26 static void grid_size_honeycomb(int width, int height, int *tilesize, int *xextent, int *yextent) { int a = HONEY_A; int b = HONEY_B; *tilesize = HONEY_TILESIZE; *xextent = (3 * a * (width-1)) + 4*a; *yextent = (2 * b * (height-1)) + 3*b; } static grid *grid_new_honeycomb(int width, int height, const char *desc) { int x, y; int a = HONEY_A; int b = HONEY_B; /* Upper bounds - don't have to be exact */ int max_faces = width * height; int max_dots = 2 * (width + 1) * (height + 1); tree234 *points; grid *g = grid_empty(); g->tilesize = HONEY_TILESIZE; g->faces = snewn(max_faces, grid_face); g->dots = snewn(max_dots, grid_dot); points = newtree234(grid_point_cmp_fn); /* generate hexagonal faces */ for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { grid_dot *d; /* face centre */ int cx = 3 * a * x; int cy = 2 * b * y; if (x % 2) cy += b; grid_face_add_new(g, 6); d = grid_get_dot(g, points, cx - a, cy - b); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, cx + a, cy - b); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, cx + 2*a, cy); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, cx + a, cy + b); grid_face_set_dot(g, d, 3); d = grid_get_dot(g, points, cx - a, cy + b); grid_face_set_dot(g, d, 4); d = grid_get_dot(g, points, cx - 2*a, cy); grid_face_set_dot(g, d, 5); } } freetree234(points); assert(g->num_faces <= max_faces); assert(g->num_dots <= max_dots); grid_make_consistent(g); return g; } #define TRIANGLE_TILESIZE 18 /* Vector for side of triangle - ratio is close to sqrt(3) */ #define TRIANGLE_VEC_X 15 #define TRIANGLE_VEC_Y 26 static void grid_size_triangular(int width, int height, int *tilesize, int *xextent, int *yextent) { int vec_x = TRIANGLE_VEC_X; int vec_y = TRIANGLE_VEC_Y; *tilesize = TRIANGLE_TILESIZE; *xextent = (width+1) * 2 * vec_x; *yextent = height * vec_y; } static char *grid_validate_desc_triangular(grid_type type, int width, int height, const char *desc) { /* * Triangular grids: an absent description is valid (indicating * the old-style approach which had 'ears', i.e. triangles * connected to only one other face, at some grid corners), and so * is a description reading just "0" (indicating the new-style * approach in which those ears are trimmed off). Anything else is * illegal. */ if (!desc || !strcmp(desc, "0")) return NULL; return "Unrecognised grid description."; } /* Doesn't use the previous method of generation, it pre-dates it! * A triangular grid is just about simple enough to do by "brute force" */ static grid *grid_new_triangular(int width, int height, const char *desc) { int x,y; int version = (desc == NULL ? -1 : atoi(desc)); /* Vector for side of triangle - ratio is close to sqrt(3) */ int vec_x = TRIANGLE_VEC_X; int vec_y = TRIANGLE_VEC_Y; int index; /* convenient alias */ int w = width + 1; grid *g = grid_empty(); g->tilesize = TRIANGLE_TILESIZE; if (version == -1) { /* * Old-style triangular grid generation, preserved as-is for * backwards compatibility with old game ids, in which it's * just a little asymmetric and there are 'ears' (faces linked * to only one other face) at two grid corners. * * Example old-style game ids, which should still work, and in * which you should see the ears in the TL/BR corners on the * first one and in the TL/BL corners on the second: * * 5x5t1:2c2a1a2a201a1a1a1112a1a2b1211f0b21a2a2a0a * 5x6t1:a022a212h1a1d1a12c2b11a012b1a20d1a0a12e */ g->num_faces = width * height * 2; g->num_dots = (width + 1) * (height + 1); g->faces = snewn(g->num_faces, grid_face); g->dots = snewn(g->num_dots, grid_dot); /* generate dots */ index = 0; for (y = 0; y <= height; y++) { for (x = 0; x <= width; x++) { grid_dot *d = g->dots + index; /* odd rows are offset to the right */ d->order = 0; d->edges = NULL; d->faces = NULL; d->x = x * 2 * vec_x + ((y % 2) ? vec_x : 0); d->y = y * vec_y; index++; } } /* generate faces */ index = 0; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { /* initialise two faces for this (x,y) */ grid_face *f1 = g->faces + index; grid_face *f2 = f1 + 1; f1->edges = NULL; f1->order = 3; f1->dots = snewn(f1->order, grid_dot*); f1->has_incentre = FALSE; f2->edges = NULL; f2->order = 3; f2->dots = snewn(f2->order, grid_dot*); f2->has_incentre = FALSE; /* face descriptions depend on whether the row-number is * odd or even */ if (y % 2) { f1->dots[0] = g->dots + y * w + x; f1->dots[1] = g->dots + (y + 1) * w + x + 1; f1->dots[2] = g->dots + (y + 1) * w + x; f2->dots[0] = g->dots + y * w + x; f2->dots[1] = g->dots + y * w + x + 1; f2->dots[2] = g->dots + (y + 1) * w + x + 1; } else { f1->dots[0] = g->dots + y * w + x; f1->dots[1] = g->dots + y * w + x + 1; f1->dots[2] = g->dots + (y + 1) * w + x; f2->dots[0] = g->dots + y * w + x + 1; f2->dots[1] = g->dots + (y + 1) * w + x + 1; f2->dots[2] = g->dots + (y + 1) * w + x; } index += 2; } } } else { /* * New-style approach, in which there are never any 'ears', * and if height is even then the grid is nicely 4-way * symmetric. * * Example new-style grids: * * 5x5t1:0_21120b11a1a01a1a00c1a0b211021c1h1a2a1a0a * 5x6t1:0_a1212c22c2a02a2f22a0c12a110d0e1c0c0a101121a1 */ tree234 *points = newtree234(grid_point_cmp_fn); /* Upper bounds - don't have to be exact */ int max_faces = height * (2*width+1); int max_dots = (height+1) * (width+1) * 4; g->faces = snewn(max_faces, grid_face); g->dots = snewn(max_dots, grid_dot); for (y = 0; y < height; y++) { /* * Each row contains (width+1) triangles one way up, and * (width) triangles the other way up. Which way up is * which varies with parity of y. Also, the dots around * each face will flip direction with parity of y, so we * set up n1 and n2 to cope with that easily. */ int y0, y1, n1, n2; y0 = y1 = y * vec_y; if (y % 2) { y1 += vec_y; n1 = 2; n2 = 1; } else { y0 += vec_y; n1 = 1; n2 = 2; } for (x = 0; x <= width; x++) { int x0 = 2*x * vec_x, x1 = x0 + vec_x, x2 = x1 + vec_x; /* * If the grid has odd height, then we skip the first * and last triangles on this row, otherwise they'll * end up as ears. */ if (height % 2 == 1 && y == height-1 && (x == 0 || x == width)) continue; grid_face_add_new(g, 3); grid_face_set_dot(g, grid_get_dot(g, points, x0, y0), 0); grid_face_set_dot(g, grid_get_dot(g, points, x1, y1), n1); grid_face_set_dot(g, grid_get_dot(g, points, x2, y0), n2); } for (x = 0; x < width; x++) { int x0 = (2*x+1) * vec_x, x1 = x0 + vec_x, x2 = x1 + vec_x; grid_face_add_new(g, 3); grid_face_set_dot(g, grid_get_dot(g, points, x0, y1), 0); grid_face_set_dot(g, grid_get_dot(g, points, x1, y0), n2); grid_face_set_dot(g, grid_get_dot(g, points, x2, y1), n1); } } freetree234(points); assert(g->num_faces <= max_faces); assert(g->num_dots <= max_dots); } grid_make_consistent(g); return g; } #define SNUBSQUARE_TILESIZE 18 /* Vector for side of triangle - ratio is close to sqrt(3) */ #define SNUBSQUARE_A 15 #define SNUBSQUARE_B 26 static void grid_size_snubsquare(int width, int height, int *tilesize, int *xextent, int *yextent) { int a = SNUBSQUARE_A; int b = SNUBSQUARE_B; *tilesize = SNUBSQUARE_TILESIZE; *xextent = (a+b) * (width-1) + a + b; *yextent = (a+b) * (height-1) + a + b; } static grid *grid_new_snubsquare(int width, int height, const char *desc) { int x, y; int a = SNUBSQUARE_A; int b = SNUBSQUARE_B; /* Upper bounds - don't have to be exact */ int max_faces = 3 * width * height; int max_dots = 2 * (width + 1) * (height + 1); tree234 *points; grid *g = grid_empty(); g->tilesize = SNUBSQUARE_TILESIZE; g->faces = snewn(max_faces, grid_face); g->dots = snewn(max_dots, grid_dot); points = newtree234(grid_point_cmp_fn); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { grid_dot *d; /* face position */ int px = (a + b) * x; int py = (a + b) * y; /* generate square faces */ grid_face_add_new(g, 4); if ((x + y) % 2) { d = grid_get_dot(g, points, px + a, py); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + a + b, py + a); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px + b, py + a + b); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px, py + b); grid_face_set_dot(g, d, 3); } else { d = grid_get_dot(g, points, px + b, py); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + a + b, py + b); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px + a, py + a + b); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px, py + a); grid_face_set_dot(g, d, 3); } /* generate up/down triangles */ if (x > 0) { grid_face_add_new(g, 3); if ((x + y) % 2) { d = grid_get_dot(g, points, px + a, py); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px, py + b); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px - a, py); grid_face_set_dot(g, d, 2); } else { d = grid_get_dot(g, points, px, py + a); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + a, py + a + b); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px - a, py + a + b); grid_face_set_dot(g, d, 2); } } /* generate left/right triangles */ if (y > 0) { grid_face_add_new(g, 3); if ((x + y) % 2) { d = grid_get_dot(g, points, px + a, py); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + a + b, py - a); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px + a + b, py + a); grid_face_set_dot(g, d, 2); } else { d = grid_get_dot(g, points, px, py - a); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + b, py); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px, py + a); grid_face_set_dot(g, d, 2); } } } } freetree234(points); assert(g->num_faces <= max_faces); assert(g->num_dots <= max_dots); grid_make_consistent(g); return g; } #define CAIRO_TILESIZE 40 /* Vector for side of pentagon - ratio is close to (4+sqrt(7))/3 */ #define CAIRO_A 14 #define CAIRO_B 31 static void grid_size_cairo(int width, int height, int *tilesize, int *xextent, int *yextent) { int b = CAIRO_B; /* a unused in determining grid size. */ *tilesize = CAIRO_TILESIZE; *xextent = 2*b*(width-1) + 2*b; *yextent = 2*b*(height-1) + 2*b; } static grid *grid_new_cairo(int width, int height, const char *desc) { int x, y; int a = CAIRO_A; int b = CAIRO_B; /* Upper bounds - don't have to be exact */ int max_faces = 2 * width * height; int max_dots = 3 * (width + 1) * (height + 1); tree234 *points; grid *g = grid_empty(); g->tilesize = CAIRO_TILESIZE; g->faces = snewn(max_faces, grid_face); g->dots = snewn(max_dots, grid_dot); points = newtree234(grid_point_cmp_fn); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { grid_dot *d; /* cell position */ int px = 2 * b * x; int py = 2 * b * y; /* horizontal pentagons */ if (y > 0) { grid_face_add_new(g, 5); if ((x + y) % 2) { d = grid_get_dot(g, points, px + a, py - b); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + 2*b - a, py - b); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px + 2*b, py); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px + b, py + a); grid_face_set_dot(g, d, 3); d = grid_get_dot(g, points, px, py); grid_face_set_dot(g, d, 4); } else { d = grid_get_dot(g, points, px, py); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + b, py - a); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px + 2*b, py); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px + 2*b - a, py + b); grid_face_set_dot(g, d, 3); d = grid_get_dot(g, points, px + a, py + b); grid_face_set_dot(g, d, 4); } } /* vertical pentagons */ if (x > 0) { grid_face_add_new(g, 5); if ((x + y) % 2) { d = grid_get_dot(g, points, px, py); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + b, py + a); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px + b, py + 2*b - a); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px, py + 2*b); grid_face_set_dot(g, d, 3); d = grid_get_dot(g, points, px - a, py + b); grid_face_set_dot(g, d, 4); } else { d = grid_get_dot(g, points, px, py); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + a, py + b); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px, py + 2*b); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px - b, py + 2*b - a); grid_face_set_dot(g, d, 3); d = grid_get_dot(g, points, px - b, py + a); grid_face_set_dot(g, d, 4); } } } } freetree234(points); assert(g->num_faces <= max_faces); assert(g->num_dots <= max_dots); grid_make_consistent(g); return g; } #define GREATHEX_TILESIZE 18 /* Vector for side of triangle - ratio is close to sqrt(3) */ #define GREATHEX_A 15 #define GREATHEX_B 26 static void grid_size_greathexagonal(int width, int height, int *tilesize, int *xextent, int *yextent) { int a = GREATHEX_A; int b = GREATHEX_B; *tilesize = GREATHEX_TILESIZE; *xextent = (3*a + b) * (width-1) + 4*a; *yextent = (2*a + 2*b) * (height-1) + 3*b + a; } static grid *grid_new_greathexagonal(int width, int height, const char *desc) { int x, y; int a = GREATHEX_A; int b = GREATHEX_B; /* Upper bounds - don't have to be exact */ int max_faces = 6 * (width + 1) * (height + 1); int max_dots = 6 * width * height; tree234 *points; grid *g = grid_empty(); g->tilesize = GREATHEX_TILESIZE; g->faces = snewn(max_faces, grid_face); g->dots = snewn(max_dots, grid_dot); points = newtree234(grid_point_cmp_fn); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { grid_dot *d; /* centre of hexagon */ int px = (3*a + b) * x; int py = (2*a + 2*b) * y; if (x % 2) py += a + b; /* hexagon */ grid_face_add_new(g, 6); d = grid_get_dot(g, points, px - a, py - b); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + a, py - b); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px + 2*a, py); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px + a, py + b); grid_face_set_dot(g, d, 3); d = grid_get_dot(g, points, px - a, py + b); grid_face_set_dot(g, d, 4); d = grid_get_dot(g, points, px - 2*a, py); grid_face_set_dot(g, d, 5); /* square below hexagon */ if (y < height - 1) { grid_face_add_new(g, 4); d = grid_get_dot(g, points, px - a, py + b); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + a, py + b); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px + a, py + 2*a + b); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px - a, py + 2*a + b); grid_face_set_dot(g, d, 3); } /* square below right */ if ((x < width - 1) && (((x % 2) == 0) || (y < height - 1))) { grid_face_add_new(g, 4); d = grid_get_dot(g, points, px + 2*a, py); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + 2*a + b, py + a); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px + a + b, py + a + b); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px + a, py + b); grid_face_set_dot(g, d, 3); } /* square below left */ if ((x > 0) && (((x % 2) == 0) || (y < height - 1))) { grid_face_add_new(g, 4); d = grid_get_dot(g, points, px - 2*a, py); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px - a, py + b); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px - a - b, py + a + b); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px - 2*a - b, py + a); grid_face_set_dot(g, d, 3); } /* Triangle below right */ if ((x < width - 1) && (y < height - 1)) { grid_face_add_new(g, 3); d = grid_get_dot(g, points, px + a, py + b); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + a + b, py + a + b); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px + a, py + 2*a + b); grid_face_set_dot(g, d, 2); } /* Triangle below left */ if ((x > 0) && (y < height - 1)) { grid_face_add_new(g, 3); d = grid_get_dot(g, points, px - a, py + b); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px - a, py + 2*a + b); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px - a - b, py + a + b); grid_face_set_dot(g, d, 2); } } } freetree234(points); assert(g->num_faces <= max_faces); assert(g->num_dots <= max_dots); grid_make_consistent(g); return g; } #define OCTAGONAL_TILESIZE 40 /* b/a approx sqrt(2) */ #define OCTAGONAL_A 29 #define OCTAGONAL_B 41 static void grid_size_octagonal(int width, int height, int *tilesize, int *xextent, int *yextent) { int a = OCTAGONAL_A; int b = OCTAGONAL_B; *tilesize = OCTAGONAL_TILESIZE; *xextent = (2*a + b) * width; *yextent = (2*a + b) * height; } static grid *grid_new_octagonal(int width, int height, const char *desc) { int x, y; int a = OCTAGONAL_A; int b = OCTAGONAL_B; /* Upper bounds - don't have to be exact */ int max_faces = 2 * width * height; int max_dots = 4 * (width + 1) * (height + 1); tree234 *points; grid *g = grid_empty(); g->tilesize = OCTAGONAL_TILESIZE; g->faces = snewn(max_faces, grid_face); g->dots = snewn(max_dots, grid_dot); points = newtree234(grid_point_cmp_fn); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { grid_dot *d; /* cell position */ int px = (2*a + b) * x; int py = (2*a + b) * y; /* octagon */ grid_face_add_new(g, 8); d = grid_get_dot(g, points, px + a, py); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + a + b, py); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px + 2*a + b, py + a); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px + 2*a + b, py + a + b); grid_face_set_dot(g, d, 3); d = grid_get_dot(g, points, px + a + b, py + 2*a + b); grid_face_set_dot(g, d, 4); d = grid_get_dot(g, points, px + a, py + 2*a + b); grid_face_set_dot(g, d, 5); d = grid_get_dot(g, points, px, py + a + b); grid_face_set_dot(g, d, 6); d = grid_get_dot(g, points, px, py + a); grid_face_set_dot(g, d, 7); /* diamond */ if ((x > 0) && (y > 0)) { grid_face_add_new(g, 4); d = grid_get_dot(g, points, px, py - a); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + a, py); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px, py + a); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px - a, py); grid_face_set_dot(g, d, 3); } } } freetree234(points); assert(g->num_faces <= max_faces); assert(g->num_dots <= max_dots); grid_make_consistent(g); return g; } #define KITE_TILESIZE 40 /* b/a approx sqrt(3) */ #define KITE_A 15 #define KITE_B 26 static void grid_size_kites(int width, int height, int *tilesize, int *xextent, int *yextent) { int a = KITE_A; int b = KITE_B; *tilesize = KITE_TILESIZE; *xextent = 4*b * width + 2*b; *yextent = 6*a * (height-1) + 8*a; } static grid *grid_new_kites(int width, int height, const char *desc) { int x, y; int a = KITE_A; int b = KITE_B; /* Upper bounds - don't have to be exact */ int max_faces = 6 * width * height; int max_dots = 6 * (width + 1) * (height + 1); tree234 *points; grid *g = grid_empty(); g->tilesize = KITE_TILESIZE; g->faces = snewn(max_faces, grid_face); g->dots = snewn(max_dots, grid_dot); points = newtree234(grid_point_cmp_fn); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { grid_dot *d; /* position of order-6 dot */ int px = 4*b * x; int py = 6*a * y; if (y % 2) px += 2*b; /* kite pointing up-left */ grid_face_add_new(g, 4); d = grid_get_dot(g, points, px, py); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + 2*b, py); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px + 2*b, py + 2*a); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px + b, py + 3*a); grid_face_set_dot(g, d, 3); /* kite pointing up */ grid_face_add_new(g, 4); d = grid_get_dot(g, points, px, py); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + b, py + 3*a); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px, py + 4*a); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px - b, py + 3*a); grid_face_set_dot(g, d, 3); /* kite pointing up-right */ grid_face_add_new(g, 4); d = grid_get_dot(g, points, px, py); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px - b, py + 3*a); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px - 2*b, py + 2*a); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px - 2*b, py); grid_face_set_dot(g, d, 3); /* kite pointing down-right */ grid_face_add_new(g, 4); d = grid_get_dot(g, points, px, py); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px - 2*b, py); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px - 2*b, py - 2*a); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px - b, py - 3*a); grid_face_set_dot(g, d, 3); /* kite pointing down */ grid_face_add_new(g, 4); d = grid_get_dot(g, points, px, py); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px - b, py - 3*a); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px, py - 4*a); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px + b, py - 3*a); grid_face_set_dot(g, d, 3); /* kite pointing down-left */ grid_face_add_new(g, 4); d = grid_get_dot(g, points, px, py); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + b, py - 3*a); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px + 2*b, py - 2*a); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px + 2*b, py); grid_face_set_dot(g, d, 3); } } freetree234(points); assert(g->num_faces <= max_faces); assert(g->num_dots <= max_dots); grid_make_consistent(g); return g; } #define FLORET_TILESIZE 150 /* -py/px is close to tan(30 - atan(sqrt(3)/9)) * using py=26 makes everything lean to the left, rather than right */ #define FLORET_PX 75 #define FLORET_PY -26 static void grid_size_floret(int width, int height, int *tilesize, int *xextent, int *yextent) { int px = FLORET_PX, py = FLORET_PY; /* |( 75, -26)| = 79.43 */ int qx = 4*px/5, qy = -py*2; /* |( 60, 52)| = 79.40 */ int ry = qy-py; /* rx unused in determining grid size. */ *tilesize = FLORET_TILESIZE; *xextent = (6*px+3*qx)/2 * (width-1) + 4*qx + 2*px; *yextent = (5*qy-4*py) * (height-1) + 4*qy + 2*ry; } static grid *grid_new_floret(int width, int height, const char *desc) { int x, y; /* Vectors for sides; weird numbers needed to keep puzzle aligned with window * -py/px is close to tan(30 - atan(sqrt(3)/9)) * using py=26 makes everything lean to the left, rather than right */ int px = FLORET_PX, py = FLORET_PY; /* |( 75, -26)| = 79.43 */ int qx = 4*px/5, qy = -py*2; /* |( 60, 52)| = 79.40 */ int rx = qx-px, ry = qy-py; /* |(-15, 78)| = 79.38 */ /* Upper bounds - don't have to be exact */ int max_faces = 6 * width * height; int max_dots = 9 * (width + 1) * (height + 1); tree234 *points; grid *g = grid_empty(); g->tilesize = FLORET_TILESIZE; g->faces = snewn(max_faces, grid_face); g->dots = snewn(max_dots, grid_dot); points = newtree234(grid_point_cmp_fn); /* generate pentagonal faces */ for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { grid_dot *d; /* face centre */ int cx = (6*px+3*qx)/2 * x; int cy = (4*py-5*qy) * y; if (x % 2) cy -= (4*py-5*qy)/2; else if (y && y == height-1) continue; /* make better looking grids? try 3x3 for instance */ grid_face_add_new(g, 5); d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, cx+2*rx , cy+2*ry ); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, cx+2*rx+qx, cy+2*ry+qy); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, cx+2*qx+rx, cy+2*qy+ry); grid_face_set_dot(g, d, 3); d = grid_get_dot(g, points, cx+2*qx , cy+2*qy ); grid_face_set_dot(g, d, 4); grid_face_add_new(g, 5); d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, cx+2*qx , cy+2*qy ); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, cx+2*qx+px, cy+2*qy+py); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, cx+2*px+qx, cy+2*py+qy); grid_face_set_dot(g, d, 3); d = grid_get_dot(g, points, cx+2*px , cy+2*py ); grid_face_set_dot(g, d, 4); grid_face_add_new(g, 5); d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, cx+2*px , cy+2*py ); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, cx+2*px-rx, cy+2*py-ry); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, cx-2*rx+px, cy-2*ry+py); grid_face_set_dot(g, d, 3); d = grid_get_dot(g, points, cx-2*rx , cy-2*ry ); grid_face_set_dot(g, d, 4); grid_face_add_new(g, 5); d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, cx-2*rx , cy-2*ry ); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, cx-2*rx-qx, cy-2*ry-qy); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, cx-2*qx-rx, cy-2*qy-ry); grid_face_set_dot(g, d, 3); d = grid_get_dot(g, points, cx-2*qx , cy-2*qy ); grid_face_set_dot(g, d, 4); grid_face_add_new(g, 5); d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, cx-2*qx , cy-2*qy ); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, cx-2*qx-px, cy-2*qy-py); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, cx-2*px-qx, cy-2*py-qy); grid_face_set_dot(g, d, 3); d = grid_get_dot(g, points, cx-2*px , cy-2*py ); grid_face_set_dot(g, d, 4); grid_face_add_new(g, 5); d = grid_get_dot(g, points, cx , cy ); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, cx-2*px , cy-2*py ); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, cx-2*px+rx, cy-2*py+ry); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, cx+2*rx-px, cy+2*ry-py); grid_face_set_dot(g, d, 3); d = grid_get_dot(g, points, cx+2*rx , cy+2*ry ); grid_face_set_dot(g, d, 4); } } freetree234(points); assert(g->num_faces <= max_faces); assert(g->num_dots <= max_dots); grid_make_consistent(g); return g; } /* DODEC_* are used for dodecagonal and great-dodecagonal grids. */ #define DODEC_TILESIZE 26 /* Vector for side of triangle - ratio is close to sqrt(3) */ #define DODEC_A 15 #define DODEC_B 26 static void grid_size_dodecagonal(int width, int height, int *tilesize, int *xextent, int *yextent) { int a = DODEC_A; int b = DODEC_B; *tilesize = DODEC_TILESIZE; *xextent = (4*a + 2*b) * (width-1) + 3*(2*a + b); *yextent = (3*a + 2*b) * (height-1) + 2*(2*a + b); } static grid *grid_new_dodecagonal(int width, int height, const char *desc) { int x, y; int a = DODEC_A; int b = DODEC_B; /* Upper bounds - don't have to be exact */ int max_faces = 3 * width * height; int max_dots = 14 * width * height; tree234 *points; grid *g = grid_empty(); g->tilesize = DODEC_TILESIZE; g->faces = snewn(max_faces, grid_face); g->dots = snewn(max_dots, grid_dot); points = newtree234(grid_point_cmp_fn); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { grid_dot *d; /* centre of dodecagon */ int px = (4*a + 2*b) * x; int py = (3*a + 2*b) * y; if (y % 2) px += 2*a + b; /* dodecagon */ grid_face_add_new(g, 12); d = grid_get_dot(g, points, px + ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px + (2*a + b), py - ( a )); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px + (2*a + b), py + ( a )); grid_face_set_dot(g, d, 3); d = grid_get_dot(g, points, px + ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 4); d = grid_get_dot(g, points, px + ( a ), py + (2*a + b)); grid_face_set_dot(g, d, 5); d = grid_get_dot(g, points, px - ( a ), py + (2*a + b)); grid_face_set_dot(g, d, 6); d = grid_get_dot(g, points, px - ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 7); d = grid_get_dot(g, points, px - (2*a + b), py + ( a )); grid_face_set_dot(g, d, 8); d = grid_get_dot(g, points, px - (2*a + b), py - ( a )); grid_face_set_dot(g, d, 9); d = grid_get_dot(g, points, px - ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 10); d = grid_get_dot(g, points, px - ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 11); /* triangle below dodecagon */ if ((y < height - 1 && (x < width - 1 || !(y % 2)) && (x > 0 || (y % 2)))) { grid_face_add_new(g, 3); d = grid_get_dot(g, points, px + a, py + (2*a + b)); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px , py + (2*a + 2*b)); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px - a, py + (2*a + b)); grid_face_set_dot(g, d, 2); } /* triangle above dodecagon */ if ((y && (x < width - 1 || !(y % 2)) && (x > 0 || (y % 2)))) { grid_face_add_new(g, 3); d = grid_get_dot(g, points, px - a, py - (2*a + b)); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px , py - (2*a + 2*b)); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px + a, py - (2*a + b)); grid_face_set_dot(g, d, 2); } } } freetree234(points); assert(g->num_faces <= max_faces); assert(g->num_dots <= max_dots); grid_make_consistent(g); return g; } static void grid_size_greatdodecagonal(int width, int height, int *tilesize, int *xextent, int *yextent) { int a = DODEC_A; int b = DODEC_B; *tilesize = DODEC_TILESIZE; *xextent = (6*a + 2*b) * (width-1) + 2*(2*a + b) + 3*a + b; *yextent = (3*a + 3*b) * (height-1) + 2*(2*a + b); } static grid *grid_new_greatdodecagonal(int width, int height, const char *desc) { int x, y; /* Vector for side of triangle - ratio is close to sqrt(3) */ int a = DODEC_A; int b = DODEC_B; /* Upper bounds - don't have to be exact */ int max_faces = 30 * width * height; int max_dots = 200 * width * height; tree234 *points; grid *g = grid_empty(); g->tilesize = DODEC_TILESIZE; g->faces = snewn(max_faces, grid_face); g->dots = snewn(max_dots, grid_dot); points = newtree234(grid_point_cmp_fn); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { grid_dot *d; /* centre of dodecagon */ int px = (6*a + 2*b) * x; int py = (3*a + 3*b) * y; if (y % 2) px += 3*a + b; /* dodecagon */ grid_face_add_new(g, 12); d = grid_get_dot(g, points, px + ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px + (2*a + b), py - ( a )); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px + (2*a + b), py + ( a )); grid_face_set_dot(g, d, 3); d = grid_get_dot(g, points, px + ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 4); d = grid_get_dot(g, points, px + ( a ), py + (2*a + b)); grid_face_set_dot(g, d, 5); d = grid_get_dot(g, points, px - ( a ), py + (2*a + b)); grid_face_set_dot(g, d, 6); d = grid_get_dot(g, points, px - ( a + b), py + ( a + b)); grid_face_set_dot(g, d, 7); d = grid_get_dot(g, points, px - (2*a + b), py + ( a )); grid_face_set_dot(g, d, 8); d = grid_get_dot(g, points, px - (2*a + b), py - ( a )); grid_face_set_dot(g, d, 9); d = grid_get_dot(g, points, px - ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 10); d = grid_get_dot(g, points, px - ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 11); /* hexagon below dodecagon */ if (y < height - 1 && (x < width - 1 || !(y % 2)) && (x > 0 || (y % 2))) { grid_face_add_new(g, 6); d = grid_get_dot(g, points, px + a, py + (2*a + b)); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + 2*a, py + (2*a + 2*b)); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px + a, py + (2*a + 3*b)); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px - a, py + (2*a + 3*b)); grid_face_set_dot(g, d, 3); d = grid_get_dot(g, points, px - 2*a, py + (2*a + 2*b)); grid_face_set_dot(g, d, 4); d = grid_get_dot(g, points, px - a, py + (2*a + b)); grid_face_set_dot(g, d, 5); } /* hexagon above dodecagon */ if (y && (x < width - 1 || !(y % 2)) && (x > 0 || (y % 2))) { grid_face_add_new(g, 6); d = grid_get_dot(g, points, px - a, py - (2*a + b)); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px - 2*a, py - (2*a + 2*b)); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px - a, py - (2*a + 3*b)); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px + a, py - (2*a + 3*b)); grid_face_set_dot(g, d, 3); d = grid_get_dot(g, points, px + 2*a, py - (2*a + 2*b)); grid_face_set_dot(g, d, 4); d = grid_get_dot(g, points, px + a, py - (2*a + b)); grid_face_set_dot(g, d, 5); } /* square on right of dodecagon */ if (x < width - 1) { grid_face_add_new(g, 4); d = grid_get_dot(g, points, px + 2*a + b, py - a); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + 4*a + b, py - a); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px + 4*a + b, py + a); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px + 2*a + b, py + a); grid_face_set_dot(g, d, 3); } /* square on top right of dodecagon */ if (y && (x < width - 1 || !(y % 2))) { grid_face_add_new(g, 4); d = grid_get_dot(g, points, px + ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px + (2*a ), py - (2*a + 2*b)); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px + (2*a + b), py - ( a + 2*b)); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px + ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 3); } /* square on top left of dodecagon */ if (y && (x || (y % 2))) { grid_face_add_new(g, 4); d = grid_get_dot(g, points, px - ( a + b), py - ( a + b)); grid_face_set_dot(g, d, 0); d = grid_get_dot(g, points, px - (2*a + b), py - ( a + 2*b)); grid_face_set_dot(g, d, 1); d = grid_get_dot(g, points, px - (2*a ), py - (2*a + 2*b)); grid_face_set_dot(g, d, 2); d = grid_get_dot(g, points, px - ( a ), py - (2*a + b)); grid_face_set_dot(g, d, 3); } } } freetree234(points); assert(g->num_faces <= max_faces); assert(g->num_dots <= max_dots); grid_make_consistent(g); return g; } typedef struct setface_ctx { int xmin, xmax, ymin, ymax; grid *g; tree234 *points; } setface_ctx; static double round_int_nearest_away(double r) { return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5); } static int set_faces(penrose_state *state, vector *vs, int n, int depth) { setface_ctx *sf_ctx = (setface_ctx *)state->ctx; int i; int xs[4], ys[4]; if (depth < state->max_depth) return 0; #ifdef DEBUG_PENROSE if (n != 4) return 0; /* triangles are sent as debugging. */ #endif for (i = 0; i < n; i++) { double tx = v_x(vs, i), ty = v_y(vs, i); xs[i] = (int)round_int_nearest_away(tx); ys[i] = (int)round_int_nearest_away(ty); if (xs[i] < sf_ctx->xmin || xs[i] > sf_ctx->xmax) return 0; if (ys[i] < sf_ctx->ymin || ys[i] > sf_ctx->ymax) return 0; } grid_face_add_new(sf_ctx->g, n); debug(("penrose: new face l=%f gen=%d...", penrose_side_length(state->start_size, depth), depth)); for (i = 0; i < n; i++) { grid_dot *d = grid_get_dot(sf_ctx->g, sf_ctx->points, xs[i], ys[i]); grid_face_set_dot(sf_ctx->g, d, i); debug((" ... dot 0x%x (%d,%d) (was %2.2f,%2.2f)", d, d->x, d->y, v_x(vs, i), v_y(vs, i))); } return 0; } #define PENROSE_TILESIZE 100 static void grid_size_penrose(int width, int height, int *tilesize, int *xextent, int *yextent) { int l = PENROSE_TILESIZE; *tilesize = l; *xextent = l * width; *yextent = l * height; } static grid *grid_new_penrose(int width, int height, int which, const char *desc); /* forward reference */ static char *grid_new_desc_penrose(grid_type type, int width, int height, random_state *rs) { int tilesize = PENROSE_TILESIZE, startsz, depth, xoff, yoff, aoff; double outer_radius; int inner_radius; char gd[255]; int which = (type == GRID_PENROSE_P2 ? PENROSE_P2 : PENROSE_P3); grid *g; while (1) { /* We want to produce a random bit of penrose tiling, so we * calculate a random offset (within the patch that penrose.c * calculates for us) and an angle (multiple of 36) to rotate * the patch. */ penrose_calculate_size(which, tilesize, width, height, &outer_radius, &startsz, &depth); /* Calculate radius of (circumcircle of) patch, subtract from * radius calculated. */ inner_radius = (int)(outer_radius - sqrt(width*width + height*height)); /* Pick a random offset (the easy way: choose within outer * square, discarding while it's outside the circle) */ do { xoff = random_upto(rs, 2*inner_radius) - inner_radius; yoff = random_upto(rs, 2*inner_radius) - inner_radius; } while (sqrt(xoff*xoff+yoff*yoff) > inner_radius); aoff = random_upto(rs, 360/36) * 36; debug(("grid_desc: ts %d, %dx%d patch, orad %2.2f irad %d", tilesize, width, height, outer_radius, inner_radius)); debug((" -> xoff %d yoff %d aoff %d", xoff, yoff, aoff)); sprintf(gd, "G%d,%d,%d", xoff, yoff, aoff); /* * Now test-generate our grid, to make sure it actually * produces something. */ g = grid_new_penrose(width, height, which, gd); if (g) { grid_free(g); break; } /* If not, go back to the top of this while loop and try again * with a different random offset. */ } return dupstr(gd); } static char *grid_validate_desc_penrose(grid_type type, int width, int height, const char *desc) { int tilesize = PENROSE_TILESIZE, startsz, depth, xoff, yoff, aoff, inner_radius; double outer_radius; int which = (type == GRID_PENROSE_P2 ? PENROSE_P2 : PENROSE_P3); grid *g; if (!desc) return "Missing grid description string."; penrose_calculate_size(which, tilesize, width, height, &outer_radius, &startsz, &depth); inner_radius = (int)(outer_radius - sqrt(width*width + height*height)); if (sscanf(desc, "G%d,%d,%d", &xoff, &yoff, &aoff) != 3) return "Invalid format grid description string."; if (sqrt(xoff*xoff + yoff*yoff) > inner_radius) return "Patch offset out of bounds."; if ((aoff % 36) != 0 || aoff < 0 || aoff >= 360) return "Angle offset out of bounds."; /* * Test-generate to ensure these parameters don't end us up with * no grid at all. */ g = grid_new_penrose(width, height, which, desc); if (!g) return "Patch coordinates do not identify a usable grid fragment"; grid_free(g); return NULL; } /* * We're asked for a grid of a particular size, and we generate enough * of the tiling so we can be sure to have enough random grid from which * to pick. */ static grid *grid_new_penrose(int width, int height, int which, const char *desc) { int max_faces, max_dots, tilesize = PENROSE_TILESIZE; int xsz, ysz, xoff, yoff, aoff; double rradius; tree234 *points; grid *g; penrose_state ps; setface_ctx sf_ctx; penrose_calculate_size(which, tilesize, width, height, &rradius, &ps.start_size, &ps.max_depth); debug(("penrose: w%d h%d, tile size %d, start size %d, depth %d", width, height, tilesize, ps.start_size, ps.max_depth)); ps.new_tile = set_faces; ps.ctx = &sf_ctx; max_faces = (width*3) * (height*3); /* somewhat paranoid... */ max_dots = max_faces * 4; /* ditto... */ g = grid_empty(); g->tilesize = tilesize; g->faces = snewn(max_faces, grid_face); g->dots = snewn(max_dots, grid_dot); points = newtree234(grid_point_cmp_fn); memset(&sf_ctx, 0, sizeof(sf_ctx)); sf_ctx.g = g; sf_ctx.points = points; if (desc != NULL) { if (sscanf(desc, "G%d,%d,%d", &xoff, &yoff, &aoff) != 3) assert(!"Invalid grid description."); } else { xoff = yoff = aoff = 0; } xsz = width * tilesize; ysz = height * tilesize; sf_ctx.xmin = xoff - xsz/2; sf_ctx.xmax = xoff + xsz/2; sf_ctx.ymin = yoff - ysz/2; sf_ctx.ymax = yoff + ysz/2; debug(("penrose: centre (%f, %f) xsz %f ysz %f", 0.0, 0.0, xsz, ysz)); debug(("penrose: x range (%f --> %f), y range (%f --> %f)", sf_ctx.xmin, sf_ctx.xmax, sf_ctx.ymin, sf_ctx.ymax)); penrose(&ps, which, aoff); freetree234(points); assert(g->num_faces <= max_faces); assert(g->num_dots <= max_dots); debug(("penrose: %d faces total (equivalent to %d wide by %d high)", g->num_faces, g->num_faces/height, g->num_faces/width)); /* * Return NULL if we ended up with an empty grid, either because * the initial generation was over too small a rectangle to * encompass any face or because grid_trim_vigorously ended up * removing absolutely everything. */ if (g->num_faces == 0 || g->num_dots == 0) { grid_free(g); return NULL; } grid_trim_vigorously(g); if (g->num_faces == 0 || g->num_dots == 0) { grid_free(g); return NULL; } grid_make_consistent(g); /* * Centre the grid in its originally promised rectangle. */ g->lowest_x -= ((sf_ctx.xmax - sf_ctx.xmin) - (g->highest_x - g->lowest_x)) / 2; g->highest_x = g->lowest_x + (sf_ctx.xmax - sf_ctx.xmin); g->lowest_y -= ((sf_ctx.ymax - sf_ctx.ymin) - (g->highest_y - g->lowest_y)) / 2; g->highest_y = g->lowest_y + (sf_ctx.ymax - sf_ctx.ymin); return g; } static void grid_size_penrose_p2_kite(int width, int height, int *tilesize, int *xextent, int *yextent) { grid_size_penrose(width, height, tilesize, xextent, yextent); } static void grid_size_penrose_p3_thick(int width, int height, int *tilesize, int *xextent, int *yextent) { grid_size_penrose(width, height, tilesize, xextent, yextent); } static grid *grid_new_penrose_p2_kite(int width, int height, const char *desc) { return grid_new_penrose(width, height, PENROSE_P2, desc); } static grid *grid_new_penrose_p3_thick(int width, int height, const char *desc) { return grid_new_penrose(width, height, PENROSE_P3, desc); } /* ----------- End of grid generators ------------- */ #define FNNEW(upper,lower) &grid_new_ ## lower, #define FNSZ(upper,lower) &grid_size_ ## lower, static grid *(*(grid_news[]))(int, int, const char*) = { GRIDGEN_LIST(FNNEW) }; static void(*(grid_sizes[]))(int, int, int*, int*, int*) = { GRIDGEN_LIST(FNSZ) }; char *grid_new_desc(grid_type type, int width, int height, random_state *rs) { if (type == GRID_PENROSE_P2 || type == GRID_PENROSE_P3) { return grid_new_desc_penrose(type, width, height, rs); } else if (type == GRID_TRIANGULAR) { return dupstr("0"); /* up-to-date version of triangular grid */ } else { return NULL; } } char *grid_validate_desc(grid_type type, int width, int height, const char *desc) { if (type == GRID_PENROSE_P2 || type == GRID_PENROSE_P3) { return grid_validate_desc_penrose(type, width, height, desc); } else if (type == GRID_TRIANGULAR) { return grid_validate_desc_triangular(type, width, height, desc); } else { if (desc != NULL) return "Grid description strings not used with this grid type"; return NULL; } } grid *grid_new(grid_type type, int width, int height, const char *desc) { char *err = grid_validate_desc(type, width, height, desc); if (err) assert(!"Invalid grid description."); return grid_news[type](width, height, desc); } void grid_compute_size(grid_type type, int width, int height, int *tilesize, int *xextent, int *yextent) { grid_sizes[type](width, height, tilesize, xextent, yextent); } /* ----------- End of grid helpers ------------- */ /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/gtk.c0000644000175300017530000023761612131530672013375 0ustar simonsimon/* * gtk.c: GTK front end for my puzzle collection. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "puzzles.h" #if GTK_CHECK_VERSION(2,0,0) # define USE_PANGO # ifdef PANGO_VERSION_CHECK # if PANGO_VERSION_CHECK(1,8,0) # define HAVE_SENSIBLE_ABSOLUTE_SIZE_FUNCTION # endif # endif #endif #if !GTK_CHECK_VERSION(2,4,0) # define OLD_FILESEL #endif #if GTK_CHECK_VERSION(2,8,0) # define USE_CAIRO #endif /* #undef USE_CAIRO */ /* #define NO_THICK_LINE */ #ifdef DEBUGGING static FILE *debug_fp = NULL; void dputs(char *buf) { if (!debug_fp) { debug_fp = fopen("debug.log", "w"); } fputs(buf, stderr); if (debug_fp) { fputs(buf, debug_fp); fflush(debug_fp); } } void debug_printf(char *fmt, ...) { char buf[4096]; va_list ap; va_start(ap, fmt); vsprintf(buf, fmt, ap); dputs(buf); va_end(ap); } #endif /* ---------------------------------------------------------------------- * Error reporting functions used elsewhere. */ void fatal(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); } /* ---------------------------------------------------------------------- * GTK front end to puzzles. */ static void changed_preset(frontend *fe); struct font { #ifdef USE_PANGO PangoFontDescription *desc; #else GdkFont *font; #endif int type; int size; }; /* * This structure holds all the data relevant to a single window. * In principle this would allow us to open multiple independent * puzzle windows, although I can't currently see any real point in * doing so. I'm just coding cleanly because there's no * particularly good reason not to. */ struct frontend { GtkWidget *window; GtkAccelGroup *accelgroup; GtkWidget *area; GtkWidget *statusbar; GtkWidget *menubar; guint statusctx; int w, h; midend *me; #ifdef USE_CAIRO const float *colours; cairo_t *cr; cairo_surface_t *image; GdkPixmap *pixmap; GdkColor background; /* for painting outside puzzle area */ #else GdkPixmap *pixmap; GdkGC *gc; GdkColor *colours; GdkColormap *colmap; int backgroundindex; /* which of colours[] is background */ #endif int ncolours; int bbox_l, bbox_r, bbox_u, bbox_d; int timer_active, timer_id; struct timeval last_time; struct font *fonts; int nfonts, fontsize; config_item *cfg; int cfg_which, cfgret; GtkWidget *cfgbox; void *paste_data; int paste_data_len; int pw, ph; /* pixmap size (w, h are area size) */ int ox, oy; /* offset of pixmap in drawing area */ #ifdef OLD_FILESEL char *filesel_name; #endif int drawing_area_shrink_pending; GSList *preset_radio; int n_preset_menu_items; int preset_threaded; GtkWidget *preset_custom; GtkWidget *copy_menu_item; }; struct blitter { #ifdef USE_CAIRO cairo_surface_t *image; #else GdkPixmap *pixmap; #endif int w, h, x, y; }; void get_random_seed(void **randseed, int *randseedsize) { struct timeval *tvp = snew(struct timeval); gettimeofday(tvp, NULL); *randseed = (void *)tvp; *randseedsize = sizeof(struct timeval); } void frontend_default_colour(frontend *fe, float *output) { GdkColor col = fe->window->style->bg[GTK_STATE_NORMAL]; output[0] = col.red / 65535.0; output[1] = col.green / 65535.0; output[2] = col.blue / 65535.0; } void gtk_status_bar(void *handle, char *text) { frontend *fe = (frontend *)handle; assert(fe->statusbar); gtk_statusbar_pop(GTK_STATUSBAR(fe->statusbar), fe->statusctx); gtk_statusbar_push(GTK_STATUSBAR(fe->statusbar), fe->statusctx, text); } /* ---------------------------------------------------------------------- * Cairo drawing functions. */ #ifdef USE_CAIRO static void setup_drawing(frontend *fe) { fe->cr = cairo_create(fe->image); cairo_set_antialias(fe->cr, CAIRO_ANTIALIAS_GRAY); cairo_set_line_width(fe->cr, 1.0); cairo_set_line_cap(fe->cr, CAIRO_LINE_CAP_SQUARE); cairo_set_line_join(fe->cr, CAIRO_LINE_JOIN_ROUND); } static void teardown_drawing(frontend *fe) { cairo_t *cr; cairo_destroy(fe->cr); fe->cr = NULL; cr = gdk_cairo_create(fe->pixmap); cairo_set_source_surface(cr, fe->image, 0, 0); cairo_rectangle(cr, fe->bbox_l - 1, fe->bbox_u - 1, fe->bbox_r - fe->bbox_l + 2, fe->bbox_d - fe->bbox_u + 2); cairo_fill(cr); cairo_destroy(cr); } static void snaffle_colours(frontend *fe) { fe->colours = midend_colours(fe->me, &fe->ncolours); } static void set_colour(frontend *fe, int colour) { cairo_set_source_rgb(fe->cr, fe->colours[3*colour + 0], fe->colours[3*colour + 1], fe->colours[3*colour + 2]); } static void set_window_background(frontend *fe, int colour) { GdkColormap *colmap; colmap = gdk_colormap_get_system(); fe->background.red = fe->colours[3*colour + 0] * 65535; fe->background.green = fe->colours[3*colour + 1] * 65535; fe->background.blue = fe->colours[3*colour + 2] * 65535; if (!gdk_colormap_alloc_color(colmap, &fe->background, FALSE, FALSE)) { g_error("couldn't allocate background (#%02x%02x%02x)\n", fe->background.red >> 8, fe->background.green >> 8, fe->background.blue >> 8); } gdk_window_set_background(fe->area->window, &fe->background); gdk_window_set_background(fe->window->window, &fe->background); } static PangoLayout *make_pango_layout(frontend *fe) { return (pango_cairo_create_layout(fe->cr)); } static void draw_pango_layout(frontend *fe, PangoLayout *layout, int x, int y) { cairo_move_to(fe->cr, x, y); pango_cairo_show_layout(fe->cr, layout); } static void save_screenshot_png(frontend *fe, const char *screenshot_file) { cairo_surface_write_to_png(fe->image, screenshot_file); } static void do_clip(frontend *fe, int x, int y, int w, int h) { cairo_new_path(fe->cr); cairo_rectangle(fe->cr, x, y, w, h); cairo_clip(fe->cr); } static void do_unclip(frontend *fe) { cairo_reset_clip(fe->cr); } static void do_draw_rect(frontend *fe, int x, int y, int w, int h) { cairo_save(fe->cr); cairo_new_path(fe->cr); cairo_set_antialias(fe->cr, CAIRO_ANTIALIAS_NONE); cairo_rectangle(fe->cr, x, y, w, h); cairo_fill(fe->cr); cairo_restore(fe->cr); } static void do_draw_line(frontend *fe, int x1, int y1, int x2, int y2) { cairo_new_path(fe->cr); cairo_move_to(fe->cr, x1 + 0.5, y1 + 0.5); cairo_line_to(fe->cr, x2 + 0.5, y2 + 0.5); cairo_stroke(fe->cr); } static void do_draw_thick_line(frontend *fe, float thickness, float x1, float y1, float x2, float y2) { cairo_save(fe->cr); cairo_set_line_width(fe->cr, thickness); cairo_new_path(fe->cr); cairo_move_to(fe->cr, x1, y1); cairo_line_to(fe->cr, x2, y2); cairo_stroke(fe->cr); cairo_restore(fe->cr); } static void do_draw_poly(frontend *fe, int *coords, int npoints, int fillcolour, int outlinecolour) { int i; cairo_new_path(fe->cr); for (i = 0; i < npoints; i++) cairo_line_to(fe->cr, coords[i*2] + 0.5, coords[i*2 + 1] + 0.5); cairo_close_path(fe->cr); if (fillcolour >= 0) { set_colour(fe, fillcolour); cairo_fill_preserve(fe->cr); } assert(outlinecolour >= 0); set_colour(fe, outlinecolour); cairo_stroke(fe->cr); } static void do_draw_circle(frontend *fe, int cx, int cy, int radius, int fillcolour, int outlinecolour) { cairo_new_path(fe->cr); cairo_arc(fe->cr, cx + 0.5, cy + 0.5, radius, 0, 2*PI); cairo_close_path(fe->cr); /* Just in case... */ if (fillcolour >= 0) { set_colour(fe, fillcolour); cairo_fill_preserve(fe->cr); } assert(outlinecolour >= 0); set_colour(fe, outlinecolour); cairo_stroke(fe->cr); } static void setup_blitter(blitter *bl, int w, int h) { bl->image = cairo_image_surface_create(CAIRO_FORMAT_RGB24, w, h); } static void teardown_blitter(blitter *bl) { cairo_surface_destroy(bl->image); } static void do_blitter_save(frontend *fe, blitter *bl, int x, int y) { cairo_t *cr = cairo_create(bl->image); cairo_set_source_surface(cr, fe->image, -x, -y); cairo_paint(cr); cairo_destroy(cr); } static void do_blitter_load(frontend *fe, blitter *bl, int x, int y) { cairo_set_source_surface(fe->cr, bl->image, x, y); cairo_paint(fe->cr); } static void clear_backing_store(frontend *fe) { fe->image = NULL; } static void setup_backing_store(frontend *fe) { cairo_t *cr; int i; fe->pixmap = gdk_pixmap_new(fe->area->window, fe->pw, fe->ph, -1); fe->image = cairo_image_surface_create(CAIRO_FORMAT_RGB24, fe->pw, fe->ph); for (i = 0; i < 3; i++) { switch (i) { case 0: cr = cairo_create(fe->image); break; case 1: cr = gdk_cairo_create(fe->pixmap); break; case 2: cr = gdk_cairo_create(fe->area->window); break; } cairo_set_source_rgb(cr, fe->colours[0], fe->colours[1], fe->colours[2]); cairo_paint(cr); cairo_destroy(cr); } } static int backing_store_ok(frontend *fe) { return (!!fe->image); } static void teardown_backing_store(frontend *fe) { cairo_surface_destroy(fe->image); gdk_pixmap_unref(fe->pixmap); fe->image = NULL; } #endif /* ---------------------------------------------------------------------- * GDK drawing functions. */ #ifndef USE_CAIRO static void setup_drawing(frontend *fe) { fe->gc = gdk_gc_new(fe->area->window); } static void teardown_drawing(frontend *fe) { gdk_gc_unref(fe->gc); fe->gc = NULL; } static void snaffle_colours(frontend *fe) { int i, ncolours; float *colours; gboolean *success; fe->colmap = gdk_colormap_get_system(); colours = midend_colours(fe->me, &ncolours); fe->ncolours = ncolours; fe->colours = snewn(ncolours, GdkColor); for (i = 0; i < ncolours; i++) { fe->colours[i].red = colours[i*3] * 0xFFFF; fe->colours[i].green = colours[i*3+1] * 0xFFFF; fe->colours[i].blue = colours[i*3+2] * 0xFFFF; } success = snewn(ncolours, gboolean); gdk_colormap_alloc_colors(fe->colmap, fe->colours, ncolours, FALSE, FALSE, success); for (i = 0; i < ncolours; i++) { if (!success[i]) { g_error("couldn't allocate colour %d (#%02x%02x%02x)\n", i, fe->colours[i].red >> 8, fe->colours[i].green >> 8, fe->colours[i].blue >> 8); } } } static void set_window_background(frontend *fe, int colour) { fe->backgroundindex = colour; gdk_window_set_background(fe->area->window, &fe->colours[colour]); gdk_window_set_background(fe->window->window, &fe->colours[colour]); } static void set_colour(frontend *fe, int colour) { gdk_gc_set_foreground(fe->gc, &fe->colours[colour]); } #ifdef USE_PANGO static PangoLayout *make_pango_layout(frontend *fe) { return (pango_layout_new(gtk_widget_get_pango_context(fe->area))); } static void draw_pango_layout(frontend *fe, PangoLayout *layout, int x, int y) { gdk_draw_layout(fe->pixmap, fe->gc, x, y, layout); } #endif static void save_screenshot_png(frontend *fe, const char *screenshot_file) { GdkPixbuf *pb; GError *gerror = NULL; midend_redraw(fe->me); pb = gdk_pixbuf_get_from_drawable(NULL, fe->pixmap, NULL, 0, 0, 0, 0, -1, -1); gdk_pixbuf_save(pb, screenshot_file, "png", &gerror, NULL); } static void do_clip(frontend *fe, int x, int y, int w, int h) { GdkRectangle rect; rect.x = x; rect.y = y; rect.width = w; rect.height = h; gdk_gc_set_clip_rectangle(fe->gc, &rect); } static void do_unclip(frontend *fe) { GdkRectangle rect; rect.x = 0; rect.y = 0; rect.width = fe->w; rect.height = fe->h; gdk_gc_set_clip_rectangle(fe->gc, &rect); } static void do_draw_rect(frontend *fe, int x, int y, int w, int h) { gdk_draw_rectangle(fe->pixmap, fe->gc, 1, x, y, w, h); } static void do_draw_line(frontend *fe, int x1, int y1, int x2, int y2) { gdk_draw_line(fe->pixmap, fe->gc, x1, y1, x2, y2); } static void do_draw_thick_line(frontend *fe, float thickness, float x1, float y1, float x2, float y2) { GdkGCValues save; gdk_gc_get_values(fe->gc, &save); gdk_gc_set_line_attributes(fe->gc, thickness, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_BEVEL); gdk_draw_line(fe->pixmap, fe->gc, x1, y1, x2, y2); gdk_gc_set_line_attributes(fe->gc, save.line_width, save.line_style, save.cap_style, save.join_style); } static void do_draw_poly(frontend *fe, int *coords, int npoints, int fillcolour, int outlinecolour) { GdkPoint *points = snewn(npoints, GdkPoint); int i; for (i = 0; i < npoints; i++) { points[i].x = coords[i*2]; points[i].y = coords[i*2+1]; } if (fillcolour >= 0) { set_colour(fe, fillcolour); gdk_draw_polygon(fe->pixmap, fe->gc, TRUE, points, npoints); } assert(outlinecolour >= 0); set_colour(fe, outlinecolour); /* * In principle we ought to be able to use gdk_draw_polygon for * the outline as well. In fact, it turns out to interact badly * with a clipping region, for no terribly obvious reason, so I * draw the outline as a sequence of lines instead. */ for (i = 0; i < npoints; i++) gdk_draw_line(fe->pixmap, fe->gc, points[i].x, points[i].y, points[(i+1)%npoints].x, points[(i+1)%npoints].y); sfree(points); } static void do_draw_circle(frontend *fe, int cx, int cy, int radius, int fillcolour, int outlinecolour) { if (fillcolour >= 0) { set_colour(fe, fillcolour); gdk_draw_arc(fe->pixmap, fe->gc, TRUE, cx - radius, cy - radius, 2 * radius, 2 * radius, 0, 360 * 64); } assert(outlinecolour >= 0); set_colour(fe, outlinecolour); gdk_draw_arc(fe->pixmap, fe->gc, FALSE, cx - radius, cy - radius, 2 * radius, 2 * radius, 0, 360 * 64); } static void setup_blitter(blitter *bl, int w, int h) { /* * We can't create the pixmap right now, because fe->window * might not yet exist. So we just cache w and h and create it * during the firs call to blitter_save. */ bl->pixmap = NULL; } static void teardown_blitter(blitter *bl) { if (bl->pixmap) gdk_pixmap_unref(bl->pixmap); } static void do_blitter_save(frontend *fe, blitter *bl, int x, int y) { if (!bl->pixmap) bl->pixmap = gdk_pixmap_new(fe->area->window, bl->w, bl->h, -1); gdk_draw_pixmap(bl->pixmap, fe->area->style->fg_gc[GTK_WIDGET_STATE(fe->area)], fe->pixmap, x, y, 0, 0, bl->w, bl->h); } static void do_blitter_load(frontend *fe, blitter *bl, int x, int y) { assert(bl->pixmap); gdk_draw_pixmap(fe->pixmap, fe->area->style->fg_gc[GTK_WIDGET_STATE(fe->area)], bl->pixmap, 0, 0, x, y, bl->w, bl->h); } static void clear_backing_store(frontend *fe) { fe->pixmap = NULL; } static void setup_backing_store(frontend *fe) { GdkGC *gc; fe->pixmap = gdk_pixmap_new(fe->area->window, fe->pw, fe->ph, -1); gc = gdk_gc_new(fe->area->window); gdk_gc_set_foreground(gc, &fe->colours[0]); gdk_draw_rectangle(fe->pixmap, gc, 1, 0, 0, fe->pw, fe->ph); gdk_draw_rectangle(fe->area->window, gc, 1, 0, 0, fe->w, fe->h); gdk_gc_unref(gc); } static int backing_store_ok(frontend *fe) { return (!!fe->pixmap); } static void teardown_backing_store(frontend *fe) { gdk_pixmap_unref(fe->pixmap); fe->pixmap = NULL; } #endif static void repaint_rectangle(frontend *fe, GtkWidget *widget, int x, int y, int w, int h) { GdkGC *gc = gdk_gc_new(widget->window); #ifdef USE_CAIRO gdk_gc_set_foreground(gc, &fe->background); #else gdk_gc_set_foreground(gc, &fe->colours[fe->backgroundindex]); #endif if (x < fe->ox) { gdk_draw_rectangle(widget->window, gc, TRUE, x, y, fe->ox - x, h); w -= (fe->ox - x); x = fe->ox; } if (y < fe->oy) { gdk_draw_rectangle(widget->window, gc, TRUE, x, y, w, fe->oy - y); h -= (fe->oy - y); y = fe->oy; } if (w > fe->pw) { gdk_draw_rectangle(widget->window, gc, TRUE, x + fe->pw, y, w - fe->pw, h); w = fe->pw; } if (h > fe->ph) { gdk_draw_rectangle(widget->window, gc, TRUE, x, y + fe->ph, w, h - fe->ph); h = fe->ph; } gdk_draw_pixmap(widget->window, gc, fe->pixmap, x - fe->ox, y - fe->oy, x, y, w, h); gdk_gc_unref(gc); } /* ---------------------------------------------------------------------- * Pango font functions. */ #ifdef USE_PANGO static void add_font(frontend *fe, int index, int fonttype, int fontsize) { /* * Use Pango to find the closest match to the requested * font. */ PangoFontDescription *fd; fd = pango_font_description_new(); /* `Monospace' and `Sans' are meta-families guaranteed to exist */ pango_font_description_set_family(fd, fonttype == FONT_FIXED ? "Monospace" : "Sans"); pango_font_description_set_weight(fd, PANGO_WEIGHT_BOLD); /* * I found some online Pango documentation which * described a function called * pango_font_description_set_absolute_size(), which is * _exactly_ what I want here. Unfortunately, none of * my local Pango installations have it (presumably * they're too old), so I'm going to have to hack round * it by figuring out the point size myself. This * limits me to X and probably also breaks in later * Pango installations, so ideally I should add another * CHECK_VERSION type ifdef and use set_absolute_size * where available. All very annoying. */ #ifdef HAVE_SENSIBLE_ABSOLUTE_SIZE_FUNCTION pango_font_description_set_absolute_size(fd, PANGO_SCALE*fontsize); #else { Display *d = GDK_DISPLAY(); int s = DefaultScreen(d); double resolution = (PANGO_SCALE * 72.27 / 25.4) * ((double) DisplayWidthMM(d, s) / DisplayWidth (d, s)); pango_font_description_set_size(fd, resolution * fontsize); } #endif fe->fonts[index].desc = fd; } static void align_and_draw_text(frontend *fe, int index, int align, int x, int y, const char *text) { PangoLayout *layout; PangoRectangle rect; layout = make_pango_layout(fe); /* * Create a layout. */ pango_layout_set_font_description(layout, fe->fonts[index].desc); pango_layout_set_text(layout, text, strlen(text)); pango_layout_get_pixel_extents(layout, NULL, &rect); if (align & ALIGN_VCENTRE) rect.y -= rect.height / 2; else rect.y -= rect.height; if (align & ALIGN_HCENTRE) rect.x -= rect.width / 2; else if (align & ALIGN_HRIGHT) rect.x -= rect.width; draw_pango_layout(fe, layout, rect.x + x, rect.y + y); g_object_unref(layout); } #endif /* ---------------------------------------------------------------------- * Old-fashioned font functions. */ #ifndef USE_PANGO static void add_font(int index, int fonttype, int fontsize) { /* * In GTK 1.2, I don't know of any plausible way to * pick a suitable font, so I'm just going to be * tedious. */ fe->fonts[i].font = gdk_font_load(fonttype == FONT_FIXED ? "fixed" : "variable"); } static void align_and_draw_text(int index, int align, int x, int y, const char *text) { int lb, rb, wid, asc, desc; /* * Measure vertical string extents with respect to the same * string always... */ gdk_string_extents(fe->fonts[i].font, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", &lb, &rb, &wid, &asc, &desc); if (align & ALIGN_VCENTRE) y += asc - (asc+desc)/2; else y += asc; /* * ... but horizontal extents with respect to the provided * string. This means that multiple pieces of text centred * on the same y-coordinate don't have different baselines. */ gdk_string_extents(fe->fonts[i].font, text, &lb, &rb, &wid, &asc, &desc); if (align & ALIGN_HCENTRE) x -= wid / 2; else if (align & ALIGN_HRIGHT) x -= wid; /* * Actually draw the text. */ gdk_draw_string(fe->pixmap, fe->fonts[i].font, fe->gc, x, y, text); } #endif /* ---------------------------------------------------------------------- * The exported drawing functions. */ void gtk_start_draw(void *handle) { frontend *fe = (frontend *)handle; fe->bbox_l = fe->w; fe->bbox_r = 0; fe->bbox_u = fe->h; fe->bbox_d = 0; setup_drawing(fe); } void gtk_clip(void *handle, int x, int y, int w, int h) { frontend *fe = (frontend *)handle; do_clip(fe, x, y, w, h); } void gtk_unclip(void *handle) { frontend *fe = (frontend *)handle; do_unclip(fe); } void gtk_draw_text(void *handle, int x, int y, int fonttype, int fontsize, int align, int colour, char *text) { frontend *fe = (frontend *)handle; int i; /* * Find or create the font. */ for (i = 0; i < fe->nfonts; i++) if (fe->fonts[i].type == fonttype && fe->fonts[i].size == fontsize) break; if (i == fe->nfonts) { if (fe->fontsize <= fe->nfonts) { fe->fontsize = fe->nfonts + 10; fe->fonts = sresize(fe->fonts, fe->fontsize, struct font); } fe->nfonts++; fe->fonts[i].type = fonttype; fe->fonts[i].size = fontsize; add_font(fe, i, fonttype, fontsize); } /* * Do the job. */ set_colour(fe, colour); align_and_draw_text(fe, i, align, x, y, text); } void gtk_draw_rect(void *handle, int x, int y, int w, int h, int colour) { frontend *fe = (frontend *)handle; set_colour(fe, colour); do_draw_rect(fe, x, y, w, h); } void gtk_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour) { frontend *fe = (frontend *)handle; set_colour(fe, colour); do_draw_line(fe, x1, y1, x2, y2); } void gtk_draw_thick_line(void *handle, float thickness, float x1, float y1, float x2, float y2, int colour) { frontend *fe = (frontend *)handle; set_colour(fe, colour); do_draw_thick_line(fe, thickness, x1, y1, x2, y2); } void gtk_draw_poly(void *handle, int *coords, int npoints, int fillcolour, int outlinecolour) { frontend *fe = (frontend *)handle; do_draw_poly(fe, coords, npoints, fillcolour, outlinecolour); } void gtk_draw_circle(void *handle, int cx, int cy, int radius, int fillcolour, int outlinecolour) { frontend *fe = (frontend *)handle; do_draw_circle(fe, cx, cy, radius, fillcolour, outlinecolour); } blitter *gtk_blitter_new(void *handle, int w, int h) { blitter *bl = snew(blitter); setup_blitter(bl, w, h); bl->w = w; bl->h = h; return bl; } void gtk_blitter_free(void *handle, blitter *bl) { teardown_blitter(bl); sfree(bl); } void gtk_blitter_save(void *handle, blitter *bl, int x, int y) { frontend *fe = (frontend *)handle; do_blitter_save(fe, bl, x, y); bl->x = x; bl->y = y; } void gtk_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; } do_blitter_load(fe, bl, x, y); } void gtk_draw_update(void *handle, int x, int y, int w, int h) { frontend *fe = (frontend *)handle; if (fe->bbox_l > x ) fe->bbox_l = x ; if (fe->bbox_r < x+w) fe->bbox_r = x+w; if (fe->bbox_u > y ) fe->bbox_u = y ; if (fe->bbox_d < y+h) fe->bbox_d = y+h; } void gtk_end_draw(void *handle) { frontend *fe = (frontend *)handle; teardown_drawing(fe); if (fe->bbox_l < fe->bbox_r && fe->bbox_u < fe->bbox_d) { repaint_rectangle(fe, fe->area, fe->bbox_l - 1 + fe->ox, fe->bbox_u - 1 + fe->oy, fe->bbox_r - fe->bbox_l + 2, fe->bbox_d - fe->bbox_u + 2); } } #ifdef USE_PANGO char *gtk_text_fallback(void *handle, const char *const *strings, int nstrings) { /* * We assume Pango can cope with any UTF-8 likely to be emitted * by a puzzle. */ return dupstr(strings[0]); } #endif const struct drawing_api gtk_drawing = { gtk_draw_text, gtk_draw_rect, gtk_draw_line, gtk_draw_poly, gtk_draw_circle, gtk_draw_update, gtk_clip, gtk_unclip, gtk_start_draw, gtk_end_draw, gtk_status_bar, gtk_blitter_new, gtk_blitter_free, gtk_blitter_save, gtk_blitter_load, NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */ NULL, NULL, /* line_width, line_dotted */ #ifdef USE_PANGO gtk_text_fallback, #else NULL, #endif #ifdef NO_THICK_LINE NULL, #else gtk_draw_thick_line, #endif }; static void destroy(GtkWidget *widget, gpointer data) { frontend *fe = (frontend *)data; deactivate_timer(fe); midend_free(fe->me); gtk_main_quit(); } static gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) { frontend *fe = (frontend *)data; int keyval; int shift = (event->state & GDK_SHIFT_MASK) ? MOD_SHFT : 0; int ctrl = (event->state & GDK_CONTROL_MASK) ? MOD_CTRL : 0; if (!backing_store_ok(fe)) return TRUE; #if !GTK_CHECK_VERSION(2,0,0) /* Gtk 1.2 passes a key event to this function even if it's also * defined as an accelerator. * Gtk 2 doesn't do this, and this function appears not to exist there. */ if (fe->accelgroup && gtk_accel_group_get_entry(fe->accelgroup, event->keyval, event->state)) return TRUE; #endif /* Handle mnemonics. */ if (gtk_window_activate_key(GTK_WINDOW(fe->window), event)) return TRUE; if (event->keyval == GDK_Up) keyval = shift | ctrl | CURSOR_UP; else if (event->keyval == GDK_KP_Up || event->keyval == GDK_KP_8) keyval = MOD_NUM_KEYPAD | '8'; else if (event->keyval == GDK_Down) keyval = shift | ctrl | CURSOR_DOWN; else if (event->keyval == GDK_KP_Down || event->keyval == GDK_KP_2) keyval = MOD_NUM_KEYPAD | '2'; else if (event->keyval == GDK_Left) keyval = shift | ctrl | CURSOR_LEFT; else if (event->keyval == GDK_KP_Left || event->keyval == GDK_KP_4) keyval = MOD_NUM_KEYPAD | '4'; else if (event->keyval == GDK_Right) keyval = shift | ctrl | CURSOR_RIGHT; else if (event->keyval == GDK_KP_Right || event->keyval == GDK_KP_6) keyval = MOD_NUM_KEYPAD | '6'; else if (event->keyval == GDK_KP_Home || event->keyval == GDK_KP_7) keyval = MOD_NUM_KEYPAD | '7'; else if (event->keyval == GDK_KP_End || event->keyval == GDK_KP_1) keyval = MOD_NUM_KEYPAD | '1'; else if (event->keyval == GDK_KP_Page_Up || event->keyval == GDK_KP_9) keyval = MOD_NUM_KEYPAD | '9'; else if (event->keyval == GDK_KP_Page_Down || event->keyval == GDK_KP_3) keyval = MOD_NUM_KEYPAD | '3'; else if (event->keyval == GDK_KP_Insert || event->keyval == GDK_KP_0) keyval = MOD_NUM_KEYPAD | '0'; else if (event->keyval == GDK_KP_Begin || event->keyval == GDK_KP_5) keyval = MOD_NUM_KEYPAD | '5'; else if (event->keyval == GDK_BackSpace || event->keyval == GDK_Delete || event->keyval == GDK_KP_Delete) keyval = '\177'; else if (event->string[0] && !event->string[1]) keyval = (unsigned char)event->string[0]; else keyval = -1; if (keyval >= 0 && !midend_process_key(fe->me, 0, 0, keyval)) gtk_widget_destroy(fe->window); return TRUE; } static gint button_event(GtkWidget *widget, GdkEventButton *event, gpointer data) { frontend *fe = (frontend *)data; int button; if (!backing_store_ok(fe)) return TRUE; if (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) return TRUE; if (event->button == 2 || (event->state & GDK_SHIFT_MASK)) button = MIDDLE_BUTTON; else if (event->button == 3 || (event->state & GDK_MOD1_MASK)) button = RIGHT_BUTTON; else if (event->button == 1) button = LEFT_BUTTON; else return FALSE; /* don't even know what button! */ if (event->type == GDK_BUTTON_RELEASE) button += LEFT_RELEASE - LEFT_BUTTON; if (!midend_process_key(fe->me, event->x - fe->ox, event->y - fe->oy, button)) gtk_widget_destroy(fe->window); return TRUE; } static gint motion_event(GtkWidget *widget, GdkEventMotion *event, gpointer data) { frontend *fe = (frontend *)data; int button; if (!backing_store_ok(fe)) return TRUE; if (event->state & (GDK_BUTTON2_MASK | GDK_SHIFT_MASK)) button = MIDDLE_DRAG; else if (event->state & GDK_BUTTON1_MASK) button = LEFT_DRAG; else if (event->state & GDK_BUTTON3_MASK) button = RIGHT_DRAG; else return FALSE; /* don't even know what button! */ if (!midend_process_key(fe->me, event->x - fe->ox, event->y - fe->oy, button)) gtk_widget_destroy(fe->window); #if GTK_CHECK_VERSION(2,12,0) gdk_event_request_motions(event); #else gdk_window_get_pointer(widget->window, NULL, NULL, NULL); #endif return TRUE; } static gint expose_area(GtkWidget *widget, GdkEventExpose *event, gpointer data) { frontend *fe = (frontend *)data; if (backing_store_ok(fe)) { repaint_rectangle(fe, widget, event->area.x, event->area.y, event->area.width, event->area.height); } return TRUE; } static gint map_window(GtkWidget *widget, GdkEvent *event, gpointer data) { frontend *fe = (frontend *)data; /* * Apparently we need to do this because otherwise the status * bar will fail to update immediately. Annoying, but there we * go. */ gtk_widget_queue_draw(fe->window); return TRUE; } static gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data) { frontend *fe = (frontend *)data; int x, y; if (backing_store_ok(fe)) teardown_backing_store(fe); x = fe->w = event->width; y = fe->h = event->height; midend_size(fe->me, &x, &y, TRUE); fe->pw = x; fe->ph = y; fe->ox = (fe->w - fe->pw) / 2; fe->oy = (fe->h - fe->ph) / 2; setup_backing_store(fe); midend_force_redraw(fe->me); return TRUE; } static gint timer_func(gpointer data) { frontend *fe = (frontend *)data; if (fe->timer_active) { struct timeval now; float elapsed; gettimeofday(&now, NULL); elapsed = ((now.tv_usec - fe->last_time.tv_usec) * 0.000001F + (now.tv_sec - fe->last_time.tv_sec)); midend_timer(fe->me, elapsed); /* may clear timer_active */ fe->last_time = now; } return fe->timer_active; } void deactivate_timer(frontend *fe) { if (!fe) return; /* can happen due to --generate */ if (fe->timer_active) gtk_timeout_remove(fe->timer_id); fe->timer_active = FALSE; } void activate_timer(frontend *fe) { if (!fe) return; /* can happen due to --generate */ if (!fe->timer_active) { fe->timer_id = gtk_timeout_add(20, timer_func, fe); gettimeofday(&fe->last_time, NULL); } fe->timer_active = TRUE; } static void window_destroy(GtkWidget *widget, gpointer data) { gtk_main_quit(); } static void msgbox_button_clicked(GtkButton *button, gpointer data) { GtkWidget *window = GTK_WIDGET(data); int v, *ip; ip = (int *)gtk_object_get_data(GTK_OBJECT(window), "user-data"); v = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(button), "user-data")); *ip = v; gtk_widget_destroy(GTK_WIDGET(data)); } static int win_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data) { GtkObject *cancelbutton = GTK_OBJECT(data); /* * `Escape' effectively clicks the cancel button */ if (event->keyval == GDK_Escape) { gtk_signal_emit_by_name(GTK_OBJECT(cancelbutton), "clicked"); return TRUE; } return FALSE; } enum { MB_OK, MB_YESNO }; int message_box(GtkWidget *parent, char *title, char *msg, int centre, int type) { GtkWidget *window, *hbox, *text, *button; char *titles; int i, def, cancel; window = gtk_dialog_new(); text = gtk_label_new(msg); gtk_misc_set_alignment(GTK_MISC(text), 0.0, 0.0); hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox), text, FALSE, FALSE, 20); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox), hbox, FALSE, FALSE, 20); gtk_widget_show(text); gtk_widget_show(hbox); gtk_window_set_title(GTK_WINDOW(window), title); gtk_label_set_line_wrap(GTK_LABEL(text), TRUE); if (type == MB_OK) { titles = GTK_STOCK_OK "\0"; def = cancel = 0; } else { assert(type == MB_YESNO); titles = GTK_STOCK_NO "\0" GTK_STOCK_YES "\0"; def = 1; cancel = 0; } i = 0; while (*titles) { button = gtk_button_new_from_stock(titles); gtk_box_pack_end(GTK_BOX(GTK_DIALOG(window)->action_area), button, FALSE, FALSE, 0); gtk_widget_show(button); if (i == def) { GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT); gtk_window_set_default(GTK_WINDOW(window), button); } if (i == cancel) { gtk_signal_connect(GTK_OBJECT(window), "key_press_event", GTK_SIGNAL_FUNC(win_key_press), button); } gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(msgbox_button_clicked), window); gtk_object_set_data(GTK_OBJECT(button), "user-data", GINT_TO_POINTER(i)); titles += strlen(titles)+1; i++; } gtk_object_set_data(GTK_OBJECT(window), "user-data", GINT_TO_POINTER(&i)); gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(window_destroy), NULL); gtk_window_set_modal(GTK_WINDOW(window), TRUE); gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(parent)); /* set_transient_window_pos(parent, window); */ gtk_widget_show(window); i = -1; gtk_main(); return (type == MB_YESNO ? i == 1 : TRUE); } void error_box(GtkWidget *parent, char *msg) { message_box(parent, "Error", msg, FALSE, MB_OK); } static void config_ok_button_clicked(GtkButton *button, gpointer data) { frontend *fe = (frontend *)data; char *err; err = midend_set_config(fe->me, fe->cfg_which, fe->cfg); if (err) error_box(fe->cfgbox, err); else { fe->cfgret = TRUE; gtk_widget_destroy(fe->cfgbox); changed_preset(fe); } } static void config_cancel_button_clicked(GtkButton *button, gpointer data) { frontend *fe = (frontend *)data; gtk_widget_destroy(fe->cfgbox); } static int editbox_key(GtkWidget *widget, GdkEventKey *event, gpointer data) { /* * GtkEntry has a nasty habit of eating the Return key, which * is unhelpful since it doesn't actually _do_ anything with it * (it calls gtk_widget_activate, but our edit boxes never need * activating). So I catch Return before GtkEntry sees it, and * pass it straight on to the parent widget. Effect: hitting * Return in an edit box will now activate the default button * in the dialog just like it will everywhere else. */ if (event->keyval == GDK_Return && widget->parent != NULL) { gint return_val; gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), "key_press_event"); gtk_signal_emit_by_name(GTK_OBJECT(widget->parent), "key_press_event", event, &return_val); return return_val; } return FALSE; } static void editbox_changed(GtkEditable *ed, gpointer data) { config_item *i = (config_item *)data; sfree(i->sval); i->sval = dupstr(gtk_entry_get_text(GTK_ENTRY(ed))); } static void button_toggled(GtkToggleButton *tb, gpointer data) { config_item *i = (config_item *)data; i->ival = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tb)); } static void droplist_sel(GtkMenuItem *item, gpointer data) { config_item *i = (config_item *)data; i->ival = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(item), "user-data")); } static int get_config(frontend *fe, int which) { GtkWidget *w, *table, *cancel; char *title; config_item *i; int y; fe->cfg = midend_get_config(fe->me, which, &title); fe->cfg_which = which; fe->cfgret = FALSE; fe->cfgbox = gtk_dialog_new(); gtk_window_set_title(GTK_WINDOW(fe->cfgbox), title); sfree(title); w = gtk_button_new_from_stock(GTK_STOCK_CANCEL); gtk_box_pack_end(GTK_BOX(GTK_DIALOG(fe->cfgbox)->action_area), w, FALSE, FALSE, 0); gtk_widget_show(w); gtk_signal_connect(GTK_OBJECT(w), "clicked", GTK_SIGNAL_FUNC(config_cancel_button_clicked), fe); cancel = w; w = gtk_button_new_from_stock(GTK_STOCK_OK); gtk_box_pack_end(GTK_BOX(GTK_DIALOG(fe->cfgbox)->action_area), w, FALSE, FALSE, 0); gtk_widget_show(w); GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT); gtk_window_set_default(GTK_WINDOW(fe->cfgbox), w); gtk_signal_connect(GTK_OBJECT(w), "clicked", GTK_SIGNAL_FUNC(config_ok_button_clicked), fe); table = gtk_table_new(1, 2, FALSE); y = 0; gtk_box_pack_end(GTK_BOX(GTK_DIALOG(fe->cfgbox)->vbox), table, FALSE, FALSE, 0); gtk_widget_show(table); for (i = fe->cfg; i->type != C_END; i++) { gtk_table_resize(GTK_TABLE(table), y+1, 2); switch (i->type) { case C_STRING: /* * Edit box with a label beside it. */ w = gtk_label_new(i->name); gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.5); gtk_table_attach(GTK_TABLE(table), w, 0, 1, y, y+1, GTK_SHRINK | GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 3, 3); gtk_widget_show(w); w = gtk_entry_new(); gtk_table_attach(GTK_TABLE(table), w, 1, 2, y, y+1, GTK_EXPAND | GTK_SHRINK | GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 3, 3); gtk_entry_set_text(GTK_ENTRY(w), i->sval); gtk_signal_connect(GTK_OBJECT(w), "changed", GTK_SIGNAL_FUNC(editbox_changed), i); gtk_signal_connect(GTK_OBJECT(w), "key_press_event", GTK_SIGNAL_FUNC(editbox_key), NULL); gtk_widget_show(w); break; case C_BOOLEAN: /* * Simple checkbox. */ w = gtk_check_button_new_with_label(i->name); gtk_signal_connect(GTK_OBJECT(w), "toggled", GTK_SIGNAL_FUNC(button_toggled), i); gtk_table_attach(GTK_TABLE(table), w, 0, 2, y, y+1, GTK_EXPAND | GTK_SHRINK | GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 3, 3); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), i->ival); gtk_widget_show(w); break; case C_CHOICES: /* * Drop-down list (GtkOptionMenu). */ w = gtk_label_new(i->name); gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.5); gtk_table_attach(GTK_TABLE(table), w, 0, 1, y, y+1, GTK_SHRINK | GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL , 3, 3); gtk_widget_show(w); w = gtk_option_menu_new(); gtk_table_attach(GTK_TABLE(table), w, 1, 2, y, y+1, GTK_EXPAND | GTK_SHRINK | GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 3, 3); gtk_widget_show(w); { int c, val; char *p, *q, *name; GtkWidget *menuitem; GtkWidget *menu = gtk_menu_new(); gtk_option_menu_set_menu(GTK_OPTION_MENU(w), menu); c = *i->sval; p = i->sval+1; val = 0; while (*p) { q = p; while (*q && *q != c) q++; name = snewn(q-p+1, char); strncpy(name, p, q-p); name[q-p] = '\0'; if (*q) q++; /* eat delimiter */ menuitem = gtk_menu_item_new_with_label(name); gtk_container_add(GTK_CONTAINER(menu), menuitem); gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", GINT_TO_POINTER(val)); gtk_signal_connect(GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(droplist_sel), i); gtk_widget_show(menuitem); val++; p = q; } gtk_option_menu_set_history(GTK_OPTION_MENU(w), i->ival); } break; } y++; } gtk_signal_connect(GTK_OBJECT(fe->cfgbox), "destroy", GTK_SIGNAL_FUNC(window_destroy), NULL); gtk_signal_connect(GTK_OBJECT(fe->cfgbox), "key_press_event", GTK_SIGNAL_FUNC(win_key_press), cancel); gtk_window_set_modal(GTK_WINDOW(fe->cfgbox), TRUE); gtk_window_set_transient_for(GTK_WINDOW(fe->cfgbox), GTK_WINDOW(fe->window)); /* set_transient_window_pos(fe->window, fe->cfgbox); */ gtk_widget_show(fe->cfgbox); gtk_main(); free_cfg(fe->cfg); return fe->cfgret; } static void menu_key_event(GtkMenuItem *menuitem, gpointer data) { frontend *fe = (frontend *)data; int key = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(menuitem), "user-data")); if (!midend_process_key(fe->me, 0, 0, key)) gtk_widget_destroy(fe->window); } static void get_size(frontend *fe, int *px, int *py) { int x, y; /* * Currently I don't want to make the GTK port scale large * puzzles to fit on the screen. This is because X does permit * extremely large windows and many window managers provide a * means of navigating round them, and the users I consulted * before deciding said that they'd rather have enormous puzzle * windows spanning multiple screen pages than have them * shrunk. I could change my mind later or introduce * configurability; this would be the place to do so, by * replacing the initial values of x and y with the screen * dimensions. */ x = INT_MAX; y = INT_MAX; midend_size(fe->me, &x, &y, FALSE); *px = x; *py = y; } #if !GTK_CHECK_VERSION(2,0,0) #define gtk_window_resize(win, x, y) \ gdk_window_resize(GTK_WIDGET(win)->window, x, y) #endif /* * Called when any other code in this file has changed the * selected game parameters. */ static void changed_preset(frontend *fe) { int n = midend_which_preset(fe->me); fe->preset_threaded = TRUE; if (n < 0 && fe->preset_custom) { gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(fe->preset_custom), TRUE); } else { GSList *gs = fe->preset_radio; int i = fe->n_preset_menu_items - 1 - n; if (fe->preset_custom) gs = gs->next; while (i && gs) { i--; gs = gs->next; } if (gs) { gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gs->data), TRUE); } else for (gs = fe->preset_radio; gs; gs = gs->next) { gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gs->data), FALSE); } } fe->preset_threaded = FALSE; /* * Update the greying on the Copy menu option. */ if (fe->copy_menu_item) { int enabled = midend_can_format_as_text_now(fe->me); gtk_widget_set_sensitive(fe->copy_menu_item, enabled); } } static gboolean not_size_allocated_yet(GtkWidget *w) { /* * This function tests whether a widget has not yet taken up space * on the screen which it will occupy in future. (Therefore, it * returns true only if the widget does exist but does not have a * size allocation. A null widget is already taking up all the * space it ever will.) */ if (!w) return FALSE; /* nonexistent widgets aren't a problem */ #if GTK_CHECK_VERSION(2,18,0) /* skip if no gtk_widget_get_allocation */ { GtkAllocation a; gtk_widget_get_allocation(w, &a); if (a.height == 0 || a.width == 0) return TRUE; /* widget exists but has no size yet */ } #endif return FALSE; } static void try_shrink_drawing_area(frontend *fe) { if (fe->drawing_area_shrink_pending && !not_size_allocated_yet(fe->menubar) && !not_size_allocated_yet(fe->statusbar)) { /* * In order to permit the user to resize the window smaller as * well as bigger, we call this function after the window size * has ended up where we want it. This shouldn't shrink the * window immediately; it just arranges that the next time the * user tries to shrink it, they can. * * However, at puzzle creation time, we defer the first of * these operations until after the menu bar and status bar * are actually visible. On Ubuntu 12.04 I've found that these * can take a while to be displayed, and that it's a mistake * to reduce the drawing area's size allocation before they've * turned up or else the drawing area makes room for them by * shrinking to less than the size we intended. */ gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), 1, 1); fe->drawing_area_shrink_pending = FALSE; } } static gint configure_window(GtkWidget *widget, GdkEventConfigure *event, gpointer data) { frontend *fe = (frontend *)data; /* * When the main puzzle window changes size, it might be because * the menu bar or status bar has turned up after starting off * absent, in which case we should have another go at enacting a * pending shrink of the drawing area. */ try_shrink_drawing_area(fe); return FALSE; } static void resize_fe(frontend *fe) { int x, y; get_size(fe, &x, &y); fe->w = x; fe->h = y; fe->drawing_area_shrink_pending = FALSE; gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y); { GtkRequisition req; gtk_widget_size_request(GTK_WIDGET(fe->window), &req); gtk_window_resize(GTK_WINDOW(fe->window), req.width, req.height); } fe->drawing_area_shrink_pending = TRUE; try_shrink_drawing_area(fe); } static void menu_preset_event(GtkMenuItem *menuitem, gpointer data) { frontend *fe = (frontend *)data; game_params *params = (game_params *)gtk_object_get_data(GTK_OBJECT(menuitem), "user-data"); if (fe->preset_threaded || (GTK_IS_CHECK_MENU_ITEM(menuitem) && !gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem)))) return; midend_set_params(fe->me, params); midend_new_game(fe->me); changed_preset(fe); resize_fe(fe); } GdkAtom compound_text_atom, utf8_string_atom; int paste_initialised = FALSE; static void set_selection(frontend *fe, GdkAtom selection) { if (!paste_initialised) { compound_text_atom = gdk_atom_intern("COMPOUND_TEXT", FALSE); utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE); paste_initialised = TRUE; } /* * For this simple application we can safely assume that the * data passed to this function is pure ASCII, which means we * can return precisely the same stuff for types STRING, * COMPOUND_TEXT or UTF8_STRING. */ if (gtk_selection_owner_set(fe->area, selection, CurrentTime)) { gtk_selection_clear_targets(fe->area, selection); gtk_selection_add_target(fe->area, selection, GDK_SELECTION_TYPE_STRING, 1); gtk_selection_add_target(fe->area, selection, compound_text_atom, 1); gtk_selection_add_target(fe->area, selection, utf8_string_atom, 1); } } void write_clip(frontend *fe, char *data) { if (fe->paste_data) sfree(fe->paste_data); fe->paste_data = data; fe->paste_data_len = strlen(data); set_selection(fe, GDK_SELECTION_PRIMARY); set_selection(fe, GDK_SELECTION_CLIPBOARD); } void selection_get(GtkWidget *widget, GtkSelectionData *seldata, guint info, guint time_stamp, gpointer data) { frontend *fe = (frontend *)data; gtk_selection_data_set(seldata, seldata->target, 8, fe->paste_data, fe->paste_data_len); } gint selection_clear(GtkWidget *widget, GdkEventSelection *seldata, gpointer data) { frontend *fe = (frontend *)data; if (fe->paste_data) sfree(fe->paste_data); fe->paste_data = NULL; fe->paste_data_len = 0; return TRUE; } static void menu_copy_event(GtkMenuItem *menuitem, gpointer data) { frontend *fe = (frontend *)data; char *text; text = midend_text_format(fe->me); if (text) { write_clip(fe, text); } else { gdk_beep(); } } #ifdef OLD_FILESEL static void filesel_ok(GtkButton *button, gpointer data) { frontend *fe = (frontend *)data; gpointer filesel = gtk_object_get_data(GTK_OBJECT(button), "user-data"); const char *name = gtk_file_selection_get_filename(GTK_FILE_SELECTION(filesel)); fe->filesel_name = dupstr(name); } static char *file_selector(frontend *fe, char *title, int save) { GtkWidget *filesel = gtk_file_selection_new(title); fe->filesel_name = NULL; gtk_window_set_modal(GTK_WINDOW(filesel), TRUE); gtk_object_set_data (GTK_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "user-data", (gpointer)filesel); gtk_signal_connect (GTK_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked", GTK_SIGNAL_FUNC(filesel_ok), fe); gtk_signal_connect_object (GTK_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), (gpointer)filesel); gtk_signal_connect_object (GTK_OBJECT(GTK_FILE_SELECTION(filesel)->cancel_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), (gpointer)filesel); gtk_signal_connect(GTK_OBJECT(filesel), "destroy", GTK_SIGNAL_FUNC(window_destroy), NULL); gtk_widget_show(filesel); gtk_window_set_transient_for(GTK_WINDOW(filesel), GTK_WINDOW(fe->window)); gtk_main(); return fe->filesel_name; } #else static char *file_selector(frontend *fe, char *title, int save) { char *filesel_name = NULL; GtkWidget *filesel = gtk_file_chooser_dialog_new(title, GTK_WINDOW(fe->window), save ? GTK_FILE_CHOOSER_ACTION_SAVE : GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, save ? GTK_STOCK_SAVE : GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); if (gtk_dialog_run(GTK_DIALOG(filesel)) == GTK_RESPONSE_ACCEPT) { const char *name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filesel)); filesel_name = dupstr(name); } gtk_widget_destroy(filesel); return filesel_name; } #endif struct savefile_write_ctx { FILE *fp; int error; }; static void savefile_write(void *wctx, void *buf, int len) { struct savefile_write_ctx *ctx = (struct savefile_write_ctx *)wctx; if (fwrite(buf, 1, len, ctx->fp) < len) ctx->error = errno; } static int savefile_read(void *wctx, void *buf, int len) { FILE *fp = (FILE *)wctx; int ret; ret = fread(buf, 1, len, fp); return (ret == len); } static void menu_save_event(GtkMenuItem *menuitem, gpointer data) { frontend *fe = (frontend *)data; char *name; name = file_selector(fe, "Enter name of game file to save", TRUE); if (name) { FILE *fp; if ((fp = fopen(name, "r")) != NULL) { char buf[256 + FILENAME_MAX]; fclose(fp); /* file exists */ sprintf(buf, "Are you sure you want to overwrite the" " file \"%.*s\"?", FILENAME_MAX, name); if (!message_box(fe->window, "Question", buf, TRUE, MB_YESNO)) return; } fp = fopen(name, "w"); sfree(name); if (!fp) { error_box(fe->window, "Unable to open save file"); return; } { struct savefile_write_ctx ctx; ctx.fp = fp; ctx.error = 0; midend_serialise(fe->me, savefile_write, &ctx); fclose(fp); if (ctx.error) { char boxmsg[512]; sprintf(boxmsg, "Error writing save file: %.400s", strerror(errno)); error_box(fe->window, boxmsg); return; } } } } static void menu_load_event(GtkMenuItem *menuitem, gpointer data) { frontend *fe = (frontend *)data; char *name, *err; name = file_selector(fe, "Enter name of saved game file to load", FALSE); if (name) { FILE *fp = fopen(name, "r"); sfree(name); if (!fp) { error_box(fe->window, "Unable to open saved game file"); return; } err = midend_deserialise(fe->me, savefile_read, fp); fclose(fp); if (err) { error_box(fe->window, err); return; } changed_preset(fe); resize_fe(fe); } } static void menu_solve_event(GtkMenuItem *menuitem, gpointer data) { frontend *fe = (frontend *)data; char *msg; msg = midend_solve(fe->me); if (msg) error_box(fe->window, msg); } static void menu_restart_event(GtkMenuItem *menuitem, gpointer data) { frontend *fe = (frontend *)data; midend_restart_game(fe->me); } static void menu_config_event(GtkMenuItem *menuitem, gpointer data) { frontend *fe = (frontend *)data; int which = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(menuitem), "user-data")); if (fe->preset_threaded || (GTK_IS_CHECK_MENU_ITEM(menuitem) && !gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem)))) return; changed_preset(fe); /* Put the old preset back! */ if (!get_config(fe, which)) return; midend_new_game(fe->me); resize_fe(fe); } static void menu_about_event(GtkMenuItem *menuitem, gpointer data) { frontend *fe = (frontend *)data; char titlebuf[256]; char textbuf[1024]; sprintf(titlebuf, "About %.200s", thegame.name); sprintf(textbuf, "%.200s\n\n" "from Simon Tatham's Portable Puzzle Collection\n\n" "%.500s", thegame.name, ver); message_box(fe->window, titlebuf, textbuf, TRUE, MB_OK); } static GtkWidget *add_menu_item_with_key(frontend *fe, GtkContainer *cont, char *text, int key) { GtkWidget *menuitem = gtk_menu_item_new_with_label(text); int keyqual; gtk_container_add(cont, menuitem); gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", GINT_TO_POINTER(key)); gtk_signal_connect(GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(menu_key_event), fe); switch (key & ~0x1F) { case 0x00: key += 0x60; keyqual = GDK_CONTROL_MASK; break; case 0x40: key += 0x20; keyqual = GDK_SHIFT_MASK; break; default: keyqual = 0; break; } gtk_widget_add_accelerator(menuitem, "activate", fe->accelgroup, key, keyqual, GTK_ACCEL_VISIBLE); gtk_widget_show(menuitem); return menuitem; } static void add_menu_separator(GtkContainer *cont) { GtkWidget *menuitem = gtk_menu_item_new(); gtk_container_add(cont, menuitem); gtk_widget_show(menuitem); } enum { ARG_EITHER, ARG_SAVE, ARG_ID }; /* for argtype */ static frontend *new_window(char *arg, int argtype, char **error) { frontend *fe; GtkBox *vbox, *hbox; GtkWidget *menu, *menuitem; GdkPixmap *iconpm; GList *iconlist; int x, y, n; char errbuf[1024]; extern char *const *const xpm_icons[]; extern const int n_xpm_icons; fe = snew(frontend); fe->timer_active = FALSE; fe->timer_id = -1; fe->me = midend_new(fe, &thegame, >k_drawing, fe); if (arg) { char *err; FILE *fp; errbuf[0] = '\0'; switch (argtype) { case ARG_ID: err = midend_game_id(fe->me, arg); if (!err) midend_new_game(fe->me); else sprintf(errbuf, "Invalid game ID: %.800s", err); break; case ARG_SAVE: fp = fopen(arg, "r"); if (!fp) { sprintf(errbuf, "Error opening file: %.800s", strerror(errno)); } else { err = midend_deserialise(fe->me, savefile_read, fp); if (err) sprintf(errbuf, "Invalid save file: %.800s", err); fclose(fp); } break; default /*case ARG_EITHER*/: /* * First try treating the argument as a game ID. */ err = midend_game_id(fe->me, arg); if (!err) { /* * It's a valid game ID. */ midend_new_game(fe->me); } else { FILE *fp = fopen(arg, "r"); if (!fp) { sprintf(errbuf, "Supplied argument is neither a game ID (%.400s)" " nor a save file (%.400s)", err, strerror(errno)); } else { err = midend_deserialise(fe->me, savefile_read, fp); if (err) sprintf(errbuf, "%.800s", err); fclose(fp); } } break; } if (*errbuf) { *error = dupstr(errbuf); midend_free(fe->me); sfree(fe); return NULL; } } else { midend_new_game(fe->me); } fe->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(fe->window), thegame.name); vbox = GTK_BOX(gtk_vbox_new(FALSE, 0)); gtk_container_add(GTK_CONTAINER(fe->window), GTK_WIDGET(vbox)); gtk_widget_show(GTK_WIDGET(vbox)); fe->accelgroup = gtk_accel_group_new(); gtk_window_add_accel_group(GTK_WINDOW(fe->window), fe->accelgroup); hbox = GTK_BOX(gtk_hbox_new(FALSE, 0)); gtk_box_pack_start(vbox, GTK_WIDGET(hbox), FALSE, FALSE, 0); gtk_widget_show(GTK_WIDGET(hbox)); fe->menubar = gtk_menu_bar_new(); gtk_box_pack_start(hbox, fe->menubar, TRUE, TRUE, 0); gtk_widget_show(fe->menubar); menuitem = gtk_menu_item_new_with_mnemonic("_Game"); gtk_container_add(GTK_CONTAINER(fe->menubar), menuitem); gtk_widget_show(menuitem); menu = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu); add_menu_item_with_key(fe, GTK_CONTAINER(menu), "New", 'n'); menuitem = gtk_menu_item_new_with_label("Restart"); gtk_container_add(GTK_CONTAINER(menu), menuitem); gtk_signal_connect(GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(menu_restart_event), fe); gtk_widget_show(menuitem); menuitem = gtk_menu_item_new_with_label("Specific..."); gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", GINT_TO_POINTER(CFG_DESC)); gtk_container_add(GTK_CONTAINER(menu), menuitem); gtk_signal_connect(GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(menu_config_event), fe); gtk_widget_show(menuitem); menuitem = gtk_menu_item_new_with_label("Random Seed..."); gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", GINT_TO_POINTER(CFG_SEED)); gtk_container_add(GTK_CONTAINER(menu), menuitem); gtk_signal_connect(GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(menu_config_event), fe); gtk_widget_show(menuitem); fe->preset_radio = NULL; fe->preset_custom = NULL; fe->n_preset_menu_items = 0; fe->preset_threaded = FALSE; if ((n = midend_num_presets(fe->me)) > 0 || thegame.can_configure) { GtkWidget *submenu; int i; menuitem = gtk_menu_item_new_with_mnemonic("_Type"); gtk_container_add(GTK_CONTAINER(fe->menubar), menuitem); gtk_widget_show(menuitem); submenu = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); for (i = 0; i < n; i++) { char *name; game_params *params; midend_fetch_preset(fe->me, i, &name, ¶ms); menuitem = gtk_radio_menu_item_new_with_label(fe->preset_radio, name); fe->preset_radio = gtk_radio_menu_item_group(GTK_RADIO_MENU_ITEM(menuitem)); fe->n_preset_menu_items++; gtk_container_add(GTK_CONTAINER(submenu), menuitem); gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", params); gtk_signal_connect(GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(menu_preset_event), fe); gtk_widget_show(menuitem); } if (thegame.can_configure) { menuitem = fe->preset_custom = gtk_radio_menu_item_new_with_label(fe->preset_radio, "Custom..."); fe->preset_radio = gtk_radio_menu_item_group(GTK_RADIO_MENU_ITEM(menuitem)); gtk_container_add(GTK_CONTAINER(submenu), menuitem); gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", GPOINTER_TO_INT(CFG_SETTINGS)); gtk_signal_connect(GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(menu_config_event), fe); gtk_widget_show(menuitem); } } add_menu_separator(GTK_CONTAINER(menu)); menuitem = gtk_menu_item_new_with_label("Load..."); gtk_container_add(GTK_CONTAINER(menu), menuitem); gtk_signal_connect(GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(menu_load_event), fe); gtk_widget_show(menuitem); menuitem = gtk_menu_item_new_with_label("Save..."); gtk_container_add(GTK_CONTAINER(menu), menuitem); gtk_signal_connect(GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(menu_save_event), fe); gtk_widget_show(menuitem); #ifndef STYLUS_BASED add_menu_separator(GTK_CONTAINER(menu)); add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Undo", 'u'); add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Redo", 'r'); #endif if (thegame.can_format_as_text_ever) { add_menu_separator(GTK_CONTAINER(menu)); menuitem = gtk_menu_item_new_with_label("Copy"); gtk_container_add(GTK_CONTAINER(menu), menuitem); gtk_signal_connect(GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(menu_copy_event), fe); gtk_widget_show(menuitem); fe->copy_menu_item = menuitem; } else { fe->copy_menu_item = NULL; } if (thegame.can_solve) { add_menu_separator(GTK_CONTAINER(menu)); menuitem = gtk_menu_item_new_with_label("Solve"); gtk_container_add(GTK_CONTAINER(menu), menuitem); gtk_signal_connect(GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(menu_solve_event), fe); gtk_widget_show(menuitem); } add_menu_separator(GTK_CONTAINER(menu)); add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Exit", 'q'); menuitem = gtk_menu_item_new_with_mnemonic("_Help"); gtk_container_add(GTK_CONTAINER(fe->menubar), menuitem); gtk_widget_show(menuitem); menu = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu); menuitem = gtk_menu_item_new_with_label("About"); gtk_container_add(GTK_CONTAINER(menu), menuitem); gtk_signal_connect(GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(menu_about_event), fe); gtk_widget_show(menuitem); #ifdef STYLUS_BASED menuitem=gtk_button_new_with_mnemonic("_Redo"); gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", GINT_TO_POINTER((int)('r'))); gtk_signal_connect(GTK_OBJECT(menuitem), "clicked", GTK_SIGNAL_FUNC(menu_key_event), fe); gtk_box_pack_end(hbox, menuitem, FALSE, FALSE, 0); gtk_widget_show(menuitem); menuitem=gtk_button_new_with_mnemonic("_Undo"); gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", GINT_TO_POINTER((int)('u'))); gtk_signal_connect(GTK_OBJECT(menuitem), "clicked", GTK_SIGNAL_FUNC(menu_key_event), fe); gtk_box_pack_end(hbox, menuitem, FALSE, FALSE, 0); gtk_widget_show(menuitem); if (thegame.flags & REQUIRE_NUMPAD) { hbox = GTK_BOX(gtk_hbox_new(FALSE, 0)); gtk_box_pack_start(vbox, GTK_WIDGET(hbox), FALSE, FALSE, 0); gtk_widget_show(GTK_WIDGET(hbox)); *((int*)errbuf)=0; errbuf[1]='\0'; for(errbuf[0]='0';errbuf[0]<='9';errbuf[0]++) { menuitem=gtk_button_new_with_label(errbuf); gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", GINT_TO_POINTER((int)(errbuf[0]))); gtk_signal_connect(GTK_OBJECT(menuitem), "clicked", GTK_SIGNAL_FUNC(menu_key_event), fe); gtk_box_pack_start(hbox, menuitem, TRUE, TRUE, 0); gtk_widget_show(menuitem); } } #endif /* STYLUS_BASED */ changed_preset(fe); snaffle_colours(fe); if (midend_wants_statusbar(fe->me)) { GtkWidget *viewport; GtkRequisition req; viewport = gtk_viewport_new(NULL, NULL); gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE); fe->statusbar = gtk_statusbar_new(); gtk_container_add(GTK_CONTAINER(viewport), fe->statusbar); gtk_widget_show(viewport); gtk_box_pack_end(vbox, viewport, FALSE, FALSE, 0); gtk_widget_show(fe->statusbar); fe->statusctx = gtk_statusbar_get_context_id (GTK_STATUSBAR(fe->statusbar), "game"); gtk_statusbar_push(GTK_STATUSBAR(fe->statusbar), fe->statusctx, "test"); gtk_widget_size_request(fe->statusbar, &req); #if 0 /* For GTK 2.0, should we be using gtk_widget_set_size_request? */ #endif gtk_widget_set_usize(viewport, -1, req.height); } else fe->statusbar = NULL; fe->area = gtk_drawing_area_new(); #if GTK_CHECK_VERSION(2,0,0) GTK_WIDGET_UNSET_FLAGS(fe->area, GTK_DOUBLE_BUFFERED); #endif get_size(fe, &x, &y); fe->drawing_area_shrink_pending = FALSE; gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y); fe->w = x; fe->h = y; gtk_box_pack_end(vbox, fe->area, TRUE, TRUE, 0); clear_backing_store(fe); fe->fonts = NULL; fe->nfonts = fe->fontsize = 0; fe->paste_data = NULL; fe->paste_data_len = 0; gtk_signal_connect(GTK_OBJECT(fe->window), "destroy", GTK_SIGNAL_FUNC(destroy), fe); gtk_signal_connect(GTK_OBJECT(fe->window), "key_press_event", GTK_SIGNAL_FUNC(key_event), fe); gtk_signal_connect(GTK_OBJECT(fe->area), "button_press_event", GTK_SIGNAL_FUNC(button_event), fe); gtk_signal_connect(GTK_OBJECT(fe->area), "button_release_event", GTK_SIGNAL_FUNC(button_event), fe); gtk_signal_connect(GTK_OBJECT(fe->area), "motion_notify_event", GTK_SIGNAL_FUNC(motion_event), fe); gtk_signal_connect(GTK_OBJECT(fe->area), "selection_get", GTK_SIGNAL_FUNC(selection_get), fe); gtk_signal_connect(GTK_OBJECT(fe->area), "selection_clear_event", GTK_SIGNAL_FUNC(selection_clear), fe); gtk_signal_connect(GTK_OBJECT(fe->area), "expose_event", GTK_SIGNAL_FUNC(expose_area), fe); gtk_signal_connect(GTK_OBJECT(fe->window), "map_event", GTK_SIGNAL_FUNC(map_window), fe); gtk_signal_connect(GTK_OBJECT(fe->area), "configure_event", GTK_SIGNAL_FUNC(configure_area), fe); gtk_signal_connect(GTK_OBJECT(fe->window), "configure_event", GTK_SIGNAL_FUNC(configure_window), fe); gtk_widget_add_events(GTK_WIDGET(fe->area), GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK); if (n_xpm_icons) { gtk_widget_realize(fe->window); iconpm = gdk_pixmap_create_from_xpm_d(fe->window->window, NULL, NULL, (gchar **)xpm_icons[0]); gdk_window_set_icon(fe->window->window, NULL, iconpm, NULL); iconlist = NULL; for (n = 0; n < n_xpm_icons; n++) { iconlist = g_list_append(iconlist, gdk_pixbuf_new_from_xpm_data((const gchar **) xpm_icons[n])); } gdk_window_set_icon_list(fe->window->window, iconlist); } gtk_widget_show(fe->area); gtk_widget_show(fe->window); fe->drawing_area_shrink_pending = TRUE; try_shrink_drawing_area(fe); set_window_background(fe, 0); return fe; } char *fgetline(FILE *fp) { char *ret = snewn(512, char); int size = 512, len = 0; while (fgets(ret + len, size - len, fp)) { len += strlen(ret + len); if (ret[len-1] == '\n') break; /* got a newline, we're done */ size = len + 512; ret = sresize(ret, size, char); } if (len == 0) { /* first fgets returned NULL */ sfree(ret); return NULL; } ret[len] = '\0'; return ret; } int main(int argc, char **argv) { char *pname = argv[0]; char *error; int ngenerate = 0, print = FALSE, px = 1, py = 1; int time_generation = FALSE, test_solve = FALSE, list_presets = FALSE; int soln = FALSE, colour = FALSE; float scale = 1.0F; float redo_proportion = 0.0F; char *savefile = NULL, *savesuffix = NULL; char *arg = NULL; int argtype = ARG_EITHER; char *screenshot_file = NULL; int doing_opts = TRUE; int ac = argc; char **av = argv; char errbuf[500]; /* * Command line parsing in this function is rather fiddly, * because GTK wants to have a go at argc/argv _first_ - and * yet we can't let it, because gtk_init() will bomb out if it * can't open an X display, whereas in fact we want to permit * our --generate and --print modes to run without an X * display. * * So what we do is: * - we parse the command line ourselves, without modifying * argc/argv * - if we encounter an error which might plausibly be the * result of a GTK command line (i.e. not detailed errors in * particular options of ours) we store the error message * and terminate parsing. * - if we got enough out of the command line to know it * specifies a non-X mode of operation, we either display * the stored error and return failure, or if there is no * stored error we do the non-X operation and return * success. * - otherwise, we go straight to gtk_init(). */ errbuf[0] = '\0'; while (--ac > 0) { char *p = *++av; if (doing_opts && !strcmp(p, "--version")) { printf("%s, from Simon Tatham's Portable Puzzle Collection\n%s\n", thegame.name, ver); return 0; } else if (doing_opts && !strcmp(p, "--generate")) { if (--ac > 0) { ngenerate = atoi(*++av); if (!ngenerate) { fprintf(stderr, "%s: '--generate' expected a number\n", pname); return 1; } } else ngenerate = 1; } else if (doing_opts && !strcmp(p, "--time-generation")) { time_generation = TRUE; } else if (doing_opts && !strcmp(p, "--test-solve")) { test_solve = TRUE; } else if (doing_opts && !strcmp(p, "--list-presets")) { list_presets = TRUE; } else if (doing_opts && !strcmp(p, "--save")) { if (--ac > 0) { savefile = *++av; } else { fprintf(stderr, "%s: '--save' expected a filename\n", pname); return 1; } } else if (doing_opts && (!strcmp(p, "--save-suffix") || !strcmp(p, "--savesuffix"))) { if (--ac > 0) { savesuffix = *++av; } else { fprintf(stderr, "%s: '--save-suffix' expected a filename\n", pname); return 1; } } else if (doing_opts && !strcmp(p, "--print")) { if (!thegame.can_print) { fprintf(stderr, "%s: this game does not support printing\n", pname); return 1; } print = TRUE; if (--ac > 0) { char *dim = *++av; if (sscanf(dim, "%dx%d", &px, &py) != 2) { fprintf(stderr, "%s: unable to parse argument '%s' to " "'--print'\n", pname, dim); return 1; } } else { px = py = 1; } } else if (doing_opts && !strcmp(p, "--scale")) { if (--ac > 0) { scale = atof(*++av); } else { fprintf(stderr, "%s: no argument supplied to '--scale'\n", pname); return 1; } } else if (doing_opts && !strcmp(p, "--redo")) { /* * This is an internal option which I don't expect * users to have any particular use for. The effect of * --redo is that once the game has been loaded and * initialised, the next move in the redo chain is * replayed, and the game screen is redrawn part way * through the making of the move. This is only * meaningful if there _is_ a next move in the redo * chain, which means in turn that this option is only * useful if you're also passing a save file on the * command line. * * This option is used by the script which generates * the puzzle icons and website screenshots, and I * don't imagine it's useful for anything else. * (Unless, I suppose, users don't like my screenshots * and want to generate their own in the same way for * some repackaged version of the puzzles.) */ if (--ac > 0) { redo_proportion = atof(*++av); } else { fprintf(stderr, "%s: no argument supplied to '--redo'\n", pname); return 1; } } else if (doing_opts && !strcmp(p, "--screenshot")) { /* * Another internal option for the icon building * script. This causes a screenshot of the central * drawing area (i.e. not including the menu bar or * status bar) to be saved to a PNG file once the * window has been drawn, and then the application * quits immediately. */ if (--ac > 0) { screenshot_file = *++av; } else { fprintf(stderr, "%s: no argument supplied to '--screenshot'\n", pname); return 1; } } else if (doing_opts && (!strcmp(p, "--with-solutions") || !strcmp(p, "--with-solution") || !strcmp(p, "--with-solns") || !strcmp(p, "--with-soln") || !strcmp(p, "--solutions") || !strcmp(p, "--solution") || !strcmp(p, "--solns") || !strcmp(p, "--soln"))) { soln = TRUE; } else if (doing_opts && !strcmp(p, "--colour")) { if (!thegame.can_print_in_colour) { fprintf(stderr, "%s: this game does not support colour" " printing\n", pname); return 1; } colour = TRUE; } else if (doing_opts && !strcmp(p, "--load")) { argtype = ARG_SAVE; } else if (doing_opts && !strcmp(p, "--game")) { argtype = ARG_ID; } else if (doing_opts && !strcmp(p, "--")) { doing_opts = FALSE; } else if (!doing_opts || p[0] != '-') { if (arg) { fprintf(stderr, "%s: more than one argument supplied\n", pname); return 1; } arg = p; } else { sprintf(errbuf, "%.100s: unrecognised option '%.100s'\n", pname, p); break; } } /* * Special standalone mode for generating puzzle IDs on the * command line. Useful for generating puzzles to be printed * out and solved offline (for puzzles where that even makes * sense - Solo, for example, is a lot more pencil-and-paper * friendly than Twiddle!) * * Usage: * * --generate [ []] * * , if present, is the number of puzzle IDs to generate. * , if present, is the same type of parameter string * you would pass to the puzzle when running it in GUI mode, * including optional extras such as the expansion factor in * Rectangles and the difficulty level in Solo. * * If you specify , you must also specify (although * you may specify it to be 1). Sorry; that was the * simplest-to-parse command-line syntax I came up with. */ if (ngenerate > 0 || print || savefile || savesuffix) { int i, n = 1; midend *me; char *id; document *doc = NULL; /* * If we're in this branch, we should display any pending * error message from the command line, since GTK isn't going * to take another crack at making sense of it. */ if (*errbuf) { fputs(errbuf, stderr); return 1; } n = ngenerate; me = midend_new(NULL, &thegame, NULL, NULL); i = 0; if (savefile && !savesuffix) savesuffix = ""; if (!savefile && savesuffix) savefile = ""; if (print) doc = document_new(px, py, scale); /* * In this loop, we either generate a game ID or read one * from stdin depending on whether we're in generate mode; * then we either write it to stdout or print it, depending * on whether we're in print mode. Thus, this loop handles * generate-to-stdout, print-from-stdin and generate-and- * immediately-print modes. * * (It could also handle a copy-stdin-to-stdout mode, * although there's currently no combination of options * which will cause this loop to be activated in that mode. * It wouldn't be _entirely_ pointless, though, because * stdin could contain bare params strings or random-seed * IDs, and stdout would contain nothing but fully * generated descriptive game IDs.) */ while (ngenerate == 0 || i < n) { char *pstr, *err, *seed; struct rusage before, after; if (ngenerate == 0) { pstr = fgetline(stdin); if (!pstr) break; pstr[strcspn(pstr, "\r\n")] = '\0'; } else { if (arg) { pstr = snewn(strlen(arg) + 40, char); strcpy(pstr, arg); if (i > 0 && strchr(arg, '#')) sprintf(pstr + strlen(pstr), "-%d", i); } else pstr = NULL; } if (pstr) { err = midend_game_id(me, pstr); if (err) { fprintf(stderr, "%s: error parsing '%s': %s\n", pname, pstr, err); return 1; } } if (time_generation) getrusage(RUSAGE_SELF, &before); midend_new_game(me); seed = midend_get_random_seed(me); if (time_generation) { double elapsed; getrusage(RUSAGE_SELF, &after); elapsed = (after.ru_utime.tv_sec - before.ru_utime.tv_sec); elapsed += (after.ru_utime.tv_usec - before.ru_utime.tv_usec) / 1000000.0; printf("%s %s: %.6f\n", thegame.name, seed, elapsed); } if (test_solve && thegame.can_solve) { /* * Now destroy the aux_info in the midend, by means of * re-entering the same game id, and then try to solve * it. */ char *game_id, *err; game_id = midend_get_game_id(me); err = midend_game_id(me, game_id); if (err) { fprintf(stderr, "%s %s: game id re-entry error: %s\n", thegame.name, seed, err); return 1; } midend_new_game(me); sfree(game_id); err = midend_solve(me); /* * If the solve operation returned the error "Solution * not known for this puzzle", that's OK, because that * just means it's a puzzle for which we don't have an * algorithmic solver and hence can't solve it without * the aux_info, e.g. Netslide. Any other error is a * problem, though. */ if (err && strcmp(err, "Solution not known for this puzzle")) { fprintf(stderr, "%s %s: solve error: %s\n", thegame.name, seed, err); return 1; } } sfree(pstr); sfree(seed); if (doc) { err = midend_print_puzzle(me, doc, soln); if (err) { fprintf(stderr, "%s: error in printing: %s\n", pname, err); return 1; } } if (savefile) { struct savefile_write_ctx ctx; char *realname = snewn(40 + strlen(savefile) + strlen(savesuffix), char); sprintf(realname, "%s%d%s", savefile, i, savesuffix); if (soln) { char *err = midend_solve(me); if (err) { fprintf(stderr, "%s: unable to show solution: %s\n", realname, err); return 1; } } ctx.fp = fopen(realname, "w"); if (!ctx.fp) { fprintf(stderr, "%s: open: %s\n", realname, strerror(errno)); return 1; } ctx.error = 0; midend_serialise(me, savefile_write, &ctx); if (ctx.error) { fprintf(stderr, "%s: write: %s\n", realname, strerror(ctx.error)); return 1; } if (fclose(ctx.fp)) { fprintf(stderr, "%s: close: %s\n", realname, strerror(errno)); return 1; } sfree(realname); } if (!doc && !savefile && !time_generation) { id = midend_get_game_id(me); puts(id); sfree(id); } i++; } if (doc) { psdata *ps = ps_init(stdout, colour); document_print(doc, ps_drawing_api(ps)); document_free(doc); ps_free(ps); } midend_free(me); return 0; } else if (list_presets) { /* * Another specialist mode which causes the puzzle to list the * game_params strings for all its preset configurations. */ int i, npresets; midend *me; me = midend_new(NULL, &thegame, NULL, NULL); npresets = midend_num_presets(me); for (i = 0; i < npresets; i++) { game_params *params; char *name, *paramstr; midend_fetch_preset(me, i, &name, ¶ms); paramstr = thegame.encode_params(params, TRUE); printf("%s %s\n", paramstr, name); sfree(paramstr); } midend_free(me); return 0; } else { frontend *fe; gtk_init(&argc, &argv); fe = new_window(arg, argtype, &error); if (!fe) { fprintf(stderr, "%s: %s\n", pname, error); return 1; } if (screenshot_file) { /* * Some puzzles will not redraw their entire area if * given a partially completed animation, which means * we must redraw now and _then_ redraw again after * freezing the move timer. */ midend_force_redraw(fe->me); } if (redo_proportion) { /* Start a redo. */ midend_process_key(fe->me, 0, 0, 'r'); /* And freeze the timer at the specified position. */ midend_freeze_timer(fe->me, redo_proportion); } if (screenshot_file) { save_screenshot_png(fe, screenshot_file); exit(0); } gtk_main(); } return 0; } puzzles-r9872/guess.c0000644000175300017530000011760412132232554013727 0ustar simonsimon/* * guess.c: Mastermind clone. */ #include #include #include #include #include #include #include "puzzles.h" #define FLASH_FRAME 0.5F enum { COL_BACKGROUND, COL_FRAME, COL_CURSOR, COL_FLASH, COL_HOLD, COL_EMPTY, /* must be COL_1 - 1 */ COL_1, COL_2, COL_3, COL_4, COL_5, COL_6, COL_7, COL_8, COL_9, COL_10, COL_CORRECTPLACE, COL_CORRECTCOLOUR, NCOLOURS }; struct game_params { int ncolours, npegs, nguesses; int allow_blank, allow_multiple; }; #define FEEDBACK_CORRECTPLACE 1 #define FEEDBACK_CORRECTCOLOUR 2 typedef struct pegrow { int npegs; int *pegs; /* 0 is 'empty' */ int *feedback; /* may well be unused */ } *pegrow; struct game_state { game_params params; pegrow *guesses; /* length params->nguesses */ int *holds; pegrow solution; int next_go; /* from 0 to nguesses-1; if next_go == nguesses then they've lost. */ int solved; /* +1 = win, -1 = lose, 0 = still playing */ }; static game_params *default_params(void) { game_params *ret = snew(game_params); /* AFAIK this is the canonical Mastermind ruleset. */ ret->ncolours = 6; ret->npegs = 4; ret->nguesses = 10; ret->allow_blank = 0; ret->allow_multiple = 1; 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 { char *name; game_params params; } guess_presets[] = { {"Standard", {6, 4, 10, FALSE, TRUE}}, {"Super", {8, 5, 12, FALSE, TRUE}}, }; static int game_fetch_preset(int i, char **name, game_params **params) { if (i < 0 || i >= lenof(guess_presets)) return FALSE; *name = dupstr(guess_presets[i].name); /* * get round annoying const issues */ { game_params tmp = guess_presets[i].params; *params = dup_params(&tmp); } return TRUE; } static void decode_params(game_params *params, char const *string) { char const *p = string; game_params *defs = default_params(); *params = *defs; free_params(defs); while (*p) { switch (*p++) { case 'c': params->ncolours = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; break; case 'p': params->npegs = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; break; case 'g': params->nguesses = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; break; case 'b': params->allow_blank = 1; break; case 'B': params->allow_blank = 0; break; case 'm': params->allow_multiple = 1; break; case 'M': params->allow_multiple = 0; break; default: ; } } } static char *encode_params(const game_params *params, int full) { char data[256]; sprintf(data, "c%dp%dg%d%s%s", params->ncolours, params->npegs, params->nguesses, params->allow_blank ? "b" : "B", params->allow_multiple ? "m" : "M"); return dupstr(data); } static config_item *game_configure(const game_params *params) { config_item *ret; char buf[80]; ret = snewn(6, config_item); ret[0].name = "Colours"; ret[0].type = C_STRING; sprintf(buf, "%d", params->ncolours); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Pegs per guess"; ret[1].type = C_STRING; sprintf(buf, "%d", params->npegs); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Guesses"; ret[2].type = C_STRING; sprintf(buf, "%d", params->nguesses); ret[2].sval = dupstr(buf); ret[2].ival = 0; ret[3].name = "Allow blanks"; ret[3].type = C_BOOLEAN; ret[3].sval = NULL; ret[3].ival = params->allow_blank; ret[4].name = "Allow duplicates"; ret[4].type = C_BOOLEAN; ret[4].sval = NULL; ret[4].ival = params->allow_multiple; ret[5].name = NULL; ret[5].type = C_END; ret[5].sval = NULL; ret[5].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->ncolours = atoi(cfg[0].sval); ret->npegs = atoi(cfg[1].sval); ret->nguesses = atoi(cfg[2].sval); ret->allow_blank = cfg[3].ival; ret->allow_multiple = cfg[4].ival; return ret; } static char *validate_params(const game_params *params, int full) { if (params->ncolours < 2 || params->npegs < 2) return "Trivial solutions are uninteresting"; /* NB as well as the no. of colours we define, max(ncolours) must * also fit in an unsigned char; see new_game_desc. */ if (params->ncolours > 10) return "Too many colours"; if (params->nguesses < 1) return "Must have at least one guess"; if (!params->allow_multiple && params->ncolours < params->npegs) return "Disallowing multiple colours requires at least as many colours as pegs"; return NULL; } static pegrow new_pegrow(int npegs) { pegrow pegs = snew(struct pegrow); pegs->npegs = npegs; pegs->pegs = snewn(pegs->npegs, int); memset(pegs->pegs, 0, pegs->npegs * sizeof(int)); pegs->feedback = snewn(pegs->npegs, int); memset(pegs->feedback, 0, pegs->npegs * sizeof(int)); return pegs; } static pegrow dup_pegrow(pegrow pegs) { pegrow newpegs = new_pegrow(pegs->npegs); memcpy(newpegs->pegs, pegs->pegs, newpegs->npegs * sizeof(int)); memcpy(newpegs->feedback, pegs->feedback, newpegs->npegs * sizeof(int)); return newpegs; } static void invalidate_pegrow(pegrow pegs) { memset(pegs->pegs, -1, pegs->npegs * sizeof(int)); memset(pegs->feedback, -1, pegs->npegs * sizeof(int)); } static void free_pegrow(pegrow pegs) { sfree(pegs->pegs); sfree(pegs->feedback); sfree(pegs); } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { unsigned char *bmp = snewn(params->npegs, unsigned char); char *ret; int i, c; pegrow colcount = new_pegrow(params->ncolours); for (i = 0; i < params->npegs; i++) { newcol: c = random_upto(rs, params->ncolours); if (!params->allow_multiple && colcount->pegs[c]) goto newcol; colcount->pegs[c]++; bmp[i] = (unsigned char)(c+1); } obfuscate_bitmap(bmp, params->npegs*8, FALSE); ret = bin2hex(bmp, params->npegs); sfree(bmp); free_pegrow(colcount); return ret; } static char *validate_desc(const game_params *params, const char *desc) { unsigned char *bmp; int i; /* desc is just an (obfuscated) bitmap of the solution; check that * it's the correct length and (when unobfuscated) contains only * sensible colours. */ if (strlen(desc) != params->npegs * 2) return "Game description is wrong length"; bmp = hex2bin(desc, params->npegs); obfuscate_bitmap(bmp, params->npegs*8, TRUE); for (i = 0; i < params->npegs; i++) { if (bmp[i] < 1 || bmp[i] > params->ncolours) { sfree(bmp); return "Game description is corrupted"; } } sfree(bmp); return NULL; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { game_state *state = snew(game_state); unsigned char *bmp; int i; state->params = *params; state->guesses = snewn(params->nguesses, pegrow); for (i = 0; i < params->nguesses; i++) state->guesses[i] = new_pegrow(params->npegs); state->holds = snewn(params->npegs, int); state->solution = new_pegrow(params->npegs); bmp = hex2bin(desc, params->npegs); obfuscate_bitmap(bmp, params->npegs*8, TRUE); for (i = 0; i < params->npegs; i++) state->solution->pegs[i] = (int)bmp[i]; sfree(bmp); memset(state->holds, 0, sizeof(int) * params->npegs); state->next_go = state->solved = 0; return state; } static game_state *dup_game(const game_state *state) { game_state *ret = snew(game_state); int i; *ret = *state; ret->guesses = snewn(state->params.nguesses, pegrow); for (i = 0; i < state->params.nguesses; i++) ret->guesses[i] = dup_pegrow(state->guesses[i]); ret->holds = snewn(state->params.npegs, int); memcpy(ret->holds, state->holds, sizeof(int) * state->params.npegs); ret->solution = dup_pegrow(state->solution); return ret; } static void free_game(game_state *state) { int i; free_pegrow(state->solution); for (i = 0; i < state->params.nguesses; i++) free_pegrow(state->guesses[i]); sfree(state->holds); sfree(state->guesses); sfree(state); } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { return dupstr("S"); } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { return NULL; } static int is_markable(const game_params *params, pegrow pegs) { int i, nset = 0, nrequired, ret = 0; pegrow colcount = new_pegrow(params->ncolours); nrequired = params->allow_blank ? 1 : params->npegs; for (i = 0; i < params->npegs; i++) { int c = pegs->pegs[i]; if (c > 0) { colcount->pegs[c-1]++; nset++; } } if (nset < nrequired) goto done; if (!params->allow_multiple) { for (i = 0; i < params->ncolours; i++) { if (colcount->pegs[i] > 1) goto done; } } ret = 1; done: free_pegrow(colcount); return ret; } struct game_ui { game_params params; pegrow curr_pegs; /* half-finished current move */ int *holds; int colour_cur; /* position of up-down colour picker cursor */ int peg_cur; /* position of left-right peg picker cursor */ int display_cur, markable; int drag_col, drag_x, drag_y; /* x and y are *center* of peg! */ int drag_opeg; /* peg index, if dragged from a peg (from current guess), otherwise -1 */ int show_labels; /* label the colours with letters */ }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); memset(ui, 0, sizeof(game_ui)); ui->params = state->params; /* structure copy */ ui->curr_pegs = new_pegrow(state->params.npegs); ui->holds = snewn(state->params.npegs, int); memset(ui->holds, 0, sizeof(int)*state->params.npegs); ui->drag_opeg = -1; return ui; } static void free_ui(game_ui *ui) { free_pegrow(ui->curr_pegs); sfree(ui->holds); sfree(ui); } static char *encode_ui(const game_ui *ui) { char *ret, *p, *sep; int i; /* * For this game it's worth storing the contents of the current * guess, and the current set of holds. */ ret = snewn(40 * ui->curr_pegs->npegs, char); p = ret; sep = ""; for (i = 0; i < ui->curr_pegs->npegs; i++) { p += sprintf(p, "%s%d%s", sep, ui->curr_pegs->pegs[i], ui->holds[i] ? "_" : ""); sep = ","; } *p++ = '\0'; assert(p - ret < 40 * ui->curr_pegs->npegs); return sresize(ret, p - ret, char); } static void decode_ui(game_ui *ui, const char *encoding) { int i; const char *p = encoding; for (i = 0; i < ui->curr_pegs->npegs; i++) { ui->curr_pegs->pegs[i] = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; if (*p == '_') { /* NB: old versions didn't store holds */ ui->holds[i] = 1; p++; } else ui->holds[i] = 0; if (*p == ',') p++; } ui->markable = is_markable(&ui->params, ui->curr_pegs); } static void game_changed_state(game_ui *ui, const game_state *oldstate, const game_state *newstate) { int i; /* Implement holds, clear other pegs. * This does something that is arguably the Right Thing even * for undo. */ for (i = 0; i < newstate->solution->npegs; i++) { if (newstate->solved) ui->holds[i] = 0; else ui->holds[i] = newstate->holds[i]; if (newstate->solved || (newstate->next_go == 0) || !ui->holds[i]) { ui->curr_pegs->pegs[i] = 0; } else ui->curr_pegs->pegs[i] = newstate->guesses[newstate->next_go-1]->pegs[i]; } ui->markable = is_markable(&newstate->params, ui->curr_pegs); /* Clean up cursor position */ if (!ui->markable && ui->peg_cur == newstate->solution->npegs) ui->peg_cur--; } #define PEGSZ (ds->pegsz) #define PEGOFF (ds->pegsz + ds->gapsz) #define HINTSZ (ds->hintsz) #define HINTOFF (ds->hintsz + ds->gapsz) #define GAP (ds->gapsz) #define CGAP (ds->gapsz / 2) #define PEGRAD (ds->pegrad) #define HINTRAD (ds->hintrad) #define COL_OX (ds->colx) #define COL_OY (ds->coly) #define COL_X(c) (COL_OX) #define COL_Y(c) (COL_OY + (c)*PEGOFF) #define COL_W PEGOFF #define COL_H (ds->colours->npegs*PEGOFF) #define GUESS_OX (ds->guessx) #define GUESS_OY (ds->guessy) #define GUESS_X(g,p) (GUESS_OX + (p)*PEGOFF) #define GUESS_Y(g,p) (GUESS_OY + (g)*PEGOFF) #define GUESS_W (ds->solution->npegs*PEGOFF) #define GUESS_H (ds->nguesses*PEGOFF) #define HINT_OX (GUESS_OX + GUESS_W + ds->gapsz) #define HINT_OY (GUESS_OY + (PEGSZ - HINTOFF - HINTSZ) / 2) #define HINT_X(g) HINT_OX #define HINT_Y(g) (HINT_OY + (g)*PEGOFF) #define HINT_W ((ds->hintw*HINTOFF) - GAP) #define HINT_H GUESS_H #define SOLN_OX GUESS_OX #define SOLN_OY (GUESS_OY + GUESS_H + ds->gapsz + 2) #define SOLN_W GUESS_W #define SOLN_H PEGOFF struct game_drawstate { int nguesses; pegrow *guesses; /* same size as state->guesses */ pegrow solution; /* only displayed if state->solved */ pegrow colours; /* length ncolours, not npegs */ int pegsz, hintsz, gapsz; /* peg size (diameter), etc. */ int pegrad, hintrad; /* radius of peg, hint */ int border; int colx, coly; /* origin of colours vertical bar */ int guessx, guessy; /* origin of guesses */ int solnx, solny; /* origin of solution */ int hintw; /* no. of hint tiles we're wide per row */ int w, h, started, solved; int next_go; blitter *blit_peg; int drag_col, blit_ox, blit_oy; }; static void set_peg(const game_params *params, game_ui *ui, int peg, int col) { ui->curr_pegs->pegs[peg] = col; ui->markable = is_markable(params, ui->curr_pegs); } static int mark_pegs(pegrow guess, pegrow solution, int ncols) { int nc_place = 0, nc_colour = 0, i, j; assert(guess && solution && (guess->npegs == solution->npegs)); for (i = 0; i < guess->npegs; i++) { if (guess->pegs[i] == solution->pegs[i]) nc_place++; } /* slight bit of cleverness: we have the following formula, from * http://mathworld.wolfram.com/Mastermind.html that gives: * * nc_colour = sum(colours, min(#solution, #guess)) - nc_place * * I think this is due to Knuth. */ for (i = 1; i <= ncols; i++) { int n_guess = 0, n_solution = 0; for (j = 0; j < guess->npegs; j++) { if (guess->pegs[j] == i) n_guess++; if (solution->pegs[j] == i) n_solution++; } nc_colour += min(n_guess, n_solution); } nc_colour -= nc_place; debug(("mark_pegs, %d pegs, %d right place, %d right colour", guess->npegs, nc_place, nc_colour)); assert((nc_colour + nc_place) <= guess->npegs); memset(guess->feedback, 0, guess->npegs*sizeof(int)); for (i = 0, j = 0; i < nc_place; i++) guess->feedback[j++] = FEEDBACK_CORRECTPLACE; for (i = 0; i < nc_colour; i++) guess->feedback[j++] = FEEDBACK_CORRECTCOLOUR; return nc_place; } static char *encode_move(const game_state *from, game_ui *ui) { char *buf, *p, *sep; int len, i; len = ui->curr_pegs->npegs * 20 + 2; buf = snewn(len, char); p = buf; *p++ = 'G'; sep = ""; for (i = 0; i < ui->curr_pegs->npegs; i++) { p += sprintf(p, "%s%d%s", sep, ui->curr_pegs->pegs[i], ui->holds[i] ? "_" : ""); sep = ","; } *p++ = '\0'; assert(p - buf <= len); buf = sresize(buf, len, char); return buf; } static char *interpret_move(const game_state *from, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { int over_col = 0; /* one-indexed */ int over_guess = -1; /* zero-indexed */ int over_past_guess_y = -1; /* zero-indexed */ int over_past_guess_x = -1; /* zero-indexed */ int over_hint = 0; /* zero or one */ char *ret = NULL; int guess_ox = GUESS_X(from->next_go, 0); int guess_oy = GUESS_Y(from->next_go, 0); /* * Enable or disable labels on colours. */ if (button == 'l' || button == 'L') { ui->show_labels = !ui->show_labels; return ""; } if (from->solved) return NULL; if (x >= COL_OX && x < (COL_OX + COL_W) && y >= COL_OY && y < (COL_OY + COL_H)) { over_col = ((y - COL_OY) / PEGOFF) + 1; assert(over_col >= 1 && over_col <= ds->colours->npegs); } else if (x >= guess_ox && y >= guess_oy && y < (guess_oy + GUESS_H)) { if (x < (guess_ox + GUESS_W)) { over_guess = (x - guess_ox) / PEGOFF; assert(over_guess >= 0 && over_guess < ds->solution->npegs); } else { over_hint = 1; } } else if (x >= guess_ox && x < (guess_ox + GUESS_W) && y >= GUESS_OY && y < guess_oy) { over_past_guess_y = (y - GUESS_OY) / PEGOFF; over_past_guess_x = (x - guess_ox) / PEGOFF; assert(over_past_guess_y >= 0 && over_past_guess_y < from->next_go); assert(over_past_guess_x >= 0 && over_past_guess_x < ds->solution->npegs); } debug(("make_move: over_col %d, over_guess %d, over_hint %d," " over_past_guess (%d,%d)", over_col, over_guess, over_hint, over_past_guess_x, over_past_guess_y)); assert(ds->blit_peg); /* mouse input */ if (button == LEFT_BUTTON) { if (over_col > 0) { ui->drag_col = over_col; ui->drag_opeg = -1; debug(("Start dragging from colours")); } else if (over_guess > -1) { int col = ui->curr_pegs->pegs[over_guess]; if (col) { ui->drag_col = col; ui->drag_opeg = over_guess; debug(("Start dragging from a guess")); } } else if (over_past_guess_y > -1) { int col = from->guesses[over_past_guess_y]->pegs[over_past_guess_x]; if (col) { ui->drag_col = col; ui->drag_opeg = -1; debug(("Start dragging from a past guess")); } } if (ui->drag_col) { ui->drag_x = x; ui->drag_y = y; debug(("Start dragging, col = %d, (%d,%d)", ui->drag_col, ui->drag_x, ui->drag_y)); ret = ""; } } else if (button == LEFT_DRAG && ui->drag_col) { ui->drag_x = x; ui->drag_y = y; debug(("Keep dragging, (%d,%d)", ui->drag_x, ui->drag_y)); ret = ""; } else if (button == LEFT_RELEASE && ui->drag_col) { if (over_guess > -1) { debug(("Dropping colour %d onto guess peg %d", ui->drag_col, over_guess)); set_peg(&from->params, ui, over_guess, ui->drag_col); } else { if (ui->drag_opeg > -1) { debug(("Removing colour %d from peg %d", ui->drag_col, ui->drag_opeg)); set_peg(&from->params, ui, ui->drag_opeg, 0); } } ui->drag_col = 0; ui->drag_opeg = -1; ui->display_cur = 0; debug(("Stop dragging.")); ret = ""; } else if (button == RIGHT_BUTTON) { if (over_guess > -1) { /* we use ths feedback in the game_ui to signify * 'carry this peg to the next guess as well'. */ ui->holds[over_guess] = 1 - ui->holds[over_guess]; ret = ""; } } else if (button == LEFT_RELEASE && over_hint && ui->markable) { /* NB this won't trigger if on the end of a drag; that's on * purpose, in case you drop by mistake... */ ret = encode_move(from, ui); } /* keyboard input */ if (button == CURSOR_UP || button == CURSOR_DOWN) { ui->display_cur = 1; if (button == CURSOR_DOWN && (ui->colour_cur+1) < from->params.ncolours) ui->colour_cur++; if (button == CURSOR_UP && ui->colour_cur > 0) ui->colour_cur--; ret = ""; } else if (button == CURSOR_LEFT || button == CURSOR_RIGHT) { int maxcur = from->params.npegs; if (ui->markable) maxcur++; ui->display_cur = 1; if (button == CURSOR_RIGHT && (ui->peg_cur+1) < maxcur) ui->peg_cur++; if (button == CURSOR_LEFT && ui->peg_cur > 0) ui->peg_cur--; ret = ""; } else if (IS_CURSOR_SELECT(button)) { ui->display_cur = 1; if (ui->peg_cur == from->params.npegs) { ret = encode_move(from, ui); } else { set_peg(&from->params, ui, ui->peg_cur, ui->colour_cur+1); ret = ""; } } else if (button == 'D' || button == 'd' || button == '\b') { ui->display_cur = 1; set_peg(&from->params, ui, ui->peg_cur, 0); ret = ""; } else if (button == 'H' || button == 'h') { ui->display_cur = 1; ui->holds[ui->peg_cur] = 1 - ui->holds[ui->peg_cur]; ret = ""; } return ret; } static game_state *execute_move(const game_state *from, const char *move) { int i, nc_place; game_state *ret; const char *p; if (!strcmp(move, "S")) { ret = dup_game(from); ret->solved = -1; return ret; } else if (move[0] == 'G') { p = move+1; ret = dup_game(from); for (i = 0; i < from->solution->npegs; i++) { int val = atoi(p); int min_colour = from->params.allow_blank? 0 : 1; if (val < min_colour || val > from->params.ncolours) { free_game(ret); return NULL; } ret->guesses[from->next_go]->pegs[i] = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; if (*p == '_') { ret->holds[i] = 1; p++; } else ret->holds[i] = 0; if (*p == ',') p++; } nc_place = mark_pegs(ret->guesses[from->next_go], ret->solution, ret->params.ncolours); if (nc_place == ret->solution->npegs) { ret->solved = +1; /* win! */ } else { ret->next_go = from->next_go + 1; if (ret->next_go >= ret->params.nguesses) ret->solved = -1; /* lose, meaning we show the pegs. */ } return ret; } else return NULL; } /* ---------------------------------------------------------------------- * Drawing routines. */ #define PEG_PREFER_SZ 32 /* next three are multipliers for pegsz. It will look much nicer if * (2*PEG_HINT) + PEG_GAP = 1.0 as the hints are formatted like that. */ #define PEG_GAP 0.10 #define PEG_HINT 0.35 #define BORDER 0.5 static void game_compute_size(const game_params *params, int tilesize, int *x, int *y) { double hmul, vmul_c, vmul_g, vmul; int hintw = (params->npegs+1)/2; hmul = BORDER * 2.0 + /* border */ 1.0 * 2.0 + /* vertical colour bar */ 1.0 * params->npegs + /* guess pegs */ PEG_GAP * params->npegs + /* guess gaps */ PEG_HINT * hintw + /* hint pegs */ PEG_GAP * (hintw - 1); /* hint gaps */ vmul_c = BORDER * 2.0 + /* border */ 1.0 * params->ncolours + /* colour pegs */ PEG_GAP * (params->ncolours - 1); /* colour gaps */ vmul_g = BORDER * 2.0 + /* border */ 1.0 * (params->nguesses + 1) + /* guesses plus solution */ PEG_GAP * (params->nguesses + 1); /* gaps plus gap above soln */ vmul = max(vmul_c, vmul_g); *x = (int)ceil((double)tilesize * hmul); *y = (int)ceil((double)tilesize * vmul); } static void game_set_size(drawing *dr, game_drawstate *ds, const game_params *params, int tilesize) { int colh, guessh; ds->pegsz = tilesize; ds->hintsz = (int)((double)ds->pegsz * PEG_HINT); ds->gapsz = (int)((double)ds->pegsz * PEG_GAP); ds->border = (int)((double)ds->pegsz * BORDER); ds->pegrad = (ds->pegsz -1)/2; /* radius of peg to fit in pegsz (which is 2r+1) */ ds->hintrad = (ds->hintsz-1)/2; colh = ((ds->pegsz + ds->gapsz) * params->ncolours) - ds->gapsz; guessh = ((ds->pegsz + ds->gapsz) * params->nguesses); /* guesses */ guessh += ds->gapsz + ds->pegsz; /* solution */ game_compute_size(params, tilesize, &ds->w, &ds->h); ds->colx = ds->border; ds->coly = (ds->h - colh) / 2; ds->guessx = ds->solnx = ds->border + ds->pegsz * 2; /* border + colours */ ds->guessy = (ds->h - guessh) / 2; ds->solny = ds->guessy + ((ds->pegsz + ds->gapsz) * params->nguesses) + ds->gapsz; assert(ds->pegsz > 0); assert(!ds->blit_peg); /* set_size is never called twice */ ds->blit_peg = blitter_new(dr, ds->pegsz+2, ds->pegsz+2); } static float *game_colours(frontend *fe, int *ncolours) { float *ret = snewn(3 * NCOLOURS, float), max; int i; frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); /* red */ ret[COL_1 * 3 + 0] = 1.0F; ret[COL_1 * 3 + 1] = 0.0F; ret[COL_1 * 3 + 2] = 0.0F; /* yellow */ ret[COL_2 * 3 + 0] = 1.0F; ret[COL_2 * 3 + 1] = 1.0F; ret[COL_2 * 3 + 2] = 0.0F; /* green */ ret[COL_3 * 3 + 0] = 0.0F; ret[COL_3 * 3 + 1] = 1.0F; ret[COL_3 * 3 + 2] = 0.0F; /* blue */ ret[COL_4 * 3 + 0] = 0.2F; ret[COL_4 * 3 + 1] = 0.3F; ret[COL_4 * 3 + 2] = 1.0F; /* orange */ ret[COL_5 * 3 + 0] = 1.0F; ret[COL_5 * 3 + 1] = 0.5F; ret[COL_5 * 3 + 2] = 0.0F; /* purple */ ret[COL_6 * 3 + 0] = 0.5F; ret[COL_6 * 3 + 1] = 0.0F; ret[COL_6 * 3 + 2] = 0.7F; /* brown */ ret[COL_7 * 3 + 0] = 0.5F; ret[COL_7 * 3 + 1] = 0.3F; ret[COL_7 * 3 + 2] = 0.3F; /* light blue */ ret[COL_8 * 3 + 0] = 0.4F; ret[COL_8 * 3 + 1] = 0.8F; ret[COL_8 * 3 + 2] = 1.0F; /* light green */ ret[COL_9 * 3 + 0] = 0.7F; ret[COL_9 * 3 + 1] = 1.0F; ret[COL_9 * 3 + 2] = 0.7F; /* pink */ ret[COL_10 * 3 + 0] = 1.0F; ret[COL_10 * 3 + 1] = 0.6F; ret[COL_10 * 3 + 2] = 1.0F; ret[COL_FRAME * 3 + 0] = 0.0F; ret[COL_FRAME * 3 + 1] = 0.0F; ret[COL_FRAME * 3 + 2] = 0.0F; ret[COL_CURSOR * 3 + 0] = 0.0F; ret[COL_CURSOR * 3 + 1] = 0.0F; ret[COL_CURSOR * 3 + 2] = 0.0F; ret[COL_FLASH * 3 + 0] = 0.5F; ret[COL_FLASH * 3 + 1] = 1.0F; ret[COL_FLASH * 3 + 2] = 1.0F; ret[COL_HOLD * 3 + 0] = 1.0F; ret[COL_HOLD * 3 + 1] = 0.5F; ret[COL_HOLD * 3 + 2] = 0.5F; ret[COL_CORRECTPLACE*3 + 0] = 0.0F; ret[COL_CORRECTPLACE*3 + 1] = 0.0F; ret[COL_CORRECTPLACE*3 + 2] = 0.0F; ret[COL_CORRECTCOLOUR*3 + 0] = 1.0F; ret[COL_CORRECTCOLOUR*3 + 1] = 1.0F; ret[COL_CORRECTCOLOUR*3 + 2] = 1.0F; /* We want to make sure we can distinguish COL_CORRECTCOLOUR * (which we hard-code as white) from COL_BACKGROUND (which * could default to white on some platforms). * Code borrowed from fifteen.c. */ max = ret[COL_BACKGROUND*3]; for (i = 1; i < 3; i++) if (ret[COL_BACKGROUND*3+i] > max) max = ret[COL_BACKGROUND*3+i]; if (max * 1.2F > 1.0F) { for (i = 0; i < 3; i++) ret[COL_BACKGROUND*3+i] /= (max * 1.2F); } /* We also want to be able to tell the difference between BACKGROUND * and EMPTY, for similar distinguishing-hint reasons. */ ret[COL_EMPTY * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 2.0F / 3.0F; ret[COL_EMPTY * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 2.0F / 3.0F; ret[COL_EMPTY * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 2.0F / 3.0F; *ncolours = NCOLOURS; return ret; } static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { struct game_drawstate *ds = snew(struct game_drawstate); int i; memset(ds, 0, sizeof(struct game_drawstate)); ds->guesses = snewn(state->params.nguesses, pegrow); ds->nguesses = state->params.nguesses; for (i = 0; i < state->params.nguesses; i++) { ds->guesses[i] = new_pegrow(state->params.npegs); invalidate_pegrow(ds->guesses[i]); } ds->solution = new_pegrow(state->params.npegs); invalidate_pegrow(ds->solution); ds->colours = new_pegrow(state->params.ncolours); invalidate_pegrow(ds->colours); ds->hintw = (state->params.npegs+1)/2; /* must round up */ ds->blit_peg = NULL; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { int i; if (ds->blit_peg) blitter_free(dr, ds->blit_peg); free_pegrow(ds->colours); free_pegrow(ds->solution); for (i = 0; i < ds->nguesses; i++) free_pegrow(ds->guesses[i]); sfree(ds->guesses); sfree(ds); } static void draw_peg(drawing *dr, game_drawstate *ds, int cx, int cy, int moving, int labelled, int col) { /* * Some platforms antialias circles, which means we shouldn't * overwrite a circle of one colour with a circle of another * colour without erasing the background first. However, if the * peg is the one being dragged, we don't erase the background * because we _want_ it to alpha-blend nicely into whatever's * behind it. */ if (!moving) draw_rect(dr, cx-CGAP, cy-CGAP, PEGSZ+CGAP*2, PEGSZ+CGAP*2, COL_BACKGROUND); if (PEGRAD > 0) { draw_circle(dr, cx+PEGRAD, cy+PEGRAD, PEGRAD, COL_EMPTY + col, (col ? COL_FRAME : COL_EMPTY)); } else draw_rect(dr, cx, cy, PEGSZ, PEGSZ, COL_EMPTY + col); if (labelled && col) { char buf[2]; buf[0] = 'a'-1 + col; buf[1] = '\0'; draw_text(dr, cx+PEGRAD, cy+PEGRAD, FONT_VARIABLE, PEGRAD, ALIGN_HCENTRE|ALIGN_VCENTRE, COL_FRAME, buf); } draw_update(dr, cx-CGAP, cy-CGAP, PEGSZ+CGAP*2, PEGSZ+CGAP*2); } static void draw_cursor(drawing *dr, game_drawstate *ds, int x, int y) { draw_circle(dr, x+PEGRAD, y+PEGRAD, PEGRAD+CGAP, -1, COL_CURSOR); draw_update(dr, x-CGAP, y-CGAP, PEGSZ+CGAP*2, PEGSZ+CGAP*2); } static void guess_redraw(drawing *dr, game_drawstate *ds, int guess, pegrow src, int *holds, int cur_col, int force, int labelled) { pegrow dest; int rowx, rowy, i, scol; if (guess == -1) { dest = ds->solution; rowx = SOLN_OX; rowy = SOLN_OY; } else { dest = ds->guesses[guess]; rowx = GUESS_X(guess,0); rowy = GUESS_Y(guess,0); } if (src) assert(src->npegs == dest->npegs); for (i = 0; i < dest->npegs; i++) { scol = src ? src->pegs[i] : 0; if (i == cur_col) scol |= 0x1000; if (holds && holds[i]) scol |= 0x2000; if (labelled) scol |= 0x4000; if ((dest->pegs[i] != scol) || force) { draw_peg(dr, ds, rowx + PEGOFF * i, rowy, FALSE, labelled, scol &~ 0x7000); /* * Hold marker. */ draw_rect(dr, rowx + PEGOFF * i, rowy + PEGSZ + ds->gapsz/2, PEGSZ, 2, (scol & 0x2000 ? COL_HOLD : COL_BACKGROUND)); draw_update(dr, rowx + PEGOFF * i, rowy + PEGSZ + ds->gapsz/2, PEGSZ, 2); if (scol & 0x1000) draw_cursor(dr, ds, rowx + PEGOFF * i, rowy); } dest->pegs[i] = scol; } } static void hint_redraw(drawing *dr, game_drawstate *ds, int guess, pegrow src, int force, int cursor, int markable) { pegrow dest = ds->guesses[guess]; int rowx, rowy, i, scol, col, hintlen; int need_redraw; int emptycol = (markable ? COL_FLASH : COL_EMPTY); if (src) assert(src->npegs == dest->npegs); hintlen = (dest->npegs + 1)/2; /* * Because of the possible presence of the cursor around this * entire section, we redraw all or none of it but never part. */ need_redraw = FALSE; for (i = 0; i < dest->npegs; i++) { scol = src ? src->feedback[i] : 0; if (i == 0 && cursor) scol |= 0x1000; if (i == 0 && markable) scol |= 0x2000; if ((scol != dest->feedback[i]) || force) { need_redraw = TRUE; } dest->feedback[i] = scol; } if (need_redraw) { int hinth = HINTSZ + GAP + HINTSZ; int hx,hy,hw,hh; hx = HINT_X(guess)-GAP; hy = HINT_Y(guess)-GAP; hw = HINT_W+GAP*2; hh = hinth+GAP*2; /* erase a large background rectangle */ draw_rect(dr, hx, hy, hw, hh, COL_BACKGROUND); for (i = 0; i < dest->npegs; i++) { scol = src ? src->feedback[i] : 0; col = ((scol == FEEDBACK_CORRECTPLACE) ? COL_CORRECTPLACE : (scol == FEEDBACK_CORRECTCOLOUR) ? COL_CORRECTCOLOUR : emptycol); rowx = HINT_X(guess); rowy = HINT_Y(guess); if (i < hintlen) { rowx += HINTOFF * i; } else { rowx += HINTOFF * (i - hintlen); rowy += HINTOFF; } if (HINTRAD > 0) { draw_circle(dr, rowx+HINTRAD, rowy+HINTRAD, HINTRAD, col, (col == emptycol ? emptycol : COL_FRAME)); } else { draw_rect(dr, rowx, rowy, HINTSZ, HINTSZ, col); } } if (cursor) { int x1,y1,x2,y2; x1 = hx + CGAP; y1 = hy + CGAP; x2 = hx + hw - CGAP; y2 = hy + hh - CGAP; draw_line(dr, x1, y1, x2, y1, COL_CURSOR); draw_line(dr, x2, y1, x2, y2, COL_CURSOR); draw_line(dr, x2, y2, x1, y2, COL_CURSOR); draw_line(dr, x1, y2, x1, y1, COL_CURSOR); } draw_update(dr, hx, hy, hw, hh); } } static void currmove_redraw(drawing *dr, game_drawstate *ds, int guess, int col) { int ox = GUESS_X(guess, 0), oy = GUESS_Y(guess, 0), off = PEGSZ/4; draw_rect(dr, ox-off-1, oy, 2, PEGSZ, col); draw_update(dr, ox-off-1, oy, 2, PEGSZ); } 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 i, new_move; new_move = (state->next_go != ds->next_go) || !ds->started; if (!ds->started) { draw_rect(dr, 0, 0, ds->w, ds->h, COL_BACKGROUND); draw_rect(dr, SOLN_OX, SOLN_OY - ds->gapsz - 1, SOLN_W, 2, COL_FRAME); draw_update(dr, 0, 0, ds->w, ds->h); } if (ds->drag_col != 0) { debug(("Loading from blitter.")); blitter_load(dr, ds->blit_peg, ds->blit_ox, ds->blit_oy); draw_update(dr, ds->blit_ox, ds->blit_oy, PEGSZ, PEGSZ); } /* draw the colours */ for (i = 0; i < state->params.ncolours; i++) { int val = i+1; if (ui->display_cur && ui->colour_cur == i) val |= 0x1000; if (ui->show_labels) val |= 0x2000; if (ds->colours->pegs[i] != val) { draw_peg(dr, ds, COL_X(i), COL_Y(i), FALSE, ui->show_labels, i+1); if (val & 0x1000) draw_cursor(dr, ds, COL_X(i), COL_Y(i)); ds->colours->pegs[i] = val; } } /* draw the guesses (so far) and the hints * (in reverse order to avoid trampling holds) */ for (i = state->params.nguesses - 1; i >= 0; i--) { if (state->next_go > i || state->solved) { /* this info is stored in the game_state already */ guess_redraw(dr, ds, i, state->guesses[i], NULL, -1, 0, ui->show_labels); hint_redraw(dr, ds, i, state->guesses[i], i == (state->next_go-1) ? 1 : 0, FALSE, FALSE); } else if (state->next_go == i) { /* this is the one we're on; the (incomplete) guess is * stored in the game_ui. */ guess_redraw(dr, ds, i, ui->curr_pegs, ui->holds, ui->display_cur ? ui->peg_cur : -1, 0, ui->show_labels); hint_redraw(dr, ds, i, NULL, 1, ui->display_cur && ui->peg_cur == state->params.npegs, ui->markable); } else { /* we've not got here yet; it's blank. */ guess_redraw(dr, ds, i, NULL, NULL, -1, 0, ui->show_labels); hint_redraw(dr, ds, i, NULL, 0, FALSE, FALSE); } } /* draw the 'current move' and 'able to mark' sign. */ if (new_move) currmove_redraw(dr, ds, ds->next_go, COL_BACKGROUND); if (!state->solved) currmove_redraw(dr, ds, state->next_go, COL_HOLD); /* draw the solution (or the big rectangle) */ if ((!state->solved ^ !ds->solved) || !ds->started) { draw_rect(dr, SOLN_OX, SOLN_OY, SOLN_W, SOLN_H, state->solved ? COL_BACKGROUND : COL_EMPTY); draw_update(dr, SOLN_OX, SOLN_OY, SOLN_W, SOLN_H); } if (state->solved) guess_redraw(dr, ds, -1, state->solution, NULL, -1, !ds->solved, ui->show_labels); ds->solved = state->solved; ds->next_go = state->next_go; /* if ui->drag_col != 0, save the screen to the blitter, * draw the peg where we saved, and set ds->drag_* == ui->drag_*. */ if (ui->drag_col != 0) { int ox = ui->drag_x - (PEGSZ/2); int oy = ui->drag_y - (PEGSZ/2); ds->blit_ox = ox - 1; ds->blit_oy = oy - 1; debug(("Saving to blitter at (%d,%d)", ds->blit_ox, ds->blit_oy)); blitter_save(dr, ds->blit_peg, ds->blit_ox, ds->blit_oy); draw_peg(dr, ds, ox, oy, TRUE, ui->show_labels, ui->drag_col); } ds->drag_col = ui->drag_col; ds->started = 1; } 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 int game_status(const game_state *state) { /* * We return nonzero whenever the solution has been revealed, even * (on spoiler grounds) if it wasn't guessed correctly. The * correct return value from this function is already in * state->solved. */ return state->solved; } static int 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 guess #endif const struct game thegame = { "Guess", "games.guess", "guess", default_params, game_fetch_preset, 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, FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, decode_ui, game_changed_state, interpret_move, execute_move, PEG_PREFER_SZ, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, FALSE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, 0, /* flags */ }; /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/inertia.c0000644000175300017530000015546212132232554014240 0ustar simonsimon/* * inertia.c: Game involving navigating round a grid picking up * gems. * * Game rules and basic generator design by Ben Olmstead. * This re-implementation was written by Simon Tatham. */ #include #include #include #include #include #include #include "puzzles.h" /* Used in the game_state */ #define BLANK 'b' #define GEM 'g' #define MINE 'm' #define STOP 's' #define WALL 'w' /* Used in the game IDs */ #define START 'S' /* Used in the game generation */ #define POSSGEM 'G' /* Used only in the game_drawstate*/ #define UNDRAWN '?' #define DIRECTIONS 8 #define DP1 (DIRECTIONS+1) #define DX(dir) ( (dir) & 3 ? (((dir) & 7) > 4 ? -1 : +1) : 0 ) #define DY(dir) ( DX((dir)+6) ) /* * Lvalue macro which expects x and y to be in range. */ #define LV_AT(w, h, grid, x, y) ( (grid)[(y)*(w)+(x)] ) /* * Rvalue macro which can cope with x and y being out of range. */ #define AT(w, h, grid, x, y) ( (x)<0 || (x)>=(w) || (y)<0 || (y)>=(h) ? \ WALL : LV_AT(w, h, grid, x, y) ) enum { COL_BACKGROUND, COL_OUTLINE, COL_HIGHLIGHT, COL_LOWLIGHT, COL_PLAYER, COL_DEAD_PLAYER, COL_MINE, COL_GEM, COL_WALL, COL_HINT, NCOLOURS }; struct game_params { int w, h; }; typedef struct soln { int refcount; int len; unsigned char *list; } soln; struct game_state { game_params p; int px, py; int gems; char *grid; int distance_moved; int dead; int cheated; int solnpos; soln *soln; }; static game_params *default_params(void) { game_params *ret = snew(game_params); ret->w = 10; #ifdef PORTRAIT_SCREEN ret->h = 10; #else ret->h = 8; #endif 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 inertia_presets[] = { #ifdef PORTRAIT_SCREEN { 10, 10 }, { 12, 12 }, { 16, 16 }, #else { 10, 8 }, { 15, 12 }, { 20, 16 }, #endif }; static int game_fetch_preset(int i, char **name, game_params **params) { game_params p, *ret; char *retname; char namebuf[80]; if (i < 0 || i >= lenof(inertia_presets)) return FALSE; p = inertia_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, int 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].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = NULL; ret[2].type = C_END; ret[2].sval = NULL; ret[2].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); return ret; } static char *validate_params(const game_params *params, int full) { /* * Avoid completely degenerate cases which only have one * row/column. We probably could generate completable puzzles * of that shape, but they'd be forced to be extremely boring * and at large sizes would take a while to happen upon at * random as well. */ if (params->w < 2 || params->h < 2) return "Width and height must both be at least two"; /* * The grid construction algorithm creates 1/5 as many gems as * grid squares, and must create at least one gem to have an * actual puzzle. However, an area-five grid is ruled out by * the above constraint, so the practical minimum is six. */ if (params->w * params->h < 6) return "Grid area must be at least six squares"; return NULL; } /* ---------------------------------------------------------------------- * Solver used by grid generator. */ struct solver_scratch { unsigned char *reachable_from, *reachable_to; int *positions; }; static struct solver_scratch *new_scratch(int w, int h) { struct solver_scratch *sc = snew(struct solver_scratch); sc->reachable_from = snewn(w * h * DIRECTIONS, unsigned char); sc->reachable_to = snewn(w * h * DIRECTIONS, unsigned char); sc->positions = snewn(w * h * DIRECTIONS, int); return sc; } static void free_scratch(struct solver_scratch *sc) { sfree(sc->reachable_from); sfree(sc->reachable_to); sfree(sc->positions); sfree(sc); } static int can_go(int w, int h, char *grid, int x1, int y1, int dir1, int x2, int y2, int dir2) { /* * Returns TRUE if we can transition directly from (x1,y1) * going in direction dir1, to (x2,y2) going in direction dir2. */ /* * If we're actually in the middle of an unoccupyable square, * we cannot make any move. */ if (AT(w, h, grid, x1, y1) == WALL || AT(w, h, grid, x1, y1) == MINE) return FALSE; /* * If a move is capable of stopping at x1,y1,dir1, and x2,y2 is * the same coordinate as x1,y1, then we can make the * transition (by stopping and changing direction). * * For this to be the case, we have to either have a wall * beyond x1,y1,dir1, or have a stop on x1,y1. */ if (x2 == x1 && y2 == y1 && (AT(w, h, grid, x1, y1) == STOP || AT(w, h, grid, x1, y1) == START || AT(w, h, grid, x1+DX(dir1), y1+DY(dir1)) == WALL)) return TRUE; /* * If a move is capable of continuing here, then x1,y1,dir1 can * move one space further on. */ if (x2 == x1+DX(dir1) && y2 == y1+DY(dir1) && dir1 == dir2 && (AT(w, h, grid, x2, y2) == BLANK || AT(w, h, grid, x2, y2) == GEM || AT(w, h, grid, x2, y2) == STOP || AT(w, h, grid, x2, y2) == START)) return TRUE; /* * That's it. */ return FALSE; } static int find_gem_candidates(int w, int h, char *grid, struct solver_scratch *sc) { int wh = w*h; int head, tail; int sx, sy, gx, gy, gd, pass, possgems; /* * This function finds all the candidate gem squares, which are * precisely those squares which can be picked up on a loop * from the starting point back to the starting point. Doing * this may involve passing through such a square in the middle * of a move; so simple breadth-first search over the _squares_ * of the grid isn't quite adequate, because it might be that * we can only reach a gem from the start by moving over it in * one direction, but can only return to the start if we were * moving over it in another direction. * * Instead, we BFS over a space which mentions each grid square * eight times - once for each direction. We also BFS twice: * once to find out what square+direction pairs we can reach * _from_ the start point, and once to find out what pairs we * can reach the start point from. Then a square is reachable * if any of the eight directions for that square has both * flags set. */ memset(sc->reachable_from, 0, wh * DIRECTIONS); memset(sc->reachable_to, 0, wh * DIRECTIONS); /* * Find the starting square. */ sx = -1; /* placate optimiser */ for (sy = 0; sy < h; sy++) { for (sx = 0; sx < w; sx++) if (AT(w, h, grid, sx, sy) == START) break; if (sx < w) break; } assert(sy < h); for (pass = 0; pass < 2; pass++) { unsigned char *reachable = (pass == 0 ? sc->reachable_from : sc->reachable_to); int sign = (pass == 0 ? +1 : -1); int dir; #ifdef SOLVER_DIAGNOSTICS printf("starting pass %d\n", pass); #endif /* * `head' and `tail' are indices within sc->positions which * track the list of board positions left to process. */ head = tail = 0; for (dir = 0; dir < DIRECTIONS; dir++) { int index = (sy*w+sx)*DIRECTIONS+dir; sc->positions[tail++] = index; reachable[index] = TRUE; #ifdef SOLVER_DIAGNOSTICS printf("starting point %d,%d,%d\n", sx, sy, dir); #endif } /* * Now repeatedly pick an element off the list and process * it. */ while (head < tail) { int index = sc->positions[head++]; int dir = index % DIRECTIONS; int x = (index / DIRECTIONS) % w; int y = index / (w * DIRECTIONS); int n, x2, y2, d2, i2; #ifdef SOLVER_DIAGNOSTICS printf("processing point %d,%d,%d\n", x, y, dir); #endif /* * The places we attempt to switch to here are: * - each possible direction change (all the other * directions in this square) * - one step further in the direction we're going (or * one step back, if we're in the reachable_to pass). */ for (n = -1; n < DIRECTIONS; n++) { if (n < 0) { x2 = x + sign * DX(dir); y2 = y + sign * DY(dir); d2 = dir; } else { x2 = x; y2 = y; d2 = n; } i2 = (y2*w+x2)*DIRECTIONS+d2; if (x2 >= 0 && x2 < w && y2 >= 0 && y2 < h && !reachable[i2]) { int ok; #ifdef SOLVER_DIAGNOSTICS printf(" trying point %d,%d,%d", x2, y2, d2); #endif if (pass == 0) ok = can_go(w, h, grid, x, y, dir, x2, y2, d2); else ok = can_go(w, h, grid, x2, y2, d2, x, y, dir); #ifdef SOLVER_DIAGNOSTICS printf(" - %sok\n", ok ? "" : "not "); #endif if (ok) { sc->positions[tail++] = i2; reachable[i2] = TRUE; } } } } } /* * And that should be it. Now all we have to do is find the * squares for which there exists _some_ direction such that * the square plus that direction form a tuple which is both * reachable from the start and reachable to the start. */ possgems = 0; for (gy = 0; gy < h; gy++) for (gx = 0; gx < w; gx++) if (AT(w, h, grid, gx, gy) == BLANK) { for (gd = 0; gd < DIRECTIONS; gd++) { int index = (gy*w+gx)*DIRECTIONS+gd; if (sc->reachable_from[index] && sc->reachable_to[index]) { #ifdef SOLVER_DIAGNOSTICS printf("space at %d,%d is reachable via" " direction %d\n", gx, gy, gd); #endif LV_AT(w, h, grid, gx, gy) = POSSGEM; possgems++; break; } } } return possgems; } /* ---------------------------------------------------------------------- * Grid generation code. */ static char *gengrid(int w, int h, random_state *rs) { int wh = w*h; char *grid = snewn(wh+1, char); struct solver_scratch *sc = new_scratch(w, h); int maxdist_threshold, tries; maxdist_threshold = 2; tries = 0; while (1) { int i, j; int possgems; int *dist, *list, head, tail, maxdist; /* * We're going to fill the grid with the five basic piece * types in about 1/5 proportion. For the moment, though, * we leave out the gems, because we'll put those in * _after_ we run the solver to tell us where the viable * locations are. */ i = 0; for (j = 0; j < wh/5; j++) grid[i++] = WALL; for (j = 0; j < wh/5; j++) grid[i++] = STOP; for (j = 0; j < wh/5; j++) grid[i++] = MINE; assert(i < wh); grid[i++] = START; while (i < wh) grid[i++] = BLANK; shuffle(grid, wh, sizeof(*grid), rs); /* * Find the viable gem locations, and immediately give up * and try again if there aren't enough of them. */ possgems = find_gem_candidates(w, h, grid, sc); if (possgems < wh/5) continue; /* * We _could_ now select wh/5 of the POSSGEMs and set them * to GEM, and have a viable level. However, there's a * chance that a large chunk of the level will turn out to * be unreachable, so first we test for that. * * We do this by finding the largest distance from any * square to the nearest POSSGEM, by breadth-first search. * If this is above a critical threshold, we abort and try * again. * * (This search is purely geometric, without regard to * walls and long ways round.) */ dist = sc->positions; list = sc->positions + wh; for (i = 0; i < wh; i++) dist[i] = -1; head = tail = 0; for (i = 0; i < wh; i++) if (grid[i] == POSSGEM) { dist[i] = 0; list[tail++] = i; } maxdist = 0; while (head < tail) { int pos, x, y, d; pos = list[head++]; if (maxdist < dist[pos]) maxdist = dist[pos]; x = pos % w; y = pos / w; for (d = 0; d < DIRECTIONS; d++) { int x2, y2, p2; x2 = x + DX(d); y2 = y + DY(d); if (x2 >= 0 && x2 < w && y2 >= 0 && y2 < h) { p2 = y2*w+x2; if (dist[p2] < 0) { dist[p2] = dist[pos] + 1; list[tail++] = p2; } } } } assert(head == wh && tail == wh); /* * Now abandon this grid and go round again if maxdist is * above the required threshold. * * We can safely start the threshold as low as 2. As we * accumulate failed generation attempts, we gradually * raise it as we get more desperate. */ if (maxdist > maxdist_threshold) { tries++; if (tries == 50) { maxdist_threshold++; tries = 0; } continue; } /* * Now our reachable squares are plausibly evenly * distributed over the grid. I'm not actually going to * _enforce_ that I place the gems in such a way as not to * increase that maxdist value; I'm now just going to trust * to the RNG to pick a sensible subset of the POSSGEMs. */ j = 0; for (i = 0; i < wh; i++) if (grid[i] == POSSGEM) list[j++] = i; shuffle(list, j, sizeof(*list), rs); for (i = 0; i < j; i++) grid[list[i]] = (i < wh/5 ? GEM : BLANK); break; } free_scratch(sc); grid[wh] = '\0'; return grid; } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { return gengrid(params->w, params->h, rs); } static char *validate_desc(const game_params *params, const char *desc) { int w = params->w, h = params->h, wh = w*h; int starts = 0, gems = 0, i; for (i = 0; i < wh; i++) { if (!desc[i]) return "Not enough data to fill grid"; if (desc[i] != WALL && desc[i] != START && desc[i] != STOP && desc[i] != GEM && desc[i] != MINE && desc[i] != BLANK) return "Unrecognised character in game description"; if (desc[i] == START) starts++; if (desc[i] == GEM) gems++; } if (desc[i]) return "Too much data to fill grid"; if (starts < 1) return "No starting square specified"; if (starts > 1) return "More than one starting square specified"; if (gems < 1) return "No gems specified"; return NULL; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { int w = params->w, h = params->h, wh = w*h; int i; game_state *state = snew(game_state); state->p = *params; /* structure copy */ state->grid = snewn(wh, char); assert(strlen(desc) == wh); memcpy(state->grid, desc, wh); state->px = state->py = -1; state->gems = 0; for (i = 0; i < wh; i++) { if (state->grid[i] == START) { state->grid[i] = STOP; state->px = i % w; state->py = i / w; } else if (state->grid[i] == GEM) { state->gems++; } } assert(state->gems > 0); assert(state->px >= 0 && state->py >= 0); state->distance_moved = 0; state->dead = FALSE; state->cheated = FALSE; state->solnpos = 0; state->soln = NULL; return state; } static game_state *dup_game(const game_state *state) { int w = state->p.w, h = state->p.h, wh = w*h; game_state *ret = snew(game_state); ret->p = state->p; ret->px = state->px; ret->py = state->py; ret->gems = state->gems; ret->grid = snewn(wh, char); ret->distance_moved = state->distance_moved; ret->dead = FALSE; memcpy(ret->grid, state->grid, wh); ret->cheated = state->cheated; ret->soln = state->soln; if (ret->soln) ret->soln->refcount++; ret->solnpos = state->solnpos; return ret; } static void free_game(game_state *state) { if (state->soln && --state->soln->refcount == 0) { sfree(state->soln->list); sfree(state->soln); } sfree(state->grid); sfree(state); } /* * Internal function used by solver. */ static int move_goes_to(int w, int h, char *grid, int x, int y, int d) { int dr; /* * See where we'd get to if we made this move. */ dr = -1; /* placate optimiser */ while (1) { if (AT(w, h, grid, x+DX(d), y+DY(d)) == WALL) { dr = DIRECTIONS; /* hit a wall, so end up stationary */ break; } x += DX(d); y += DY(d); if (AT(w, h, grid, x, y) == STOP) { dr = DIRECTIONS; /* hit a stop, so end up stationary */ break; } if (AT(w, h, grid, x, y) == GEM) { dr = d; /* hit a gem, so we're still moving */ break; } if (AT(w, h, grid, x, y) == MINE) return -1; /* hit a mine, so move is invalid */ } assert(dr >= 0); return (y*w+x)*DP1+dr; } static int compare_integers(const void *av, const void *bv) { const int *a = (const int *)av; const int *b = (const int *)bv; if (*a < *b) return -1; else if (*a > *b) return +1; else return 0; } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { int w = state->p.w, h = state->p.h, wh = w*h; int *nodes, *nodeindex, *edges, *backedges, *edgei, *backedgei, *circuit; int nedges; int *dist, *dist2, *list; int *unvisited; int circuitlen, circuitsize; int head, tail, pass, i, j, n, x, y, d, dd; char *err, *soln, *p; /* * Before anything else, deal with the special case in which * all the gems are already collected. */ for (i = 0; i < wh; i++) if (currstate->grid[i] == GEM) break; if (i == wh) { *error = "Game is already solved"; return NULL; } /* * Solving Inertia is a question of first building up the graph * of where you can get to from where, and secondly finding a * tour of the graph which takes in every gem. * * This is of course a close cousin of the travelling salesman * problem, which is NP-complete; so I rather doubt that any * _optimal_ tour can be found in plausible time. Hence I'll * restrict myself to merely finding a not-too-bad one. * * First construct the graph, by bfsing out move by move from * the current player position. Graph vertices will be * - every endpoint of a move (place the ball can be * stationary) * - every gem (place the ball can go through in motion). * Vertices of this type have an associated direction, since * if a gem can be collected by sliding through it in two * different directions it doesn't follow that you can * change direction at it. * * I'm going to refer to a non-directional vertex as * (y*w+x)*DP1+DIRECTIONS, and a directional one as * (y*w+x)*DP1+d. */ /* * nodeindex[] maps node codes as shown above to numeric * indices in the nodes[] array. */ nodeindex = snewn(DP1*wh, int); for (i = 0; i < DP1*wh; i++) nodeindex[i] = -1; /* * Do the bfs to find all the interesting graph nodes. */ nodes = snewn(DP1*wh, int); head = tail = 0; nodes[tail] = (currstate->py * w + currstate->px) * DP1 + DIRECTIONS; nodeindex[nodes[0]] = tail; tail++; while (head < tail) { int nc = nodes[head++], nnc; d = nc % DP1; /* * Plot all possible moves from this node. If the node is * directed, there's only one. */ for (dd = 0; dd < DIRECTIONS; dd++) { x = nc / DP1; y = x / w; x %= w; if (d < DIRECTIONS && d != dd) continue; nnc = move_goes_to(w, h, currstate->grid, x, y, dd); if (nnc >= 0 && nnc != nc) { if (nodeindex[nnc] < 0) { nodes[tail] = nnc; nodeindex[nnc] = tail; tail++; } } } } n = head; /* * Now we know how many nodes we have, allocate the edge array * and go through setting up the edges. */ edges = snewn(DIRECTIONS*n, int); edgei = snewn(n+1, int); nedges = 0; for (i = 0; i < n; i++) { int nc = nodes[i]; edgei[i] = nedges; d = nc % DP1; x = nc / DP1; y = x / w; x %= w; for (dd = 0; dd < DIRECTIONS; dd++) { int nnc; if (d >= DIRECTIONS || d == dd) { nnc = move_goes_to(w, h, currstate->grid, x, y, dd); if (nnc >= 0 && nnc != nc) edges[nedges++] = nodeindex[nnc]; } } } edgei[n] = nedges; /* * Now set up the backedges array. */ backedges = snewn(nedges, int); backedgei = snewn(n+1, int); for (i = j = 0; i < nedges; i++) { while (j+1 < n && i >= edgei[j+1]) j++; backedges[i] = edges[i] * n + j; } qsort(backedges, nedges, sizeof(int), compare_integers); backedgei[0] = 0; for (i = j = 0; i < nedges; i++) { int k = backedges[i] / n; backedges[i] %= n; while (j < k) backedgei[++j] = i; } backedgei[n] = nedges; /* * Set up the initial tour. At all times, our tour is a circuit * of graph vertices (which may, and probably will often, * repeat vertices). To begin with, it's got exactly one vertex * in it, which is the player's current starting point. */ circuitsize = 256; circuit = snewn(circuitsize, int); circuitlen = 0; circuit[circuitlen++] = 0; /* node index 0 is the starting posn */ /* * Track which gems are as yet unvisited. */ unvisited = snewn(wh, int); for (i = 0; i < wh; i++) unvisited[i] = FALSE; for (i = 0; i < wh; i++) if (currstate->grid[i] == GEM) unvisited[i] = TRUE; /* * Allocate space for doing bfses inside the main loop. */ dist = snewn(n, int); dist2 = snewn(n, int); list = snewn(n, int); err = NULL; soln = NULL; /* * Now enter the main loop, in each iteration of which we * extend the tour to take in an as yet uncollected gem. */ while (1) { int target, n1, n2, bestdist, extralen, targetpos; #ifdef TSP_DIAGNOSTICS printf("circuit is"); for (i = 0; i < circuitlen; i++) { int nc = nodes[circuit[i]]; printf(" (%d,%d,%d)", nc/DP1%w, nc/(DP1*w), nc%DP1); } printf("\n"); printf("moves are "); x = nodes[circuit[0]] / DP1 % w; y = nodes[circuit[0]] / DP1 / w; for (i = 1; i < circuitlen; i++) { int x2, y2, dx, dy; if (nodes[circuit[i]] % DP1 != DIRECTIONS) continue; x2 = nodes[circuit[i]] / DP1 % w; y2 = nodes[circuit[i]] / DP1 / w; dx = (x2 > x ? +1 : x2 < x ? -1 : 0); dy = (y2 > y ? +1 : y2 < y ? -1 : 0); for (d = 0; d < DIRECTIONS; d++) if (DX(d) == dx && DY(d) == dy) printf("%c", "89632147"[d]); x = x2; y = y2; } printf("\n"); #endif /* * First, start a pair of bfses at _every_ vertex currently * in the tour, and extend them outwards to find the * nearest as yet unreached gem vertex. * * This is largely a heuristic: we could pick _any_ doubly * reachable node here and still get a valid tour as * output. I hope that picking a nearby one will result in * generally good tours. */ for (pass = 0; pass < 2; pass++) { int *ep = (pass == 0 ? edges : backedges); int *ei = (pass == 0 ? edgei : backedgei); int *dp = (pass == 0 ? dist : dist2); head = tail = 0; for (i = 0; i < n; i++) dp[i] = -1; for (i = 0; i < circuitlen; i++) { int ni = circuit[i]; if (dp[ni] < 0) { dp[ni] = 0; list[tail++] = ni; } } while (head < tail) { int ni = list[head++]; for (i = ei[ni]; i < ei[ni+1]; i++) { int ti = ep[i]; if (ti >= 0 && dp[ti] < 0) { dp[ti] = dp[ni] + 1; list[tail++] = ti; } } } } /* Now find the nearest unvisited gem. */ bestdist = -1; target = -1; for (i = 0; i < n; i++) { if (unvisited[nodes[i] / DP1] && dist[i] >= 0 && dist2[i] >= 0) { int thisdist = dist[i] + dist2[i]; if (bestdist < 0 || bestdist > thisdist) { bestdist = thisdist; target = i; } } } if (target < 0) { /* * If we get to here, we haven't found a gem we can get * at all, which means we terminate this loop. */ break; } /* * Now we have a graph vertex at list[tail-1] which is an * unvisited gem. We want to add that vertex to our tour. * So we run two more breadth-first searches: one starting * from that vertex and following forward edges, and * another starting from the same vertex and following * backward edges. This allows us to determine, for each * node on the current tour, how quickly we can get both to * and from the target vertex from that node. */ #ifdef TSP_DIAGNOSTICS printf("target node is %d (%d,%d,%d)\n", target, nodes[target]/DP1%w, nodes[target]/DP1/w, nodes[target]%DP1); #endif for (pass = 0; pass < 2; pass++) { int *ep = (pass == 0 ? edges : backedges); int *ei = (pass == 0 ? edgei : backedgei); int *dp = (pass == 0 ? dist : dist2); for (i = 0; i < n; i++) dp[i] = -1; head = tail = 0; dp[target] = 0; list[tail++] = target; while (head < tail) { int ni = list[head++]; for (i = ei[ni]; i < ei[ni+1]; i++) { int ti = ep[i]; if (ti >= 0 && dp[ti] < 0) { dp[ti] = dp[ni] + 1; /*printf("pass %d: set dist of vertex %d to %d (via %d)\n", pass, ti, dp[ti], ni);*/ list[tail++] = ti; } } } } /* * Now for every node n, dist[n] gives the length of the * shortest path from the target vertex to n, and dist2[n] * gives the length of the shortest path from n to the * target vertex. * * Our next step is to search linearly along the tour to * find the optimum place to insert a trip to the target * vertex and back. Our two options are either * (a) to find two adjacent vertices A,B in the tour and * replace the edge A->B with the path A->target->B * (b) to find a single vertex X in the tour and replace * it with the complete round trip X->target->X. * We do whichever takes the fewest moves. */ n1 = n2 = -1; bestdist = -1; for (i = 0; i < circuitlen; i++) { int thisdist; /* * Try a round trip from vertex i. */ if (dist[circuit[i]] >= 0 && dist2[circuit[i]] >= 0) { thisdist = dist[circuit[i]] + dist2[circuit[i]]; if (bestdist < 0 || thisdist < bestdist) { bestdist = thisdist; n1 = n2 = i; } } /* * Try a trip from vertex i via target to vertex i+1. */ if (i+1 < circuitlen && dist2[circuit[i]] >= 0 && dist[circuit[i+1]] >= 0) { thisdist = dist2[circuit[i]] + dist[circuit[i+1]]; if (bestdist < 0 || thisdist < bestdist) { bestdist = thisdist; n1 = i; n2 = i+1; } } } if (bestdist < 0) { /* * We couldn't find a round trip taking in this gem _at * all_. Give up. */ err = "Unable to find a solution from this starting point"; break; } #ifdef TSP_DIAGNOSTICS printf("insertion point: n1=%d, n2=%d, dist=%d\n", n1, n2, bestdist); #endif #ifdef TSP_DIAGNOSTICS printf("circuit before lengthening is"); for (i = 0; i < circuitlen; i++) { printf(" %d", circuit[i]); } printf("\n"); #endif /* * Now actually lengthen the tour to take in this round * trip. */ extralen = dist2[circuit[n1]] + dist[circuit[n2]]; if (n1 != n2) extralen--; circuitlen += extralen; if (circuitlen >= circuitsize) { circuitsize = circuitlen + 256; circuit = sresize(circuit, circuitsize, int); } memmove(circuit + n2 + extralen, circuit + n2, (circuitlen - n2 - extralen) * sizeof(int)); n2 += extralen; #ifdef TSP_DIAGNOSTICS printf("circuit in middle of lengthening is"); for (i = 0; i < circuitlen; i++) { printf(" %d", circuit[i]); } printf("\n"); #endif /* * Find the shortest-path routes to and from the target, * and write them into the circuit. */ targetpos = n1 + dist2[circuit[n1]]; assert(targetpos - dist2[circuit[n1]] == n1); assert(targetpos + dist[circuit[n2]] == n2); for (pass = 0; pass < 2; pass++) { int dir = (pass == 0 ? -1 : +1); int *ep = (pass == 0 ? backedges : edges); int *ei = (pass == 0 ? backedgei : edgei); int *dp = (pass == 0 ? dist : dist2); int nn = (pass == 0 ? n2 : n1); int ni = circuit[nn], ti, dest = nn; while (1) { circuit[dest] = ni; if (dp[ni] == 0) break; dest += dir; ti = -1; /*printf("pass %d: looking at vertex %d\n", pass, ni);*/ for (i = ei[ni]; i < ei[ni+1]; i++) { ti = ep[i]; if (ti >= 0 && dp[ti] == dp[ni] - 1) break; } assert(i < ei[ni+1] && ti >= 0); ni = ti; } } #ifdef TSP_DIAGNOSTICS printf("circuit after lengthening is"); for (i = 0; i < circuitlen; i++) { printf(" %d", circuit[i]); } printf("\n"); #endif /* * Finally, mark all gems that the new piece of circuit * passes through as visited. */ for (i = n1; i <= n2; i++) { int pos = nodes[circuit[i]] / DP1; assert(pos >= 0 && pos < wh); unvisited[pos] = FALSE; } } #ifdef TSP_DIAGNOSTICS printf("before reduction, moves are "); x = nodes[circuit[0]] / DP1 % w; y = nodes[circuit[0]] / DP1 / w; for (i = 1; i < circuitlen; i++) { int x2, y2, dx, dy; if (nodes[circuit[i]] % DP1 != DIRECTIONS) continue; x2 = nodes[circuit[i]] / DP1 % w; y2 = nodes[circuit[i]] / DP1 / w; dx = (x2 > x ? +1 : x2 < x ? -1 : 0); dy = (y2 > y ? +1 : y2 < y ? -1 : 0); for (d = 0; d < DIRECTIONS; d++) if (DX(d) == dx && DY(d) == dy) printf("%c", "89632147"[d]); x = x2; y = y2; } printf("\n"); #endif /* * That's got a basic solution. Now optimise it by removing * redundant sections of the circuit: it's entirely possible * that a piece of circuit we carefully inserted at one stage * to collect a gem has become pointless because the steps * required to collect some _later_ gem necessarily passed * through the same one. * * So first we go through and work out how many times each gem * is collected. Then we look for maximal sections of circuit * which are redundant in the sense that their removal would * not reduce any gem's collection count to zero, and replace * each one with a bfs-derived fastest path between their * endpoints. */ while (1) { int oldlen = circuitlen; int dir; for (dir = +1; dir >= -1; dir -= 2) { for (i = 0; i < wh; i++) unvisited[i] = 0; for (i = 0; i < circuitlen; i++) { int xy = nodes[circuit[i]] / DP1; if (currstate->grid[xy] == GEM) unvisited[xy]++; } /* * If there's any gem we didn't end up visiting at all, * give up. */ for (i = 0; i < wh; i++) { if (currstate->grid[i] == GEM && unvisited[i] == 0) { err = "Unable to find a solution from this starting point"; break; } } if (i < wh) break; for (i = j = (dir > 0 ? 0 : circuitlen-1); i < circuitlen && i >= 0; i += dir) { int xy = nodes[circuit[i]] / DP1; if (currstate->grid[xy] == GEM && unvisited[xy] > 1) { unvisited[xy]--; } else if (currstate->grid[xy] == GEM || i == circuitlen-1) { /* * circuit[i] collects a gem for the only time, * or is the last node in the circuit. * Therefore it cannot be removed; so we now * want to replace the path from circuit[j] to * circuit[i] with a bfs-shortest path. */ int p, q, k, dest, ni, ti, thisdist; /* * Set up the upper and lower bounds of the * reduced section. */ p = min(i, j); q = max(i, j); #ifdef TSP_DIAGNOSTICS printf("optimising section from %d - %d\n", p, q); #endif for (k = 0; k < n; k++) dist[k] = -1; head = tail = 0; dist[circuit[p]] = 0; list[tail++] = circuit[p]; while (head < tail && dist[circuit[q]] < 0) { int ni = list[head++]; for (k = edgei[ni]; k < edgei[ni+1]; k++) { int ti = edges[k]; if (ti >= 0 && dist[ti] < 0) { dist[ti] = dist[ni] + 1; list[tail++] = ti; } } } thisdist = dist[circuit[q]]; assert(thisdist >= 0 && thisdist <= q-p); memmove(circuit+p+thisdist, circuit+q, (circuitlen - q) * sizeof(int)); circuitlen -= q-p; q = p + thisdist; circuitlen += q-p; if (dir > 0) i = q; /* resume loop from the right place */ #ifdef TSP_DIAGNOSTICS printf("new section runs from %d - %d\n", p, q); #endif dest = q; assert(dest >= 0); ni = circuit[q]; while (1) { /* printf("dest=%d circuitlen=%d ni=%d dist[ni]=%d\n", dest, circuitlen, ni, dist[ni]); */ circuit[dest] = ni; if (dist[ni] == 0) break; dest--; ti = -1; for (k = backedgei[ni]; k < backedgei[ni+1]; k++) { ti = backedges[k]; if (ti >= 0 && dist[ti] == dist[ni] - 1) break; } assert(k < backedgei[ni+1] && ti >= 0); ni = ti; } /* * Now re-increment the visit counts for the * new path. */ while (++p < q) { int xy = nodes[circuit[p]] / DP1; if (currstate->grid[xy] == GEM) unvisited[xy]++; } j = i; #ifdef TSP_DIAGNOSTICS printf("during reduction, circuit is"); for (k = 0; k < circuitlen; k++) { int nc = nodes[circuit[k]]; printf(" (%d,%d,%d)", nc/DP1%w, nc/(DP1*w), nc%DP1); } printf("\n"); printf("moves are "); x = nodes[circuit[0]] / DP1 % w; y = nodes[circuit[0]] / DP1 / w; for (k = 1; k < circuitlen; k++) { int x2, y2, dx, dy; if (nodes[circuit[k]] % DP1 != DIRECTIONS) continue; x2 = nodes[circuit[k]] / DP1 % w; y2 = nodes[circuit[k]] / DP1 / w; dx = (x2 > x ? +1 : x2 < x ? -1 : 0); dy = (y2 > y ? +1 : y2 < y ? -1 : 0); for (d = 0; d < DIRECTIONS; d++) if (DX(d) == dx && DY(d) == dy) printf("%c", "89632147"[d]); x = x2; y = y2; } printf("\n"); #endif } } #ifdef TSP_DIAGNOSTICS printf("after reduction, moves are "); x = nodes[circuit[0]] / DP1 % w; y = nodes[circuit[0]] / DP1 / w; for (i = 1; i < circuitlen; i++) { int x2, y2, dx, dy; if (nodes[circuit[i]] % DP1 != DIRECTIONS) continue; x2 = nodes[circuit[i]] / DP1 % w; y2 = nodes[circuit[i]] / DP1 / w; dx = (x2 > x ? +1 : x2 < x ? -1 : 0); dy = (y2 > y ? +1 : y2 < y ? -1 : 0); for (d = 0; d < DIRECTIONS; d++) if (DX(d) == dx && DY(d) == dy) printf("%c", "89632147"[d]); x = x2; y = y2; } printf("\n"); #endif } /* * If we've managed an entire reduction pass in each * direction and not made the solution any shorter, we're * _really_ done. */ if (circuitlen == oldlen) break; } /* * Encode the solution as a move string. */ if (!err) { soln = snewn(circuitlen+2, char); p = soln; *p++ = 'S'; x = nodes[circuit[0]] / DP1 % w; y = nodes[circuit[0]] / DP1 / w; for (i = 1; i < circuitlen; i++) { int x2, y2, dx, dy; if (nodes[circuit[i]] % DP1 != DIRECTIONS) continue; x2 = nodes[circuit[i]] / DP1 % w; y2 = nodes[circuit[i]] / DP1 / w; dx = (x2 > x ? +1 : x2 < x ? -1 : 0); dy = (y2 > y ? +1 : y2 < y ? -1 : 0); for (d = 0; d < DIRECTIONS; d++) if (DX(d) == dx && DY(d) == dy) { *p++ = '0' + d; break; } assert(d < DIRECTIONS); x = x2; y = y2; } *p++ = '\0'; assert(p - soln < circuitlen+2); } sfree(list); sfree(dist); sfree(dist2); sfree(unvisited); sfree(circuit); sfree(backedgei); sfree(backedges); sfree(edgei); sfree(edges); sfree(nodeindex); sfree(nodes); if (err) *error = err; return soln; } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { return NULL; } struct game_ui { float anim_length; int flashtype; int deaths; int just_made_move; int just_died; }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->anim_length = 0.0F; ui->flashtype = 0; ui->deaths = 0; ui->just_made_move = FALSE; ui->just_died = FALSE; return ui; } static void free_ui(game_ui *ui) { sfree(ui); } static char *encode_ui(const game_ui *ui) { char buf[80]; /* * The deaths counter needs preserving across a serialisation. */ sprintf(buf, "D%d", ui->deaths); return dupstr(buf); } static void decode_ui(game_ui *ui, const char *encoding) { int p = 0; sscanf(encoding, "D%d%n", &ui->deaths, &p); } static void game_changed_state(game_ui *ui, const game_state *oldstate, const game_state *newstate) { /* * Increment the deaths counter. We only do this if * ui->just_made_move is set (redoing a suicide move doesn't * kill you _again_), and also we only do it if the game wasn't * already completed (once you're finished, you can play). */ if (!oldstate->dead && newstate->dead && ui->just_made_move && oldstate->gems) { ui->deaths++; ui->just_died = TRUE; } else { ui->just_died = FALSE; } ui->just_made_move = FALSE; } struct game_drawstate { game_params p; int tilesize; int started; unsigned short *grid; blitter *player_background; int player_bg_saved, pbgx, pbgy; }; #define PREFERRED_TILESIZE 32 #define TILESIZE (ds->tilesize) #ifdef SMALL_SCREEN #define BORDER (TILESIZE / 4) #else #define BORDER (TILESIZE) #endif #define HIGHLIGHT_WIDTH (TILESIZE / 10) #define COORD(x) ( (x) * TILESIZE + BORDER ) #define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 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->p.w, h = state->p.h /*, wh = w*h */; int dir; char buf[80]; dir = -1; if (button == LEFT_BUTTON) { /* * Mouse-clicking near the target point (or, more * accurately, in the appropriate octant) is an alternative * way to input moves. */ if (FROMCOORD(x) != state->px || FROMCOORD(y) != state->py) { int dx, dy; float angle; dx = FROMCOORD(x) - state->px; dy = FROMCOORD(y) - state->py; /* I pass dx,dy rather than dy,dx so that the octants * end up the right way round. */ angle = atan2(dx, -dy); angle = (angle + (PI/8)) / (PI/4); assert(angle > -16.0F); dir = (int)(angle + 16.0F) & 7; } } else if (button == CURSOR_UP || button == (MOD_NUM_KEYPAD | '8')) dir = 0; else if (button == CURSOR_DOWN || button == (MOD_NUM_KEYPAD | '2')) dir = 4; else if (button == CURSOR_LEFT || button == (MOD_NUM_KEYPAD | '4')) dir = 6; else if (button == CURSOR_RIGHT || button == (MOD_NUM_KEYPAD | '6')) dir = 2; else if (button == (MOD_NUM_KEYPAD | '7')) dir = 7; else if (button == (MOD_NUM_KEYPAD | '1')) dir = 5; else if (button == (MOD_NUM_KEYPAD | '9')) dir = 1; else if (button == (MOD_NUM_KEYPAD | '3')) dir = 3; else if (IS_CURSOR_SELECT(button) && state->soln && state->solnpos < state->soln->len) dir = state->soln->list[state->solnpos]; if (dir < 0) return NULL; /* * Reject the move if we can't make it at all due to a wall * being in the way. */ if (AT(w, h, state->grid, state->px+DX(dir), state->py+DY(dir)) == WALL) return NULL; /* * Reject the move if we're dead! */ if (state->dead) return NULL; /* * Otherwise, we can make the move. All we need to specify is * the direction. */ ui->just_made_move = TRUE; sprintf(buf, "%d", dir); return dupstr(buf); } static game_state *execute_move(const game_state *state, const char *move) { int w = state->p.w, h = state->p.h /*, wh = w*h */; int dir; game_state *ret; if (*move == 'S') { int len, i; soln *sol; /* * This is a solve move, so we don't actually _change_ the * grid but merely set up a stored solution path. */ move++; len = strlen(move); sol = snew(soln); sol->len = len; sol->list = snewn(len, unsigned char); for (i = 0; i < len; i++) sol->list[i] = move[i] - '0'; ret = dup_game(state); ret->cheated = TRUE; if (ret->soln && --ret->soln->refcount == 0) { sfree(ret->soln->list); sfree(ret->soln); } ret->soln = sol; ret->solnpos = 0; sol->refcount = 1; return ret; } dir = atoi(move); if (dir < 0 || dir >= DIRECTIONS) return NULL; /* huh? */ if (state->dead) return NULL; if (AT(w, h, state->grid, state->px+DX(dir), state->py+DY(dir)) == WALL) return NULL; /* wall in the way! */ /* * Now make the move. */ ret = dup_game(state); ret->distance_moved = 0; while (1) { ret->px += DX(dir); ret->py += DY(dir); ret->distance_moved++; if (AT(w, h, ret->grid, ret->px, ret->py) == GEM) { LV_AT(w, h, ret->grid, ret->px, ret->py) = BLANK; ret->gems--; } if (AT(w, h, ret->grid, ret->px, ret->py) == MINE) { ret->dead = TRUE; break; } if (AT(w, h, ret->grid, ret->px, ret->py) == STOP || AT(w, h, ret->grid, ret->px+DX(dir), ret->py+DY(dir)) == WALL) break; } if (ret->soln) { /* * If this move is the correct next one in the stored * solution path, advance solnpos. */ if (ret->soln->list[ret->solnpos] == dir && ret->solnpos+1 < ret->soln->len) { ret->solnpos++; } else { /* * Otherwise, the user has strayed from the path, so * the path is no longer valid. */ ret->soln->refcount--; assert(ret->soln->refcount > 0);/* `state' at least still exists */ ret->soln = NULL; ret->solnpos = 0; } } 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; assert(!ds->player_background); /* set_size is never called twice */ assert(!ds->player_bg_saved); ds->player_background = blitter_new(dr, 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_DEAD_PLAYER * 3 + 0] = 1.0F; ret[COL_DEAD_PLAYER * 3 + 1] = 0.0F; ret[COL_DEAD_PLAYER * 3 + 2] = 0.0F; ret[COL_MINE * 3 + 0] = 0.0F; ret[COL_MINE * 3 + 1] = 0.0F; ret[COL_MINE * 3 + 2] = 0.0F; ret[COL_GEM * 3 + 0] = 0.6F; ret[COL_GEM * 3 + 1] = 1.0F; ret[COL_GEM * 3 + 2] = 1.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; } ret[COL_HINT * 3 + 0] = 1.0F; ret[COL_HINT * 3 + 1] = 1.0F; ret[COL_HINT * 3 + 2] = 0.0F; *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, wh = w*h; struct game_drawstate *ds = snew(struct game_drawstate); int i; ds->tilesize = 0; /* We can't allocate the blitter rectangle for the player background * until we know what size to make it. */ ds->player_background = NULL; ds->player_bg_saved = FALSE; ds->pbgx = ds->pbgy = -1; ds->p = state->p; /* structure copy */ ds->started = FALSE; ds->grid = snewn(wh, unsigned short); for (i = 0; i < wh; i++) ds->grid[i] = UNDRAWN; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { if (ds->player_background) blitter_free(dr, ds->player_background); sfree(ds->grid); sfree(ds); } static void draw_player(drawing *dr, game_drawstate *ds, int x, int y, int dead, int hintdir) { if (dead) { int coords[DIRECTIONS*4]; int d; for (d = 0; d < DIRECTIONS; d++) { float x1, y1, x2, y2, x3, y3, len; x1 = DX(d); y1 = DY(d); len = sqrt(x1*x1+y1*y1); x1 /= len; y1 /= len; x3 = DX(d+1); y3 = DY(d+1); len = sqrt(x3*x3+y3*y3); x3 /= len; y3 /= len; x2 = (x1+x3) / 4; y2 = (y1+y3) / 4; coords[d*4+0] = x + TILESIZE/2 + (int)((TILESIZE*3/7) * x1); coords[d*4+1] = y + TILESIZE/2 + (int)((TILESIZE*3/7) * y1); coords[d*4+2] = x + TILESIZE/2 + (int)((TILESIZE*3/7) * x2); coords[d*4+3] = y + TILESIZE/2 + (int)((TILESIZE*3/7) * y2); } draw_polygon(dr, coords, DIRECTIONS*2, COL_DEAD_PLAYER, COL_OUTLINE); } else { draw_circle(dr, x + TILESIZE/2, y + TILESIZE/2, TILESIZE/3, COL_PLAYER, COL_OUTLINE); } if (!dead && hintdir >= 0) { float scale = (DX(hintdir) && DY(hintdir) ? 0.8F : 1.0F); int ax = (TILESIZE*2/5) * scale * DX(hintdir); int ay = (TILESIZE*2/5) * scale * DY(hintdir); int px = -ay, py = ax; int ox = x + TILESIZE/2, oy = y + TILESIZE/2; int coords[14], *c; c = coords; *c++ = ox + px/9; *c++ = oy + py/9; *c++ = ox + px/9 + ax*2/3; *c++ = oy + py/9 + ay*2/3; *c++ = ox + px/3 + ax*2/3; *c++ = oy + py/3 + ay*2/3; *c++ = ox + ax; *c++ = oy + ay; *c++ = ox - px/3 + ax*2/3; *c++ = oy - py/3 + ay*2/3; *c++ = ox - px/9 + ax*2/3; *c++ = oy - py/9 + ay*2/3; *c++ = ox - px/9; *c++ = oy - py/9; draw_polygon(dr, coords, 7, COL_HINT, COL_OUTLINE); } draw_update(dr, x, y, TILESIZE, TILESIZE); } #define FLASH_DEAD 0x100 #define FLASH_WIN 0x200 #define FLASH_MASK 0x300 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 & FLASH_DEAD ? COL_DEAD_PLAYER : v & FLASH_WIN ? COL_HIGHLIGHT : COL_BACKGROUND); v &= ~FLASH_MASK; 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 == MINE) { int cx = tx + TILESIZE / 2; int cy = ty + TILESIZE / 2; int r = TILESIZE / 2 - 3; draw_circle(dr, cx, cy, 5*r/6, COL_MINE, COL_MINE); draw_rect(dr, cx - r/6, cy - r, 2*(r/6)+1, 2*r+1, COL_MINE); draw_rect(dr, cx - r, cy - r/6, 2*r+1, 2*(r/6)+1, COL_MINE); draw_rect(dr, cx-r/3, cy-r/3, r/3, r/4, COL_HIGHLIGHT); } else if (v == STOP) { draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2, TILESIZE*3/7, -1, COL_OUTLINE); draw_rect(dr, tx + TILESIZE*3/7, ty+1, TILESIZE - 2*(TILESIZE*3/7) + 1, TILESIZE-1, bg); draw_rect(dr, tx+1, ty + TILESIZE*3/7, TILESIZE-1, TILESIZE - 2*(TILESIZE*3/7) + 1, bg); } else if (v == GEM) { int coords[8]; coords[0] = tx+TILESIZE/2; coords[1] = ty+TILESIZE/2-TILESIZE*5/14; coords[2] = tx+TILESIZE/2-TILESIZE*5/14; coords[3] = ty+TILESIZE/2; coords[4] = tx+TILESIZE/2; coords[5] = ty+TILESIZE/2+TILESIZE*5/14; coords[6] = tx+TILESIZE/2+TILESIZE*5/14; coords[7] = ty+TILESIZE/2; draw_polygon(dr, coords, 4, COL_GEM, COL_OUTLINE); } unclip(dr); draw_update(dr, tx, ty, TILESIZE, TILESIZE); } #define BASE_ANIM_LENGTH 0.1F #define FLASH_LENGTH 0.3F 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; float ap; int player_dist; int flashtype; int gems, deaths; char status[256]; if (flashtime && !((int)(flashtime * 3 / FLASH_LENGTH) % 2)) flashtype = ui->flashtype; else flashtype = 0; /* * Erase the player sprite. */ if (ds->player_bg_saved) { assert(ds->player_background); blitter_load(dr, ds->player_background, ds->pbgx, ds->pbgy); draw_update(dr, ds->pbgx, ds->pbgy, TILESIZE, TILESIZE); ds->player_bg_saved = FALSE; } /* * 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; } /* * If we're in the process of animating a move, let's start by * working out how far the player has moved from their _older_ * state. */ if (oldstate) { ap = animtime / ui->anim_length; player_dist = ap * (dir > 0 ? state : oldstate)->distance_moved; } else { player_dist = 0; ap = 0.0F; } /* * Draw the grid contents. * * We count the gems as we go round this loop, for the purposes * of the status bar. Of course we have a gems counter in the * game_state already, but if we do the counting in this loop * then it tracks gems being picked up in a sliding move, and * updates one by one. */ gems = 0; for (y = 0; y < h; y++) for (x = 0; x < w; x++) { unsigned short v = (unsigned char)state->grid[y*w+x]; /* * Special case: if the player is in the process of * moving over a gem, we draw the gem iff they haven't * gone past it yet. */ if (oldstate && oldstate->grid[y*w+x] != state->grid[y*w+x]) { /* * Compute the distance from this square to the * original player position. */ int dist = max(abs(x - oldstate->px), abs(y - oldstate->py)); /* * If the player has reached here, use the new grid * element. Otherwise use the old one. */ if (player_dist < dist) v = oldstate->grid[y*w+x]; else v = state->grid[y*w+x]; } /* * Special case: erase the mine the dead player is * sitting on. Only at the end of the move. */ if (v == MINE && !oldstate && state->dead && x == state->px && y == state->py) v = BLANK; if (v == GEM) gems++; v |= flashtype; if (ds->grid[y*w+x] != v) { draw_tile(dr, ds, x, y, v); ds->grid[y*w+x] = v; } } /* * Gem counter in the status bar. We replace it with * `COMPLETED!' when it reaches zero ... or rather, when the * _current state_'s gem counter is zero. (Thus, `Gems: 0' is * shown between the collection of the last gem and the * completion of the move animation that did it.) */ if (state->dead && (!oldstate || oldstate->dead)) { sprintf(status, "DEAD!"); } else if (state->gems || (oldstate && oldstate->gems)) { if (state->cheated) sprintf(status, "Auto-solver used. "); else *status = '\0'; sprintf(status + strlen(status), "Gems: %d", gems); } else if (state->cheated) { sprintf(status, "Auto-solved."); } else { sprintf(status, "COMPLETED!"); } /* We subtract one from the visible death counter if we're still * animating the move at the end of which the death took place. */ deaths = ui->deaths; if (oldstate && ui->just_died) { assert(deaths > 0); deaths--; } if (deaths) sprintf(status + strlen(status), " Deaths: %d", deaths); status_bar(dr, status); /* * Draw the player sprite. */ assert(!ds->player_bg_saved); assert(ds->player_background); { int ox, oy, nx, ny; nx = COORD(state->px); ny = COORD(state->py); if (oldstate) { ox = COORD(oldstate->px); oy = COORD(oldstate->py); } else { ox = nx; oy = ny; } ds->pbgx = ox + ap * (nx - ox); ds->pbgy = oy + ap * (ny - oy); } blitter_save(dr, ds->player_background, ds->pbgx, ds->pbgy); draw_player(dr, ds, ds->pbgx, ds->pbgy, (state->dead && !oldstate), (!oldstate && state->soln ? state->soln->list[state->solnpos] : -1)); ds->player_bg_saved = TRUE; } static float game_anim_length(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui) { int dist; if (dir > 0) dist = newstate->distance_moved; else dist = oldstate->distance_moved; ui->anim_length = sqrt(dist) * BASE_ANIM_LENGTH; return ui->anim_length; } static float game_flash_length(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui) { if (!oldstate->dead && newstate->dead) { ui->flashtype = FLASH_DEAD; return FLASH_LENGTH; } else if (oldstate->gems && !newstate->gems) { ui->flashtype = FLASH_WIN; return FLASH_LENGTH; } return 0.0F; } static int game_status(const game_state *state) { /* * We never report the game as lost, on the grounds that if the * player has died they're quite likely to want to undo and carry * on. */ return state->gems == 0 ? +1 : 0; } static int 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 inertia #endif const struct game thegame = { "Inertia", "games.inertia", "inertia", default_params, game_fetch_preset, 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, FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, decode_ui, 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_status, FALSE, FALSE, game_print_size, game_print, TRUE, /* wants_statusbar */ FALSE, game_timing_state, 0, /* flags */ }; puzzles-r9872/keen.c0000644000175300017530000017103012132232554013514 0ustar simonsimon/* * keen.c: an implementation of the Times's 'KenKen' puzzle. */ #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(EASY,Easy,solver_easy,e) \ 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 keen_diffnames[] = { DIFFLIST(TITLE) }; static char const keen_diffchars[] = DIFFLIST(ENCODE); #define DIFFCONFIG DIFFLIST(CONFIG) /* * Clue notation. Important here that ADD and MUL come before SUB * and DIV, and that DIV comes last. */ #define C_ADD 0x00000000L #define C_MUL 0x20000000L #define C_SUB 0x40000000L #define C_DIV 0x60000000L #define CMASK 0x60000000L #define CUNIT 0x20000000L /* * Maximum size of any clue block. Very large ones are annoying in UI * terms (if they're multiplicative you end up with too many digits to * fit in the square) and also in solver terms (too many possibilities * to iterate over). */ #define MAXBLK 6 enum { COL_BACKGROUND, COL_GRID, COL_USER, COL_HIGHLIGHT, COL_ERROR, COL_PENCIL, NCOLOURS }; struct game_params { int w, diff; }; struct clues { int refcount; int w; int *dsf; long *clues; }; struct game_state { game_params par; struct clues *clues; digit *grid; int *pencil; /* bitmaps using bits 1<<1..1<w = 6; ret->diff = DIFF_NORMAL; return ret; } const static struct game_params keen_presets[] = { { 4, DIFF_EASY }, { 5, DIFF_EASY }, { 6, DIFF_EASY }, { 6, DIFF_NORMAL }, { 6, DIFF_HARD }, { 6, DIFF_EXTREME }, { 6, DIFF_UNREASONABLE }, { 9, DIFF_NORMAL }, }; static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char buf[80]; if (i < 0 || i >= lenof(keen_presets)) return FALSE; ret = snew(game_params); *ret = keen_presets[i]; /* structure copy */ sprintf(buf, "%dx%d %s", ret->w, ret->w, keen_diffnames[ret->diff]); *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++; if (*p == 'd') { int i; p++; params->diff = DIFFCOUNT+1; /* ...which is invalid */ if (*p) { for (i = 0; i < DIFFCOUNT; i++) { if (*p == keen_diffchars[i]) params->diff = i; } p++; } } } static char *encode_params(const game_params *params, int full) { char ret[80]; sprintf(ret, "%d", params->w); if (full) sprintf(ret + strlen(ret), "d%c", keen_diffchars[params->diff]); return dupstr(ret); } static config_item *game_configure(const game_params *params) { config_item *ret; char buf[80]; ret = snewn(3, config_item); ret[0].name = "Grid size"; ret[0].type = C_STRING; sprintf(buf, "%d", params->w); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Difficulty"; ret[1].type = C_CHOICES; ret[1].sval = DIFFCONFIG; ret[1].ival = params->diff; ret[2].name = NULL; ret[2].type = C_END; ret[2].sval = NULL; ret[2].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->diff = cfg[1].ival; return ret; } static char *validate_params(const game_params *params, int full) { if (params->w < 3 || params->w > 9) return "Grid size must be between 3 and 9"; if (params->diff >= DIFFCOUNT) return "Unknown difficulty rating"; return NULL; } /* ---------------------------------------------------------------------- * Solver. */ struct solver_ctx { int w, diff; int nboxes; int *boxes, *boxlist, *whichbox; long *clues; digit *soln; digit *dscratch; int *iscratch; }; static void solver_clue_candidate(struct solver_ctx *ctx, int diff, int box) { int w = ctx->w; int n = ctx->boxes[box+1] - ctx->boxes[box]; int j; /* * This function is called from the main clue-based solver * routine when we discover a candidate layout for a given clue * box consistent with everything we currently know about the * digit constraints in that box. We expect to find the digits * of the candidate layout in ctx->dscratch, and we update * ctx->iscratch as appropriate. */ if (diff == DIFF_EASY) { unsigned mask = 0; /* * Easy-mode clue deductions: we do not record information * about which squares take which values, so we amalgamate * all the values in dscratch and OR them all into * everywhere. */ for (j = 0; j < n; j++) mask |= 1 << ctx->dscratch[j]; for (j = 0; j < n; j++) ctx->iscratch[j] |= mask; } else if (diff == DIFF_NORMAL) { /* * Normal-mode deductions: we process the information in * dscratch in the obvious way. */ for (j = 0; j < n; j++) ctx->iscratch[j] |= 1 << ctx->dscratch[j]; } else if (diff == DIFF_HARD) { /* * Hard-mode deductions: instead of ruling things out * _inside_ the clue box, we look for numbers which occur in * a given row or column in all candidate layouts, and rule * them out of all squares in that row or column that * _aren't_ part of this clue box. */ int *sq = ctx->boxlist + ctx->boxes[box]; for (j = 0; j < 2*w; j++) ctx->iscratch[2*w+j] = 0; for (j = 0; j < n; j++) { int x = sq[j] / w, y = sq[j] % w; ctx->iscratch[2*w+x] |= 1 << ctx->dscratch[j]; ctx->iscratch[3*w+y] |= 1 << ctx->dscratch[j]; } for (j = 0; j < 2*w; j++) ctx->iscratch[j] &= ctx->iscratch[2*w+j]; } } static int solver_common(struct latin_solver *solver, void *vctx, int diff) { struct solver_ctx *ctx = (struct solver_ctx *)vctx; int w = ctx->w; int box, i, j, k; int ret = 0, total; /* * Iterate over each clue box and deduce what we can. */ for (box = 0; box < ctx->nboxes; box++) { int *sq = ctx->boxlist + ctx->boxes[box]; int n = ctx->boxes[box+1] - ctx->boxes[box]; long value = ctx->clues[box] & ~CMASK; long op = ctx->clues[box] & CMASK; if (diff == DIFF_HARD) { for (i = 0; i < n; i++) ctx->iscratch[i] = (1 << (w+1)) - (1 << 1); } else { for (i = 0; i < n; i++) ctx->iscratch[i] = 0; } switch (op) { case C_SUB: case C_DIV: /* * These two clue types must always apply to a box of * area 2. Also, the two digits in these boxes can never * be the same (because any domino must have its two * squares in either the same row or the same column). * So we simply iterate over all possibilities for the * two squares (both ways round), rule out any which are * inconsistent with the digit constraints we already * have, and update the digit constraints with any new * information thus garnered. */ assert(n == 2); for (i = 1; i <= w; i++) { j = (op == C_SUB ? i + value : i * value); if (j > w) break; /* (i,j) is a valid digit pair. Try it both ways round. */ if (solver->cube[sq[0]*w+i-1] && solver->cube[sq[1]*w+j-1]) { ctx->dscratch[0] = i; ctx->dscratch[1] = j; solver_clue_candidate(ctx, diff, box); } if (solver->cube[sq[0]*w+j-1] && solver->cube[sq[1]*w+i-1]) { ctx->dscratch[0] = j; ctx->dscratch[1] = i; solver_clue_candidate(ctx, diff, box); } } break; case C_ADD: case C_MUL: /* * For these clue types, I have no alternative but to go * through all possible number combinations. * * Instead of a tedious physical recursion, I iterate in * the scratch array through all possibilities. At any * given moment, i indexes the element of the box that * will next be incremented. */ i = 0; ctx->dscratch[i] = 0; total = value; /* start with the identity */ while (1) { if (i < n) { /* * Find the next valid value for cell i. */ for (j = ctx->dscratch[i] + 1; j <= w; j++) { if (op == C_ADD ? (total < j) : (total % j != 0)) continue; /* this one won't fit */ if (!solver->cube[sq[i]*w+j-1]) continue; /* this one is ruled out already */ for (k = 0; k < i; k++) if (ctx->dscratch[k] == j && (sq[k] % w == sq[i] % w || sq[k] / w == sq[i] / w)) break; /* clashes with another row/col */ if (k < i) continue; /* Found one. */ break; } if (j > w) { /* No valid values left; drop back. */ i--; if (i < 0) break; /* overall iteration is finished */ if (op == C_ADD) total += ctx->dscratch[i]; else total *= ctx->dscratch[i]; } else { /* Got a valid value; store it and move on. */ ctx->dscratch[i++] = j; if (op == C_ADD) total -= j; else total /= j; ctx->dscratch[i] = 0; } } else { if (total == (op == C_ADD ? 0 : 1)) solver_clue_candidate(ctx, diff, box); i--; if (op == C_ADD) total += ctx->dscratch[i]; else total *= ctx->dscratch[i]; } } break; } if (diff < DIFF_HARD) { #ifdef STANDALONE_SOLVER char prefix[256]; if (solver_show_working) sprintf(prefix, "%*susing clue at (%d,%d):\n", solver_recurse_depth*4, "", sq[0]/w+1, sq[0]%w+1); else prefix[0] = '\0'; /* placate optimiser */ #endif for (i = 0; i < n; i++) for (j = 1; j <= w; j++) { if (solver->cube[sq[i]*w+j-1] && !(ctx->iscratch[i] & (1 << j))) { #ifdef STANDALONE_SOLVER if (solver_show_working) { printf("%s%*s ruling out %d at (%d,%d)\n", prefix, solver_recurse_depth*4, "", j, sq[i]/w+1, sq[i]%w+1); prefix[0] = '\0'; } #endif solver->cube[sq[i]*w+j-1] = 0; ret = 1; } } } else { #ifdef STANDALONE_SOLVER char prefix[256]; if (solver_show_working) sprintf(prefix, "%*susing clue at (%d,%d):\n", solver_recurse_depth*4, "", sq[0]/w+1, sq[0]%w+1); else prefix[0] = '\0'; /* placate optimiser */ #endif for (i = 0; i < 2*w; i++) { int start = (i < w ? i*w : i-w); int step = (i < w ? 1 : w); for (j = 1; j <= w; j++) if (ctx->iscratch[i] & (1 << j)) { #ifdef STANDALONE_SOLVER char prefix2[256]; if (solver_show_working) sprintf(prefix2, "%*s this clue requires %d in" " %s %d:\n", solver_recurse_depth*4, "", j, i < w ? "column" : "row", i%w+1); else prefix2[0] = '\0'; /* placate optimiser */ #endif for (k = 0; k < w; k++) { int pos = start + k*step; if (ctx->whichbox[pos] != box && solver->cube[pos*w+j-1]) { #ifdef STANDALONE_SOLVER if (solver_show_working) { printf("%s%s%*s ruling out %d at (%d,%d)\n", prefix, prefix2, solver_recurse_depth*4, "", j, pos/w+1, pos%w+1); prefix[0] = prefix2[0] = '\0'; } #endif solver->cube[pos*w+j-1] = 0; ret = 1; } } } } /* * Once we find one block we can do something with in * this way, revert to trying easier deductions, so as * not to generate solver diagnostics that make the * problem look harder than it is. (We have to do this * for the Hard deductions but not the Easy/Normal ones, * because only the Hard deductions are cross-box.) */ if (ret) return ret; } } return ret; } static int solver_easy(struct latin_solver *solver, void *vctx) { /* * Omit the EASY deductions when solving at NORMAL level, since * the NORMAL deductions are a superset of them anyway and it * saves on time and confusing solver diagnostics. * * Note that this breaks the natural semantics of the return * value of latin_solver. Without this hack, you could determine * a puzzle's difficulty in one go by trying to solve it at * maximum difficulty and seeing what difficulty value was * returned; but with this hack, solving an Easy puzzle on * Normal difficulty will typically return Normal. Hence the * uses of the solver to determine difficulty are all arranged * so as to double-check by re-solving at the next difficulty * level down and making sure it failed. */ struct solver_ctx *ctx = (struct solver_ctx *)vctx; if (ctx->diff > DIFF_EASY) return 0; return solver_common(solver, vctx, DIFF_EASY); } static int solver_normal(struct latin_solver *solver, void *vctx) { return solver_common(solver, vctx, DIFF_NORMAL); } static int solver_hard(struct latin_solver *solver, void *vctx) { return solver_common(solver, vctx, DIFF_HARD); } #define SOLVER(upper,title,func,lower) func, static usersolver_t const keen_solvers[] = { DIFFLIST(SOLVER) }; static int solver(int w, int *dsf, long *clues, digit *soln, int maxdiff) { int a = w*w; struct solver_ctx ctx; int ret; int i, j, n, m; ctx.w = w; ctx.soln = soln; ctx.diff = maxdiff; /* * Transform the dsf-formatted clue list into one over which we * can iterate more easily. * * Also transpose the x- and y-coordinates at this point, * because the 'cube' array in the general Latin square solver * puts x first (oops). */ for (ctx.nboxes = i = 0; i < a; i++) if (dsf_canonify(dsf, i) == i) ctx.nboxes++; ctx.boxlist = snewn(a, int); ctx.boxes = snewn(ctx.nboxes+1, int); ctx.clues = snewn(ctx.nboxes, long); ctx.whichbox = snewn(a, int); for (n = m = i = 0; i < a; i++) if (dsf_canonify(dsf, i) == i) { ctx.clues[n] = clues[i]; ctx.boxes[n] = m; for (j = 0; j < a; j++) if (dsf_canonify(dsf, j) == i) { ctx.boxlist[m++] = (j % w) * w + (j / w); /* transpose */ ctx.whichbox[ctx.boxlist[m-1]] = n; } n++; } assert(n == ctx.nboxes); assert(m == a); ctx.boxes[n] = m; ctx.dscratch = snewn(a+1, digit); ctx.iscratch = snewn(max(a+1, 4*w), int); ret = latin_solver(soln, w, maxdiff, DIFF_EASY, DIFF_HARD, DIFF_EXTREME, DIFF_EXTREME, DIFF_UNREASONABLE, keen_solvers, &ctx, NULL, NULL); sfree(ctx.dscratch); sfree(ctx.iscratch); sfree(ctx.whichbox); sfree(ctx.boxlist); sfree(ctx.boxes); sfree(ctx.clues); return ret; } /* ---------------------------------------------------------------------- * Grid generation. */ static char *encode_block_structure(char *p, int w, int *dsf) { int i, currrun = 0; char *orig, *q, *r, c; orig = p; /* * Encode the block structure. We do this by encoding the * pattern of dividing lines: first we iterate over the w*(w-1) * internal vertical grid lines in ordinary reading order, then * over the w*(w-1) internal horizontal ones in transposed * reading order. * * We encode the number of non-lines between the lines; _ means * zero (two adjacent divisions), a means 1, ..., y means 25, * and z means 25 non-lines _and no following line_ (so that za * means 26, zb 27 etc). */ for (i = 0; i <= 2*w*(w-1); i++) { int x, y, p0, p1, edge; if (i == 2*w*(w-1)) { edge = TRUE; /* terminating virtual edge */ } else { if (i < w*(w-1)) { y = i/(w-1); x = i%(w-1); p0 = y*w+x; p1 = y*w+x+1; } else { x = i/(w-1) - w; y = i%(w-1); p0 = y*w+x; p1 = (y+1)*w+x; } edge = (dsf_canonify(dsf, p0) != dsf_canonify(dsf, p1)); } if (edge) { while (currrun > 25) *p++ = 'z', currrun -= 25; if (currrun) *p++ = 'a'-1 + currrun; else *p++ = '_'; currrun = 0; } else currrun++; } /* * Now go through and compress the string by replacing runs of * the same letter with a single copy of that letter followed by * a repeat count, where that makes it shorter. (This puzzle * seems to generate enough long strings of _ to make this a * worthwhile step.) */ for (q = r = orig; r < p ;) { *q++ = c = *r; for (i = 0; r+i < p && r[i] == c; i++); r += i; if (i == 2) { *q++ = c; } else if (i > 2) { q += sprintf(q, "%d", i); } } return q; } static char *parse_block_structure(const char **p, int w, int *dsf) { int a = w*w; int pos = 0; int repc = 0, repn = 0; dsf_init(dsf, a); while (**p && (repn > 0 || **p != ',')) { int c, adv; if (repn > 0) { repn--; c = repc; } else if (**p == '_' || (**p >= 'a' && **p <= 'z')) { c = (**p == '_' ? 0 : **p - 'a' + 1); (*p)++; if (**p && isdigit((unsigned char)**p)) { repc = c; repn = atoi(*p)-1; while (**p && isdigit((unsigned char)**p)) (*p)++; } } else return "Invalid character in game description"; adv = (c != 25); /* 'z' is a special case */ while (c-- > 0) { int p0, p1; /* * Non-edge; merge the two dsf classes on either * side of it. */ if (pos >= 2*w*(w-1)) return "Too much data in block structure specification"; if (pos < w*(w-1)) { int y = pos/(w-1); int x = pos%(w-1); p0 = y*w+x; p1 = y*w+x+1; } else { int x = pos/(w-1) - w; int y = pos%(w-1); p0 = y*w+x; p1 = (y+1)*w+x; } dsf_merge(dsf, p0, p1); pos++; } if (adv) { pos++; if (pos > 2*w*(w-1)+1) return "Too much data in block structure specification"; } } /* * When desc is exhausted, we expect to have gone exactly * one space _past_ the end of the grid, due to the dummy * edge at the end. */ if (pos != 2*w*(w-1)+1) return "Not enough data in block structure specification"; return NULL; } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { int w = params->w, a = w*w; digit *grid, *soln; int *order, *revorder, *singletons, *dsf; long *clues, *cluevals; int i, j, k, n, x, y, ret; int diff = params->diff; char *desc, *p; /* * Difficulty exceptions: 3x3 puzzles at difficulty Hard or * higher are currently not generable - the generator will spin * forever looking for puzzles of the appropriate difficulty. We * dial each of these down to the next lower difficulty. * * 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 e n h x u; do for i in {3..9}; do echo ./keen --generate 1 ${i}d${d} perl -e 'alarm 30; exec @ARGV' ./keen --generate 5 ${i}d${d} >/dev/null \ || echo broken 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 == 3 && diff > DIFF_NORMAL) diff = DIFF_NORMAL; grid = NULL; order = snewn(a, int); revorder = snewn(a, int); singletons = snewn(a, int); dsf = snew_dsf(a); clues = snewn(a, long); cluevals = snewn(a, long); soln = snewn(a, digit); while (1) { /* * First construct a latin square to be the solution. */ sfree(grid); grid = latin_generate(w, rs); /* * Divide the grid into arbitrarily sized blocks, but so as * to arrange plenty of dominoes which can be SUB/DIV clues. * We do this by first placing dominoes at random for a * while, then tying the remaining singletons one by one * into neighbouring blocks. */ for (i = 0; i < a; i++) order[i] = i; shuffle(order, a, sizeof(*order), rs); for (i = 0; i < a; i++) revorder[order[i]] = i; for (i = 0; i < a; i++) singletons[i] = TRUE; dsf_init(dsf, a); /* Place dominoes. */ for (i = 0; i < a; i++) { if (singletons[i]) { int best = -1; x = i % w; y = i / w; if (x > 0 && singletons[i-1] && (best == -1 || revorder[i-1] < revorder[best])) best = i-1; if (x+1 < w && singletons[i+1] && (best == -1 || revorder[i+1] < revorder[best])) best = i+1; if (y > 0 && singletons[i-w] && (best == -1 || revorder[i-w] < revorder[best])) best = i-w; if (y+1 < w && singletons[i+w] && (best == -1 || revorder[i+w] < revorder[best])) best = i+w; /* * When we find a potential domino, we place it with * probability 3/4, which seems to strike a decent * balance between plenty of dominoes and leaving * enough singletons to make interesting larger * shapes. */ if (best >= 0 && random_upto(rs, 4)) { singletons[i] = singletons[best] = FALSE; dsf_merge(dsf, i, best); } } } /* Fold in singletons. */ for (i = 0; i < a; i++) { if (singletons[i]) { int best = -1; x = i % w; y = i / w; if (x > 0 && dsf_size(dsf, i-1) < MAXBLK && (best == -1 || revorder[i-1] < revorder[best])) best = i-1; if (x+1 < w && dsf_size(dsf, i+1) < MAXBLK && (best == -1 || revorder[i+1] < revorder[best])) best = i+1; if (y > 0 && dsf_size(dsf, i-w) < MAXBLK && (best == -1 || revorder[i-w] < revorder[best])) best = i-w; if (y+1 < w && dsf_size(dsf, i+w) < MAXBLK && (best == -1 || revorder[i+w] < revorder[best])) best = i+w; if (best >= 0) { singletons[i] = singletons[best] = FALSE; dsf_merge(dsf, i, best); } } } /* Quit and start again if we have any singletons left over * which we weren't able to do anything at all with. */ for (i = 0; i < a; i++) if (singletons[i]) break; if (i < a) continue; /* * Decide what would be acceptable clues for each block. * * Blocks larger than 2 have free choice of ADD or MUL; * blocks of size 2 can be anything in principle (except * that they can only be DIV if the two numbers have an * integer quotient, of course), but we rule out (or try to * avoid) some clues because they're of low quality. * * Hence, we iterate once over the grid, stopping at the * canonical element of every >2 block and the _non_- * canonical element of every 2-block; the latter means that * we can make our decision about a 2-block in the knowledge * of both numbers in it. * * We reuse the 'singletons' array (finished with in the * above loop) to hold information about which blocks are * suitable for what. */ #define F_ADD 0x01 #define F_SUB 0x02 #define F_MUL 0x04 #define F_DIV 0x08 #define BAD_SHIFT 4 for (i = 0; i < a; i++) { singletons[i] = 0; j = dsf_canonify(dsf, i); k = dsf_size(dsf, j); if (j == i && k > 2) { singletons[j] |= F_ADD | F_MUL; } else if (j != i && k == 2) { /* Fetch the two numbers and sort them into order. */ int p = grid[j], q = grid[i], v; if (p < q) { int t = p; p = q; q = t; } /* * Addition clues are always allowed, but we try to * avoid sums of 3, 4, (2w-1) and (2w-2) if we can, * because they're too easy - they only leave one * option for the pair of numbers involved. */ v = p + q; if (v > 4 && v < 2*w-2) singletons[j] |= F_ADD; else singletons[j] |= F_ADD << BAD_SHIFT; /* * Multiplication clues: above Normal difficulty, we * prefer (but don't absolutely insist on) clues of * this type which leave multiple options open. */ v = p * q; n = 0; for (k = 1; k <= w; k++) if (v % k == 0 && v / k <= w && v / k != k) n++; if (n <= 2 && diff > DIFF_NORMAL) singletons[j] |= F_MUL << BAD_SHIFT; else singletons[j] |= F_MUL; /* * Subtraction: we completely avoid a difference of * w-1. */ v = p - q; if (v < w-1) singletons[j] |= F_SUB; /* * Division: for a start, the quotient must be an * integer or the clue type is impossible. Also, we * never use quotients strictly greater than w/2, * because they're not only too easy but also * inelegant. */ if (p % q == 0 && 2 * (p / q) <= w) singletons[j] |= F_DIV; } } /* * Actually choose a clue for each block, trying to keep the * numbers of each type even, and starting with the * preferred candidates for each type where possible. * * I'm sure there should be a faster algorithm for doing * this, but I can't be bothered: O(N^2) is good enough when * N is at most the number of dominoes that fits into a 9x9 * square. */ shuffle(order, a, sizeof(*order), rs); for (i = 0; i < a; i++) clues[i] = 0; while (1) { int done_something = FALSE; for (k = 0; k < 4; k++) { long clue; int good, bad; switch (k) { case 0: clue = C_DIV; good = F_DIV; break; case 1: clue = C_SUB; good = F_SUB; break; case 2: clue = C_MUL; good = F_MUL; break; default /* case 3 */ : clue = C_ADD; good = F_ADD; break; } for (i = 0; i < a; i++) { j = order[i]; if (singletons[j] & good) { clues[j] = clue; singletons[j] = 0; break; } } if (i == a) { /* didn't find a nice one, use a nasty one */ bad = good << BAD_SHIFT; for (i = 0; i < a; i++) { j = order[i]; if (singletons[j] & bad) { clues[j] = clue; singletons[j] = 0; break; } } } if (i < a) done_something = TRUE; } if (!done_something) break; } #undef F_ADD #undef F_SUB #undef F_MUL #undef F_DIV #undef BAD_SHIFT /* * Having chosen the clue types, calculate the clue values. */ for (i = 0; i < a; i++) { j = dsf_canonify(dsf, i); if (j == i) { cluevals[j] = grid[i]; } else { switch (clues[j]) { case C_ADD: cluevals[j] += grid[i]; break; case C_MUL: cluevals[j] *= grid[i]; break; case C_SUB: cluevals[j] = abs(cluevals[j] - grid[i]); break; case C_DIV: { int d1 = cluevals[j], d2 = grid[i]; if (d1 == 0 || d2 == 0) cluevals[j] = 0; else cluevals[j] = d2/d1 + d1/d2;/* one is 0 :-) */ } break; } } } for (i = 0; i < a; i++) { j = dsf_canonify(dsf, i); if (j == i) { clues[j] |= cluevals[j]; } } /* * See if the game can be solved at the specified difficulty * level, but not at the one below. */ if (diff > 0) { memset(soln, 0, a); ret = solver(w, dsf, clues, soln, diff-1); if (ret <= diff-1) continue; } memset(soln, 0, a); ret = solver(w, dsf, clues, soln, diff); if (ret != diff) continue; /* go round again */ /* * I wondered if at this point it would be worth trying to * merge adjacent blocks together, to make the puzzle * gradually more difficult if it's currently easier than * specced, increasing the chance of a given generation run * being successful. * * It doesn't seem to be critical for the generation speed, * though, so for the moment I'm leaving it out. */ /* * We've got a usable puzzle! */ break; } /* * Encode the puzzle description. */ desc = snewn(40*a, char); p = desc; p = encode_block_structure(p, w, dsf); *p++ = ','; for (i = 0; i < a; i++) { j = dsf_canonify(dsf, i); if (j == i) { switch (clues[j] & CMASK) { case C_ADD: *p++ = 'a'; break; case C_SUB: *p++ = 's'; break; case C_MUL: *p++ = 'm'; break; case C_DIV: *p++ = 'd'; break; } p += sprintf(p, "%ld", clues[j] & ~CMASK); } } *p++ = '\0'; desc = sresize(desc, p - desc, char); /* * Encode the solution. */ assert(memcmp(soln, grid, a) == 0); *aux = snewn(a+2, char); (*aux)[0] = 'S'; for (i = 0; i < a; i++) (*aux)[i+1] = '0' + soln[i]; (*aux)[a+1] = '\0'; sfree(grid); sfree(order); sfree(revorder); sfree(singletons); sfree(dsf); sfree(clues); sfree(cluevals); sfree(soln); return desc; } /* ---------------------------------------------------------------------- * Gameplay. */ static char *validate_desc(const game_params *params, const char *desc) { int w = params->w, a = w*w; int *dsf; char *ret; const char *p = desc; int i; /* * Verify that the block structure makes sense. */ dsf = snew_dsf(a); ret = parse_block_structure(&p, w, dsf); if (ret) { sfree(dsf); return ret; } if (*p != ',') return "Expected ',' after block structure description"; p++; /* * Verify that the right number of clues are given, and that SUB * and DIV clues don't apply to blocks of the wrong size. */ for (i = 0; i < a; i++) { if (dsf_canonify(dsf, i) == i) { if (*p == 'a' || *p == 'm') { /* these clues need no validation */ } else if (*p == 'd' || *p == 's') { if (dsf_size(dsf, i) != 2) return "Subtraction and division blocks must have area 2"; } else if (!*p) { return "Too few clues for block structure"; } else { return "Unrecognised clue type"; } p++; while (*p && isdigit((unsigned char)*p)) p++; } } if (*p) return "Too many clues for block structure"; return NULL; } 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); const char *p = desc; int i; state->par = *params; /* structure copy */ state->clues = snew(struct clues); state->clues->refcount = 1; state->clues->w = w; state->clues->dsf = snew_dsf(a); parse_block_structure(&p, w, state->clues->dsf); assert(*p == ','); p++; state->clues->clues = snewn(a, long); for (i = 0; i < a; i++) { if (dsf_canonify(state->clues->dsf, i) == i) { long clue = 0; switch (*p) { case 'a': clue = C_ADD; break; case 'm': clue = C_MUL; break; case 's': clue = C_SUB; assert(dsf_size(state->clues->dsf, i) == 2); break; case 'd': clue = C_DIV; assert(dsf_size(state->clues->dsf, i) == 2); break; default: assert(!"Bad description in new_game"); } p++; clue |= atol(p); while (*p && isdigit((unsigned char)*p)) p++; state->clues->clues[i] = clue; } else state->clues->clues[i] = 0; } state->grid = snewn(a, digit); state->pencil = snewn(a, int); for (i = 0; i < a; i++) { state->grid[i] = 0; state->pencil[i] = 0; } state->completed = 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->clues = state->clues; ret->clues->refcount++; ret->grid = snewn(a, digit); ret->pencil = snewn(a, int); memcpy(ret->grid, state->grid, a*sizeof(digit)); memcpy(ret->pencil, state->pencil, a*sizeof(int)); ret->completed = state->completed; ret->cheated = state->cheated; return ret; } static void free_game(game_state *state) { sfree(state->grid); sfree(state->pencil); if (--state->clues->refcount <= 0) { sfree(state->clues->dsf); sfree(state->clues->clues); sfree(state->clues); } sfree(state); } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, 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); memset(soln, 0, a); ret = solver(w, state->clues->dsf, state->clues->clues, 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] = '0' + soln[i]; out[a+1] = '\0'; } sfree(soln); return out; } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { return NULL; } struct game_ui { /* * These are the coordinates of the currently highlighted * square on the grid, if hshow = 1. */ int hx, hy; /* * This indicates whether the current highlight is a * pencil-mark one or a real one. */ int 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. */ int 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. */ int hcursor; }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->hx = ui->hy = 0; ui->hpencil = ui->hshow = ui->hcursor = 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 = 0; } } #define PREFERRED_TILESIZE 48 #define TILESIZE (ds->tilesize) #define BORDER (TILESIZE / 2) #define GRIDEXTRA max((TILESIZE / 32),1) #define COORD(x) ((x)*TILESIZE + BORDER) #define FROMCOORD(x) (((x)+(TILESIZE-BORDER)) / TILESIZE - 1) #define FLASH_TIME 0.4F #define DF_PENCIL_SHIFT 16 #define DF_ERR_LATIN 0x8000 #define DF_ERR_CLUE 0x4000 #define DF_HIGHLIGHT 0x2000 #define DF_HIGHLIGHT_PENCIL 0x1000 #define DF_DIGIT_MASK 0x000F struct game_drawstate { int tilesize; int started; long *tiles; long *errors; char *minus_sign, *times_sign, *divide_sign; }; static int check_errors(const game_state *state, long *errors) { int w = state->par.w, a = w*w; int i, j, x, y, errs = FALSE; long *cluevals; int *full; cluevals = snewn(a, long); full = snewn(a, int); if (errors) for (i = 0; i < a; i++) { errors[i] = 0; full[i] = TRUE; } for (i = 0; i < a; i++) { long clue; j = dsf_canonify(state->clues->dsf, i); if (j == i) { cluevals[i] = state->grid[i]; } else { clue = state->clues->clues[j] & CMASK; switch (clue) { case C_ADD: cluevals[j] += state->grid[i]; break; case C_MUL: cluevals[j] *= state->grid[i]; break; case C_SUB: cluevals[j] = abs(cluevals[j] - state->grid[i]); break; case C_DIV: { int d1 = min(cluevals[j], state->grid[i]); int d2 = max(cluevals[j], state->grid[i]); if (d1 == 0 || d2 % d1 != 0) cluevals[j] = 0; else cluevals[j] = d2 / d1; } break; } } if (!state->grid[i]) full[j] = FALSE; } for (i = 0; i < a; i++) { j = dsf_canonify(state->clues->dsf, i); if (j == i) { if ((state->clues->clues[j] & ~CMASK) != cluevals[i]) { errs = TRUE; if (errors && full[j]) errors[j] |= DF_ERR_CLUE; } } } sfree(cluevals); sfree(full); for (y = 0; y < w; y++) { int mask = 0, errmask = 0; for (x = 0; x < w; x++) { int bit = 1 << state->grid[y*w+x]; errmask |= (mask & bit); mask |= bit; } if (mask != (1 << (w+1)) - (1 << 1)) { errs = TRUE; errmask &= ~1; if (errors) { for (x = 0; x < w; x++) if (errmask & (1 << state->grid[y*w+x])) errors[y*w+x] |= DF_ERR_LATIN; } } } for (x = 0; x < w; x++) { int mask = 0, errmask = 0; for (y = 0; y < w; y++) { int bit = 1 << state->grid[y*w+x]; errmask |= (mask & bit); mask |= bit; } if (mask != (1 << (w+1)) - (1 << 1)) { errs = TRUE; errmask &= ~1; if (errors) { for (y = 0; y < w; y++) if (errmask & (1 << state->grid[y*w+x])) errors[y*w+x] |= DF_ERR_LATIN; } } } return errs; } 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 (tx >= 0 && tx < w && ty >= 0 && ty < w) { if (button == LEFT_BUTTON) { if (tx == ui->hx && ty == ui->hy && ui->hshow && ui->hpencil == 0) { ui->hshow = 0; } else { ui->hx = tx; ui->hy = ty; ui->hshow = 1; ui->hpencil = 0; } ui->hcursor = 0; return ""; /* UI activity occurred */ } 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 = 0; } else { ui->hpencil = 1; ui->hx = tx; ui->hy = ty; ui->hshow = 1; } } else { ui->hshow = 0; } ui->hcursor = 0; return ""; /* UI activity occurred */ } } if (IS_CURSOR_MOVE(button)) { move_cursor(button, &ui->hx, &ui->hy, w, w, 0); ui->hshow = ui->hcursor = 1; return ""; } if (ui->hshow && (button == CURSOR_SELECT)) { ui->hpencil = 1 - ui->hpencil; ui->hcursor = 1; return ""; } if (ui->hshow && ((button >= '0' && button <= '9' && button - '0' <= w) || button == CURSOR_SELECT2 || button == '\b')) { int n = button - '0'; if (button == CURSOR_SELECT2 || button == '\b') n = 0; /* * 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[ui->hy*w+ui->hx]) return NULL; sprintf(buf, "%c%d,%d,%d", (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n); if (!ui->hcursor) ui->hshow = 0; return dupstr(buf); } 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, n; if (move[0] == 'S') { ret = dup_game(from); ret->completed = ret->cheated = TRUE; for (i = 0; i < a; i++) { if (move[i+1] < '1' || move[i+1] > '0'+w) { free_game(ret); return NULL; } ret->grid[i] = move[i+1] - '0'; 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", &x, &y, &n) == 3 && x >= 0 && x < w && y >= 0 && y < w && n >= 0 && n <= w) { ret = dup_game(from); 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 (!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 return NULL; /* couldn't parse move string */ } /* ---------------------------------------------------------------------- * Drawing routines. */ #define SIZE(w) ((w) * TILESIZE + 2*BORDER) 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]; *ncolours = NCOLOURS; return ret; } static const char *const minus_signs[] = { "\xE2\x88\x92", "-" }; static const char *const times_signs[] = { "\xC3\x97", "*" }; static const char *const divide_signs[] = { "\xC3\xB7", "/" }; 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->tilesize = 0; ds->started = FALSE; ds->tiles = snewn(a, long); for (i = 0; i < a; i++) ds->tiles[i] = -1; ds->errors = snewn(a, long); ds->minus_sign = text_fallback(dr, minus_signs, lenof(minus_signs)); ds->times_sign = text_fallback(dr, times_signs, lenof(times_signs)); ds->divide_sign = text_fallback(dr, divide_signs, lenof(divide_signs)); return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->tiles); sfree(ds->errors); sfree(ds->minus_sign); sfree(ds->times_sign); sfree(ds->divide_sign); sfree(ds); } static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues, int x, int y, long tile) { int w = clues->w /* , a = w*w */; int tx, ty, tw, th; int cx, cy, cw, ch; char str[64]; tx = BORDER + x * TILESIZE + 1 + GRIDEXTRA; ty = BORDER + y * TILESIZE + 1 + GRIDEXTRA; cx = tx; cy = ty; cw = tw = TILESIZE-1-2*GRIDEXTRA; ch = th = TILESIZE-1-2*GRIDEXTRA; if (x > 0 && dsf_canonify(clues->dsf, y*w+x) == dsf_canonify(clues->dsf, y*w+x-1)) cx -= GRIDEXTRA, cw += GRIDEXTRA; if (x+1 < w && dsf_canonify(clues->dsf, y*w+x) == dsf_canonify(clues->dsf, y*w+x+1)) cw += GRIDEXTRA; if (y > 0 && dsf_canonify(clues->dsf, y*w+x) == dsf_canonify(clues->dsf, (y-1)*w+x)) cy -= GRIDEXTRA, ch += GRIDEXTRA; if (y+1 < w && dsf_canonify(clues->dsf, y*w+x) == dsf_canonify(clues->dsf, (y+1)*w+x)) ch += GRIDEXTRA; clip(dr, cx, cy, cw, ch); /* background needs erasing */ draw_rect(dr, cx, cy, cw, ch, (tile & DF_HIGHLIGHT) ? COL_HIGHLIGHT : COL_BACKGROUND); /* * Draw the corners of thick lines in corner-adjacent squares, * which jut into this square by one pixel. */ if (x > 0 && y > 0 && dsf_canonify(clues->dsf, y*w+x) != dsf_canonify(clues->dsf, (y-1)*w+x-1)) draw_rect(dr, tx-GRIDEXTRA, ty-GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID); if (x+1 < w && y > 0 && dsf_canonify(clues->dsf, y*w+x) != dsf_canonify(clues->dsf, (y-1)*w+x+1)) draw_rect(dr, tx+TILESIZE-1-2*GRIDEXTRA, ty-GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID); if (x > 0 && y+1 < w && dsf_canonify(clues->dsf, y*w+x) != dsf_canonify(clues->dsf, (y+1)*w+x-1)) draw_rect(dr, tx-GRIDEXTRA, ty+TILESIZE-1-2*GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID); if (x+1 < w && y+1 < w && dsf_canonify(clues->dsf, y*w+x) != dsf_canonify(clues->dsf, (y+1)*w+x+1)) draw_rect(dr, tx+TILESIZE-1-2*GRIDEXTRA, ty+TILESIZE-1-2*GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, 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); } /* Draw the box clue. */ if (dsf_canonify(clues->dsf, y*w+x) == y*w+x) { long clue = clues->clues[y*w+x]; long cluetype = clue & CMASK, clueval = clue & ~CMASK; int size = dsf_size(clues->dsf, y*w+x); /* * Special case of clue-drawing: a box with only one square * is written as just the number, with no operation, because * it doesn't matter whether the operation is ADD or MUL. * The generation code above should never produce puzzles * containing such a thing - I think they're inelegant - but * it's possible to type in game IDs from elsewhere, so I * want to display them right if so. */ sprintf (str, "%ld%s", clueval, (size == 1 ? "" : cluetype == C_ADD ? "+" : cluetype == C_SUB ? ds->minus_sign : cluetype == C_MUL ? ds->times_sign : /* cluetype == C_DIV ? */ ds->divide_sign)); draw_text(dr, tx + GRIDEXTRA * 2, ty + GRIDEXTRA * 2 + TILESIZE/4, FONT_VARIABLE, TILESIZE/4, ALIGN_VNORMAL | ALIGN_HLEFT, (tile & DF_ERR_CLUE ? COL_ERROR : COL_GRID), str); } /* new number needs drawing? */ if (tile & DF_DIGIT_MASK) { str[1] = '\0'; str[0] = (tile & DF_DIGIT_MASK) + '0'; draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/2, FONT_VARIABLE, TILESIZE/2, ALIGN_VCENTRE | ALIGN_HCENTRE, (tile & DF_ERR_LATIN) ? COL_ERROR : COL_USER, str); } 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 (tile & (1L << (i + DF_PENCIL_SHIFT))) 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; if (dsf_canonify(clues->dsf, y*w+x) == y*w+x) { /* * Make space for the clue text. */ pt += TILESIZE/4; /* minph--; */ } /* * 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; /* * And move it down a bit if it's collided with some * clue text. */ if (dsf_canonify(clues->dsf, y*w+x) == y*w+x) { pt = max(pt, ty + GRIDEXTRA * 3 + TILESIZE/4); } /* * Now actually draw the pencil marks. */ for (i = 1, j = 0; i <= w; i++) if (tile & (1L << (i + DF_PENCIL_SHIFT))) { int dx = j % pw, dy = j / pw; str[1] = '\0'; str[0] = i + '0'; 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; 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->errors); for (y = 0; y < w; y++) { for (x = 0; x < w; x++) { long tile = 0L; if (state->grid[y*w+x]) tile = state->grid[y*w+x]; else tile = (long)state->pencil[y*w+x] << DF_PENCIL_SHIFT; if (ui->hshow && ui->hx == x && ui->hy == y) 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 */ tile |= ds->errors[y*w+x]; if (ds->tiles[y*w+x] != tile) { ds->tiles[y*w+x] = tile; draw_tile(dr, ds, state->clues, x, y, tile); } } } } 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 int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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; } /* * Subfunction to draw the thick lines between cells. In order to do * this using the line-drawing rather than rectangle-drawing API (so * as to get line thicknesses to scale correctly) and yet have * correctly mitred joins between lines, we must do this by tracing * the boundary of each sub-block and drawing it in one go as a * single polygon. */ static void outline_block_structure(drawing *dr, game_drawstate *ds, int w, int *dsf, int ink) { int a = w*w; int *coords; int i, n; int x, y, dx, dy, sx, sy, sdx, sdy; coords = snewn(4*a, int); /* * Iterate over all the blocks. */ for (i = 0; i < a; i++) { if (dsf_canonify(dsf, i) != i) continue; /* * For each block, we need a starting square within it which * has a boundary at the left. Conveniently, we have one * right here, by construction. */ x = i % w; y = i / w; dx = -1; dy = 0; /* * Now begin tracing round the perimeter. At all * times, (x,y) describes some square within the * block, and (x+dx,y+dy) is some adjacent square * outside it; so the edge between those two squares * is always an edge of the block. */ sx = x, sy = y, sdx = dx, sdy = dy; /* save starting position */ n = 0; do { int cx, cy, tx, ty, nin; /* * Advance to the next edge, by looking at the two * squares beyond it. If they're both outside the block, * we turn right (by leaving x,y the same and rotating * dx,dy clockwise); if they're both inside, we turn * left (by rotating dx,dy anticlockwise and contriving * to leave x+dx,y+dy unchanged); if one of each, we go * straight on (and may enforce by assertion that * they're one of each the _right_ way round). */ nin = 0; tx = x - dy + dx; ty = y + dx + dy; nin += (tx >= 0 && tx < w && ty >= 0 && ty < w && dsf_canonify(dsf, ty*w+tx) == i); tx = x - dy; ty = y + dx; nin += (tx >= 0 && tx < w && ty >= 0 && ty < w && dsf_canonify(dsf, ty*w+tx) == i); if (nin == 0) { /* * Turn right. */ int tmp; tmp = dx; dx = -dy; dy = tmp; } else if (nin == 2) { /* * Turn left. */ int tmp; x += dx; y += dy; tmp = dx; dx = dy; dy = -tmp; x -= dx; y -= dy; } else { /* * Go straight on. */ x -= dy; y += dx; } /* * Now enforce by assertion that we ended up * somewhere sensible. */ assert(x >= 0 && x < w && y >= 0 && y < w && dsf_canonify(dsf, y*w+x) == i); assert(x+dx < 0 || x+dx >= w || y+dy < 0 || y+dy >= w || dsf_canonify(dsf, (y+dy)*w+(x+dx)) != i); /* * Record the point we just went past at one end of the * edge. To do this, we translate (x,y) down and right * by half a unit (so they're describing a point in the * _centre_ of the square) and then translate back again * in a manner rotated by dy and dx. */ assert(n < 2*w+2); cx = ((2*x+1) + dy + dx) / 2; cy = ((2*y+1) - dx + dy) / 2; coords[2*n+0] = BORDER + cx * TILESIZE; coords[2*n+1] = BORDER + cy * TILESIZE; n++; } while (x != sx || y != sy || dx != sdx || dy != sdy); /* * That's our polygon; now draw it. */ draw_polygon(dr, coords, n, -1, ink); } sfree(coords); } 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; char *minus_sign, *times_sign, *divide_sign; /* Ick: fake up `ds->tilesize' for macro expansion purposes */ game_drawstate ads, *ds = &ads; game_set_size(dr, ds, NULL, tilesize); minus_sign = text_fallback(dr, minus_signs, lenof(minus_signs)); times_sign = text_fallback(dr, times_signs, lenof(times_signs)); divide_sign = text_fallback(dr, divide_signs, lenof(divide_signs)); /* * Border. */ print_line_width(dr, 3 * TILESIZE / 40); draw_rect_outline(dr, BORDER, BORDER, w*TILESIZE, w*TILESIZE, ink); /* * Main grid. */ for (x = 1; x < w; x++) { print_line_width(dr, TILESIZE / 40); draw_line(dr, BORDER+x*TILESIZE, BORDER, BORDER+x*TILESIZE, BORDER+w*TILESIZE, ink); } for (y = 1; y < w; y++) { print_line_width(dr, TILESIZE / 40); draw_line(dr, BORDER, BORDER+y*TILESIZE, BORDER+w*TILESIZE, BORDER+y*TILESIZE, ink); } /* * Thick lines between cells. */ print_line_width(dr, 3 * TILESIZE / 40); outline_block_structure(dr, ds, w, state->clues->dsf, ink); /* * Clues. */ for (y = 0; y < w; y++) for (x = 0; x < w; x++) if (dsf_canonify(state->clues->dsf, y*w+x) == y*w+x) { long clue = state->clues->clues[y*w+x]; long cluetype = clue & CMASK, clueval = clue & ~CMASK; int size = dsf_size(state->clues->dsf, y*w+x); char str[64]; /* * As in the drawing code, we omit the operator for * blocks of area 1. */ sprintf (str, "%ld%s", clueval, (size == 1 ? "" : cluetype == C_ADD ? "+" : cluetype == C_SUB ? minus_sign : cluetype == C_MUL ? times_sign : /* cluetype == C_DIV ? */ divide_sign)); draw_text(dr, BORDER+x*TILESIZE + 5*TILESIZE/80, BORDER+y*TILESIZE + 20*TILESIZE/80, FONT_VARIABLE, TILESIZE/4, ALIGN_VNORMAL | ALIGN_HLEFT, ink, str); } /* * Numbers for the solution, if any. */ 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] = state->grid[y*w+x] + '0'; draw_text(dr, BORDER + x*TILESIZE + TILESIZE/2, BORDER + y*TILESIZE + TILESIZE/2, FONT_VARIABLE, TILESIZE/2, ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str); } sfree(minus_sign); sfree(times_sign); sfree(divide_sign); } #ifdef COMBINED #define thegame keen #endif const struct game thegame = { "Keen", "games.keen", "keen", default_params, game_fetch_preset, 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, FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, decode_ui, 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_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, *err; int grade = FALSE; int ret, diff, 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); /* * When solving an Easy 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 = FALSE; for (diff = 0; diff < DIFFCOUNT; diff++) { memset(s->grid, 0, p->w * p->w); ret = solver(p->w, s->clues->dsf, s->clues->clues, s->grid, diff); if (ret <= diff) break; } if (diff == DIFFCOUNT) { 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", keen_diffnames[ret]); } else { solver_show_working = really_show_working; memset(s->grid, 0, p->w * p->w); ret = solver(p->w, s->clues->dsf, s->clues->clues, s->grid, diff); if (ret != diff) printf("Puzzle is inconsistent\n"); else { /* * We don't have a game_text_format for this game, * so we have to output the solution manually. */ int x, y; for (y = 0; y < p->w; y++) { for (x = 0; x < p->w; x++) { printf("%s%c", x>0?" ":"", '0' + s->grid[y*p->w+x]); } putchar('\n'); } } } } return 0; } #endif /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/latin.c0000644000175300017530000011663411322713103013704 0ustar simonsimon#include #include #include #include "puzzles.h" #include "tree234.h" #include "maxflow.h" #ifdef STANDALONE_LATIN_TEST #define STANDALONE_SOLVER #endif #include "latin.h" /* -------------------------------------------------------- * Solver. */ static int latin_solver_top(struct latin_solver *solver, int maxdiff, int diff_simple, int diff_set_0, int diff_set_1, int diff_forcing, int diff_recursive, usersolver_t const *usersolvers, void *ctx, ctxnew_t ctxnew, ctxfree_t ctxfree); #ifdef STANDALONE_SOLVER int solver_show_working, solver_recurse_depth; #endif /* * Function called when we are certain that a particular square has * a particular number in it. The y-coordinate passed in here is * transformed. */ void latin_solver_place(struct latin_solver *solver, int x, int y, int n) { int i, o = solver->o; assert(n <= o); assert(cube(x,y,n)); /* * Rule out all other numbers in this square. */ for (i = 1; i <= o; i++) if (i != n) cube(x,y,i) = FALSE; /* * Rule out this number in all other positions in the row. */ for (i = 0; i < o; i++) if (i != y) cube(x,i,n) = FALSE; /* * Rule out this number in all other positions in the column. */ for (i = 0; i < o; i++) if (i != x) cube(i,y,n) = FALSE; /* * Enter the number in the result grid. */ solver->grid[y*o+x] = n; /* * Cross out this number from the list of numbers left to place * in its row, its column and its block. */ solver->row[y*o+n-1] = solver->col[x*o+n-1] = TRUE; } int latin_solver_elim(struct latin_solver *solver, int start, int step #ifdef STANDALONE_SOLVER , char *fmt, ... #endif ) { int o = solver->o; #ifdef STANDALONE_SOLVER char **names = solver->names; #endif int fpos, m, i; /* * Count the number of set bits within this section of the * cube. */ m = 0; fpos = -1; for (i = 0; i < o; i++) if (solver->cube[start+i*step]) { fpos = start+i*step; m++; } if (m == 1) { int x, y, n; assert(fpos >= 0); n = 1 + fpos % o; y = fpos / o; x = y / o; y %= o; if (!solver->grid[y*o+x]) { #ifdef STANDALONE_SOLVER if (solver_show_working) { va_list ap; printf("%*s", solver_recurse_depth*4, ""); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); printf(":\n%*s placing %s at (%d,%d)\n", solver_recurse_depth*4, "", names[n-1], x+1, y+1); } #endif latin_solver_place(solver, x, y, n); return +1; } } else if (m == 0) { #ifdef STANDALONE_SOLVER if (solver_show_working) { va_list ap; printf("%*s", solver_recurse_depth*4, ""); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); printf(":\n%*s no possibilities available\n", solver_recurse_depth*4, ""); } #endif return -1; } return 0; } struct latin_solver_scratch { unsigned char *grid, *rowidx, *colidx, *set; int *neighbours, *bfsqueue; #ifdef STANDALONE_SOLVER int *bfsprev; #endif }; int latin_solver_set(struct latin_solver *solver, struct latin_solver_scratch *scratch, int start, int step1, int step2 #ifdef STANDALONE_SOLVER , char *fmt, ... #endif ) { int o = solver->o; #ifdef STANDALONE_SOLVER char **names = solver->names; #endif int i, j, n, count; unsigned char *grid = scratch->grid; unsigned char *rowidx = scratch->rowidx; unsigned char *colidx = scratch->colidx; unsigned char *set = scratch->set; /* * We are passed a o-by-o matrix of booleans. Our first job * is to winnow it by finding any definite placements - i.e. * any row with a solitary 1 - and discarding that row and the * column containing the 1. */ memset(rowidx, TRUE, o); memset(colidx, TRUE, o); for (i = 0; i < o; i++) { int count = 0, first = -1; for (j = 0; j < o; j++) if (solver->cube[start+i*step1+j*step2]) first = j, count++; if (count == 0) return -1; if (count == 1) rowidx[i] = colidx[first] = FALSE; } /* * Convert each of rowidx/colidx from a list of 0s and 1s to a * list of the indices of the 1s. */ for (i = j = 0; i < o; i++) if (rowidx[i]) rowidx[j++] = i; n = j; for (i = j = 0; i < o; i++) if (colidx[i]) colidx[j++] = i; assert(n == j); /* * And create the smaller matrix. */ for (i = 0; i < n; i++) for (j = 0; j < n; j++) grid[i*o+j] = solver->cube[start+rowidx[i]*step1+colidx[j]*step2]; /* * Having done that, we now have a matrix in which every row * has at least two 1s in. Now we search to see if we can find * a rectangle of zeroes (in the set-theoretic sense of * `rectangle', i.e. a subset of rows crossed with a subset of * columns) whose width and height add up to n. */ memset(set, 0, n); count = 0; while (1) { /* * We have a candidate set. If its size is <=1 or >=n-1 * then we move on immediately. */ if (count > 1 && count < n-1) { /* * The number of rows we need is n-count. See if we can * find that many rows which each have a zero in all * the positions listed in `set'. */ int rows = 0; for (i = 0; i < n; i++) { int ok = TRUE; for (j = 0; j < n; j++) if (set[j] && grid[i*o+j]) { ok = FALSE; break; } if (ok) rows++; } /* * We expect never to be able to get _more_ than * n-count suitable rows: this would imply that (for * example) there are four numbers which between them * have at most three possible positions, and hence it * indicates a faulty deduction before this point or * even a bogus clue. */ if (rows > n - count) { #ifdef STANDALONE_SOLVER if (solver_show_working) { va_list ap; printf("%*s", solver_recurse_depth*4, ""); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); printf(":\n%*s contradiction reached\n", solver_recurse_depth*4, ""); } #endif return -1; } if (rows >= n - count) { int progress = FALSE; /* * We've got one! Now, for each row which _doesn't_ * satisfy the criterion, eliminate all its set * bits in the positions _not_ listed in `set'. * Return +1 (meaning progress has been made) if we * successfully eliminated anything at all. * * This involves referring back through * rowidx/colidx in order to work out which actual * positions in the cube to meddle with. */ for (i = 0; i < n; i++) { int ok = TRUE; for (j = 0; j < n; j++) if (set[j] && grid[i*o+j]) { ok = FALSE; break; } if (!ok) { for (j = 0; j < n; j++) if (!set[j] && grid[i*o+j]) { int fpos = (start+rowidx[i]*step1+ colidx[j]*step2); #ifdef STANDALONE_SOLVER if (solver_show_working) { int px, py, pn; if (!progress) { va_list ap; printf("%*s", solver_recurse_depth*4, ""); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); printf(":\n"); } pn = 1 + fpos % o; py = fpos / o; px = py / o; py %= o; printf("%*s ruling out %s at (%d,%d)\n", solver_recurse_depth*4, "", names[pn-1], px+1, py+1); } #endif progress = TRUE; solver->cube[fpos] = FALSE; } } } if (progress) { return +1; } } } /* * Binary increment: change the rightmost 0 to a 1, and * change all 1s to the right of it to 0s. */ i = n; while (i > 0 && set[i-1]) set[--i] = 0, count--; if (i > 0) set[--i] = 1, count++; else break; /* done */ } return 0; } /* * Look for forcing chains. A forcing chain is a path of * pairwise-exclusive squares (i.e. each pair of adjacent squares * in the path are in the same row, column or block) with the * following properties: * * (a) Each square on the path has precisely two possible numbers. * * (b) Each pair of squares which are adjacent on the path share * at least one possible number in common. * * (c) Each square in the middle of the path shares _both_ of its * numbers with at least one of its neighbours (not the same * one with both neighbours). * * These together imply that at least one of the possible number * choices at one end of the path forces _all_ the rest of the * numbers along the path. In order to make real use of this, we * need further properties: * * (c) Ruling out some number N from the square at one end * of the path forces the square at the other end to * take number N. * * (d) The two end squares are both in line with some third * square. * * (e) That third square currently has N as a possibility. * * If we can find all of that lot, we can deduce that at least one * of the two ends of the forcing chain has number N, and that * therefore the mutually adjacent third square does not. * * To find forcing chains, we're going to start a bfs at each * suitable square, once for each of its two possible numbers. */ int latin_solver_forcing(struct latin_solver *solver, struct latin_solver_scratch *scratch) { int o = solver->o; #ifdef STANDALONE_SOLVER char **names = solver->names; #endif int *bfsqueue = scratch->bfsqueue; #ifdef STANDALONE_SOLVER int *bfsprev = scratch->bfsprev; #endif unsigned char *number = scratch->grid; int *neighbours = scratch->neighbours; int x, y; for (y = 0; y < o; y++) for (x = 0; x < o; x++) { int count, t, n; /* * If this square doesn't have exactly two candidate * numbers, don't try it. * * In this loop we also sum the candidate numbers, * which is a nasty hack to allow us to quickly find * `the other one' (since we will shortly know there * are exactly two). */ for (count = t = 0, n = 1; n <= o; n++) if (cube(x, y, n)) count++, t += n; if (count != 2) continue; /* * Now attempt a bfs for each candidate. */ for (n = 1; n <= o; n++) if (cube(x, y, n)) { int orign, currn, head, tail; /* * Begin a bfs. */ orign = n; memset(number, o+1, o*o); head = tail = 0; bfsqueue[tail++] = y*o+x; #ifdef STANDALONE_SOLVER bfsprev[y*o+x] = -1; #endif number[y*o+x] = t - n; while (head < tail) { int xx, yy, nneighbours, xt, yt, i; xx = bfsqueue[head++]; yy = xx / o; xx %= o; currn = number[yy*o+xx]; /* * Find neighbours of yy,xx. */ nneighbours = 0; for (yt = 0; yt < o; yt++) neighbours[nneighbours++] = yt*o+xx; for (xt = 0; xt < o; xt++) neighbours[nneighbours++] = yy*o+xt; /* * Try visiting each of those neighbours. */ for (i = 0; i < nneighbours; i++) { int cc, tt, nn; xt = neighbours[i] % o; yt = neighbours[i] / o; /* * We need this square to not be * already visited, and to include * currn as a possible number. */ if (number[yt*o+xt] <= o) continue; if (!cube(xt, yt, currn)) continue; /* * Don't visit _this_ square a second * time! */ if (xt == xx && yt == yy) continue; /* * To continue with the bfs, we need * this square to have exactly two * possible numbers. */ for (cc = tt = 0, nn = 1; nn <= o; nn++) if (cube(xt, yt, nn)) cc++, tt += nn; if (cc == 2) { bfsqueue[tail++] = yt*o+xt; #ifdef STANDALONE_SOLVER bfsprev[yt*o+xt] = yy*o+xx; #endif number[yt*o+xt] = tt - currn; } /* * One other possibility is that this * might be the square in which we can * make a real deduction: if it's * adjacent to x,y, and currn is equal * to the original number we ruled out. */ if (currn == orign && (xt == x || yt == y)) { #ifdef STANDALONE_SOLVER if (solver_show_working) { char *sep = ""; int xl, yl; printf("%*sforcing chain, %s at ends of ", solver_recurse_depth*4, "", names[orign-1]); xl = xx; yl = yy; while (1) { printf("%s(%d,%d)", sep, xl+1, yl+1); xl = bfsprev[yl*o+xl]; if (xl < 0) break; yl = xl / o; xl %= o; sep = "-"; } printf("\n%*s ruling out %s at (%d,%d)\n", solver_recurse_depth*4, "", names[orign-1], xt+1, yt+1); } #endif cube(xt, yt, orign) = FALSE; return 1; } } } } } return 0; } struct latin_solver_scratch *latin_solver_new_scratch(struct latin_solver *solver) { struct latin_solver_scratch *scratch = snew(struct latin_solver_scratch); int o = solver->o; scratch->grid = snewn(o*o, unsigned char); scratch->rowidx = snewn(o, unsigned char); scratch->colidx = snewn(o, unsigned char); scratch->set = snewn(o, unsigned char); scratch->neighbours = snewn(3*o, int); scratch->bfsqueue = snewn(o*o, int); #ifdef STANDALONE_SOLVER scratch->bfsprev = snewn(o*o, int); #endif return scratch; } void latin_solver_free_scratch(struct latin_solver_scratch *scratch) { #ifdef STANDALONE_SOLVER sfree(scratch->bfsprev); #endif sfree(scratch->bfsqueue); sfree(scratch->neighbours); sfree(scratch->set); sfree(scratch->colidx); sfree(scratch->rowidx); sfree(scratch->grid); sfree(scratch); } void latin_solver_alloc(struct latin_solver *solver, digit *grid, int o) { int x, y; solver->o = o; solver->cube = snewn(o*o*o, unsigned char); solver->grid = grid; /* write straight back to the input */ memset(solver->cube, TRUE, o*o*o); solver->row = snewn(o*o, unsigned char); solver->col = snewn(o*o, unsigned char); memset(solver->row, FALSE, o*o); memset(solver->col, FALSE, o*o); for (x = 0; x < o; x++) for (y = 0; y < o; y++) if (grid[y*o+x]) latin_solver_place(solver, x, y, grid[y*o+x]); #ifdef STANDALONE_SOLVER solver->names = NULL; #endif } void latin_solver_free(struct latin_solver *solver) { sfree(solver->cube); sfree(solver->row); sfree(solver->col); } int latin_solver_diff_simple(struct latin_solver *solver) { int x, y, n, ret, o = solver->o; #ifdef STANDALONE_SOLVER char **names = solver->names; #endif /* * Row-wise positional elimination. */ for (y = 0; y < o; y++) for (n = 1; n <= o; n++) if (!solver->row[y*o+n-1]) { ret = latin_solver_elim(solver, cubepos(0,y,n), o*o #ifdef STANDALONE_SOLVER , "positional elimination," " %s in row %d", names[n-1], y+1 #endif ); if (ret != 0) return ret; } /* * Column-wise positional elimination. */ for (x = 0; x < o; x++) for (n = 1; n <= o; n++) if (!solver->col[x*o+n-1]) { ret = latin_solver_elim(solver, cubepos(x,0,n), o #ifdef STANDALONE_SOLVER , "positional elimination," " %s in column %d", names[n-1], x+1 #endif ); if (ret != 0) return ret; } /* * Numeric elimination. */ for (x = 0; x < o; x++) for (y = 0; y < o; y++) if (!solver->grid[y*o+x]) { ret = latin_solver_elim(solver, cubepos(x,y,1), 1 #ifdef STANDALONE_SOLVER , "numeric elimination at (%d,%d)", x+1, y+1 #endif ); if (ret != 0) return ret; } return 0; } int latin_solver_diff_set(struct latin_solver *solver, struct latin_solver_scratch *scratch, int extreme) { int x, y, n, ret, o = solver->o; #ifdef STANDALONE_SOLVER char **names = solver->names; #endif if (!extreme) { /* * Row-wise set elimination. */ for (y = 0; y < o; y++) { ret = latin_solver_set(solver, scratch, cubepos(0,y,1), o*o, 1 #ifdef STANDALONE_SOLVER , "set elimination, row %d", y+1 #endif ); if (ret != 0) return ret; } /* * Column-wise set elimination. */ for (x = 0; x < o; x++) { ret = latin_solver_set(solver, scratch, cubepos(x,0,1), o, 1 #ifdef STANDALONE_SOLVER , "set elimination, column %d", x+1 #endif ); if (ret != 0) return ret; } } else { /* * Row-vs-column set elimination on a single number * (much tricker for a human to do!) */ for (n = 1; n <= o; n++) { ret = latin_solver_set(solver, scratch, cubepos(0,0,n), o*o, o #ifdef STANDALONE_SOLVER , "positional set elimination on %s", names[n-1] #endif ); if (ret != 0) return ret; } } return 0; } /* * Returns: * 0 for 'didn't do anything' implying it was already solved. * -1 for 'impossible' (no solution) * 1 for 'single solution' * >1 for 'multiple solutions' (you don't get to know how many, and * the first such solution found will be set. * * and this function may well assert if given an impossible board. */ static int latin_solver_recurse (struct latin_solver *solver, int diff_simple, int diff_set_0, int diff_set_1, int diff_forcing, int diff_recursive, usersolver_t const *usersolvers, void *ctx, ctxnew_t ctxnew, ctxfree_t ctxfree) { int best, bestcount; int o = solver->o, x, y, n; #ifdef STANDALONE_SOLVER char **names = solver->names; #endif best = -1; bestcount = o+1; for (y = 0; y < o; y++) for (x = 0; x < o; x++) if (!solver->grid[y*o+x]) { int count; /* * An unfilled square. Count the number of * possible digits in it. */ count = 0; for (n = 1; n <= o; n++) if (cube(x,y,n)) count++; /* * We should have found any impossibilities * already, so this can safely be an assert. */ assert(count > 1); if (count < bestcount) { bestcount = count; best = y*o+x; } } if (best == -1) /* we were complete already. */ return 0; else { int i, j; digit *list, *ingrid, *outgrid; int diff = diff_impossible; /* no solution found yet */ /* * Attempt recursion. */ y = best / o; x = best % o; list = snewn(o, digit); ingrid = snewn(o*o, digit); outgrid = snewn(o*o, digit); memcpy(ingrid, solver->grid, o*o); /* Make a list of the possible digits. */ for (j = 0, n = 1; n <= o; n++) if (cube(x,y,n)) list[j++] = n; #ifdef STANDALONE_SOLVER if (solver_show_working) { char *sep = ""; printf("%*srecursing on (%d,%d) [", solver_recurse_depth*4, "", x+1, y+1); for (i = 0; i < j; i++) { printf("%s%s", sep, names[list[i]-1]); sep = " or "; } printf("]\n"); } #endif /* * And step along the list, recursing back into the * main solver at every stage. */ for (i = 0; i < j; i++) { int ret; void *newctx; struct latin_solver subsolver; memcpy(outgrid, ingrid, o*o); outgrid[y*o+x] = list[i]; #ifdef STANDALONE_SOLVER if (solver_show_working) printf("%*sguessing %s at (%d,%d)\n", solver_recurse_depth*4, "", names[list[i]-1], x+1, y+1); solver_recurse_depth++; #endif if (ctxnew) { newctx = ctxnew(ctx); } else { newctx = ctx; } latin_solver_alloc(&subsolver, outgrid, o); #ifdef STANDALONE_SOLVER subsolver.names = solver->names; #endif ret = latin_solver_top(&subsolver, diff_recursive, diff_simple, diff_set_0, diff_set_1, diff_forcing, diff_recursive, usersolvers, newctx, ctxnew, ctxfree); latin_solver_free(&subsolver); if (ctxnew) ctxfree(newctx); #ifdef STANDALONE_SOLVER solver_recurse_depth--; if (solver_show_working) { printf("%*sretracting %s at (%d,%d)\n", solver_recurse_depth*4, "", names[list[i]-1], x+1, y+1); } #endif /* we recurse as deep as we can, so we should never find * find ourselves giving up on a puzzle without declaring it * impossible. */ assert(ret != diff_unfinished); /* * If we have our first solution, copy it into the * grid we will return. */ if (diff == diff_impossible && ret != diff_impossible) memcpy(solver->grid, outgrid, o*o); if (ret == diff_ambiguous) diff = diff_ambiguous; else if (ret == diff_impossible) /* do not change our return value */; else { /* the recursion turned up exactly one solution */ if (diff == diff_impossible) diff = diff_recursive; else diff = diff_ambiguous; } /* * As soon as we've found more than one solution, * give up immediately. */ if (diff == diff_ambiguous) break; } sfree(outgrid); sfree(ingrid); sfree(list); if (diff == diff_impossible) return -1; else if (diff == diff_ambiguous) return 2; else { assert(diff == diff_recursive); return 1; } } } static int latin_solver_top(struct latin_solver *solver, int maxdiff, int diff_simple, int diff_set_0, int diff_set_1, int diff_forcing, int diff_recursive, usersolver_t const *usersolvers, void *ctx, ctxnew_t ctxnew, ctxfree_t ctxfree) { struct latin_solver_scratch *scratch = latin_solver_new_scratch(solver); int ret, diff = diff_simple; assert(maxdiff <= diff_recursive); /* * Now loop over the grid repeatedly trying all permitted modes * of reasoning. The loop terminates if we complete an * iteration without making any progress; we then return * failure or success depending on whether the grid is full or * not. */ while (1) { int i; cont: latin_solver_debug(solver->cube, solver->o); for (i = 0; i <= maxdiff; i++) { if (usersolvers[i]) ret = usersolvers[i](solver, ctx); else ret = 0; if (ret == 0 && i == diff_simple) ret = latin_solver_diff_simple(solver); if (ret == 0 && i == diff_set_0) ret = latin_solver_diff_set(solver, scratch, 0); if (ret == 0 && i == diff_set_1) ret = latin_solver_diff_set(solver, scratch, 1); if (ret == 0 && i == diff_forcing) ret = latin_solver_forcing(solver, scratch); if (ret < 0) { diff = diff_impossible; goto got_result; } else if (ret > 0) { diff = max(diff, i); goto cont; } } /* * If we reach here, we have made no deductions in this * iteration, so the algorithm terminates. */ break; } /* * Last chance: if we haven't fully solved the puzzle yet, try * recursing based on guesses for a particular square. We pick * one of the most constrained empty squares we can find, which * has the effect of pruning the search tree as much as * possible. */ if (maxdiff == diff_recursive) { int nsol = latin_solver_recurse(solver, diff_simple, diff_set_0, diff_set_1, diff_forcing, diff_recursive, usersolvers, ctx, ctxnew, ctxfree); if (nsol < 0) diff = diff_impossible; else if (nsol == 1) diff = diff_recursive; else if (nsol > 1) diff = diff_ambiguous; /* if nsol == 0 then we were complete anyway * (and thus don't need to change diff) */ } else { /* * We're forbidden to use recursion, so we just see whether * our grid is fully solved, and return diff_unfinished * otherwise. */ int x, y, o = solver->o; for (y = 0; y < o; y++) for (x = 0; x < o; x++) if (!solver->grid[y*o+x]) diff = diff_unfinished; } got_result: #ifdef STANDALONE_SOLVER if (solver_show_working) printf("%*s%s found\n", solver_recurse_depth*4, "", diff == diff_impossible ? "no solution (impossible)" : diff == diff_unfinished ? "no solution (unfinished)" : diff == diff_ambiguous ? "multiple solutions" : "one solution"); #endif latin_solver_free_scratch(scratch); return diff; } int latin_solver_main(struct latin_solver *solver, int maxdiff, int diff_simple, int diff_set_0, int diff_set_1, int diff_forcing, int diff_recursive, usersolver_t const *usersolvers, void *ctx, ctxnew_t ctxnew, ctxfree_t ctxfree) { int diff; #ifdef STANDALONE_SOLVER int o = solver->o; char *text = NULL, **names = NULL; #endif #ifdef STANDALONE_SOLVER if (!solver->names) { char *p; int i; text = snewn(40 * o, char); p = text; solver->names = snewn(o, char *); for (i = 0; i < o; i++) { solver->names[i] = p; p += 1 + sprintf(p, "%d", i+1); } } #endif diff = latin_solver_top(solver, maxdiff, diff_simple, diff_set_0, diff_set_1, diff_forcing, diff_recursive, usersolvers, ctx, ctxnew, ctxfree); #ifdef STANDALONE_SOLVER sfree(names); sfree(text); #endif return diff; } int latin_solver(digit *grid, int o, int maxdiff, int diff_simple, int diff_set_0, int diff_set_1, int diff_forcing, int diff_recursive, usersolver_t const *usersolvers, void *ctx, ctxnew_t ctxnew, ctxfree_t ctxfree) { struct latin_solver solver; int diff; latin_solver_alloc(&solver, grid, o); diff = latin_solver_main(&solver, maxdiff, diff_simple, diff_set_0, diff_set_1, diff_forcing, diff_recursive, usersolvers, ctx, ctxnew, ctxfree); latin_solver_free(&solver); return diff; } void latin_solver_debug(unsigned char *cube, int o) { #ifdef STANDALONE_SOLVER if (solver_show_working > 1) { struct latin_solver ls, *solver = &ls; char *dbg; int x, y, i, c = 0; ls.cube = cube; ls.o = o; /* for cube() to work */ dbg = snewn(3*o*o*o, char); for (y = 0; y < o; y++) { for (x = 0; x < o; x++) { for (i = 1; i <= o; i++) { if (cube(x,y,i)) dbg[c++] = i + '0'; else dbg[c++] = '.'; } dbg[c++] = ' '; } dbg[c++] = '\n'; } dbg[c++] = '\n'; dbg[c++] = '\0'; printf("%s", dbg); sfree(dbg); } #endif } void latin_debug(digit *sq, int o) { #ifdef STANDALONE_SOLVER if (solver_show_working) { int x, y; for (y = 0; y < o; y++) { for (x = 0; x < o; x++) { printf("%2d ", sq[y*o+x]); } printf("\n"); } printf("\n"); } #endif } /* -------------------------------------------------------- * Generation. */ digit *latin_generate(int o, random_state *rs) { digit *sq; int *edges, *backedges, *capacity, *flow; void *scratch; int ne, scratchsize; int i, j, k; digit *row, *col, *numinv, *num; /* * To efficiently generate a latin square in such a way that * all possible squares are possible outputs from the function, * we make use of a theorem which states that any r x n latin * rectangle, with r < n, can be extended into an (r+1) x n * latin rectangle. In other words, we can reliably generate a * latin square row by row, by at every stage writing down any * row at all which doesn't conflict with previous rows, and * the theorem guarantees that we will never have to backtrack. * * To find a viable row at each stage, we can make use of the * support functions in maxflow.c. */ sq = snewn(o*o, digit); /* * In case this method of generation introduces a really subtle * top-to-bottom directional bias, we'll generate the rows in * random order. */ row = snewn(o, digit); col = snewn(o, digit); numinv = snewn(o, digit); num = snewn(o, digit); for (i = 0; i < o; i++) row[i] = i; shuffle(row, i, sizeof(*row), rs); /* * Set up the infrastructure for the maxflow algorithm. */ scratchsize = maxflow_scratch_size(o * 2 + 2); scratch = smalloc(scratchsize); backedges = snewn(o*o + 2*o, int); edges = snewn((o*o + 2*o) * 2, int); capacity = snewn(o*o + 2*o, int); flow = snewn(o*o + 2*o, int); /* Set up the edge array, and the initial capacities. */ ne = 0; for (i = 0; i < o; i++) { /* Each LHS vertex is connected to all RHS vertices. */ for (j = 0; j < o; j++) { edges[ne*2] = i; edges[ne*2+1] = j+o; /* capacity for this edge is set later on */ ne++; } } for (i = 0; i < o; i++) { /* Each RHS vertex is connected to the distinguished sink vertex. */ edges[ne*2] = i+o; edges[ne*2+1] = o*2+1; capacity[ne] = 1; ne++; } for (i = 0; i < o; i++) { /* And the distinguished source vertex connects to each LHS vertex. */ edges[ne*2] = o*2; edges[ne*2+1] = i; capacity[ne] = 1; ne++; } assert(ne == o*o + 2*o); /* Now set up backedges. */ maxflow_setup_backedges(ne, edges, backedges); /* * Now generate each row of the latin square. */ for (i = 0; i < o; i++) { /* * To prevent maxflow from behaving deterministically, we * separately permute the columns and the digits for the * purposes of the algorithm, differently for every row. */ for (j = 0; j < o; j++) col[j] = num[j] = j; shuffle(col, j, sizeof(*col), rs); shuffle(num, j, sizeof(*num), rs); /* We need the num permutation in both forward and inverse forms. */ for (j = 0; j < o; j++) numinv[num[j]] = j; /* * Set up the capacities for the maxflow run, by examining * the existing latin square. */ for (j = 0; j < o*o; j++) capacity[j] = 1; for (j = 0; j < i; j++) for (k = 0; k < o; k++) { int n = num[sq[row[j]*o + col[k]] - 1]; capacity[k*o+n] = 0; } /* * Run maxflow. */ j = maxflow_with_scratch(scratch, o*2+2, 2*o, 2*o+1, ne, edges, backedges, capacity, flow, NULL); assert(j == o); /* by the above theorem, this must have succeeded */ /* * And examine the flow array to pick out the new row of * the latin square. */ for (j = 0; j < o; j++) { for (k = 0; k < o; k++) { if (flow[j*o+k]) break; } assert(k < o); sq[row[i]*o + col[j]] = numinv[k] + 1; } } /* * Done. Free our internal workspaces... */ sfree(flow); sfree(capacity); sfree(edges); sfree(backedges); sfree(scratch); sfree(numinv); sfree(num); sfree(col); sfree(row); /* * ... and return our completed latin square. */ return sq; } digit *latin_generate_rect(int w, int h, random_state *rs) { int o = max(w, h), x, y; digit *latin, *latin_rect; latin = latin_generate(o, rs); latin_rect = snewn(w*h, digit); for (x = 0; x < w; x++) { for (y = 0; y < h; y++) { latin_rect[y*w + x] = latin[y*o + x]; } } sfree(latin); return latin_rect; } /* -------------------------------------------------------- * Checking. */ typedef struct lcparams { digit elt; int count; } lcparams; static int latin_check_cmp(void *v1, void *v2) { lcparams *lc1 = (lcparams *)v1; lcparams *lc2 = (lcparams *)v2; if (lc1->elt < lc2->elt) return -1; if (lc1->elt > lc2->elt) return 1; return 0; } #define ELT(sq,x,y) (sq[((y)*order)+(x)]) /* returns non-zero if sq is not a latin square. */ int latin_check(digit *sq, int order) { tree234 *dict = newtree234(latin_check_cmp); int c, r; int ret = 0; lcparams *lcp, lc, *aret; /* Use a tree234 as a simple hash table, go through the square * adding elements as we go or incrementing their counts. */ for (c = 0; c < order; c++) { for (r = 0; r < order; r++) { lc.elt = ELT(sq, c, r); lc.count = 0; lcp = find234(dict, &lc, NULL); if (!lcp) { lcp = snew(lcparams); lcp->elt = ELT(sq, c, r); lcp->count = 1; aret = add234(dict, lcp); assert(aret == lcp); } else { lcp->count++; } } } /* There should be precisely 'order' letters in the alphabet, * each occurring 'order' times (making the OxO tree) */ if (count234(dict) != order) ret = 1; else { for (c = 0; (lcp = index234(dict, c)) != NULL; c++) { if (lcp->count != order) ret = 1; } } for (c = 0; (lcp = index234(dict, c)) != NULL; c++) sfree(lcp); freetree234(dict); return ret; } /* -------------------------------------------------------- * Testing (and printing). */ #ifdef STANDALONE_LATIN_TEST #include #include const char *quis; static void latin_print(digit *sq, int order) { int x, y; for (y = 0; y < order; y++) { for (x = 0; x < order; x++) { printf("%2u ", ELT(sq, x, y)); } printf("\n"); } printf("\n"); } static void gen(int order, random_state *rs, int debug) { digit *sq; solver_show_working = debug; sq = latin_generate(order, rs); latin_print(sq, order); if (latin_check(sq, order)) { fprintf(stderr, "Square is not a latin square!"); exit(1); } sfree(sq); } void test_soak(int order, random_state *rs) { digit *sq; int n = 0; time_t tt_start, tt_now, tt_last; solver_show_working = 0; tt_now = tt_start = time(NULL); while(1) { sq = latin_generate(order, rs); sfree(sq); n++; tt_last = time(NULL); if (tt_last > tt_now) { tt_now = tt_last; printf("%d total, %3.1f/s\n", n, (double)n / (double)(tt_now - tt_start)); } } } void usage_exit(const char *msg) { if (msg) fprintf(stderr, "%s: %s\n", quis, msg); fprintf(stderr, "Usage: %s [--seed SEED] --soak | [game_id [game_id ...]]\n", quis); exit(1); } int main(int argc, char *argv[]) { int i, soak = 0; random_state *rs; time_t seed = time(NULL); quis = argv[0]; while (--argc > 0) { const char *p = *++argv; if (!strcmp(p, "--soak")) soak = 1; else if (!strcmp(p, "--seed")) { if (argc == 0) usage_exit("--seed needs an argument"); seed = (time_t)atoi(*++argv); argc--; } else if (*p == '-') usage_exit("unrecognised option"); else break; /* finished options */ } rs = random_new((void*)&seed, sizeof(time_t)); if (soak == 1) { if (argc != 1) usage_exit("only one argument for --soak"); test_soak(atoi(*argv), rs); } else { if (argc > 0) { for (i = 0; i < argc; i++) { gen(atoi(*argv++), rs, 1); } } else { while (1) { i = random_upto(rs, 20) + 1; gen(i, rs, 0); } } } random_free(rs); return 0; } #endif /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/laydomino.c0000644000175300017530000002101211153033567014564 0ustar simonsimon/* * laydomino.c: code for performing a domino (2x1 tile) layout of * a given area of code. */ #include #include #include #include "puzzles.h" /* * This function returns an array size w x h representing a grid: * each grid[i] = j, where j is the other end of a 2x1 domino. * If w*h is odd, one square will remain referring to itself. */ int *domino_layout(int w, int h, random_state *rs) { int *grid, *grid2, *list; int wh = w*h; /* * Allocate space in which to lay the grid out. */ grid = snewn(wh, int); grid2 = snewn(wh, int); list = snewn(2*wh, int); domino_layout_prealloc(w, h, rs, grid, grid2, list); sfree(grid2); sfree(list); return grid; } /* * As for domino_layout, but with preallocated buffers. * grid and grid2 should be size w*h, and list size 2*w*h. */ void domino_layout_prealloc(int w, int h, random_state *rs, int *grid, int *grid2, int *list) { int i, j, k, m, wh = w*h, todo, done; /* * To begin with, set grid[i] = i for all i to indicate * that all squares are currently singletons. Later we'll * set grid[i] to be the index of the other end of the * domino on i. */ for (i = 0; i < wh; i++) grid[i] = i; /* * Now prepare a list of the possible domino locations. There * are w*(h-1) possible vertical locations, and (w-1)*h * horizontal ones, for a total of 2*wh - h - w. * * I'm going to denote the vertical domino placement with * its top in square i as 2*i, and the horizontal one with * its left half in square i as 2*i+1. */ k = 0; for (j = 0; j < h-1; j++) for (i = 0; i < w; i++) list[k++] = 2 * (j*w+i); /* vertical positions */ for (j = 0; j < h; j++) for (i = 0; i < w-1; i++) list[k++] = 2 * (j*w+i) + 1; /* horizontal positions */ assert(k == 2*wh - h - w); /* * Shuffle the list. */ shuffle(list, k, sizeof(*list), rs); /* * Work down the shuffled list, placing a domino everywhere * we can. */ for (i = 0; i < k; i++) { int horiz, xy, xy2; horiz = list[i] % 2; xy = list[i] / 2; xy2 = xy + (horiz ? 1 : w); if (grid[xy] == xy && grid[xy2] == xy2) { /* * We can place this domino. Do so. */ grid[xy] = xy2; grid[xy2] = xy; } } #ifdef GENERATION_DIAGNOSTICS printf("generated initial layout\n"); #endif /* * Now we've placed as many dominoes as we can immediately * manage. There will be squares remaining, but they'll be * singletons. So loop round and deal with the singletons * two by two. */ while (1) { #ifdef GENERATION_DIAGNOSTICS for (j = 0; j < h; j++) { for (i = 0; i < w; i++) { int xy = j*w+i; int v = grid[xy]; int c = (v == xy+1 ? '[' : v == xy-1 ? ']' : v == xy+w ? 'n' : v == xy-w ? 'U' : '.'); putchar(c); } putchar('\n'); } putchar('\n'); #endif /* * Our strategy is: * * First find a singleton square. * * Then breadth-first search out from the starting * square. From that square (and any others we reach on * the way), examine all four neighbours of the square. * If one is an end of a domino, we move to the _other_ * end of that domino before looking at neighbours * again. When we encounter another singleton on this * search, stop. * * This will give us a path of adjacent squares such * that all but the two ends are covered in dominoes. * So we can now shuffle every domino on the path up by * one. * * (Chessboard colours are mathematically important * here: we always end up pairing each singleton with a * singleton of the other colour. However, we never * have to track this manually, since it's * automatically taken care of by the fact that we * always make an even number of orthogonal moves.) */ k = 0; for (j = 0; j < wh; j++) { if (grid[j] == j) { k++; i = j; /* start BFS here. */ } } if (k == (wh % 2)) break; /* if area is even, we have no more singletons; if area is odd, we have one singleton. either way, we're done. */ #ifdef GENERATION_DIAGNOSTICS printf("starting b.f.s. at singleton %d\n", i); #endif /* * Set grid2 to -1 everywhere. It will hold our * distance-from-start values, and also our * backtracking data, during the b.f.s. */ for (j = 0; j < wh; j++) grid2[j] = -1; grid2[i] = 0; /* starting square has distance zero */ /* * Start our to-do list of squares. It'll live in * `list'; since the b.f.s can cover every square at * most once there is no need for it to be circular. * We'll just have two counters tracking the end of the * list and the squares we've already dealt with. */ done = 0; todo = 1; list[0] = i; /* * Now begin the b.f.s. loop. */ while (done < todo) { int d[4], nd, x, y; i = list[done++]; #ifdef GENERATION_DIAGNOSTICS printf("b.f.s. iteration from %d\n", i); #endif x = i % w; y = i / w; nd = 0; if (x > 0) d[nd++] = i - 1; if (x+1 < w) d[nd++] = i + 1; if (y > 0) d[nd++] = i - w; if (y+1 < h) d[nd++] = i + w; /* * To avoid directional bias, process the * neighbours of this square in a random order. */ shuffle(d, nd, sizeof(*d), rs); for (j = 0; j < nd; j++) { k = d[j]; if (grid[k] == k) { #ifdef GENERATION_DIAGNOSTICS printf("found neighbouring singleton %d\n", k); #endif grid2[k] = i; break; /* found a target singleton! */ } /* * We're moving through a domino here, so we * have two entries in grid2 to fill with * useful data. In grid[k] - the square * adjacent to where we came from - I'm going * to put the address _of_ the square we came * from. In the other end of the domino - the * square from which we will continue the * search - I'm going to put the distance. */ m = grid[k]; if (grid2[m] < 0 || grid2[m] > grid2[i]+1) { #ifdef GENERATION_DIAGNOSTICS printf("found neighbouring domino %d/%d\n", k, m); #endif grid2[m] = grid2[i]+1; grid2[k] = i; /* * And since we've now visited a new * domino, add m to the to-do list. */ assert(todo < wh); list[todo++] = m; } } if (j < nd) { i = k; #ifdef GENERATION_DIAGNOSTICS printf("terminating b.f.s. loop, i = %d\n", i); #endif break; } i = -1; /* just in case the loop terminates */ } /* * We expect this b.f.s. to have found us a target * square. */ assert(i >= 0); /* * Now we can follow the trail back to our starting * singleton, re-laying dominoes as we go. */ while (1) { j = grid2[i]; assert(j >= 0 && j < wh); k = grid[j]; grid[i] = j; grid[j] = i; #ifdef GENERATION_DIAGNOSTICS printf("filling in domino %d/%d (next %d)\n", i, j, k); #endif if (j == k) break; /* we've reached the other singleton */ i = k; } #ifdef GENERATION_DIAGNOSTICS printf("fixup path completed\n"); #endif } } /* vim: set shiftwidth=4 :set textwidth=80: */ puzzles-r9872/lightup.c0000644000175300017530000022422512132232554014253 0ustar simonsimon/* * lightup.c: Implementation of the Nikoli game 'Light Up'. * * Possible future solver enhancements: * * - In a situation where two clues are diagonally adjacent, you can * deduce bounds on the number of lights shared between them. For * instance, suppose a 3 clue is diagonally adjacent to a 1 clue: * of the two squares adjacent to both clues, at least one must be * a light (or the 3 would be unsatisfiable) and yet at most one * must be a light (or the 1 would be overcommitted), so in fact * _exactly_ one must be a light, and hence the other two squares * adjacent to the 3 must also be lights and the other two adjacent * to the 1 must not. Likewise if the 3 is replaced with a 2 but * one of its other two squares is known not to be a light, and so * on. * * - In a situation where two clues are orthogonally separated (not * necessarily directly adjacent), you may be able to deduce * something about the squares that align with each other. For * instance, suppose two clues are vertically adjacent. Consider * the pair of squares A,B horizontally adjacent to the top clue, * and the pair C,D horizontally adjacent to the bottom clue. * Assuming no intervening obstacles, A and C align with each other * and hence at most one of them can be a light, and B and D * likewise, so we must have at most two lights between the four * squares. So if the clues indicate that there are at _least_ two * lights in those four squares because the top clue requires at * least one of AB to be a light and the bottom one requires at * least one of CD, then we can in fact deduce that there are * _exactly_ two lights between the four squares, and fill in the * other squares adjacent to each clue accordingly. For instance, * if both clues are 3s, then we instantly deduce that all four of * the squares _vertically_ adjacent to the two clues must be * lights. (For that to happen, of course, there'd also have to be * a black square in between the clues, so the two inner lights * don't light each other.) * * - I haven't thought it through carefully, but there's always the * possibility that both of the above deductions are special cases * of some more general pattern which can be made computationally * feasible... */ #include #include #include #include #include #include #include "puzzles.h" /* * In standalone solver mode, `verbose' is a variable which can be * set by command-line option; in debugging mode it's simply always * true. */ #if defined STANDALONE_SOLVER #define SOLVER_DIAGNOSTICS int verbose = 0; #undef debug #define debug(x) printf x #elif defined SOLVER_DIAGNOSTICS #define verbose 2 #endif /* --- Constants, structure definitions, etc. --- */ #define PREFERRED_TILE_SIZE 32 #define TILE_SIZE (ds->tilesize) #define BORDER (TILE_SIZE / 2) #define TILE_RADIUS (ds->crad) #define COORD(x) ( (x) * TILE_SIZE + BORDER ) #define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 ) #define FLASH_TIME 0.30F enum { COL_BACKGROUND, COL_GRID, COL_BLACK, /* black */ COL_LIGHT, /* white */ COL_LIT, /* yellow */ COL_ERROR, /* red */ COL_CURSOR, NCOLOURS }; enum { SYMM_NONE, SYMM_REF2, SYMM_ROT2, SYMM_REF4, SYMM_ROT4, SYMM_MAX }; #define DIFFCOUNT 2 struct game_params { int w, h; int blackpc; /* %age of black squares */ int symm; int difficulty; /* 0 to DIFFCOUNT */ }; #define F_BLACK 1 /* flags for black squares */ #define F_NUMBERED 2 /* it has a number attached */ #define F_NUMBERUSED 4 /* this number was useful for solving */ /* flags for non-black squares */ #define F_IMPOSSIBLE 8 /* can't put a light here */ #define F_LIGHT 16 #define F_MARK 32 struct game_state { int w, h, nlights; int *lights; /* For black squares, (optionally) the number of surrounding lights. For non-black squares, the number of times it's lit. size h*w*/ unsigned int *flags; /* size h*w */ int completed, used_solve; }; #define GRID(gs,grid,x,y) (gs->grid[(y)*((gs)->w) + (x)]) /* A ll_data holds information about which lights would be lit by * a particular grid location's light (or conversely, which locations * could light a specific other location). */ /* most things should consider this struct opaque. */ typedef struct { int ox,oy; int minx, maxx, miny, maxy; int include_origin; } ll_data; /* Macro that executes 'block' once per light in lld, including * the origin if include_origin is specified. 'block' can use * lx and ly as the coords. */ #define FOREACHLIT(lld,block) do { \ int lx,ly; \ ly = (lld)->oy; \ for (lx = (lld)->minx; lx <= (lld)->maxx; lx++) { \ if (lx == (lld)->ox) continue; \ block \ } \ lx = (lld)->ox; \ for (ly = (lld)->miny; ly <= (lld)->maxy; ly++) { \ if (!(lld)->include_origin && ly == (lld)->oy) continue; \ block \ } \ } while(0) typedef struct { struct { int x, y; unsigned int f; } points[4]; int npoints; } surrounds; /* Fills in (doesn't allocate) a surrounds structure with the grid locations * around a given square, taking account of the edges. */ static void get_surrounds(const game_state *state, int ox, int oy, surrounds *s) { assert(ox >= 0 && ox < state->w && oy >= 0 && oy < state->h); s->npoints = 0; #define ADDPOINT(cond,nx,ny) do {\ if (cond) { \ s->points[s->npoints].x = (nx); \ s->points[s->npoints].y = (ny); \ s->points[s->npoints].f = 0; \ s->npoints++; \ } } while(0) ADDPOINT(ox > 0, ox-1, oy); ADDPOINT(ox < (state->w-1), ox+1, oy); ADDPOINT(oy > 0, ox, oy-1); ADDPOINT(oy < (state->h-1), ox, oy+1); } /* --- Game parameter functions --- */ #define DEFAULT_PRESET 0 const struct game_params lightup_presets[] = { { 7, 7, 20, SYMM_ROT4, 0 }, { 7, 7, 20, SYMM_ROT4, 1 }, { 7, 7, 20, SYMM_ROT4, 2 }, { 10, 10, 20, SYMM_ROT2, 0 }, { 10, 10, 20, SYMM_ROT2, 1 }, #ifdef SLOW_SYSTEM { 12, 12, 20, SYMM_ROT2, 0 }, { 12, 12, 20, SYMM_ROT2, 1 }, #else { 10, 10, 20, SYMM_ROT2, 2 }, { 14, 14, 20, SYMM_ROT2, 0 }, { 14, 14, 20, SYMM_ROT2, 1 }, { 14, 14, 20, SYMM_ROT2, 2 } #endif }; static game_params *default_params(void) { game_params *ret = snew(game_params); *ret = lightup_presets[DEFAULT_PRESET]; return ret; } static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char buf[80]; if (i < 0 || i >= lenof(lightup_presets)) return FALSE; ret = default_params(); *ret = lightup_presets[i]; *params = ret; sprintf(buf, "%dx%d %s", ret->w, ret->h, ret->difficulty == 2 ? "hard" : ret->difficulty == 1 ? "tricky" : "easy"); *name = dupstr(buf); 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; } #define EATNUM(x) do { \ (x) = atoi(string); \ while (*string && isdigit((unsigned char)*string)) string++; \ } while(0) static void decode_params(game_params *params, char const *string) { EATNUM(params->w); if (*string == 'x') { string++; EATNUM(params->h); } if (*string == 'b') { string++; EATNUM(params->blackpc); } if (*string == 's') { string++; EATNUM(params->symm); } else { /* cope with user input such as '18x10' by ensuring symmetry * is not selected by default to be incompatible with dimensions */ if (params->symm == SYMM_ROT4 && params->w != params->h) params->symm = SYMM_ROT2; } params->difficulty = 0; /* cope with old params */ if (*string == 'r') { params->difficulty = 2; string++; } if (*string == 'd') { string++; EATNUM(params->difficulty); } } static char *encode_params(const game_params *params, int full) { char buf[80]; if (full) { sprintf(buf, "%dx%db%ds%dd%d", params->w, params->h, params->blackpc, params->symm, params->difficulty); } else { sprintf(buf, "%dx%d", params->w, params->h); } return dupstr(buf); } static config_item *game_configure(const game_params *params) { config_item *ret; char buf[80]; ret = snewn(6, config_item); ret[0].name = "Width"; ret[0].type = C_STRING; sprintf(buf, "%d", params->w); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "%age of black squares"; ret[2].type = C_STRING; sprintf(buf, "%d", params->blackpc); ret[2].sval = dupstr(buf); ret[2].ival = 0; ret[3].name = "Symmetry"; ret[3].type = C_CHOICES; ret[3].sval = ":None" ":2-way mirror:2-way rotational" ":4-way mirror:4-way rotational"; ret[3].ival = params->symm; ret[4].name = "Difficulty"; ret[4].type = C_CHOICES; ret[4].sval = ":Easy:Tricky:Hard"; ret[4].ival = params->difficulty; ret[5].name = NULL; ret[5].type = C_END; ret[5].sval = NULL; ret[5].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); ret->blackpc = atoi(cfg[2].sval); ret->symm = cfg[3].ival; ret->difficulty = cfg[4].ival; return ret; } static char *validate_params(const game_params *params, int full) { if (params->w < 2 || params->h < 2) return "Width and height must be at least 2"; if (full) { if (params->blackpc < 5 || params->blackpc > 100) return "Percentage of black squares must be between 5% and 100%"; if (params->w != params->h) { if (params->symm == SYMM_ROT4) return "4-fold symmetry is only available with square grids"; } if (params->symm < 0 || params->symm >= SYMM_MAX) return "Unknown symmetry type"; if (params->difficulty < 0 || params->difficulty > DIFFCOUNT) return "Unknown difficulty level"; } return NULL; } /* --- Game state construction/freeing helper functions --- */ static game_state *new_state(const game_params *params) { game_state *ret = snew(game_state); ret->w = params->w; ret->h = params->h; ret->lights = snewn(ret->w * ret->h, int); ret->nlights = 0; memset(ret->lights, 0, ret->w * ret->h * sizeof(int)); ret->flags = snewn(ret->w * ret->h, unsigned int); memset(ret->flags, 0, ret->w * ret->h * sizeof(unsigned int)); ret->completed = ret->used_solve = 0; return ret; } static game_state *dup_game(const game_state *state) { game_state *ret = snew(game_state); ret->w = state->w; ret->h = state->h; ret->lights = snewn(ret->w * ret->h, int); memcpy(ret->lights, state->lights, ret->w * ret->h * sizeof(int)); ret->nlights = state->nlights; ret->flags = snewn(ret->w * ret->h, unsigned int); memcpy(ret->flags, state->flags, ret->w * ret->h * sizeof(unsigned int)); ret->completed = state->completed; ret->used_solve = state->used_solve; return ret; } static void free_game(game_state *state) { sfree(state->lights); sfree(state->flags); sfree(state); } static void debug_state(game_state *state) { int x, y; char c = '?'; for (y = 0; y < state->h; y++) { for (x = 0; x < state->w; x++) { c = '.'; if (GRID(state, flags, x, y) & F_BLACK) { if (GRID(state, flags, x, y) & F_NUMBERED) c = GRID(state, lights, x, y) + '0'; else c = '#'; } else { if (GRID(state, flags, x, y) & F_LIGHT) c = 'O'; else if (GRID(state, flags, x, y) & F_IMPOSSIBLE) c = 'X'; } debug(("%c", (int)c)); } debug((" ")); for (x = 0; x < state->w; x++) { if (GRID(state, flags, x, y) & F_BLACK) c = '#'; else { c = (GRID(state, flags, x, y) & F_LIGHT) ? 'A' : 'a'; c += GRID(state, lights, x, y); } debug(("%c", (int)c)); } debug(("\n")); } } /* --- Game completion test routines. --- */ /* These are split up because occasionally functions are only * interested in one particular aspect. */ /* Returns non-zero if all grid spaces are lit. */ static int grid_lit(game_state *state) { int x, y; for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { if (GRID(state,flags,x,y) & F_BLACK) continue; if (GRID(state,lights,x,y) == 0) return 0; } } return 1; } /* Returns non-zero if any lights are lit by other lights. */ static int grid_overlap(game_state *state) { int x, y; for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { if (!(GRID(state, flags, x, y) & F_LIGHT)) continue; if (GRID(state, lights, x, y) > 1) return 1; } } return 0; } static int number_wrong(const game_state *state, int x, int y) { surrounds s; int i, n, empty, lights = GRID(state, lights, x, y); /* * This function computes the display hint for a number: we * turn the number red if it is definitely wrong. This means * that either * * (a) it has too many lights around it, or * (b) it would have too few lights around it even if all the * plausible squares (not black, lit or F_IMPOSSIBLE) were * filled with lights. */ assert(GRID(state, flags, x, y) & F_NUMBERED); get_surrounds(state, x, y, &s); empty = n = 0; for (i = 0; i < s.npoints; i++) { if (GRID(state,flags,s.points[i].x,s.points[i].y) & F_LIGHT) { n++; continue; } if (GRID(state,flags,s.points[i].x,s.points[i].y) & F_BLACK) continue; if (GRID(state,flags,s.points[i].x,s.points[i].y) & F_IMPOSSIBLE) continue; if (GRID(state,lights,s.points[i].x,s.points[i].y)) continue; empty++; } return (n > lights || (n + empty < lights)); } static int number_correct(game_state *state, int x, int y) { surrounds s; int n = 0, i, lights = GRID(state, lights, x, y); assert(GRID(state, flags, x, y) & F_NUMBERED); get_surrounds(state, x, y, &s); for (i = 0; i < s.npoints; i++) { if (GRID(state,flags,s.points[i].x,s.points[i].y) & F_LIGHT) n++; } return (n == lights) ? 1 : 0; } /* Returns non-zero if any numbers add up incorrectly. */ static int grid_addsup(game_state *state) { int x, y; for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { if (!(GRID(state, flags, x, y) & F_NUMBERED)) continue; if (!number_correct(state, x, y)) return 0; } } return 1; } static int grid_correct(game_state *state) { if (grid_lit(state) && !grid_overlap(state) && grid_addsup(state)) return 1; return 0; } /* --- Board initial setup (blacks, lights, numbers) --- */ static void clean_board(game_state *state, int leave_blacks) { int x,y; for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { if (leave_blacks) GRID(state, flags, x, y) &= F_BLACK; else GRID(state, flags, x, y) = 0; GRID(state, lights, x, y) = 0; } } state->nlights = 0; } static void set_blacks(game_state *state, const game_params *params, random_state *rs) { int x, y, degree = 0, rotate = 0, nblack; int rh, rw, i; int wodd = (state->w % 2) ? 1 : 0; int hodd = (state->h % 2) ? 1 : 0; int xs[4], ys[4]; switch (params->symm) { case SYMM_NONE: degree = 1; rotate = 0; break; case SYMM_ROT2: degree = 2; rotate = 1; break; case SYMM_REF2: degree = 2; rotate = 0; break; case SYMM_ROT4: degree = 4; rotate = 1; break; case SYMM_REF4: degree = 4; rotate = 0; break; default: assert(!"Unknown symmetry type"); } if (params->symm == SYMM_ROT4 && (state->h != state->w)) assert(!"4-fold symmetry unavailable without square grid"); if (degree == 4) { rw = state->w/2; rh = state->h/2; if (!rotate) rw += wodd; /* ... but see below. */ rh += hodd; } else if (degree == 2) { rw = state->w; rh = state->h/2; rh += hodd; } else { rw = state->w; rh = state->h; } /* clear, then randomise, required region. */ clean_board(state, 0); nblack = (rw * rh * params->blackpc) / 100; for (i = 0; i < nblack; i++) { do { x = random_upto(rs,rw); y = random_upto(rs,rh); } while (GRID(state,flags,x,y) & F_BLACK); GRID(state, flags, x, y) |= F_BLACK; } /* Copy required region. */ if (params->symm == SYMM_NONE) return; for (x = 0; x < rw; x++) { for (y = 0; y < rh; y++) { if (degree == 4) { xs[0] = x; ys[0] = y; xs[1] = state->w - 1 - (rotate ? y : x); ys[1] = rotate ? x : y; xs[2] = rotate ? (state->w - 1 - x) : x; ys[2] = state->h - 1 - y; xs[3] = rotate ? y : (state->w - 1 - x); ys[3] = state->h - 1 - (rotate ? x : y); } else { xs[0] = x; ys[0] = y; xs[1] = rotate ? (state->w - 1 - x) : x; ys[1] = state->h - 1 - y; } for (i = 1; i < degree; i++) { GRID(state, flags, xs[i], ys[i]) = GRID(state, flags, xs[0], ys[0]); } } } /* SYMM_ROT4 misses the middle square above; fix that here. */ if (degree == 4 && rotate && wodd && (random_upto(rs,100) <= (unsigned int)params->blackpc)) GRID(state,flags, state->w/2 + wodd - 1, state->h/2 + hodd - 1) |= F_BLACK; #ifdef SOLVER_DIAGNOSTICS if (verbose) debug_state(state); #endif } /* Fills in (does not allocate) a ll_data with all the tiles that would * be illuminated by a light at point (ox,oy). If origin=1 then the * origin is included in this list. */ static void list_lights(game_state *state, int ox, int oy, int origin, ll_data *lld) { int x,y; lld->ox = lld->minx = lld->maxx = ox; lld->oy = lld->miny = lld->maxy = oy; lld->include_origin = origin; y = oy; for (x = ox-1; x >= 0; x--) { if (GRID(state, flags, x, y) & F_BLACK) break; if (x < lld->minx) lld->minx = x; } for (x = ox+1; x < state->w; x++) { if (GRID(state, flags, x, y) & F_BLACK) break; if (x > lld->maxx) lld->maxx = x; } x = ox; for (y = oy-1; y >= 0; y--) { if (GRID(state, flags, x, y) & F_BLACK) break; if (y < lld->miny) lld->miny = y; } for (y = oy+1; y < state->h; y++) { if (GRID(state, flags, x, y) & F_BLACK) break; if (y > lld->maxy) lld->maxy = y; } } /* Makes sure a light is the given state, editing the lights table to suit the * new state if necessary. */ static void set_light(game_state *state, int ox, int oy, int on) { ll_data lld; int diff = 0; assert(!(GRID(state,flags,ox,oy) & F_BLACK)); if (!on && GRID(state,flags,ox,oy) & F_LIGHT) { diff = -1; GRID(state,flags,ox,oy) &= ~F_LIGHT; state->nlights--; } else if (on && !(GRID(state,flags,ox,oy) & F_LIGHT)) { diff = 1; GRID(state,flags,ox,oy) |= F_LIGHT; state->nlights++; } if (diff != 0) { list_lights(state,ox,oy,1,&lld); FOREACHLIT(&lld, GRID(state,lights,lx,ly) += diff; ); } } /* Returns 1 if removing a light at (x,y) would cause a square to go dark. */ static int check_dark(game_state *state, int x, int y) { ll_data lld; list_lights(state, x, y, 1, &lld); FOREACHLIT(&lld, if (GRID(state,lights,lx,ly) == 1) { return 1; } ); return 0; } /* Sets up an initial random correct position (i.e. every * space lit, and no lights lit by other lights) by filling the * grid with lights and then removing lights one by one at random. */ static void place_lights(game_state *state, random_state *rs) { int i, x, y, n, *numindices, wh = state->w*state->h; ll_data lld; numindices = snewn(wh, int); for (i = 0; i < wh; i++) numindices[i] = i; shuffle(numindices, wh, sizeof(*numindices), rs); /* Place a light on all grid squares without lights. */ for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { GRID(state, flags, x, y) &= ~F_MARK; /* we use this later. */ if (GRID(state, flags, x, y) & F_BLACK) continue; set_light(state, x, y, 1); } } for (i = 0; i < wh; i++) { y = numindices[i] / state->w; x = numindices[i] % state->w; if (!(GRID(state, flags, x, y) & F_LIGHT)) continue; if (GRID(state, flags, x, y) & F_MARK) continue; list_lights(state, x, y, 0, &lld); /* If we're not lighting any lights ourself, don't remove anything. */ n = 0; FOREACHLIT(&lld, if (GRID(state,flags,lx,ly) & F_LIGHT) { n += 1; } ); if (n == 0) continue; /* [1] */ /* Check whether removing lights we're lighting would cause anything * to go dark. */ n = 0; FOREACHLIT(&lld, if (GRID(state,flags,lx,ly) & F_LIGHT) { n += check_dark(state,lx,ly); } ); if (n == 0) { /* No, it wouldn't, so we can remove them all. */ FOREACHLIT(&lld, set_light(state,lx,ly, 0); ); GRID(state,flags,x,y) |= F_MARK; } if (!grid_overlap(state)) { sfree(numindices); return; /* we're done. */ } assert(grid_lit(state)); } /* could get here if the line at [1] continue'd out of the loop. */ if (grid_overlap(state)) { debug_state(state); assert(!"place_lights failed to resolve overlapping lights!"); } sfree(numindices); } /* Fills in all black squares with numbers of adjacent lights. */ static void place_numbers(game_state *state) { int x, y, i, n; surrounds s; for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { if (!(GRID(state,flags,x,y) & F_BLACK)) continue; get_surrounds(state, x, y, &s); n = 0; for (i = 0; i < s.npoints; i++) { if (GRID(state,flags,s.points[i].x, s.points[i].y) & F_LIGHT) n++; } GRID(state,flags,x,y) |= F_NUMBERED; GRID(state,lights,x,y) = n; } } } /* --- Actual solver, with helper subroutines. --- */ static void tsl_callback(game_state *state, int lx, int ly, int *x, int *y, int *n) { if (GRID(state,flags,lx,ly) & F_IMPOSSIBLE) return; if (GRID(state,lights,lx,ly) > 0) return; *x = lx; *y = ly; (*n)++; } static int try_solve_light(game_state *state, int ox, int oy, unsigned int flags, int lights) { ll_data lld; int sx = 0, sy = 0, n = 0; if (lights > 0) return 0; if (flags & F_BLACK) return 0; /* We have an unlit square; count how many ways there are left to * place a light that lights us (including this square); if only * one, we must put a light there. Squares that could light us * are, of course, the same as the squares we would light... */ list_lights(state, ox, oy, 1, &lld); FOREACHLIT(&lld, { tsl_callback(state, lx, ly, &sx, &sy, &n); }); if (n == 1) { set_light(state, sx, sy, 1); #ifdef SOLVER_DIAGNOSTICS debug(("(%d,%d) can only be lit from (%d,%d); setting to LIGHT\n", ox,oy,sx,sy)); if (verbose) debug_state(state); #endif return 1; } return 0; } static int could_place_light(unsigned int flags, int lights) { if (flags & (F_BLACK | F_IMPOSSIBLE)) return 0; return (lights > 0) ? 0 : 1; } static int could_place_light_xy(game_state *state, int x, int y) { int lights = GRID(state,lights,x,y); unsigned int flags = GRID(state,flags,x,y); return (could_place_light(flags, lights)) ? 1 : 0; } /* For a given number square, determine whether we have enough info * to unambiguously place its lights. */ static int try_solve_number(game_state *state, int nx, int ny, unsigned int nflags, int nlights) { surrounds s; int x, y, nl, ns, i, ret = 0, lights; unsigned int flags; if (!(nflags & F_NUMBERED)) return 0; nl = nlights; get_surrounds(state,nx,ny,&s); ns = s.npoints; /* nl is no. of lights we need to place, ns is no. of spaces we * have to place them in. Try and narrow these down, and mark * points we can ignore later. */ for (i = 0; i < s.npoints; i++) { x = s.points[i].x; y = s.points[i].y; flags = GRID(state,flags,x,y); lights = GRID(state,lights,x,y); if (flags & F_LIGHT) { /* light here already; one less light for one less place. */ nl--; ns--; s.points[i].f |= F_MARK; } else if (!could_place_light(flags, lights)) { ns--; s.points[i].f |= F_MARK; } } if (ns == 0) return 0; /* nowhere to put anything. */ if (nl == 0) { /* we have placed all lights we need to around here; all remaining * surrounds are therefore IMPOSSIBLE. */ GRID(state,flags,nx,ny) |= F_NUMBERUSED; for (i = 0; i < s.npoints; i++) { if (!(s.points[i].f & F_MARK)) { GRID(state,flags,s.points[i].x,s.points[i].y) |= F_IMPOSSIBLE; ret = 1; } } #ifdef SOLVER_DIAGNOSTICS printf("Clue at (%d,%d) full; setting unlit to IMPOSSIBLE.\n", nx,ny); if (verbose) debug_state(state); #endif } else if (nl == ns) { /* we have as many lights to place as spaces; fill them all. */ GRID(state,flags,nx,ny) |= F_NUMBERUSED; for (i = 0; i < s.npoints; i++) { if (!(s.points[i].f & F_MARK)) { set_light(state, s.points[i].x,s.points[i].y, 1); ret = 1; } } #ifdef SOLVER_DIAGNOSTICS printf("Clue at (%d,%d) trivial; setting unlit to LIGHT.\n", nx,ny); if (verbose) debug_state(state); #endif } return ret; } struct setscratch { int x, y; int n; }; #define SCRATCHSZ (state->w+state->h) /* New solver algorithm: overlapping sets can add IMPOSSIBLE flags. * Algorithm thanks to Simon: * * (a) Any square where you can place a light has a set of squares * which would become non-lights as a result. (This includes * squares lit by the first square, and can also include squares * adjacent to the same clue square if the new light is the last * one around that clue.) Call this MAKESDARK(x,y) with (x,y) being * the square you place a light. * (b) Any unlit square has a set of squares on which you could place * a light to illuminate it. (Possibly including itself, of * course.) This set of squares has the property that _at least * one_ of them must contain a light. Sets of this type also arise * from clue squares. Call this MAKESLIGHT(x,y), again with (x,y) * the square you would place a light. * (c) If there exists (dx,dy) and (lx,ly) such that MAKESDARK(dx,dy) is * a superset of MAKESLIGHT(lx,ly), this implies that placing a light at * (dx,dy) would either leave no remaining way to illuminate a certain * square, or would leave no remaining way to fulfill a certain clue * (at lx,ly). In either case, a light can be ruled out at that position. * * So, we construct all possible MAKESLIGHT sets, both from unlit squares * and clue squares, and then we look for plausible MAKESDARK sets that include * our (lx,ly) to see if we can find a (dx,dy) to rule out. By the time we have * constructed the MAKESLIGHT set we don't care about (lx,ly), just the set * members. * * Once we have such a set, Simon came up with a Cunning Plan to find * the most sensible MAKESDARK candidate: * * (a) for each square S in your set X, find all the squares which _would_ * rule it out. That means any square which would light S, plus * any square adjacent to the same clue square as S (provided * that clue square has only one remaining light to be placed). * It's not hard to make this list. Don't do anything with this * data at the moment except _count_ the squares. * (b) Find the square S_min in the original set which has the * _smallest_ number of other squares which would rule it out. * (c) Find all the squares that rule out S_min (it's probably * better to recompute this than to have stored it during step * (a), since the CPU requirement is modest but the storage * cost would get ugly.) For each of these squares, see if it * rules out everything else in the set X. Any which does can * be marked as not-a-light. * */ typedef void (*trl_cb)(game_state *state, int dx, int dy, struct setscratch *scratch, int n, void *ctx); static void try_rule_out(game_state *state, int x, int y, struct setscratch *scratch, int n, trl_cb cb, void *ctx); static void trl_callback_search(game_state *state, int dx, int dy, struct setscratch *scratch, int n, void *ignored) { int i; #ifdef SOLVER_DIAGNOSTICS if (verbose) debug(("discount cb: light at (%d,%d)\n", dx, dy)); #endif for (i = 0; i < n; i++) { if (dx == scratch[i].x && dy == scratch[i].y) { scratch[i].n = 1; return; } } } static void trl_callback_discount(game_state *state, int dx, int dy, struct setscratch *scratch, int n, void *ctx) { int *didsth = (int *)ctx; int i; if (GRID(state,flags,dx,dy) & F_IMPOSSIBLE) { #ifdef SOLVER_DIAGNOSTICS debug(("Square at (%d,%d) already impossible.\n", dx,dy)); #endif return; } /* Check whether a light at (dx,dy) rules out everything * in scratch, and mark (dx,dy) as IMPOSSIBLE if it does. * We can use try_rule_out for this as well, as the set of * squares which would rule out (x,y) is the same as the * set of squares which (x,y) would rule out. */ #ifdef SOLVER_DIAGNOSTICS if (verbose) debug(("Checking whether light at (%d,%d) rules out everything in scratch.\n", dx, dy)); #endif for (i = 0; i < n; i++) scratch[i].n = 0; try_rule_out(state, dx, dy, scratch, n, trl_callback_search, NULL); for (i = 0; i < n; i++) { if (scratch[i].n == 0) return; } /* The light ruled out everything in scratch. Yay. */ GRID(state,flags,dx,dy) |= F_IMPOSSIBLE; #ifdef SOLVER_DIAGNOSTICS debug(("Set reduction discounted square at (%d,%d):\n", dx,dy)); if (verbose) debug_state(state); #endif *didsth = 1; } static void trl_callback_incn(game_state *state, int dx, int dy, struct setscratch *scratch, int n, void *ctx) { struct setscratch *s = (struct setscratch *)ctx; s->n++; } static void try_rule_out(game_state *state, int x, int y, struct setscratch *scratch, int n, trl_cb cb, void *ctx) { /* XXX Find all the squares which would rule out (x,y); anything * that would light it as well as squares adjacent to same clues * as X assuming that clue only has one remaining light. * Call the callback with each square. */ ll_data lld; surrounds s, ss; int i, j, curr_lights, tot_lights; /* Find all squares that would rule out a light at (x,y) and call trl_cb * with them: anything that would light (x,y)... */ list_lights(state, x, y, 0, &lld); FOREACHLIT(&lld, { if (could_place_light_xy(state, lx, ly)) { cb(state, lx, ly, scratch, n, ctx); } }); /* ... as well as any empty space (that isn't x,y) next to any clue square * next to (x,y) that only has one light left to place. */ get_surrounds(state, x, y, &s); for (i = 0; i < s.npoints; i++) { if (!(GRID(state,flags,s.points[i].x,s.points[i].y) & F_NUMBERED)) continue; /* we have an adjacent clue square; find /its/ surrounds * and count the remaining lights it needs. */ get_surrounds(state,s.points[i].x,s.points[i].y,&ss); curr_lights = 0; for (j = 0; j < ss.npoints; j++) { if (GRID(state,flags,ss.points[j].x,ss.points[j].y) & F_LIGHT) curr_lights++; } tot_lights = GRID(state, lights, s.points[i].x, s.points[i].y); /* We have a clue with tot_lights to fill, and curr_lights currently * around it. If adding a light at (x,y) fills up the clue (i.e. * curr_lights + 1 = tot_lights) then we need to discount all other * unlit squares around the clue. */ if ((curr_lights + 1) == tot_lights) { for (j = 0; j < ss.npoints; j++) { int lx = ss.points[j].x, ly = ss.points[j].y; if (lx == x && ly == y) continue; if (could_place_light_xy(state, lx, ly)) cb(state, lx, ly, scratch, n, ctx); } } } } #ifdef SOLVER_DIAGNOSTICS static void debug_scratch(const char *msg, struct setscratch *scratch, int n) { int i; debug(("%s scratch (%d elements):\n", msg, n)); for (i = 0; i < n; i++) { debug((" (%d,%d) n%d\n", scratch[i].x, scratch[i].y, scratch[i].n)); } } #endif static int discount_set(game_state *state, struct setscratch *scratch, int n) { int i, besti, bestn, didsth = 0; #ifdef SOLVER_DIAGNOSTICS if (verbose > 1) debug_scratch("discount_set", scratch, n); #endif if (n == 0) return 0; for (i = 0; i < n; i++) { try_rule_out(state, scratch[i].x, scratch[i].y, scratch, n, trl_callback_incn, (void*)&(scratch[i])); } #ifdef SOLVER_DIAGNOSTICS if (verbose > 1) debug_scratch("discount_set after count", scratch, n); #endif besti = -1; bestn = SCRATCHSZ; for (i = 0; i < n; i++) { if (scratch[i].n < bestn) { bestn = scratch[i].n; besti = i; } } #ifdef SOLVER_DIAGNOSTICS if (verbose > 1) debug(("best square (%d,%d) with n%d.\n", scratch[besti].x, scratch[besti].y, scratch[besti].n)); #endif try_rule_out(state, scratch[besti].x, scratch[besti].y, scratch, n, trl_callback_discount, (void*)&didsth); #ifdef SOLVER_DIAGNOSTICS if (didsth) debug((" [from square (%d,%d)]\n", scratch[besti].x, scratch[besti].y)); #endif return didsth; } static void discount_clear(game_state *state, struct setscratch *scratch, int *n) { *n = 0; memset(scratch, 0, SCRATCHSZ * sizeof(struct setscratch)); } static void unlit_cb(game_state *state, int lx, int ly, struct setscratch *scratch, int *n) { if (could_place_light_xy(state, lx, ly)) { scratch[*n].x = lx; scratch[*n].y = ly; (*n)++; } } /* Construct a MAKESLIGHT set from an unlit square. */ static int discount_unlit(game_state *state, int x, int y, struct setscratch *scratch) { ll_data lld; int n, didsth; #ifdef SOLVER_DIAGNOSTICS if (verbose) debug(("Trying to discount for unlit square at (%d,%d).\n", x, y)); if (verbose > 1) debug_state(state); #endif discount_clear(state, scratch, &n); list_lights(state, x, y, 1, &lld); FOREACHLIT(&lld, { unlit_cb(state, lx, ly, scratch, &n); }); didsth = discount_set(state, scratch, n); #ifdef SOLVER_DIAGNOSTICS if (didsth) debug((" [from unlit square at (%d,%d)].\n", x, y)); #endif return didsth; } /* Construct a series of MAKESLIGHT sets from a clue square. * for a clue square with N remaining spaces that must contain M lights, every * subset of size N-M+1 of those N spaces forms such a set. */ static int discount_clue(game_state *state, int x, int y, struct setscratch *scratch) { int slen, m = GRID(state, lights, x, y), n, i, didsth = 0, lights; unsigned int flags; surrounds s, sempty; combi_ctx *combi; if (m == 0) return 0; #ifdef SOLVER_DIAGNOSTICS if (verbose) debug(("Trying to discount for sets at clue (%d,%d).\n", x, y)); if (verbose > 1) debug_state(state); #endif /* m is no. of lights still to place; starts off at the clue value * and decreases when we find a light already down. * n is no. of spaces left; starts off at 0 and goes up when we find * a plausible space. */ get_surrounds(state, x, y, &s); memset(&sempty, 0, sizeof(surrounds)); for (i = 0; i < s.npoints; i++) { int lx = s.points[i].x, ly = s.points[i].y; flags = GRID(state,flags,lx,ly); lights = GRID(state,lights,lx,ly); if (flags & F_LIGHT) m--; if (could_place_light(flags, lights)) { sempty.points[sempty.npoints].x = lx; sempty.points[sempty.npoints].y = ly; sempty.npoints++; } } n = sempty.npoints; /* sempty is now a surrounds of only blank squares. */ if (n == 0) return 0; /* clue is full already. */ if (m < 0 || m > n) return 0; /* become impossible. */ combi = new_combi(n - m + 1, n); while (next_combi(combi)) { discount_clear(state, scratch, &slen); for (i = 0; i < combi->r; i++) { scratch[slen].x = sempty.points[combi->a[i]].x; scratch[slen].y = sempty.points[combi->a[i]].y; slen++; } if (discount_set(state, scratch, slen)) didsth = 1; } free_combi(combi); #ifdef SOLVER_DIAGNOSTICS if (didsth) debug((" [from clue at (%d,%d)].\n", x, y)); #endif return didsth; } #define F_SOLVE_FORCEUNIQUE 1 #define F_SOLVE_DISCOUNTSETS 2 #define F_SOLVE_ALLOWRECURSE 4 static unsigned int flags_from_difficulty(int difficulty) { unsigned int sflags = F_SOLVE_FORCEUNIQUE; assert(difficulty <= DIFFCOUNT); if (difficulty >= 1) sflags |= F_SOLVE_DISCOUNTSETS; if (difficulty >= 2) sflags |= F_SOLVE_ALLOWRECURSE; return sflags; } #define MAXRECURSE 5 static int solve_sub(game_state *state, unsigned int solve_flags, int depth, int *maxdepth) { unsigned int flags; int x, y, didstuff, ncanplace, lights; int bestx, besty, n, bestn, copy_soluble, self_soluble, ret, maxrecurse = 0; game_state *scopy; ll_data lld; struct setscratch *sscratch = NULL; #ifdef SOLVER_DIAGNOSTICS printf("solve_sub: depth = %d\n", depth); #endif if (maxdepth && *maxdepth < depth) *maxdepth = depth; if (solve_flags & F_SOLVE_ALLOWRECURSE) maxrecurse = MAXRECURSE; while (1) { if (grid_overlap(state)) { /* Our own solver, from scratch, should never cause this to happen * (assuming a soluble grid). However, if we're trying to solve * from a half-completed *incorrect* grid this might occur; we * just return the 'no solutions' code in this case. */ ret = 0; goto done; } if (grid_correct(state)) { ret = 1; goto done; } ncanplace = 0; didstuff = 0; /* These 2 loops, and the functions they call, are the critical loops * for timing; any optimisations should look here first. */ for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { flags = GRID(state,flags,x,y); lights = GRID(state,lights,x,y); ncanplace += could_place_light(flags, lights); if (try_solve_light(state, x, y, flags, lights)) didstuff = 1; if (try_solve_number(state, x, y, flags, lights)) didstuff = 1; } } if (didstuff) continue; if (!ncanplace) { /* nowhere to put a light, puzzle is unsoluble. */ ret = 0; goto done; } if (solve_flags & F_SOLVE_DISCOUNTSETS) { if (!sscratch) sscratch = snewn(SCRATCHSZ, struct setscratch); /* Try a more cunning (and more involved) way... more details above. */ for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { flags = GRID(state,flags,x,y); lights = GRID(state,lights,x,y); if (!(flags & F_BLACK) && lights == 0) { if (discount_unlit(state, x, y, sscratch)) { didstuff = 1; goto reduction_success; } } else if (flags & F_NUMBERED) { if (discount_clue(state, x, y, sscratch)) { didstuff = 1; goto reduction_success; } } } } } reduction_success: if (didstuff) continue; /* We now have to make a guess; we have places to put lights but * no definite idea about where they can go. */ if (depth >= maxrecurse) { /* mustn't delve any deeper. */ ret = -1; goto done; } /* Of all the squares that we could place a light, pick the one * that would light the most currently unlit squares. */ /* This heuristic was just plucked from the air; there may well be * a more efficient way of choosing a square to flip to minimise * recursion. */ bestn = 0; bestx = besty = -1; /* suyb */ for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { flags = GRID(state,flags,x,y); lights = GRID(state,lights,x,y); if (!could_place_light(flags, lights)) continue; n = 0; list_lights(state, x, y, 1, &lld); FOREACHLIT(&lld, { if (GRID(state,lights,lx,ly) == 0) n++; }); if (n > bestn) { bestn = n; bestx = x; besty = y; } } } assert(bestn > 0); assert(bestx >= 0 && besty >= 0); /* Now we've chosen a plausible (x,y), try to solve it once as 'lit' * and once as 'impossible'; we need to make one copy to do this. */ scopy = dup_game(state); #ifdef SOLVER_DIAGNOSTICS debug(("Recursing #1: trying (%d,%d) as IMPOSSIBLE\n", bestx, besty)); #endif GRID(state,flags,bestx,besty) |= F_IMPOSSIBLE; self_soluble = solve_sub(state, solve_flags, depth+1, maxdepth); if (!(solve_flags & F_SOLVE_FORCEUNIQUE) && self_soluble > 0) { /* we didn't care about finding all solutions, and we just * found one; return with it immediately. */ free_game(scopy); ret = self_soluble; goto done; } #ifdef SOLVER_DIAGNOSTICS debug(("Recursing #2: trying (%d,%d) as LIGHT\n", bestx, besty)); #endif set_light(scopy, bestx, besty, 1); copy_soluble = solve_sub(scopy, solve_flags, depth+1, maxdepth); /* If we wanted a unique solution but we hit our recursion limit * (on either branch) then we have to assume we didn't find possible * extra solutions, and return 'not soluble'. */ if ((solve_flags & F_SOLVE_FORCEUNIQUE) && ((copy_soluble < 0) || (self_soluble < 0))) { ret = -1; /* Make sure that whether or not it was self or copy (or both) that * were soluble, that we return a solved state in self. */ } else if (copy_soluble <= 0) { /* copy wasn't soluble; keep self state and return that result. */ ret = self_soluble; } else if (self_soluble <= 0) { /* copy solved and we didn't, so copy in copy's (now solved) * flags and light state. */ memcpy(state->lights, scopy->lights, scopy->w * scopy->h * sizeof(int)); memcpy(state->flags, scopy->flags, scopy->w * scopy->h * sizeof(unsigned int)); ret = copy_soluble; } else { ret = copy_soluble + self_soluble; } free_game(scopy); goto done; } done: if (sscratch) sfree(sscratch); #ifdef SOLVER_DIAGNOSTICS if (ret < 0) debug(("solve_sub: depth = %d returning, ran out of recursion.\n", depth)); else debug(("solve_sub: depth = %d returning, %d solutions.\n", depth, ret)); #endif return ret; } /* Fills in the (possibly partially-complete) game_state as far as it can, * returning the number of possible solutions. If it returns >0 then the * game_state will be in a solved state, but you won't know which one. */ static int dosolve(game_state *state, int solve_flags, int *maxdepth) { int x, y, nsol; for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { GRID(state,flags,x,y) &= ~F_NUMBERUSED; } } nsol = solve_sub(state, solve_flags, 0, maxdepth); return nsol; } static int strip_unused_nums(game_state *state) { int x,y,n=0; for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { if ((GRID(state,flags,x,y) & F_NUMBERED) && !(GRID(state,flags,x,y) & F_NUMBERUSED)) { GRID(state,flags,x,y) &= ~F_NUMBERED; GRID(state,lights,x,y) = 0; n++; } } } debug(("Stripped %d unused numbers.\n", n)); return n; } static void unplace_lights(game_state *state) { int x,y; for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { if (GRID(state,flags,x,y) & F_LIGHT) set_light(state,x,y,0); GRID(state,flags,x,y) &= ~F_IMPOSSIBLE; GRID(state,flags,x,y) &= ~F_NUMBERUSED; } } } static int puzzle_is_good(game_state *state, int difficulty) { int nsol, mdepth = 0; unsigned int sflags = flags_from_difficulty(difficulty); unplace_lights(state); #ifdef SOLVER_DIAGNOSTICS debug(("Trying to solve with difficulty %d (0x%x):\n", difficulty, sflags)); if (verbose) debug_state(state); #endif nsol = dosolve(state, sflags, &mdepth); /* if we wanted an easy puzzle, make sure we didn't need recursion. */ if (!(sflags & F_SOLVE_ALLOWRECURSE) && mdepth > 0) { debug(("Ignoring recursive puzzle.\n")); return 0; } debug(("%d solutions found.\n", nsol)); if (nsol <= 0) return 0; if (nsol > 1) return 0; return 1; } /* --- New game creation and user input code. --- */ /* The basic algorithm here is to generate the most complex grid possible * while honouring two restrictions: * * * we require a unique solution, and * * either we require solubility with no recursion (!params->recurse) * * or we require some recursion. (params->recurse). * * The solver helpfully keeps track of the numbers it needed to use to * get its solution, so we use that to remove an initial set of numbers * and check we still satsify our requirements (on uniqueness and * non-recursiveness, if applicable; we don't check explicit recursiveness * until the end). * * Then we try to remove all numbers in a random order, and see if we * still satisfy requirements (putting them back if we didn't). * * Removing numbers will always, in general terms, make a puzzle require * more recursion but it may also mean a puzzle becomes non-unique. * * Once we're done, if we wanted a recursive puzzle but the most difficult * puzzle we could come up with was non-recursive, we give up and try a new * grid. */ #define MAX_GRIDGEN_TRIES 20 static char *new_game_desc(const game_params *params_in, random_state *rs, char **aux, int interactive) { game_params params_copy = *params_in; /* structure copy */ game_params *params = ¶ms_copy; game_state *news = new_state(params), *copys; int i, j, run, x, y, wh = params->w*params->h, num; char *ret, *p; int *numindices; /* Construct a shuffled list of grid positions; we only * do this once, because if it gets used more than once it'll * be on a different grid layout. */ numindices = snewn(wh, int); for (j = 0; j < wh; j++) numindices[j] = j; shuffle(numindices, wh, sizeof(*numindices), rs); while (1) { for (i = 0; i < MAX_GRIDGEN_TRIES; i++) { set_blacks(news, params, rs); /* also cleans board. */ /* set up lights and then the numbers, and remove the lights */ place_lights(news, rs); debug(("Generating initial grid.\n")); place_numbers(news); if (!puzzle_is_good(news, params->difficulty)) continue; /* Take a copy, remove numbers we didn't use and check there's * still a unique solution; if so, use the copy subsequently. */ copys = dup_game(news); strip_unused_nums(copys); if (!puzzle_is_good(copys, params->difficulty)) { debug(("Stripped grid is not good, reverting.\n")); free_game(copys); } else { free_game(news); news = copys; } /* Go through grid removing numbers at random one-by-one and * trying to solve again; if it ceases to be good put the number back. */ for (j = 0; j < wh; j++) { y = numindices[j] / params->w; x = numindices[j] % params->w; if (!(GRID(news, flags, x, y) & F_NUMBERED)) continue; num = GRID(news, lights, x, y); GRID(news, lights, x, y) = 0; GRID(news, flags, x, y) &= ~F_NUMBERED; if (!puzzle_is_good(news, params->difficulty)) { GRID(news, lights, x, y) = num; GRID(news, flags, x, y) |= F_NUMBERED; } else debug(("Removed (%d,%d) still soluble.\n", x, y)); } if (params->difficulty > 0) { /* Was the maximally-difficult puzzle difficult enough? * Check we can't solve it with a more simplistic solver. */ if (puzzle_is_good(news, params->difficulty-1)) { debug(("Maximally-hard puzzle still not hard enough, skipping.\n")); continue; } } goto goodpuzzle; } /* Couldn't generate a good puzzle in however many goes. Ramp up the * %age of black squares (if we didn't already have lots; in which case * why couldn't we generate a puzzle?) and try again. */ if (params->blackpc < 90) params->blackpc += 5; debug(("New black layout %d%%.\n", params->blackpc)); } goodpuzzle: /* Game is encoded as a long string one character per square; * 'S' is a space * 'B' is a black square with no number * '0', '1', '2', '3', '4' is a black square with a number. */ ret = snewn((params->w * params->h) + 1, char); p = ret; run = 0; for (y = 0; y < params->h; y++) { for (x = 0; x < params->w; x++) { if (GRID(news,flags,x,y) & F_BLACK) { if (run) { *p++ = ('a'-1) + run; run = 0; } if (GRID(news,flags,x,y) & F_NUMBERED) *p++ = '0' + GRID(news,lights,x,y); else *p++ = 'B'; } else { if (run == 26) { *p++ = ('a'-1) + run; run = 0; } run++; } } } if (run) { *p++ = ('a'-1) + run; run = 0; } *p = '\0'; assert(p - ret <= params->w * params->h); free_game(news); sfree(numindices); return ret; } static char *validate_desc(const game_params *params, const char *desc) { int i; for (i = 0; i < params->w*params->h; i++) { if (*desc >= '0' && *desc <= '4') /* OK */; else if (*desc == 'B') /* OK */; else if (*desc >= 'a' && *desc <= 'z') i += *desc - 'a'; /* and the i++ will add another one */ else if (!*desc) return "Game description shorter than expected"; else return "Game description contained unexpected character"; desc++; } if (*desc || i > params->w*params->h) return "Game description longer than expected"; return NULL; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { game_state *ret = new_state(params); int x,y; int run = 0; for (y = 0; y < params->h; y++) { for (x = 0; x < params->w; x++) { char c = '\0'; if (run == 0) { c = *desc++; assert(c != 'S'); if (c >= 'a' && c <= 'z') run = c - 'a' + 1; } if (run > 0) { c = 'S'; run--; } switch (c) { case '0': case '1': case '2': case '3': case '4': GRID(ret,flags,x,y) |= F_NUMBERED; GRID(ret,lights,x,y) = (c - '0'); /* run-on... */ case 'B': GRID(ret,flags,x,y) |= F_BLACK; break; case 'S': /* empty square */ break; default: assert(!"Malformed desc."); break; } } } if (*desc) assert(!"Over-long desc."); return ret; } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { game_state *solved; char *move = NULL, buf[80]; int movelen, movesize, x, y, len; unsigned int oldflags, solvedflags, sflags; /* We don't care here about non-unique puzzles; if the * user entered one themself then I doubt they care. */ sflags = F_SOLVE_ALLOWRECURSE | F_SOLVE_DISCOUNTSETS; /* Try and solve from where we are now (for non-unique * puzzles this may produce a different answer). */ solved = dup_game(currstate); if (dosolve(solved, sflags, NULL) > 0) goto solved; free_game(solved); /* That didn't work; try solving from the clean puzzle. */ solved = dup_game(state); if (dosolve(solved, sflags, NULL) > 0) goto solved; *error = "Unable to find a solution to this puzzle."; goto done; solved: movesize = 256; move = snewn(movesize, char); movelen = 0; move[movelen++] = 'S'; move[movelen] = '\0'; for (x = 0; x < currstate->w; x++) { for (y = 0; y < currstate->h; y++) { len = 0; oldflags = GRID(currstate, flags, x, y); solvedflags = GRID(solved, flags, x, y); if ((oldflags & F_LIGHT) != (solvedflags & F_LIGHT)) len = sprintf(buf, ";L%d,%d", x, y); else if ((oldflags & F_IMPOSSIBLE) != (solvedflags & F_IMPOSSIBLE)) len = sprintf(buf, ";I%d,%d", x, y); if (len) { if (movelen + len >= movesize) { movesize = movelen + len + 256; move = sresize(move, movesize, char); } strcpy(move + movelen, buf); movelen += len; } } } done: free_game(solved); return move; } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } /* 'borrowed' from slant.c, mainly. I could have printed it one * character per cell (like debug_state) but that comes out tiny. * 'L' is used for 'light here' because 'O' looks too much like '0' * (black square with no surrounding lights). */ static char *game_text_format(const game_state *state) { int w = state->w, h = state->h, W = w+1, H = h+1; int x, y, len, lights; unsigned int flags; char *ret, *p; len = (h+H) * (w+W+1) + 1; ret = snewn(len, char); p = ret; for (y = 0; y < H; y++) { for (x = 0; x < W; x++) { *p++ = '+'; if (x < w) *p++ = '-'; } *p++ = '\n'; if (y < h) { for (x = 0; x < W; x++) { *p++ = '|'; if (x < w) { /* actual interesting bit. */ flags = GRID(state, flags, x, y); lights = GRID(state, lights, x, y); if (flags & F_BLACK) { if (flags & F_NUMBERED) *p++ = '0' + lights; else *p++ = '#'; } else { if (flags & F_LIGHT) *p++ = 'L'; else if (flags & F_IMPOSSIBLE) *p++ = 'x'; else if (lights > 0) *p++ = '.'; else *p++ = ' '; } } } *p++ = '\n'; } } *p++ = '\0'; assert(p - ret == len); return ret; } struct game_ui { int cur_x, cur_y, cur_visible; }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->cur_x = ui->cur_y = ui->cur_visible = 0; return ui; } static void free_ui(game_ui *ui) { sfree(ui); } static char *encode_ui(const game_ui *ui) { /* nothing to encode. */ return NULL; } static void decode_ui(game_ui *ui, const char *encoding) { /* nothing to decode. */ } static void game_changed_state(game_ui *ui, const game_state *oldstate, const game_state *newstate) { if (newstate->completed) ui->cur_visible = 0; } #define DF_BLACK 1 /* black square */ #define DF_NUMBERED 2 /* black square with number */ #define DF_LIT 4 /* display (white) square lit up */ #define DF_LIGHT 8 /* display light in square */ #define DF_OVERLAP 16 /* display light as overlapped */ #define DF_CURSOR 32 /* display cursor */ #define DF_NUMBERWRONG 64 /* display black numbered square as error. */ #define DF_FLASH 128 /* background flash is on. */ #define DF_IMPOSSIBLE 256 /* display non-light little square */ struct game_drawstate { int tilesize, crad; int w, h; unsigned int *flags; /* width * height */ int started; }; /* Believe it or not, this empty = "" hack is needed to get around a bug in * the prc-tools gcc when optimisation is turned on; before, it produced: lightup-sect.c: In function `interpret_move': lightup-sect.c:1416: internal error--unrecognizable insn: (insn 582 580 583 (set (reg:SI 134) (pc)) -1 (nil) (nil)) */ static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { enum { NONE, FLIP_LIGHT, FLIP_IMPOSSIBLE } action = NONE; int cx = -1, cy = -1; unsigned int flags; char buf[80], *nullret = NULL, *empty = "", c; if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { if (ui->cur_visible) nullret = empty; ui->cur_visible = 0; cx = FROMCOORD(x); cy = FROMCOORD(y); action = (button == LEFT_BUTTON) ? FLIP_LIGHT : FLIP_IMPOSSIBLE; } else if (IS_CURSOR_SELECT(button) || button == 'i' || button == 'I' || button == ' ' || button == '\r' || button == '\n') { if (ui->cur_visible) { /* Only allow cursor-effect operations if the cursor is visible * (otherwise you have no idea which square it might be affecting) */ cx = ui->cur_x; cy = ui->cur_y; action = (button == 'i' || button == 'I' || button == CURSOR_SELECT2) ? FLIP_IMPOSSIBLE : FLIP_LIGHT; } ui->cur_visible = 1; } else if (IS_CURSOR_MOVE(button)) { move_cursor(button, &ui->cur_x, &ui->cur_y, state->w, state->h, 0); ui->cur_visible = 1; nullret = empty; } else return NULL; switch (action) { case FLIP_LIGHT: case FLIP_IMPOSSIBLE: if (cx < 0 || cy < 0 || cx >= state->w || cy >= state->h) return nullret; flags = GRID(state, flags, cx, cy); if (flags & F_BLACK) return nullret; if (action == FLIP_LIGHT) { #ifdef STYLUS_BASED if (flags & F_IMPOSSIBLE || flags & F_LIGHT) c = 'I'; else c = 'L'; #else if (flags & F_IMPOSSIBLE) return nullret; c = 'L'; #endif } else { #ifdef STYLUS_BASED if (flags & F_IMPOSSIBLE || flags & F_LIGHT) c = 'L'; else c = 'I'; #else if (flags & F_LIGHT) return nullret; c = 'I'; #endif } sprintf(buf, "%c%d,%d", (int)c, cx, cy); break; case NONE: return nullret; default: assert(!"Shouldn't get here!"); } return dupstr(buf); } static game_state *execute_move(const game_state *state, const char *move) { game_state *ret = dup_game(state); int x, y, n, flags; char c; if (!*move) goto badmove; while (*move) { c = *move; if (c == 'S') { ret->used_solve = TRUE; move++; } else if (c == 'L' || c == 'I') { move++; if (sscanf(move, "%d,%d%n", &x, &y, &n) != 2 || x < 0 || y < 0 || x >= ret->w || y >= ret->h) goto badmove; flags = GRID(ret, flags, x, y); if (flags & F_BLACK) goto badmove; /* LIGHT and IMPOSSIBLE are mutually exclusive. */ if (c == 'L') { GRID(ret, flags, x, y) &= ~F_IMPOSSIBLE; set_light(ret, x, y, (flags & F_LIGHT) ? 0 : 1); } else { set_light(ret, x, y, 0); GRID(ret, flags, x, y) ^= F_IMPOSSIBLE; } move += n; } else goto badmove; if (*move == ';') move++; else if (*move) goto badmove; } if (grid_correct(ret)) ret->completed = 1; return ret; badmove: free_game(ret); return NULL; } /* ---------------------------------------------------------------------- * Drawing routines. */ /* XXX entirely cloned from fifteen.c; separate out? */ 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 = TILE_SIZE * params->w + 2 * BORDER; *y = TILE_SIZE * params->h + 2 * BORDER; } static void game_set_size(drawing *dr, game_drawstate *ds, const game_params *params, int tilesize) { ds->tilesize = tilesize; ds->crad = 3*(tilesize-1)/8; } static float *game_colours(frontend *fe, int *ncolours) { float *ret = snewn(3 * NCOLOURS, float); int i; frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); for (i = 0; i < 3; i++) { ret[COL_BLACK * 3 + i] = 0.0F; ret[COL_LIGHT * 3 + i] = 1.0F; ret[COL_CURSOR * 3 + i] = ret[COL_BACKGROUND * 3 + i] / 2.0F; ret[COL_GRID * 3 + i] = ret[COL_BACKGROUND * 3 + i] / 1.5F; } ret[COL_ERROR * 3 + 0] = 1.0F; ret[COL_ERROR * 3 + 1] = 0.25F; ret[COL_ERROR * 3 + 2] = 0.25F; ret[COL_LIT * 3 + 0] = 1.0F; ret[COL_LIT * 3 + 1] = 1.0F; ret[COL_LIT * 3 + 2] = 0.0F; *ncolours = NCOLOURS; return ret; } static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { struct game_drawstate *ds = snew(struct game_drawstate); int i; ds->tilesize = ds->crad = 0; ds->w = state->w; ds->h = state->h; ds->flags = snewn(ds->w*ds->h, unsigned int); for (i = 0; i < ds->w*ds->h; i++) ds->flags[i] = -1; ds->started = 0; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->flags); sfree(ds); } /* At some stage we should put these into a real options struct. * Note that tile_redraw has no #ifdeffery; it relies on tile_flags not * to put those flags in. */ #define HINT_LIGHTS #define HINT_OVERLAPS #define HINT_NUMBERS static unsigned int tile_flags(game_drawstate *ds, const game_state *state, const game_ui *ui, int x, int y, int flashing) { unsigned int flags = GRID(state, flags, x, y); int lights = GRID(state, lights, x, y); unsigned int ret = 0; if (flashing) ret |= DF_FLASH; if (ui && ui->cur_visible && x == ui->cur_x && y == ui->cur_y) ret |= DF_CURSOR; if (flags & F_BLACK) { ret |= DF_BLACK; if (flags & F_NUMBERED) { #ifdef HINT_NUMBERS if (number_wrong(state, x, y)) ret |= DF_NUMBERWRONG; #endif ret |= DF_NUMBERED; } } else { #ifdef HINT_LIGHTS if (lights > 0) ret |= DF_LIT; #endif if (flags & F_LIGHT) { ret |= DF_LIGHT; #ifdef HINT_OVERLAPS if (lights > 1) ret |= DF_OVERLAP; #endif } if (flags & F_IMPOSSIBLE) ret |= DF_IMPOSSIBLE; } return ret; } static void tile_redraw(drawing *dr, game_drawstate *ds, const game_state *state, int x, int y) { unsigned int ds_flags = GRID(ds, flags, x, y); int dx = COORD(x), dy = COORD(y); int lit = (ds_flags & DF_FLASH) ? COL_GRID : COL_LIT; if (ds_flags & DF_BLACK) { draw_rect(dr, dx, dy, TILE_SIZE, TILE_SIZE, COL_BLACK); if (ds_flags & DF_NUMBERED) { int ccol = (ds_flags & DF_NUMBERWRONG) ? COL_ERROR : COL_LIGHT; char str[32]; /* We know that this won't change over the course of the game * so it's OK to ignore this when calculating whether or not * to redraw the tile. */ sprintf(str, "%d", GRID(state, lights, x, y)); draw_text(dr, dx + TILE_SIZE/2, dy + TILE_SIZE/2, FONT_VARIABLE, TILE_SIZE*3/5, ALIGN_VCENTRE | ALIGN_HCENTRE, ccol, str); } } else { draw_rect(dr, dx, dy, TILE_SIZE, TILE_SIZE, (ds_flags & DF_LIT) ? lit : COL_BACKGROUND); draw_rect_outline(dr, dx, dy, TILE_SIZE, TILE_SIZE, COL_GRID); if (ds_flags & DF_LIGHT) { int lcol = (ds_flags & DF_OVERLAP) ? COL_ERROR : COL_LIGHT; draw_circle(dr, dx + TILE_SIZE/2, dy + TILE_SIZE/2, TILE_RADIUS, lcol, COL_BLACK); } else if ((ds_flags & DF_IMPOSSIBLE)) { static int draw_blobs_when_lit = -1; if (draw_blobs_when_lit < 0) { char *env = getenv("LIGHTUP_LIT_BLOBS"); draw_blobs_when_lit = (!env || (env[0] == 'y' || env[0] == 'Y')); } if (!(ds_flags & DF_LIT) || draw_blobs_when_lit) { int rlen = TILE_SIZE / 4; draw_rect(dr, dx + TILE_SIZE/2 - rlen/2, dy + TILE_SIZE/2 - rlen/2, rlen, rlen, COL_BLACK); } } } if (ds_flags & DF_CURSOR) { int coff = TILE_SIZE/8; draw_rect_outline(dr, dx + coff, dy + coff, TILE_SIZE - coff*2, TILE_SIZE - coff*2, COL_CURSOR); } draw_update(dr, dx, dy, TILE_SIZE, TILE_SIZE); } 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 flashing = FALSE; int x,y; if (flashtime) flashing = (int)(flashtime * 3 / FLASH_TIME) != 1; if (!ds->started) { draw_rect(dr, 0, 0, TILE_SIZE * ds->w + 2 * BORDER, TILE_SIZE * ds->h + 2 * BORDER, COL_BACKGROUND); draw_rect_outline(dr, COORD(0)-1, COORD(0)-1, TILE_SIZE * ds->w + 2, TILE_SIZE * ds->h + 2, COL_GRID); draw_update(dr, 0, 0, TILE_SIZE * ds->w + 2 * BORDER, TILE_SIZE * ds->h + 2 * BORDER); ds->started = 1; } for (x = 0; x < ds->w; x++) { for (y = 0; y < ds->h; y++) { unsigned int ds_flags = tile_flags(ds, state, ui, x, y, flashing); if (ds_flags != GRID(ds, flags, x, y)) { GRID(ds, flags, x, y) = ds_flags; tile_redraw(dr, ds, state, x, y); } } } } 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->used_solve && !newstate->used_solve) return FLASH_TIME; return 0.0F; } static int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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) { int pw, ph; /* * I'll use 6mm squares by default. */ game_compute_size(params, 600, &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->w, h = state->h; int ink = print_mono_colour(dr, 0); int paper = print_mono_colour(dr, 1); 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, TILE_SIZE / 16); draw_rect_outline(dr, COORD(0), COORD(0), TILE_SIZE * w, TILE_SIZE * h, ink); /* * Grid. */ print_line_width(dr, TILE_SIZE / 24); for (x = 1; x < w; x++) draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), ink); for (y = 1; y < h; y++) draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), ink); /* * Grid contents. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) { unsigned int ds_flags = tile_flags(ds, state, NULL, x, y, FALSE); int dx = COORD(x), dy = COORD(y); if (ds_flags & DF_BLACK) { draw_rect(dr, dx, dy, TILE_SIZE, TILE_SIZE, ink); if (ds_flags & DF_NUMBERED) { char str[32]; sprintf(str, "%d", GRID(state, lights, x, y)); draw_text(dr, dx + TILE_SIZE/2, dy + TILE_SIZE/2, FONT_VARIABLE, TILE_SIZE*3/5, ALIGN_VCENTRE | ALIGN_HCENTRE, paper, str); } } else if (ds_flags & DF_LIGHT) { draw_circle(dr, dx + TILE_SIZE/2, dy + TILE_SIZE/2, TILE_RADIUS, -1, ink); } } } #ifdef COMBINED #define thegame lightup #endif const struct game thegame = { "Light Up", "games.lightup", "lightup", default_params, game_fetch_preset, 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, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, 0, /* flags */ }; #ifdef STANDALONE_SOLVER int main(int argc, char **argv) { game_params *p; game_state *s; char *id = NULL, *desc, *err, *result; int nsol, diff, really_verbose = 0; unsigned int sflags; while (--argc > 0) { char *p = *++argv; if (!strcmp(p, "-v")) { really_verbose++; } else if (*p == '-') { fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p); return 1; } else { id = p; } } if (!id) { fprintf(stderr, "usage: %s [-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); /* Run the solvers easiest to hardest until we find one that * can solve our puzzle. If it's soluble we know that the * hardest (recursive) solver will always find the solution. */ nsol = sflags = 0; for (diff = 0; diff <= DIFFCOUNT; diff++) { printf("\nSolving with difficulty %d.\n", diff); sflags = flags_from_difficulty(diff); unplace_lights(s); nsol = dosolve(s, sflags, NULL); if (nsol == 1) break; } printf("\n"); if (nsol == 0) { printf("Puzzle has no solution.\n"); } else if (nsol < 0) { printf("Unable to find a unique solution.\n"); } else if (nsol > 1) { printf("Puzzle has multiple solutions.\n"); } else { verbose = really_verbose; unplace_lights(s); printf("Puzzle has difficulty %d: solving...\n", diff); dosolve(s, sflags, NULL); /* sflags from last successful solve */ result = game_text_format(s); printf("%s", result); sfree(result); } return 0; } #endif /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/list.c0000644000175300017530000000203212161170560013540 0ustar simonsimon/* * 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) \ A(blackbox) \ A(bridges) \ A(cube) \ A(dominosa) \ A(fifteen) \ A(filling) \ A(flip) \ A(galaxies) \ A(guess) \ A(inertia) \ A(keen) \ A(lightup) \ A(loopy) \ A(magnets) \ A(map) \ A(mines) \ A(net) \ A(netslide) \ A(pattern) \ A(pearl) \ A(pegs) \ A(range) \ A(rect) \ A(samegame) \ A(signpost) \ A(singles) \ A(sixteen) \ A(slant) \ A(solo) \ A(tents) \ A(towers) \ A(twiddle) \ A(undead) \ A(unequal) \ A(unruly) \ A(untangle) \ #define DECL(x) extern const game x; #define REF(x) &x, GAMELIST(DECL) const game *gamelist[] = { GAMELIST(REF) }; const int gamecount = lenof(gamelist); puzzles-r9872/loopgen.c0000644000175300017530000005304411707014702014241 0ustar simonsimon/* * loopgen.c: loop generation functions for grid.[ch]. */ #include #include #include #include #include #include #include #include "puzzles.h" #include "tree234.h" #include "grid.h" #include "loopgen.h" /* We're going to store lists of current candidate faces for colouring black * or white. * Each face gets a 'score', which tells us how adding that face right * now would affect the curliness of the solution loop. We're trying to * maximise that quantity so will bias our random selection of faces to * colour those with high scores */ struct face_score { int white_score; int black_score; unsigned long random; /* No need to store a grid_face* here. The 'face_scores' array will * be a list of 'face_score' objects, one for each face of the grid, so * the position (index) within the 'face_scores' array will determine * which face corresponds to a particular face_score. * Having a single 'face_scores' array for all faces simplifies memory * management, and probably improves performance, because we don't have to * malloc/free each individual face_score, and we don't have to maintain * a mapping from grid_face* pointers to face_score* pointers. */ }; static int generic_sort_cmpfn(void *v1, void *v2, size_t offset) { struct face_score *f1 = v1; struct face_score *f2 = v2; int r; r = *(int *)((char *)f2 + offset) - *(int *)((char *)f1 + offset); if (r) { return r; } if (f1->random < f2->random) return -1; else if (f1->random > f2->random) return 1; /* * It's _just_ possible that two faces might have been given * the same random value. In that situation, fall back to * comparing based on the positions within the face_scores list. * This introduces a tiny directional bias, but not a significant one. */ return f1 - f2; } static int white_sort_cmpfn(void *v1, void *v2) { return generic_sort_cmpfn(v1, v2, offsetof(struct face_score,white_score)); } static int black_sort_cmpfn(void *v1, void *v2) { return generic_sort_cmpfn(v1, v2, offsetof(struct face_score,black_score)); } /* 'board' is an array of enum face_colour, indicating which faces are * currently black/white/grey. 'colour' is FACE_WHITE or FACE_BLACK. * Returns whether it's legal to colour the given face with this colour. */ static int can_colour_face(grid *g, char* board, int face_index, enum face_colour colour) { int i, j; grid_face *test_face = g->faces + face_index; grid_face *starting_face, *current_face; grid_dot *starting_dot; int transitions; int current_state, s; /* booleans: equal or not-equal to 'colour' */ int found_same_coloured_neighbour = FALSE; assert(board[face_index] != colour); /* Can only consider a face for colouring if it's adjacent to a face * with the same colour. */ for (i = 0; i < test_face->order; i++) { grid_edge *e = test_face->edges[i]; grid_face *f = (e->face1 == test_face) ? e->face2 : e->face1; if (FACE_COLOUR(f) == colour) { found_same_coloured_neighbour = TRUE; break; } } if (!found_same_coloured_neighbour) return FALSE; /* Need to avoid creating a loop of faces of this colour around some * differently-coloured faces. * Also need to avoid meeting a same-coloured face at a corner, with * other-coloured faces in between. Here's a simple test that (I believe) * takes care of both these conditions: * * Take the circular path formed by this face's edges, and inflate it * slightly outwards. Imagine walking around this path and consider * the faces that you visit in sequence. This will include all faces * touching the given face, either along an edge or just at a corner. * Count the number of 'colour'/not-'colour' transitions you encounter, as * you walk along the complete loop. This will obviously turn out to be * an even number. * If 0, we're either in the middle of an "island" of this colour (should * be impossible as we're not supposed to create black or white loops), * or we're about to start a new island - also not allowed. * If 4 or greater, there are too many separate coloured regions touching * this face, and colouring it would create a loop or a corner-violation. * The only allowed case is when the count is exactly 2. */ /* i points to a dot around the test face. * j points to a face around the i^th dot. * The current face will always be: * test_face->dots[i]->faces[j] * We assume dots go clockwise around the test face, * and faces go clockwise around dots. */ /* * The end condition is slightly fiddly. In sufficiently strange * degenerate grids, our test face may be adjacent to the same * other face multiple times (typically if it's the exterior * face). Consider this, in particular: * * +--+ * | | * +--+--+ * | | | * +--+--+ * * The bottom left face there is adjacent to the exterior face * twice, so we can't just terminate our iteration when we reach * the same _face_ we started at. Furthermore, we can't * condition on having the same (i,j) pair either, because * several (i,j) pairs identify the bottom left contiguity with * the exterior face! We canonicalise the (i,j) pair by taking * one step around before we set the termination tracking. */ i = j = 0; current_face = test_face->dots[0]->faces[0]; if (current_face == test_face) { j = 1; current_face = test_face->dots[0]->faces[1]; } transitions = 0; current_state = (FACE_COLOUR(current_face) == colour); starting_dot = NULL; starting_face = NULL; while (TRUE) { /* Advance to next face. * Need to loop here because it might take several goes to * find it. */ while (TRUE) { j++; if (j == test_face->dots[i]->order) j = 0; if (test_face->dots[i]->faces[j] == test_face) { /* Advance to next dot round test_face, then * find current_face around new dot * and advance to the next face clockwise */ i++; if (i == test_face->order) i = 0; for (j = 0; j < test_face->dots[i]->order; j++) { if (test_face->dots[i]->faces[j] == current_face) break; } /* Must actually find current_face around new dot, * or else something's wrong with the grid. */ assert(j != test_face->dots[i]->order); /* Found, so advance to next face and try again */ } else { break; } } /* (i,j) are now advanced to next face */ current_face = test_face->dots[i]->faces[j]; s = (FACE_COLOUR(current_face) == colour); if (!starting_dot) { starting_dot = test_face->dots[i]; starting_face = current_face; current_state = s; } else { if (s != current_state) { ++transitions; current_state = s; if (transitions > 2) break; } if (test_face->dots[i] == starting_dot && current_face == starting_face) break; } } return (transitions == 2) ? TRUE : FALSE; } /* Count the number of neighbours of 'face', having colour 'colour' */ static int face_num_neighbours(grid *g, char *board, grid_face *face, enum face_colour colour) { int colour_count = 0; int i; grid_face *f; grid_edge *e; for (i = 0; i < face->order; i++) { e = face->edges[i]; f = (e->face1 == face) ? e->face2 : e->face1; if (FACE_COLOUR(f) == colour) ++colour_count; } return colour_count; } /* The 'score' of a face reflects its current desirability for selection * as the next face to colour white or black. We want to encourage moving * into grey areas and increasing loopiness, so we give scores according to * how many of the face's neighbours are currently coloured the same as the * proposed colour. */ static int face_score(grid *g, char *board, grid_face *face, enum face_colour colour) { /* Simple formula: score = 0 - num. same-coloured neighbours, * so a higher score means fewer same-coloured neighbours. */ return -face_num_neighbours(g, board, face, colour); } /* * Generate a new complete random closed loop for the given grid. * * The method is to generate a WHITE/BLACK colouring of all the faces, * such that the WHITE faces will define the inside of the path, and the * BLACK faces define the outside. * To do this, we initially colour all faces GREY. The infinite space outside * the grid is coloured BLACK, and we choose a random face to colour WHITE. * Then we gradually grow the BLACK and the WHITE regions, eliminating GREY * faces, until the grid is filled with BLACK/WHITE. As we grow the regions, * we avoid creating loops of a single colour, to preserve the topological * shape of the WHITE and BLACK regions. * We also try to make the boundary as loopy and twisty as possible, to avoid * generating paths that are uninteresting. * The algorithm works by choosing a BLACK/WHITE colour, then choosing a GREY * face that can be coloured with that colour (without violating the * topological shape of that region). It's not obvious, but I think this * algorithm is guaranteed to terminate without leaving any GREY faces behind. * Indeed, if there are any GREY faces at all, both the WHITE and BLACK * regions can be grown. * This is checked using assert()ions, and I haven't seen any failures yet. * * Hand-wavy proof: imagine what can go wrong... * * Could the white faces get completely cut off by the black faces, and still * leave some grey faces remaining? * No, because then the black faces would form a loop around both the white * faces and the grey faces, which is disallowed because we continually * maintain the correct topological shape of the black region. * Similarly, the black faces can never get cut off by the white faces. That * means both the WHITE and BLACK regions always have some room to grow into * the GREY regions. * Could it be that we can't colour some GREY face, because there are too many * WHITE/BLACK transitions as we walk round the face? (see the * can_colour_face() function for details) * No. Imagine otherwise, and we see WHITE/BLACK/WHITE/BLACK as we walk * around the face. The two WHITE faces would be connected by a WHITE path, * and the BLACK faces would be connected by a BLACK path. These paths would * have to cross, which is impossible. * Another thing that could go wrong: perhaps we can't find any GREY face to * colour WHITE, because it would create a loop-violation or a corner-violation * with the other WHITE faces? * This is a little bit tricky to prove impossible. Imagine you have such a * GREY face (that is, if you coloured it WHITE, you would create a WHITE loop * or corner violation). * That would cut all the non-white area into two blobs. One of those blobs * must be free of BLACK faces (because the BLACK stuff is a connected blob). * So we have a connected GREY area, completely surrounded by WHITE * (including the GREY face we've tentatively coloured WHITE). * A well-known result in graph theory says that you can always find a GREY * face whose removal leaves the remaining GREY area connected. And it says * there are at least two such faces, so we can always choose the one that * isn't the "tentative" GREY face. Colouring that face WHITE leaves * everything nice and connected, including that "tentative" GREY face which * acts as a gateway to the rest of the non-WHITE grid. */ void generate_loop(grid *g, char *board, random_state *rs, loopgen_bias_fn_t bias, void *biasctx) { int i, j; int num_faces = g->num_faces; struct face_score *face_scores; /* Array of face_score objects */ struct face_score *fs; /* Points somewhere in the above list */ struct grid_face *cur_face; tree234 *lightable_faces_sorted; tree234 *darkable_faces_sorted; int *face_list; int do_random_pass; /* Make a board */ memset(board, FACE_GREY, num_faces); /* Create and initialise the list of face_scores */ face_scores = snewn(num_faces, struct face_score); for (i = 0; i < num_faces; i++) { face_scores[i].random = random_bits(rs, 31); face_scores[i].black_score = face_scores[i].white_score = 0; } /* Colour a random, finite face white. The infinite face is implicitly * coloured black. Together, they will seed the random growth process * for the black and white areas. */ i = random_upto(rs, num_faces); board[i] = FACE_WHITE; /* We need a way of favouring faces that will increase our loopiness. * We do this by maintaining a list of all candidate faces sorted by * their score and choose randomly from that with appropriate skew. * In order to avoid consistently biasing towards particular faces, we * need the sort order _within_ each group of scores to be completely * random. But it would be abusing the hospitality of the tree234 data * structure if our comparison function were nondeterministic :-). So with * each face we associate a random number that does not change during a * particular run of the generator, and use that as a secondary sort key. * Yes, this means we will be biased towards particular random faces in * any one run but that doesn't actually matter. */ lightable_faces_sorted = newtree234(white_sort_cmpfn); darkable_faces_sorted = newtree234(black_sort_cmpfn); /* Initialise the lists of lightable and darkable faces. This is * slightly different from the code inside the while-loop, because we need * to check every face of the board (the grid structure does not keep a * list of the infinite face's neighbours). */ for (i = 0; i < num_faces; i++) { grid_face *f = g->faces + i; struct face_score *fs = face_scores + i; if (board[i] != FACE_GREY) continue; /* We need the full colourability check here, it's not enough simply * to check neighbourhood. On some grids, a neighbour of the infinite * face is not necessarily darkable. */ if (can_colour_face(g, board, i, FACE_BLACK)) { fs->black_score = face_score(g, board, f, FACE_BLACK); add234(darkable_faces_sorted, fs); } if (can_colour_face(g, board, i, FACE_WHITE)) { fs->white_score = face_score(g, board, f, FACE_WHITE); add234(lightable_faces_sorted, fs); } } /* Colour faces one at a time until no more faces are colourable. */ while (TRUE) { enum face_colour colour; tree234 *faces_to_pick; int c_lightable = count234(lightable_faces_sorted); int c_darkable = count234(darkable_faces_sorted); if (c_lightable == 0 && c_darkable == 0) { /* No more faces we can use at all. */ break; } assert(c_lightable != 0 && c_darkable != 0); /* Choose a colour, and colour the best available face * with that colour. */ colour = random_upto(rs, 2) ? FACE_WHITE : FACE_BLACK; if (colour == FACE_WHITE) faces_to_pick = lightable_faces_sorted; else faces_to_pick = darkable_faces_sorted; if (bias) { /* * Go through all the candidate faces and pick the one the * bias function likes best, breaking ties using the * ordering in our tree234 (which is why we replace only * if score > bestscore, not >=). */ int j, k; struct face_score *best = NULL; int score, bestscore = 0; for (j = 0; (fs = (struct face_score *)index234(faces_to_pick, j))!=NULL; j++) { assert(fs); k = fs - face_scores; assert(board[k] == FACE_GREY); board[k] = colour; score = bias(biasctx, board, k); board[k] = FACE_GREY; bias(biasctx, board, k); /* let bias know we put it back */ if (!best || score > bestscore) { bestscore = score; best = fs; } } fs = best; } else { fs = (struct face_score *)index234(faces_to_pick, 0); } assert(fs); i = fs - face_scores; assert(board[i] == FACE_GREY); board[i] = colour; if (bias) bias(biasctx, board, i); /* notify bias function of the change */ /* Remove this newly-coloured face from the lists. These lists should * only contain grey faces. */ del234(lightable_faces_sorted, fs); del234(darkable_faces_sorted, fs); /* Remember which face we've just coloured */ cur_face = g->faces + i; /* The face we've just coloured potentially affects the colourability * and the scores of any neighbouring faces (touching at a corner or * edge). So the search needs to be conducted around all faces * touching the one we've just lit. Iterate over its corners, then * over each corner's faces. For each such face, we remove it from * the lists, recalculate any scores, then add it back to the lists * (depending on whether it is lightable, darkable or both). */ for (i = 0; i < cur_face->order; i++) { grid_dot *d = cur_face->dots[i]; for (j = 0; j < d->order; j++) { grid_face *f = d->faces[j]; int fi; /* face index of f */ if (f == NULL) continue; if (f == cur_face) continue; /* If the face is already coloured, it won't be on our * lightable/darkable lists anyway, so we can skip it without * bothering with the removal step. */ if (FACE_COLOUR(f) != FACE_GREY) continue; /* Find the face index and face_score* corresponding to f */ fi = f - g->faces; fs = face_scores + fi; /* Remove from lightable list if it's in there. We do this, * even if it is still lightable, because the score might * be different, and we need to remove-then-add to maintain * correct sort order. */ del234(lightable_faces_sorted, fs); if (can_colour_face(g, board, fi, FACE_WHITE)) { fs->white_score = face_score(g, board, f, FACE_WHITE); add234(lightable_faces_sorted, fs); } /* Do the same for darkable list. */ del234(darkable_faces_sorted, fs); if (can_colour_face(g, board, fi, FACE_BLACK)) { fs->black_score = face_score(g, board, f, FACE_BLACK); add234(darkable_faces_sorted, fs); } } } } /* Clean up */ freetree234(lightable_faces_sorted); freetree234(darkable_faces_sorted); sfree(face_scores); /* The next step requires a shuffled list of all faces */ face_list = snewn(num_faces, int); for (i = 0; i < num_faces; ++i) { face_list[i] = i; } shuffle(face_list, num_faces, sizeof(int), rs); /* The above loop-generation algorithm can often leave large clumps * of faces of one colour. In extreme cases, the resulting path can be * degenerate and not very satisfying to solve. * This next step alleviates this problem: * Go through the shuffled list, and flip the colour of any face we can * legally flip, and which is adjacent to only one face of the opposite * colour - this tends to grow 'tendrils' into any clumps. * Repeat until we can find no more faces to flip. This will * eventually terminate, because each flip increases the loop's * perimeter, which cannot increase for ever. * The resulting path will have maximal loopiness (in the sense that it * cannot be improved "locally". Unfortunately, this allows a player to * make some illicit deductions. To combat this (and make the path more * interesting), we do one final pass making random flips. */ /* Set to TRUE for final pass */ do_random_pass = FALSE; while (TRUE) { /* Remember whether a flip occurred during this pass */ int flipped = FALSE; for (i = 0; i < num_faces; ++i) { int j = face_list[i]; enum face_colour opp = (board[j] == FACE_WHITE) ? FACE_BLACK : FACE_WHITE; if (can_colour_face(g, board, j, opp)) { grid_face *face = g->faces +j; if (do_random_pass) { /* final random pass */ if (!random_upto(rs, 10)) board[j] = opp; } else { /* normal pass - flip when neighbour count is 1 */ if (face_num_neighbours(g, board, face, opp) == 1) { board[j] = opp; flipped = TRUE; } } } } if (do_random_pass) break; if (!flipped) do_random_pass = TRUE; } sfree(face_list); } puzzles-r9872/loopy.c0000644000175300017530000033734712132232554013753 0ustar simonsimon/* * loopy.c: * * An implementation of the Nikoli game 'Loop the loop'. * (c) Mike Pinna, 2005, 2006 * Substantially rewritten to allowing for more general types of grid. * (c) Lambros Lambrou 2008 * * vim: set shiftwidth=4 :set textwidth=80: */ /* * Possible future solver enhancements: * * - There's an interesting deductive technique which makes use * of topology rather than just graph theory. Each _face_ in * the grid is either inside or outside the loop; you can tell * that two faces are on the same side of the loop if they're * separated by a LINE_NO (or, more generally, by a path * crossing no LINE_UNKNOWNs and an even number of LINE_YESes), * and on the opposite side of the loop if they're separated by * a LINE_YES (or an odd number of LINE_YESes and no * LINE_UNKNOWNs). Oh, and any face separated from the outside * of the grid by a LINE_YES or a LINE_NO is on the inside or * outside respectively. So if you can track this for all * faces, you figure out the state of the line between a pair * once their relative insideness is known. * + The way I envisage this working is simply to keep an edsf * of all _faces_, which indicates whether they're on * opposite sides of the loop from one another. We also * include a special entry in the edsf for the infinite * exterior "face". * + So, the simple way to do this is to just go through the * edges: every time we see an edge in a state other than * LINE_UNKNOWN which separates two faces that aren't in the * same edsf class, we can rectify that by merging the * classes. Then, conversely, an edge in LINE_UNKNOWN state * which separates two faces that _are_ in the same edsf * class can immediately have its state determined. * + But you can go one better, if you're prepared to loop * over all _pairs_ of edges. Suppose we have edges A and B, * which respectively separate faces A1,A2 and B1,B2. * Suppose that A,B are in the same edge-edsf class and that * A1,B1 (wlog) are in the same face-edsf class; then we can * immediately place A2,B2 into the same face-edsf class (as * each other, not as A1 and A2) one way round or the other. * And conversely again, if A1,B1 are in the same face-edsf * class and so are A2,B2, then we can put A,B into the same * face-edsf class. * * Of course, this deduction requires a quadratic-time * loop over all pairs of edges in the grid, so it should * be reserved until there's nothing easier left to be * done. * * - The generalised grid support has made me (SGT) notice a * possible extension to the loop-avoidance code. When you have * a path of connected edges such that no other edges at all * are incident on any vertex in the middle of the path - or, * alternatively, such that any such edges are already known to * be LINE_NO - then you know those edges are either all * LINE_YES or all LINE_NO. Hence you can mentally merge the * entire path into a single long curly edge for the purposes * of loop avoidance, and look directly at whether or not the * extreme endpoints of the path are connected by some other * route. I find this coming up fairly often when I play on the * octagonal grid setting, so it might be worth implementing in * the solver. * * - (Just a speed optimisation.) Consider some todo list queue where every * time we modify something we mark it for consideration by other bits of * the solver, to save iteration over things that have already been done. */ #include #include #include #include #include #include #include #include "puzzles.h" #include "tree234.h" #include "grid.h" #include "loopgen.h" /* Debugging options */ /* #define DEBUG_CACHES #define SHOW_WORKING #define DEBUG_DLINES */ /* ---------------------------------------------------------------------- * Struct, enum and function declarations */ enum { COL_BACKGROUND, COL_FOREGROUND, COL_LINEUNKNOWN, COL_HIGHLIGHT, COL_MISTAKE, COL_SATISFIED, COL_FAINT, NCOLOURS }; struct game_state { grid *game_grid; /* ref-counted (internally) */ /* Put -1 in a face that doesn't get a clue */ signed char *clues; /* Array of line states, to store whether each line is * YES, NO or UNKNOWN */ char *lines; unsigned char *line_errors; int solved; int cheated; /* Used in game_text_format(), so that it knows what type of * grid it's trying to render as ASCII text. */ int grid_type; }; enum solver_status { SOLVER_SOLVED, /* This is the only solution the solver could find */ SOLVER_MISTAKE, /* This is definitely not a solution */ SOLVER_AMBIGUOUS, /* This _might_ be an ambiguous solution */ SOLVER_INCOMPLETE /* This may be a partial solution */ }; /* ------ Solver state ------ */ typedef struct solver_state { game_state *state; enum solver_status solver_status; /* NB looplen is the number of dots that are joined together at a point, ie a * looplen of 1 means there are no lines to a particular dot */ int *looplen; /* Difficulty level of solver. Used by solver functions that want to * vary their behaviour depending on the requested difficulty level. */ int diff; /* caches */ char *dot_yes_count; char *dot_no_count; char *face_yes_count; char *face_no_count; char *dot_solved, *face_solved; int *dotdsf; /* Information for Normal level deductions: * For each dline, store a bitmask for whether we know: * (bit 0) at least one is YES * (bit 1) at most one is YES */ char *dlines; /* Hard level information */ int *linedsf; } solver_state; /* * 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(EASY,Easy,e) \ A(NORMAL,Normal,n) \ A(TRICKY,Tricky,t) \ A(HARD,Hard,h) #define ENUM(upper,title,lower) DIFF_ ## upper, #define TITLE(upper,title,lower) #title, #define ENCODE(upper,title,lower) #lower #define CONFIG(upper,title,lower) ":" #title enum { DIFFLIST(ENUM) DIFF_MAX }; static char const *const diffnames[] = { DIFFLIST(TITLE) }; static char const diffchars[] = DIFFLIST(ENCODE); #define DIFFCONFIG DIFFLIST(CONFIG) /* * Solver routines, sorted roughly in order of computational cost. * The solver will run the faster deductions first, and slower deductions are * only invoked when the faster deductions are unable to make progress. * Each function is associated with a difficulty level, so that the generated * puzzles are solvable by applying only the functions with the chosen * difficulty level or lower. */ #define SOLVERLIST(A) \ A(trivial_deductions, DIFF_EASY) \ A(dline_deductions, DIFF_NORMAL) \ A(linedsf_deductions, DIFF_HARD) \ A(loop_deductions, DIFF_EASY) #define SOLVER_FN_DECL(fn,diff) static int fn(solver_state *); #define SOLVER_FN(fn,diff) &fn, #define SOLVER_DIFF(fn,diff) diff, SOLVERLIST(SOLVER_FN_DECL) static int (*(solver_fns[]))(solver_state *) = { SOLVERLIST(SOLVER_FN) }; static int const solver_diffs[] = { SOLVERLIST(SOLVER_DIFF) }; static const int NUM_SOLVERS = sizeof(solver_diffs)/sizeof(*solver_diffs); struct game_params { int w, h; int diff; int type; }; /* line_drawstate is the same as line_state, but with the extra ERROR * possibility. The drawing code copies line_state to line_drawstate, * except in the case that the line is an error. */ enum line_state { LINE_YES, LINE_UNKNOWN, LINE_NO }; enum line_drawstate { DS_LINE_YES, DS_LINE_UNKNOWN, DS_LINE_NO, DS_LINE_ERROR }; #define OPP(line_state) \ (2 - line_state) struct game_drawstate { int started; int tilesize; int flashing; int *textx, *texty; char *lines; char *clue_error; char *clue_satisfied; }; static char *validate_desc(const game_params *params, const char *desc); static int dot_order(const game_state* state, int i, char line_type); static int face_order(const game_state* state, int i, char line_type); static solver_state *solve_game_rec(const solver_state *sstate); #ifdef DEBUG_CACHES static void check_caches(const solver_state* sstate); #else #define check_caches(s) #endif /* ------- List of grid generators ------- */ #define GRIDLIST(A) \ A(Squares,GRID_SQUARE,3,3) \ A(Triangular,GRID_TRIANGULAR,3,3) \ A(Honeycomb,GRID_HONEYCOMB,3,3) \ A(Snub-Square,GRID_SNUBSQUARE,3,3) \ A(Cairo,GRID_CAIRO,3,4) \ A(Great-Hexagonal,GRID_GREATHEXAGONAL,3,3) \ A(Octagonal,GRID_OCTAGONAL,3,3) \ A(Kites,GRID_KITE,3,3) \ A(Floret,GRID_FLORET,1,2) \ A(Dodecagonal,GRID_DODECAGONAL,2,2) \ A(Great-Dodecagonal,GRID_GREATDODECAGONAL,2,2) \ A(Penrose (kite/dart),GRID_PENROSE_P2,3,3) \ A(Penrose (rhombs),GRID_PENROSE_P3,3,3) #define GRID_NAME(title,type,amin,omin) #title, #define GRID_CONFIG(title,type,amin,omin) ":" #title #define GRID_TYPE(title,type,amin,omin) type, #define GRID_SIZES(title,type,amin,omin) \ {amin, omin, \ "Width and height for this grid type must both be at least " #amin, \ "At least one of width and height for this grid type must be at least " #omin,}, static char const *const gridnames[] = { GRIDLIST(GRID_NAME) }; #define GRID_CONFIGS GRIDLIST(GRID_CONFIG) static grid_type grid_types[] = { GRIDLIST(GRID_TYPE) }; #define NUM_GRID_TYPES (sizeof(grid_types) / sizeof(grid_types[0])) static const struct { int amin, omin; char *aerr, *oerr; } grid_size_limits[] = { GRIDLIST(GRID_SIZES) }; /* Generates a (dynamically allocated) new grid, according to the * type and size requested in params. Does nothing if the grid is already * generated. */ static grid *loopy_generate_grid(const game_params *params, const char *grid_desc) { return grid_new(grid_types[params->type], params->w, params->h, grid_desc); } /* ---------------------------------------------------------------------- * Preprocessor magic */ /* General constants */ #define PREFERRED_TILE_SIZE 32 #define BORDER(tilesize) ((tilesize) / 2) #define FLASH_TIME 0.5F #define BIT_SET(field, bit) ((field) & (1<<(bit))) #define SET_BIT(field, bit) (BIT_SET(field, bit) ? FALSE : \ ((field) |= (1<<(bit)), TRUE)) #define CLEAR_BIT(field, bit) (BIT_SET(field, bit) ? \ ((field) &= ~(1<<(bit)), TRUE) : FALSE) #define CLUE2CHAR(c) \ ((c < 0) ? ' ' : c < 10 ? c + '0' : c - 10 + 'A') /* ---------------------------------------------------------------------- * General struct manipulation and other straightforward code */ static game_state *dup_game(const game_state *state) { game_state *ret = snew(game_state); ret->game_grid = state->game_grid; ret->game_grid->refcount++; ret->solved = state->solved; ret->cheated = state->cheated; ret->clues = snewn(state->game_grid->num_faces, signed char); memcpy(ret->clues, state->clues, state->game_grid->num_faces); ret->lines = snewn(state->game_grid->num_edges, char); memcpy(ret->lines, state->lines, state->game_grid->num_edges); ret->line_errors = snewn(state->game_grid->num_edges, unsigned char); memcpy(ret->line_errors, state->line_errors, state->game_grid->num_edges); ret->grid_type = state->grid_type; return ret; } static void free_game(game_state *state) { if (state) { grid_free(state->game_grid); sfree(state->clues); sfree(state->lines); sfree(state->line_errors); sfree(state); } } static solver_state *new_solver_state(const game_state *state, int diff) { int i; int num_dots = state->game_grid->num_dots; int num_faces = state->game_grid->num_faces; int num_edges = state->game_grid->num_edges; solver_state *ret = snew(solver_state); ret->state = dup_game(state); ret->solver_status = SOLVER_INCOMPLETE; ret->diff = diff; ret->dotdsf = snew_dsf(num_dots); ret->looplen = snewn(num_dots, int); for (i = 0; i < num_dots; i++) { ret->looplen[i] = 1; } ret->dot_solved = snewn(num_dots, char); ret->face_solved = snewn(num_faces, char); memset(ret->dot_solved, FALSE, num_dots); memset(ret->face_solved, FALSE, num_faces); ret->dot_yes_count = snewn(num_dots, char); memset(ret->dot_yes_count, 0, num_dots); ret->dot_no_count = snewn(num_dots, char); memset(ret->dot_no_count, 0, num_dots); ret->face_yes_count = snewn(num_faces, char); memset(ret->face_yes_count, 0, num_faces); ret->face_no_count = snewn(num_faces, char); memset(ret->face_no_count, 0, num_faces); if (diff < DIFF_NORMAL) { ret->dlines = NULL; } else { ret->dlines = snewn(2*num_edges, char); memset(ret->dlines, 0, 2*num_edges); } if (diff < DIFF_HARD) { ret->linedsf = NULL; } else { ret->linedsf = snew_dsf(state->game_grid->num_edges); } return ret; } static void free_solver_state(solver_state *sstate) { if (sstate) { free_game(sstate->state); sfree(sstate->dotdsf); sfree(sstate->looplen); sfree(sstate->dot_solved); sfree(sstate->face_solved); sfree(sstate->dot_yes_count); sfree(sstate->dot_no_count); sfree(sstate->face_yes_count); sfree(sstate->face_no_count); /* OK, because sfree(NULL) is a no-op */ sfree(sstate->dlines); sfree(sstate->linedsf); sfree(sstate); } } static solver_state *dup_solver_state(const solver_state *sstate) { game_state *state = sstate->state; int num_dots = state->game_grid->num_dots; int num_faces = state->game_grid->num_faces; int num_edges = state->game_grid->num_edges; solver_state *ret = snew(solver_state); ret->state = state = dup_game(sstate->state); ret->solver_status = sstate->solver_status; ret->diff = sstate->diff; ret->dotdsf = snewn(num_dots, int); ret->looplen = snewn(num_dots, int); memcpy(ret->dotdsf, sstate->dotdsf, num_dots * sizeof(int)); memcpy(ret->looplen, sstate->looplen, num_dots * sizeof(int)); ret->dot_solved = snewn(num_dots, char); ret->face_solved = snewn(num_faces, char); memcpy(ret->dot_solved, sstate->dot_solved, num_dots); memcpy(ret->face_solved, sstate->face_solved, num_faces); ret->dot_yes_count = snewn(num_dots, char); memcpy(ret->dot_yes_count, sstate->dot_yes_count, num_dots); ret->dot_no_count = snewn(num_dots, char); memcpy(ret->dot_no_count, sstate->dot_no_count, num_dots); ret->face_yes_count = snewn(num_faces, char); memcpy(ret->face_yes_count, sstate->face_yes_count, num_faces); ret->face_no_count = snewn(num_faces, char); memcpy(ret->face_no_count, sstate->face_no_count, num_faces); if (sstate->dlines) { ret->dlines = snewn(2*num_edges, char); memcpy(ret->dlines, sstate->dlines, 2*num_edges); } else { ret->dlines = NULL; } if (sstate->linedsf) { ret->linedsf = snewn(num_edges, int); memcpy(ret->linedsf, sstate->linedsf, num_edges * sizeof(int)); } else { ret->linedsf = NULL; } return ret; } static game_params *default_params(void) { game_params *ret = snew(game_params); #ifdef SLOW_SYSTEM ret->h = 7; ret->w = 7; #else ret->h = 10; ret->w = 10; #endif ret->diff = DIFF_EASY; ret->type = 0; return ret; } static game_params *dup_params(const game_params *params) { game_params *ret = snew(game_params); *ret = *params; /* structure copy */ return ret; } static const game_params presets[] = { #ifdef SMALL_SCREEN { 7, 7, DIFF_EASY, 0 }, { 7, 7, DIFF_NORMAL, 0 }, { 7, 7, DIFF_HARD, 0 }, { 7, 7, DIFF_HARD, 1 }, { 7, 7, DIFF_HARD, 2 }, { 5, 5, DIFF_HARD, 3 }, { 7, 7, DIFF_HARD, 4 }, { 5, 4, DIFF_HARD, 5 }, { 5, 5, DIFF_HARD, 6 }, { 5, 5, DIFF_HARD, 7 }, { 3, 3, DIFF_HARD, 8 }, { 3, 3, DIFF_HARD, 9 }, { 3, 3, DIFF_HARD, 10 }, { 6, 6, DIFF_HARD, 11 }, { 6, 6, DIFF_HARD, 12 }, #else { 7, 7, DIFF_EASY, 0 }, { 10, 10, DIFF_EASY, 0 }, { 7, 7, DIFF_NORMAL, 0 }, { 10, 10, DIFF_NORMAL, 0 }, { 7, 7, DIFF_HARD, 0 }, { 10, 10, DIFF_HARD, 0 }, { 10, 10, DIFF_HARD, 1 }, { 12, 10, DIFF_HARD, 2 }, { 7, 7, DIFF_HARD, 3 }, { 9, 9, DIFF_HARD, 4 }, { 5, 4, DIFF_HARD, 5 }, { 7, 7, DIFF_HARD, 6 }, { 5, 5, DIFF_HARD, 7 }, { 5, 5, DIFF_HARD, 8 }, { 5, 4, DIFF_HARD, 9 }, { 5, 4, DIFF_HARD, 10 }, { 10, 10, DIFF_HARD, 11 }, { 10, 10, DIFF_HARD, 12 } #endif }; static int game_fetch_preset(int i, char **name, game_params **params) { game_params *tmppar; char buf[80]; if (i < 0 || i >= lenof(presets)) return FALSE; tmppar = snew(game_params); *tmppar = presets[i]; *params = tmppar; sprintf(buf, "%dx%d %s - %s", tmppar->h, tmppar->w, gridnames[tmppar->type], diffnames[tmppar->diff]); *name = dupstr(buf); return TRUE; } static void free_params(game_params *params) { sfree(params); } static void decode_params(game_params *params, char const *string) { params->h = params->w = atoi(string); params->diff = DIFF_EASY; while (*string && isdigit((unsigned char)*string)) string++; if (*string == 'x') { string++; params->h = atoi(string); while (*string && isdigit((unsigned char)*string)) string++; } if (*string == 't') { string++; params->type = atoi(string); while (*string && isdigit((unsigned char)*string)) string++; } if (*string == 'd') { int i; string++; for (i = 0; i < DIFF_MAX; i++) if (*string == diffchars[i]) params->diff = i; if (*string) string++; } } static char *encode_params(const game_params *params, int full) { char str[80]; sprintf(str, "%dx%dt%d", params->w, params->h, params->type); if (full) sprintf(str + strlen(str), "d%c", diffchars[params->diff]); return dupstr(str); } static config_item *game_configure(const game_params *params) { config_item *ret; char buf[80]; ret = snewn(5, config_item); ret[0].name = "Width"; ret[0].type = C_STRING; sprintf(buf, "%d", params->w); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Grid type"; ret[2].type = C_CHOICES; ret[2].sval = GRID_CONFIGS; ret[2].ival = params->type; ret[3].name = "Difficulty"; ret[3].type = C_CHOICES; ret[3].sval = DIFFCONFIG; ret[3].ival = params->diff; ret[4].name = NULL; ret[4].type = C_END; ret[4].sval = NULL; ret[4].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); ret->type = cfg[2].ival; ret->diff = cfg[3].ival; return ret; } static char *validate_params(const game_params *params, int full) { if (params->type < 0 || params->type >= NUM_GRID_TYPES) return "Illegal grid type"; if (params->w < grid_size_limits[params->type].amin || params->h < grid_size_limits[params->type].amin) return grid_size_limits[params->type].aerr; if (params->w < grid_size_limits[params->type].omin && params->h < grid_size_limits[params->type].omin) return grid_size_limits[params->type].oerr; /* * This shouldn't be able to happen at all, since decode_params * and custom_params will never generate anything that isn't * within range. */ assert(params->diff < DIFF_MAX); return NULL; } /* Returns a newly allocated string describing the current puzzle */ static char *state_to_text(const game_state *state) { grid *g = state->game_grid; char *retval; int num_faces = g->num_faces; char *description = snewn(num_faces + 1, char); char *dp = description; int empty_count = 0; int i; for (i = 0; i < num_faces; i++) { if (state->clues[i] < 0) { if (empty_count > 25) { dp += sprintf(dp, "%c", (int)(empty_count + 'a' - 1)); empty_count = 0; } empty_count++; } else { if (empty_count) { dp += sprintf(dp, "%c", (int)(empty_count + 'a' - 1)); empty_count = 0; } dp += sprintf(dp, "%c", (int)CLUE2CHAR(state->clues[i])); } } if (empty_count) dp += sprintf(dp, "%c", (int)(empty_count + 'a' - 1)); retval = dupstr(description); sfree(description); return retval; } #define GRID_DESC_SEP '_' /* Splits up a (optional) grid_desc from the game desc. Returns the * grid_desc (which needs freeing) and updates the desc pointer to * start of real desc, or returns NULL if no desc. */ static char *extract_grid_desc(const char **desc) { char *sep = strchr(*desc, GRID_DESC_SEP), *gd; int gd_len; if (!sep) return NULL; gd_len = sep - (*desc); gd = snewn(gd_len+1, char); memcpy(gd, *desc, gd_len); gd[gd_len] = '\0'; *desc = sep+1; return gd; } /* We require that the params pass the test in validate_params and that the * description fills the entire game area */ static char *validate_desc(const game_params *params, const char *desc) { int count = 0; grid *g; char *grid_desc, *ret; /* It's pretty inefficient to do this just for validation. All we need to * know is the precise number of faces. */ grid_desc = extract_grid_desc(&desc); ret = grid_validate_desc(grid_types[params->type], params->w, params->h, grid_desc); if (ret) return ret; g = loopy_generate_grid(params, grid_desc); if (grid_desc) sfree(grid_desc); for (; *desc; ++desc) { if ((*desc >= '0' && *desc <= '9') || (*desc >= 'A' && *desc <= 'Z')) { count++; continue; } if (*desc >= 'a') { count += *desc - 'a' + 1; continue; } return "Unknown character in description"; } if (count < g->num_faces) return "Description too short for board size"; if (count > g->num_faces) return "Description too long for board size"; grid_free(g); return NULL; } /* Sums the lengths of the numbers in range [0,n) */ /* See equivalent function in solo.c for justification of this. */ static int len_0_to_n(int n) { int len = 1; /* Counting 0 as a bit of a special case */ int i; for (i = 1; i < n; i *= 10) { len += max(n - i, 0); } return len; } static char *encode_solve_move(const game_state *state) { int len; char *ret, *p; int i; int num_edges = state->game_grid->num_edges; /* This is going to return a string representing the moves needed to set * every line in a grid to be the same as the ones in 'state'. The exact * length of this string is predictable. */ len = 1; /* Count the 'S' prefix */ /* Numbers in all lines */ len += len_0_to_n(num_edges); /* For each line we also have a letter */ len += num_edges; ret = snewn(len + 1, char); p = ret; p += sprintf(p, "S"); for (i = 0; i < num_edges; i++) { switch (state->lines[i]) { case LINE_YES: p += sprintf(p, "%dy", i); break; case LINE_NO: p += sprintf(p, "%dn", i); break; } } /* No point in doing sums like that if they're going to be wrong */ assert(strlen(ret) <= (size_t)len); return ret; } 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) { } static void game_compute_size(const game_params *params, int tilesize, int *x, int *y) { int grid_width, grid_height, rendered_width, rendered_height; int g_tilesize; grid_compute_size(grid_types[params->type], params->w, params->h, &g_tilesize, &grid_width, &grid_height); /* multiply first to minimise rounding error on integer division */ rendered_width = grid_width * tilesize / g_tilesize; rendered_height = grid_height * tilesize / g_tilesize; *x = rendered_width + 2 * BORDER(tilesize) + 1; *y = rendered_height + 2 * BORDER(tilesize) + 1; } 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(4 * NCOLOURS, float); frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); ret[COL_FOREGROUND * 3 + 0] = 0.0F; ret[COL_FOREGROUND * 3 + 1] = 0.0F; ret[COL_FOREGROUND * 3 + 2] = 0.0F; /* * We want COL_LINEUNKNOWN to be a yellow which is a bit darker * than the background. (I previously set it to 0.8,0.8,0, but * found that this went badly with the 0.8,0.8,0.8 favoured as a * background by the Java frontend.) */ ret[COL_LINEUNKNOWN * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.9F; ret[COL_LINEUNKNOWN * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.9F; ret[COL_LINEUNKNOWN * 3 + 2] = 0.0F; ret[COL_HIGHLIGHT * 3 + 0] = 1.0F; ret[COL_HIGHLIGHT * 3 + 1] = 1.0F; ret[COL_HIGHLIGHT * 3 + 2] = 1.0F; ret[COL_MISTAKE * 3 + 0] = 1.0F; ret[COL_MISTAKE * 3 + 1] = 0.0F; ret[COL_MISTAKE * 3 + 2] = 0.0F; ret[COL_SATISFIED * 3 + 0] = 0.0F; ret[COL_SATISFIED * 3 + 1] = 0.0F; ret[COL_SATISFIED * 3 + 2] = 0.0F; /* We want the faint lines to be a bit darker than the background. * Except if the background is pretty dark already; then it ought to be a * bit lighter. Oy vey. */ ret[COL_FAINT * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.9F; ret[COL_FAINT * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.9F; ret[COL_FAINT * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 0.9F; *ncolours = NCOLOURS; return ret; } static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { struct game_drawstate *ds = snew(struct game_drawstate); int num_faces = state->game_grid->num_faces; int num_edges = state->game_grid->num_edges; int i; ds->tilesize = 0; ds->started = 0; ds->lines = snewn(num_edges, char); ds->clue_error = snewn(num_faces, char); ds->clue_satisfied = snewn(num_faces, char); ds->textx = snewn(num_faces, int); ds->texty = snewn(num_faces, int); ds->flashing = 0; memset(ds->lines, LINE_UNKNOWN, num_edges); memset(ds->clue_error, 0, num_faces); memset(ds->clue_satisfied, 0, num_faces); for (i = 0; i < num_faces; i++) ds->textx[i] = ds->texty[i] = -1; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->textx); sfree(ds->texty); sfree(ds->clue_error); sfree(ds->clue_satisfied); sfree(ds->lines); sfree(ds); } static int game_timing_state(const game_state *state, game_ui *ui) { return TRUE; } static float game_anim_length(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui) { return 0.0F; } static int game_can_format_as_text_now(const game_params *params) { if (params->type != 0) return FALSE; return TRUE; } static char *game_text_format(const game_state *state) { int w, h, W, H; int x, y, i; int cell_size; char *ret; grid *g = state->game_grid; grid_face *f; assert(state->grid_type == 0); /* Work out the basic size unit */ f = g->faces; /* first face */ assert(f->order == 4); /* The dots are ordered clockwise, so the two opposite * corners are guaranteed to span the square */ cell_size = abs(f->dots[0]->x - f->dots[2]->x); w = (g->highest_x - g->lowest_x) / cell_size; h = (g->highest_y - g->lowest_y) / cell_size; /* Create a blank "canvas" to "draw" on */ W = 2 * w + 2; H = 2 * h + 1; ret = snewn(W * H + 1, char); for (y = 0; y < H; y++) { for (x = 0; x < W-1; x++) { ret[y*W + x] = ' '; } ret[y*W + W-1] = '\n'; } ret[H*W] = '\0'; /* Fill in edge info */ for (i = 0; i < g->num_edges; i++) { grid_edge *e = g->edges + i; /* Cell coordinates, from (0,0) to (w-1,h-1) */ int x1 = (e->dot1->x - g->lowest_x) / cell_size; int x2 = (e->dot2->x - g->lowest_x) / cell_size; int y1 = (e->dot1->y - g->lowest_y) / cell_size; int y2 = (e->dot2->y - g->lowest_y) / cell_size; /* Midpoint, in canvas coordinates (canvas coordinates are just twice * cell coordinates) */ x = x1 + x2; y = y1 + y2; switch (state->lines[i]) { case LINE_YES: ret[y*W + x] = (y1 == y2) ? '-' : '|'; break; case LINE_NO: ret[y*W + x] = 'x'; break; case LINE_UNKNOWN: break; /* already a space */ default: assert(!"Illegal line state"); } } /* Fill in clues */ for (i = 0; i < g->num_faces; i++) { int x1, x2, y1, y2; f = g->faces + i; assert(f->order == 4); /* Cell coordinates, from (0,0) to (w-1,h-1) */ x1 = (f->dots[0]->x - g->lowest_x) / cell_size; x2 = (f->dots[2]->x - g->lowest_x) / cell_size; y1 = (f->dots[0]->y - g->lowest_y) / cell_size; y2 = (f->dots[2]->y - g->lowest_y) / cell_size; /* Midpoint, in canvas coordinates */ x = x1 + x2; y = y1 + y2; ret[y*W + x] = CLUE2CHAR(state->clues[i]); } return ret; } /* ---------------------------------------------------------------------- * Debug code */ #ifdef DEBUG_CACHES static void check_caches(const solver_state* sstate) { int i; const game_state *state = sstate->state; const grid *g = state->game_grid; for (i = 0; i < g->num_dots; i++) { assert(dot_order(state, i, LINE_YES) == sstate->dot_yes_count[i]); assert(dot_order(state, i, LINE_NO) == sstate->dot_no_count[i]); } for (i = 0; i < g->num_faces; i++) { assert(face_order(state, i, LINE_YES) == sstate->face_yes_count[i]); assert(face_order(state, i, LINE_NO) == sstate->face_no_count[i]); } } #if 0 #define check_caches(s) \ do { \ fprintf(stderr, "check_caches at line %d\n", __LINE__); \ check_caches(s); \ } while (0) #endif #endif /* DEBUG_CACHES */ /* ---------------------------------------------------------------------- * Solver utility functions */ /* Sets the line (with index i) to the new state 'line_new', and updates * the cached counts of any affected faces and dots. * Returns TRUE if this actually changed the line's state. */ static int solver_set_line(solver_state *sstate, int i, enum line_state line_new #ifdef SHOW_WORKING , const char *reason #endif ) { game_state *state = sstate->state; grid *g; grid_edge *e; assert(line_new != LINE_UNKNOWN); check_caches(sstate); if (state->lines[i] == line_new) { return FALSE; /* nothing changed */ } state->lines[i] = line_new; #ifdef SHOW_WORKING fprintf(stderr, "solver: set line [%d] to %s (%s)\n", i, line_new == LINE_YES ? "YES" : "NO", reason); #endif g = state->game_grid; e = g->edges + i; /* Update the cache for both dots and both faces affected by this. */ if (line_new == LINE_YES) { sstate->dot_yes_count[e->dot1 - g->dots]++; sstate->dot_yes_count[e->dot2 - g->dots]++; if (e->face1) { sstate->face_yes_count[e->face1 - g->faces]++; } if (e->face2) { sstate->face_yes_count[e->face2 - g->faces]++; } } else { sstate->dot_no_count[e->dot1 - g->dots]++; sstate->dot_no_count[e->dot2 - g->dots]++; if (e->face1) { sstate->face_no_count[e->face1 - g->faces]++; } if (e->face2) { sstate->face_no_count[e->face2 - g->faces]++; } } check_caches(sstate); return TRUE; } #ifdef SHOW_WORKING #define solver_set_line(a, b, c) \ solver_set_line(a, b, c, __FUNCTION__) #endif /* * Merge two dots due to the existence of an edge between them. * Updates the dsf tracking equivalence classes, and keeps track of * the length of path each dot is currently a part of. * Returns TRUE if the dots were already linked, ie if they are part of a * closed loop, and false otherwise. */ static int merge_dots(solver_state *sstate, int edge_index) { int i, j, len; grid *g = sstate->state->game_grid; grid_edge *e = g->edges + edge_index; i = e->dot1 - g->dots; j = e->dot2 - g->dots; i = dsf_canonify(sstate->dotdsf, i); j = dsf_canonify(sstate->dotdsf, j); if (i == j) { return TRUE; } else { len = sstate->looplen[i] + sstate->looplen[j]; dsf_merge(sstate->dotdsf, i, j); i = dsf_canonify(sstate->dotdsf, i); sstate->looplen[i] = len; return FALSE; } } /* Merge two lines because the solver has deduced that they must be either * identical or opposite. Returns TRUE if this is new information, otherwise * FALSE. */ static int merge_lines(solver_state *sstate, int i, int j, int inverse #ifdef SHOW_WORKING , const char *reason #endif ) { int inv_tmp; assert(i < sstate->state->game_grid->num_edges); assert(j < sstate->state->game_grid->num_edges); i = edsf_canonify(sstate->linedsf, i, &inv_tmp); inverse ^= inv_tmp; j = edsf_canonify(sstate->linedsf, j, &inv_tmp); inverse ^= inv_tmp; edsf_merge(sstate->linedsf, i, j, inverse); #ifdef SHOW_WORKING if (i != j) { fprintf(stderr, "%s [%d] [%d] %s(%s)\n", __FUNCTION__, i, j, inverse ? "inverse " : "", reason); } #endif return (i != j); } #ifdef SHOW_WORKING #define merge_lines(a, b, c, d) \ merge_lines(a, b, c, d, __FUNCTION__) #endif /* Count the number of lines of a particular type currently going into the * given dot. */ static int dot_order(const game_state* state, int dot, char line_type) { int n = 0; grid *g = state->game_grid; grid_dot *d = g->dots + dot; int i; for (i = 0; i < d->order; i++) { grid_edge *e = d->edges[i]; if (state->lines[e - g->edges] == line_type) ++n; } return n; } /* Count the number of lines of a particular type currently surrounding the * given face */ static int face_order(const game_state* state, int face, char line_type) { int n = 0; grid *g = state->game_grid; grid_face *f = g->faces + face; int i; for (i = 0; i < f->order; i++) { grid_edge *e = f->edges[i]; if (state->lines[e - g->edges] == line_type) ++n; } return n; } /* Set all lines bordering a dot of type old_type to type new_type * Return value tells caller whether this function actually did anything */ static int dot_setall(solver_state *sstate, int dot, char old_type, char new_type) { int retval = FALSE, r; game_state *state = sstate->state; grid *g; grid_dot *d; int i; if (old_type == new_type) return FALSE; g = state->game_grid; d = g->dots + dot; for (i = 0; i < d->order; i++) { int line_index = d->edges[i] - g->edges; if (state->lines[line_index] == old_type) { r = solver_set_line(sstate, line_index, new_type); assert(r == TRUE); retval = TRUE; } } return retval; } /* Set all lines bordering a face of type old_type to type new_type */ static int face_setall(solver_state *sstate, int face, char old_type, char new_type) { int retval = FALSE, r; game_state *state = sstate->state; grid *g; grid_face *f; int i; if (old_type == new_type) return FALSE; g = state->game_grid; f = g->faces + face; for (i = 0; i < f->order; i++) { int line_index = f->edges[i] - g->edges; if (state->lines[line_index] == old_type) { r = solver_set_line(sstate, line_index, new_type); assert(r == TRUE); retval = TRUE; } } return retval; } /* ---------------------------------------------------------------------- * Loop generation and clue removal */ static void add_full_clues(game_state *state, random_state *rs) { signed char *clues = state->clues; grid *g = state->game_grid; char *board = snewn(g->num_faces, char); int i; generate_loop(g, board, rs, NULL, NULL); /* Fill out all the clues by initialising to 0, then iterating over * all edges and incrementing each clue as we find edges that border * between BLACK/WHITE faces. While we're at it, we verify that the * algorithm does work, and there aren't any GREY faces still there. */ memset(clues, 0, g->num_faces); for (i = 0; i < g->num_edges; i++) { grid_edge *e = g->edges + i; grid_face *f1 = e->face1; grid_face *f2 = e->face2; enum face_colour c1 = FACE_COLOUR(f1); enum face_colour c2 = FACE_COLOUR(f2); assert(c1 != FACE_GREY); assert(c2 != FACE_GREY); if (c1 != c2) { if (f1) clues[f1 - g->faces]++; if (f2) clues[f2 - g->faces]++; } } sfree(board); } static int game_has_unique_soln(const game_state *state, int diff) { int ret; solver_state *sstate_new; solver_state *sstate = new_solver_state((game_state *)state, diff); sstate_new = solve_game_rec(sstate); assert(sstate_new->solver_status != SOLVER_MISTAKE); ret = (sstate_new->solver_status == SOLVER_SOLVED); free_solver_state(sstate_new); free_solver_state(sstate); return ret; } /* Remove clues one at a time at random. */ static game_state *remove_clues(game_state *state, random_state *rs, int diff) { int *face_list; int num_faces = state->game_grid->num_faces; game_state *ret = dup_game(state), *saved_ret; int n; /* We need to remove some clues. We'll do this by forming a list of all * available clues, shuffling it, then going along one at a * time clearing each clue in turn for which doing so doesn't render the * board unsolvable. */ face_list = snewn(num_faces, int); for (n = 0; n < num_faces; ++n) { face_list[n] = n; } shuffle(face_list, num_faces, sizeof(int), rs); for (n = 0; n < num_faces; ++n) { saved_ret = dup_game(ret); ret->clues[face_list[n]] = -1; if (game_has_unique_soln(ret, diff)) { free_game(saved_ret); } else { free_game(ret); ret = saved_ret; } } sfree(face_list); return ret; } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { /* solution and description both use run-length encoding in obvious ways */ char *retval, *game_desc, *grid_desc; grid *g; game_state *state = snew(game_state); game_state *state_new; grid_desc = grid_new_desc(grid_types[params->type], params->w, params->h, rs); state->game_grid = g = loopy_generate_grid(params, grid_desc); state->clues = snewn(g->num_faces, signed char); state->lines = snewn(g->num_edges, char); state->line_errors = snewn(g->num_edges, unsigned char); state->grid_type = params->type; newboard_please: memset(state->lines, LINE_UNKNOWN, g->num_edges); memset(state->line_errors, 0, g->num_edges); state->solved = state->cheated = FALSE; /* Get a new random solvable board with all its clues filled in. Yes, this * can loop for ever if the params are suitably unfavourable, but * preventing games smaller than 4x4 seems to stop this happening */ do { add_full_clues(state, rs); } while (!game_has_unique_soln(state, params->diff)); state_new = remove_clues(state, rs, params->diff); free_game(state); state = state_new; if (params->diff > 0 && game_has_unique_soln(state, params->diff-1)) { #ifdef SHOW_WORKING fprintf(stderr, "Rejecting board, it is too easy\n"); #endif goto newboard_please; } game_desc = state_to_text(state); free_game(state); if (grid_desc) { retval = snewn(strlen(grid_desc) + 1 + strlen(game_desc) + 1, char); sprintf(retval, "%s%c%s", grid_desc, (int)GRID_DESC_SEP, game_desc); sfree(grid_desc); sfree(game_desc); } else { retval = game_desc; } assert(!validate_desc(params, retval)); return retval; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { int i; game_state *state = snew(game_state); int empties_to_make = 0; int n,n2; const char *dp; char *grid_desc; grid *g; int num_faces, num_edges; grid_desc = extract_grid_desc(&desc); state->game_grid = g = loopy_generate_grid(params, grid_desc); if (grid_desc) sfree(grid_desc); dp = desc; num_faces = g->num_faces; num_edges = g->num_edges; state->clues = snewn(num_faces, signed char); state->lines = snewn(num_edges, char); state->line_errors = snewn(num_edges, unsigned char); state->solved = state->cheated = FALSE; state->grid_type = params->type; for (i = 0; i < num_faces; i++) { if (empties_to_make) { empties_to_make--; state->clues[i] = -1; continue; } assert(*dp); n = *dp - '0'; n2 = *dp - 'A' + 10; if (n >= 0 && n < 10) { state->clues[i] = n; } else if (n2 >= 10 && n2 < 36) { state->clues[i] = n2; } else { n = *dp - 'a' + 1; assert(n > 0); state->clues[i] = -1; empties_to_make = n - 1; } ++dp; } memset(state->lines, LINE_UNKNOWN, num_edges); memset(state->line_errors, 0, num_edges); return state; } /* Calculates the line_errors data, and checks if the current state is a * solution */ static int check_completion(game_state *state) { grid *g = state->game_grid; int *dsf; int num_faces = g->num_faces; int i; int infinite_area, finite_area; int loops_found = 0; int found_edge_not_in_loop = FALSE; memset(state->line_errors, 0, g->num_edges); /* LL implementation of SGT's idea: * A loop will partition the grid into an inside and an outside. * If there is more than one loop, the grid will be partitioned into * even more distinct regions. We can therefore track equivalence of * faces, by saying that two faces are equivalent when there is a non-YES * edge between them. * We could keep track of the number of connected components, by counting * the number of dsf-merges that aren't no-ops. * But we're only interested in 3 separate cases: * no loops, one loop, more than one loop. * * No loops: all faces are equivalent to the infinite face. * One loop: only two equivalence classes - finite and infinite. * >= 2 loops: there are 2 distinct finite regions. * * So we simply make two passes through all the edges. * In the first pass, we dsf-merge the two faces bordering each non-YES * edge. * In the second pass, we look for YES-edges bordering: * a) two non-equivalent faces. * b) two non-equivalent faces, and one of them is part of a different * finite area from the first finite area we've seen. * * An occurrence of a) means there is at least one loop. * An occurrence of b) means there is more than one loop. * Edges satisfying a) are marked as errors. * * While we're at it, we set a flag if we find a YES edge that is not * part of a loop. * This information will help decide, if there's a single loop, whether it * is a candidate for being a solution (that is, all YES edges are part of * this loop). * * If there is a candidate loop, we then go through all clues and check * they are all satisfied. If so, we have found a solution and we can * unmark all line_errors. */ /* Infinite face is at the end - its index is num_faces. * This macro is just to make this obvious! */ #define INF_FACE num_faces dsf = snewn(num_faces + 1, int); dsf_init(dsf, num_faces + 1); /* First pass */ for (i = 0; i < g->num_edges; i++) { grid_edge *e = g->edges + i; int f1 = e->face1 ? e->face1 - g->faces : INF_FACE; int f2 = e->face2 ? e->face2 - g->faces : INF_FACE; if (state->lines[i] != LINE_YES) dsf_merge(dsf, f1, f2); } /* Second pass */ infinite_area = dsf_canonify(dsf, INF_FACE); finite_area = -1; for (i = 0; i < g->num_edges; i++) { grid_edge *e = g->edges + i; int f1 = e->face1 ? e->face1 - g->faces : INF_FACE; int can1 = dsf_canonify(dsf, f1); int f2 = e->face2 ? e->face2 - g->faces : INF_FACE; int can2 = dsf_canonify(dsf, f2); if (state->lines[i] != LINE_YES) continue; if (can1 == can2) { /* Faces are equivalent, so this edge not part of a loop */ found_edge_not_in_loop = TRUE; continue; } state->line_errors[i] = TRUE; if (loops_found == 0) loops_found = 1; /* Don't bother with further checks if we've already found 2 loops */ if (loops_found == 2) continue; if (finite_area == -1) { /* Found our first finite area */ if (can1 != infinite_area) finite_area = can1; else finite_area = can2; } /* Have we found a second area? */ if (finite_area != -1) { if (can1 != infinite_area && can1 != finite_area) { loops_found = 2; continue; } if (can2 != infinite_area && can2 != finite_area) { loops_found = 2; } } } /* printf("loops_found = %d\n", loops_found); printf("found_edge_not_in_loop = %s\n", found_edge_not_in_loop ? "TRUE" : "FALSE"); */ sfree(dsf); /* No longer need the dsf */ /* Have we found a candidate loop? */ if (loops_found == 1 && !found_edge_not_in_loop) { /* Yes, so check all clues are satisfied */ int found_clue_violation = FALSE; for (i = 0; i < num_faces; i++) { int c = state->clues[i]; if (c >= 0) { if (face_order(state, i, LINE_YES) != c) { found_clue_violation = TRUE; break; } } } if (!found_clue_violation) { /* The loop is good */ memset(state->line_errors, 0, g->num_edges); return TRUE; /* No need to bother checking for dot violations */ } } /* Check for dot violations */ for (i = 0; i < g->num_dots; i++) { int yes = dot_order(state, i, LINE_YES); int unknown = dot_order(state, i, LINE_UNKNOWN); if ((yes == 1 && unknown == 0) || (yes >= 3)) { /* violation, so mark all YES edges as errors */ grid_dot *d = g->dots + i; int j; for (j = 0; j < d->order; j++) { int e = d->edges[j] - g->edges; if (state->lines[e] == LINE_YES) state->line_errors[e] = TRUE; } } } return FALSE; } /* ---------------------------------------------------------------------- * Solver logic * * Our solver modes operate as follows. Each mode also uses the modes above it. * * Easy Mode * Just implement the rules of the game. * * Normal and Tricky Modes * For each (adjacent) pair of lines through each dot we store a bit for * whether at least one of them is on and whether at most one is on. (If we * know both or neither is on that's already stored more directly.) * * Advanced Mode * Use edsf data structure to make equivalence classes of lines that are * known identical to or opposite to one another. */ /* DLines: * For general grids, we consider "dlines" to be pairs of lines joined * at a dot. The lines must be adjacent around the dot, so we can think of * a dline as being a dot+face combination. Or, a dot+edge combination where * the second edge is taken to be the next clockwise edge from the dot. * Original loopy code didn't have this extra restriction of the lines being * adjacent. From my tests with square grids, this extra restriction seems to * take little, if anything, away from the quality of the puzzles. * A dline can be uniquely identified by an edge/dot combination, given that * a dline-pair always goes clockwise around its common dot. The edge/dot * combination can be represented by an edge/bool combination - if bool is * TRUE, use edge->dot1 else use edge->dot2. So the total number of dlines is * exactly twice the number of edges in the grid - although the dlines * spanning the infinite face are not all that useful to the solver. * Note that, by convention, a dline goes clockwise around its common dot, * which means the dline goes anti-clockwise around its common face. */ /* Helper functions for obtaining an index into an array of dlines, given * various information. We assume the grid layout conventions about how * the various lists are interleaved - see grid_make_consistent() for * details. */ /* i points to the first edge of the dline pair, reading clockwise around * the dot. */ static int dline_index_from_dot(grid *g, grid_dot *d, int i) { grid_edge *e = d->edges[i]; int ret; #ifdef DEBUG_DLINES grid_edge *e2; int i2 = i+1; if (i2 == d->order) i2 = 0; e2 = d->edges[i2]; #endif ret = 2 * (e - g->edges) + ((e->dot1 == d) ? 1 : 0); #ifdef DEBUG_DLINES printf("dline_index_from_dot: d=%d,i=%d, edges [%d,%d] - %d\n", (int)(d - g->dots), i, (int)(e - g->edges), (int)(e2 - g->edges), ret); #endif return ret; } /* i points to the second edge of the dline pair, reading clockwise around * the face. That is, the edges of the dline, starting at edge{i}, read * anti-clockwise around the face. By layout conventions, the common dot * of the dline will be f->dots[i] */ static int dline_index_from_face(grid *g, grid_face *f, int i) { grid_edge *e = f->edges[i]; grid_dot *d = f->dots[i]; int ret; #ifdef DEBUG_DLINES grid_edge *e2; int i2 = i - 1; if (i2 < 0) i2 += f->order; e2 = f->edges[i2]; #endif ret = 2 * (e - g->edges) + ((e->dot1 == d) ? 1 : 0); #ifdef DEBUG_DLINES printf("dline_index_from_face: f=%d,i=%d, edges [%d,%d] - %d\n", (int)(f - g->faces), i, (int)(e - g->edges), (int)(e2 - g->edges), ret); #endif return ret; } static int is_atleastone(const char *dline_array, int index) { return BIT_SET(dline_array[index], 0); } static int set_atleastone(char *dline_array, int index) { return SET_BIT(dline_array[index], 0); } static int is_atmostone(const char *dline_array, int index) { return BIT_SET(dline_array[index], 1); } static int set_atmostone(char *dline_array, int index) { return SET_BIT(dline_array[index], 1); } static void array_setall(char *array, char from, char to, int len) { char *p = array, *p_old = p; int len_remaining = len; while ((p = memchr(p, from, len_remaining))) { *p = to; len_remaining -= p - p_old; p_old = p; } } /* Helper, called when doing dline dot deductions, in the case where we * have 4 UNKNOWNs, and two of them (adjacent) have *exactly* one YES between * them (because of dline atmostone/atleastone). * On entry, edge points to the first of these two UNKNOWNs. This function * will find the opposite UNKNOWNS (if they are adjacent to one another) * and set their corresponding dline to atleastone. (Setting atmostone * already happens in earlier dline deductions) */ static int dline_set_opp_atleastone(solver_state *sstate, grid_dot *d, int edge) { game_state *state = sstate->state; grid *g = state->game_grid; int N = d->order; int opp, opp2; for (opp = 0; opp < N; opp++) { int opp_dline_index; if (opp == edge || opp == edge+1 || opp == edge-1) continue; if (opp == 0 && edge == N-1) continue; if (opp == N-1 && edge == 0) continue; opp2 = opp + 1; if (opp2 == N) opp2 = 0; /* Check if opp, opp2 point to LINE_UNKNOWNs */ if (state->lines[d->edges[opp] - g->edges] != LINE_UNKNOWN) continue; if (state->lines[d->edges[opp2] - g->edges] != LINE_UNKNOWN) continue; /* Found opposite UNKNOWNS and they're next to each other */ opp_dline_index = dline_index_from_dot(g, d, opp); return set_atleastone(sstate->dlines, opp_dline_index); } return FALSE; } /* Set pairs of lines around this face which are known to be identical, to * the given line_state */ static int face_setall_identical(solver_state *sstate, int face_index, enum line_state line_new) { /* can[dir] contains the canonical line associated with the line in * direction dir from the square in question. Similarly inv[dir] is * whether or not the line in question is inverse to its canonical * element. */ int retval = FALSE; game_state *state = sstate->state; grid *g = state->game_grid; grid_face *f = g->faces + face_index; int N = f->order; int i, j; int can1, can2, inv1, inv2; for (i = 0; i < N; i++) { int line1_index = f->edges[i] - g->edges; if (state->lines[line1_index] != LINE_UNKNOWN) continue; for (j = i + 1; j < N; j++) { int line2_index = f->edges[j] - g->edges; if (state->lines[line2_index] != LINE_UNKNOWN) continue; /* Found two UNKNOWNS */ can1 = edsf_canonify(sstate->linedsf, line1_index, &inv1); can2 = edsf_canonify(sstate->linedsf, line2_index, &inv2); if (can1 == can2 && inv1 == inv2) { solver_set_line(sstate, line1_index, line_new); solver_set_line(sstate, line2_index, line_new); } } } return retval; } /* Given a dot or face, and a count of LINE_UNKNOWNs, find them and * return the edge indices into e. */ static void find_unknowns(game_state *state, grid_edge **edge_list, /* Edge list to search (from a face or a dot) */ int expected_count, /* Number of UNKNOWNs (comes from solver's cache) */ int *e /* Returned edge indices */) { int c = 0; grid *g = state->game_grid; while (c < expected_count) { int line_index = *edge_list - g->edges; if (state->lines[line_index] == LINE_UNKNOWN) { e[c] = line_index; c++; } ++edge_list; } } /* If we have a list of edges, and we know whether the number of YESs should * be odd or even, and there are only a few UNKNOWNs, we can do some simple * linedsf deductions. This can be used for both face and dot deductions. * Returns the difficulty level of the next solver that should be used, * or DIFF_MAX if no progress was made. */ static int parity_deductions(solver_state *sstate, grid_edge **edge_list, /* Edge list (from a face or a dot) */ int total_parity, /* Expected number of YESs modulo 2 (either 0 or 1) */ int unknown_count) { game_state *state = sstate->state; int diff = DIFF_MAX; int *linedsf = sstate->linedsf; if (unknown_count == 2) { /* Lines are known alike/opposite, depending on inv. */ int e[2]; find_unknowns(state, edge_list, 2, e); if (merge_lines(sstate, e[0], e[1], total_parity)) diff = min(diff, DIFF_HARD); } else if (unknown_count == 3) { int e[3]; int can[3]; /* canonical edges */ int inv[3]; /* whether can[x] is inverse to e[x] */ find_unknowns(state, edge_list, 3, e); can[0] = edsf_canonify(linedsf, e[0], inv); can[1] = edsf_canonify(linedsf, e[1], inv+1); can[2] = edsf_canonify(linedsf, e[2], inv+2); if (can[0] == can[1]) { if (solver_set_line(sstate, e[2], (total_parity^inv[0]^inv[1]) ? LINE_YES : LINE_NO)) diff = min(diff, DIFF_EASY); } if (can[0] == can[2]) { if (solver_set_line(sstate, e[1], (total_parity^inv[0]^inv[2]) ? LINE_YES : LINE_NO)) diff = min(diff, DIFF_EASY); } if (can[1] == can[2]) { if (solver_set_line(sstate, e[0], (total_parity^inv[1]^inv[2]) ? LINE_YES : LINE_NO)) diff = min(diff, DIFF_EASY); } } else if (unknown_count == 4) { int e[4]; int can[4]; /* canonical edges */ int inv[4]; /* whether can[x] is inverse to e[x] */ find_unknowns(state, edge_list, 4, e); can[0] = edsf_canonify(linedsf, e[0], inv); can[1] = edsf_canonify(linedsf, e[1], inv+1); can[2] = edsf_canonify(linedsf, e[2], inv+2); can[3] = edsf_canonify(linedsf, e[3], inv+3); if (can[0] == can[1]) { if (merge_lines(sstate, e[2], e[3], total_parity^inv[0]^inv[1])) diff = min(diff, DIFF_HARD); } else if (can[0] == can[2]) { if (merge_lines(sstate, e[1], e[3], total_parity^inv[0]^inv[2])) diff = min(diff, DIFF_HARD); } else if (can[0] == can[3]) { if (merge_lines(sstate, e[1], e[2], total_parity^inv[0]^inv[3])) diff = min(diff, DIFF_HARD); } else if (can[1] == can[2]) { if (merge_lines(sstate, e[0], e[3], total_parity^inv[1]^inv[2])) diff = min(diff, DIFF_HARD); } else if (can[1] == can[3]) { if (merge_lines(sstate, e[0], e[2], total_parity^inv[1]^inv[3])) diff = min(diff, DIFF_HARD); } else if (can[2] == can[3]) { if (merge_lines(sstate, e[0], e[1], total_parity^inv[2]^inv[3])) diff = min(diff, DIFF_HARD); } } return diff; } /* * These are the main solver functions. * * Their return values are diff values corresponding to the lowest mode solver * that would notice the work that they have done. For example if the normal * mode solver adds actual lines or crosses, it will return DIFF_EASY as the * easy mode solver might be able to make progress using that. It doesn't make * sense for one of them to return a diff value higher than that of the * function itself. * * Each function returns the lowest value it can, as early as possible, in * order to try and pass as much work as possible back to the lower level * solvers which progress more quickly. */ /* PROPOSED NEW DESIGN: * We have a work queue consisting of 'events' notifying us that something has * happened that a particular solver mode might be interested in. For example * the hard mode solver might do something that helps the normal mode solver at * dot [x,y] in which case it will enqueue an event recording this fact. Then * we pull events off the work queue, and hand each in turn to the solver that * is interested in them. If a solver reports that it failed we pass the same * event on to progressively more advanced solvers and the loop detector. Once * we've exhausted an event, or it has helped us progress, we drop it and * continue to the next one. The events are sorted first in order of solver * complexity (easy first) then order of insertion (oldest first). * Once we run out of events we loop over each permitted solver in turn * (easiest first) until either a deduction is made (and an event therefore * emerges) or no further deductions can be made (in which case we've failed). * * QUESTIONS: * * How do we 'loop over' a solver when both dots and squares are concerned. * Answer: first all squares then all dots. */ static int trivial_deductions(solver_state *sstate) { int i, current_yes, current_no; game_state *state = sstate->state; grid *g = state->game_grid; int diff = DIFF_MAX; /* Per-face deductions */ for (i = 0; i < g->num_faces; i++) { grid_face *f = g->faces + i; if (sstate->face_solved[i]) continue; current_yes = sstate->face_yes_count[i]; current_no = sstate->face_no_count[i]; if (current_yes + current_no == f->order) { sstate->face_solved[i] = TRUE; continue; } if (state->clues[i] < 0) continue; /* * This code checks whether the numeric clue on a face is so * large as to permit all its remaining LINE_UNKNOWNs to be * filled in as LINE_YES, or alternatively so small as to * permit them all to be filled in as LINE_NO. */ if (state->clues[i] < current_yes) { sstate->solver_status = SOLVER_MISTAKE; return DIFF_EASY; } if (state->clues[i] == current_yes) { if (face_setall(sstate, i, LINE_UNKNOWN, LINE_NO)) diff = min(diff, DIFF_EASY); sstate->face_solved[i] = TRUE; continue; } if (f->order - state->clues[i] < current_no) { sstate->solver_status = SOLVER_MISTAKE; return DIFF_EASY; } if (f->order - state->clues[i] == current_no) { if (face_setall(sstate, i, LINE_UNKNOWN, LINE_YES)) diff = min(diff, DIFF_EASY); sstate->face_solved[i] = TRUE; continue; } if (f->order - state->clues[i] == current_no + 1 && f->order - current_yes - current_no > 2) { /* * One small refinement to the above: we also look for any * adjacent pair of LINE_UNKNOWNs around the face with * some LINE_YES incident on it from elsewhere. If we find * one, then we know that pair of LINE_UNKNOWNs can't * _both_ be LINE_YES, and hence that pushes us one line * closer to being able to determine all the rest. */ int j, k, e1, e2, e, d; for (j = 0; j < f->order; j++) { e1 = f->edges[j] - g->edges; e2 = f->edges[j+1 < f->order ? j+1 : 0] - g->edges; if (g->edges[e1].dot1 == g->edges[e2].dot1 || g->edges[e1].dot1 == g->edges[e2].dot2) { d = g->edges[e1].dot1 - g->dots; } else { assert(g->edges[e1].dot2 == g->edges[e2].dot1 || g->edges[e1].dot2 == g->edges[e2].dot2); d = g->edges[e1].dot2 - g->dots; } if (state->lines[e1] == LINE_UNKNOWN && state->lines[e2] == LINE_UNKNOWN) { for (k = 0; k < g->dots[d].order; k++) { int e = g->dots[d].edges[k] - g->edges; if (state->lines[e] == LINE_YES) goto found; /* multi-level break */ } } } continue; found: /* * If we get here, we've found such a pair of edges, and * they're e1 and e2. */ for (j = 0; j < f->order; j++) { e = f->edges[j] - g->edges; if (state->lines[e] == LINE_UNKNOWN && e != e1 && e != e2) { int r = solver_set_line(sstate, e, LINE_YES); assert(r); diff = min(diff, DIFF_EASY); } } } } check_caches(sstate); /* Per-dot deductions */ for (i = 0; i < g->num_dots; i++) { grid_dot *d = g->dots + i; int yes, no, unknown; if (sstate->dot_solved[i]) continue; yes = sstate->dot_yes_count[i]; no = sstate->dot_no_count[i]; unknown = d->order - yes - no; if (yes == 0) { if (unknown == 0) { sstate->dot_solved[i] = TRUE; } else if (unknown == 1) { dot_setall(sstate, i, LINE_UNKNOWN, LINE_NO); diff = min(diff, DIFF_EASY); sstate->dot_solved[i] = TRUE; } } else if (yes == 1) { if (unknown == 0) { sstate->solver_status = SOLVER_MISTAKE; return DIFF_EASY; } else if (unknown == 1) { dot_setall(sstate, i, LINE_UNKNOWN, LINE_YES); diff = min(diff, DIFF_EASY); } } else if (yes == 2) { if (unknown > 0) { dot_setall(sstate, i, LINE_UNKNOWN, LINE_NO); diff = min(diff, DIFF_EASY); } sstate->dot_solved[i] = TRUE; } else { sstate->solver_status = SOLVER_MISTAKE; return DIFF_EASY; } } check_caches(sstate); return diff; } static int dline_deductions(solver_state *sstate) { game_state *state = sstate->state; grid *g = state->game_grid; char *dlines = sstate->dlines; int i; int diff = DIFF_MAX; /* ------ Face deductions ------ */ /* Given a set of dline atmostone/atleastone constraints, need to figure * out if we can deduce any further info. For more general faces than * squares, this turns out to be a tricky problem. * The approach taken here is to define (per face) NxN matrices: * "maxs" and "mins". * The entries maxs(j,k) and mins(j,k) define the upper and lower limits * for the possible number of edges that are YES between positions j and k * going clockwise around the face. Can think of j and k as marking dots * around the face (recall the labelling scheme: edge0 joins dot0 to dot1, * edge1 joins dot1 to dot2 etc). * Trivially, mins(j,j) = maxs(j,j) = 0, and we don't even bother storing * these. mins(j,j+1) and maxs(j,j+1) are determined by whether edge{j} * is YES, NO or UNKNOWN. mins(j,j+2) and maxs(j,j+2) are related to * the dline atmostone/atleastone status for edges j and j+1. * * Then we calculate the remaining entries recursively. We definitely * know that * mins(j,k) >= { mins(j,u) + mins(u,k) } for any u between j and k. * This is because any valid placement of YESs between j and k must give * a valid placement between j and u, and also between u and k. * I believe it's sufficient to use just the two values of u: * j+1 and j+2. Seems to work well in practice - the bounds we compute * are rigorous, even if they might not be best-possible. * * Once we have maxs and mins calculated, we can make inferences about * each dline{j,j+1} by looking at the possible complementary edge-counts * mins(j+2,j) and maxs(j+2,j) and comparing these with the face clue. * As well as dlines, we can make similar inferences about single edges. * For example, consider a pentagon with clue 3, and we know at most one * of (edge0, edge1) is YES, and at most one of (edge2, edge3) is YES. * We could then deduce edge4 is YES, because maxs(0,4) would be 2, so * that final edge would have to be YES to make the count up to 3. */ /* Much quicker to allocate arrays on the stack than the heap, so * define the largest possible face size, and base our array allocations * on that. We check this with an assertion, in case someone decides to * make a grid which has larger faces than this. Note, this algorithm * could get quite expensive if there are many large faces. */ #define MAX_FACE_SIZE 12 for (i = 0; i < g->num_faces; i++) { int maxs[MAX_FACE_SIZE][MAX_FACE_SIZE]; int mins[MAX_FACE_SIZE][MAX_FACE_SIZE]; grid_face *f = g->faces + i; int N = f->order; int j,m; int clue = state->clues[i]; assert(N <= MAX_FACE_SIZE); if (sstate->face_solved[i]) continue; if (clue < 0) continue; /* Calculate the (j,j+1) entries */ for (j = 0; j < N; j++) { int edge_index = f->edges[j] - g->edges; int dline_index; enum line_state line1 = state->lines[edge_index]; enum line_state line2; int tmp; int k = j + 1; if (k >= N) k = 0; maxs[j][k] = (line1 == LINE_NO) ? 0 : 1; mins[j][k] = (line1 == LINE_YES) ? 1 : 0; /* Calculate the (j,j+2) entries */ dline_index = dline_index_from_face(g, f, k); edge_index = f->edges[k] - g->edges; line2 = state->lines[edge_index]; k++; if (k >= N) k = 0; /* max */ tmp = 2; if (line1 == LINE_NO) tmp--; if (line2 == LINE_NO) tmp--; if (tmp == 2 && is_atmostone(dlines, dline_index)) tmp = 1; maxs[j][k] = tmp; /* min */ tmp = 0; if (line1 == LINE_YES) tmp++; if (line2 == LINE_YES) tmp++; if (tmp == 0 && is_atleastone(dlines, dline_index)) tmp = 1; mins[j][k] = tmp; } /* Calculate the (j,j+m) entries for m between 3 and N-1 */ for (m = 3; m < N; m++) { for (j = 0; j < N; j++) { int k = j + m; int u = j + 1; int v = j + 2; int tmp; if (k >= N) k -= N; if (u >= N) u -= N; if (v >= N) v -= N; maxs[j][k] = maxs[j][u] + maxs[u][k]; mins[j][k] = mins[j][u] + mins[u][k]; tmp = maxs[j][v] + maxs[v][k]; maxs[j][k] = min(maxs[j][k], tmp); tmp = mins[j][v] + mins[v][k]; mins[j][k] = max(mins[j][k], tmp); } } /* See if we can make any deductions */ for (j = 0; j < N; j++) { int k; grid_edge *e = f->edges[j]; int line_index = e - g->edges; int dline_index; if (state->lines[line_index] != LINE_UNKNOWN) continue; k = j + 1; if (k >= N) k = 0; /* minimum YESs in the complement of this edge */ if (mins[k][j] > clue) { sstate->solver_status = SOLVER_MISTAKE; return DIFF_EASY; } if (mins[k][j] == clue) { /* setting this edge to YES would make at least * (clue+1) edges - contradiction */ solver_set_line(sstate, line_index, LINE_NO); diff = min(diff, DIFF_EASY); } if (maxs[k][j] < clue - 1) { sstate->solver_status = SOLVER_MISTAKE; return DIFF_EASY; } if (maxs[k][j] == clue - 1) { /* Only way to satisfy the clue is to set edge{j} as YES */ solver_set_line(sstate, line_index, LINE_YES); diff = min(diff, DIFF_EASY); } /* More advanced deduction that allows propagation along diagonal * chains of faces connected by dots, for example, 3-2-...-2-3 * in square grids. */ if (sstate->diff >= DIFF_TRICKY) { /* Now see if we can make dline deduction for edges{j,j+1} */ e = f->edges[k]; if (state->lines[e - g->edges] != LINE_UNKNOWN) /* Only worth doing this for an UNKNOWN,UNKNOWN pair. * Dlines where one of the edges is known, are handled in the * dot-deductions */ continue; dline_index = dline_index_from_face(g, f, k); k++; if (k >= N) k = 0; /* minimum YESs in the complement of this dline */ if (mins[k][j] > clue - 2) { /* Adding 2 YESs would break the clue */ if (set_atmostone(dlines, dline_index)) diff = min(diff, DIFF_NORMAL); } /* maximum YESs in the complement of this dline */ if (maxs[k][j] < clue) { /* Adding 2 NOs would mean not enough YESs */ if (set_atleastone(dlines, dline_index)) diff = min(diff, DIFF_NORMAL); } } } } if (diff < DIFF_NORMAL) return diff; /* ------ Dot deductions ------ */ for (i = 0; i < g->num_dots; i++) { grid_dot *d = g->dots + i; int N = d->order; int yes, no, unknown; int j; if (sstate->dot_solved[i]) continue; yes = sstate->dot_yes_count[i]; no = sstate->dot_no_count[i]; unknown = N - yes - no; for (j = 0; j < N; j++) { int k; int dline_index; int line1_index, line2_index; enum line_state line1, line2; k = j + 1; if (k >= N) k = 0; dline_index = dline_index_from_dot(g, d, j); line1_index = d->edges[j] - g->edges; line2_index = d->edges[k] - g->edges; line1 = state->lines[line1_index]; line2 = state->lines[line2_index]; /* Infer dline state from line state */ if (line1 == LINE_NO || line2 == LINE_NO) { if (set_atmostone(dlines, dline_index)) diff = min(diff, DIFF_NORMAL); } if (line1 == LINE_YES || line2 == LINE_YES) { if (set_atleastone(dlines, dline_index)) diff = min(diff, DIFF_NORMAL); } /* Infer line state from dline state */ if (is_atmostone(dlines, dline_index)) { if (line1 == LINE_YES && line2 == LINE_UNKNOWN) { solver_set_line(sstate, line2_index, LINE_NO); diff = min(diff, DIFF_EASY); } if (line2 == LINE_YES && line1 == LINE_UNKNOWN) { solver_set_line(sstate, line1_index, LINE_NO); diff = min(diff, DIFF_EASY); } } if (is_atleastone(dlines, dline_index)) { if (line1 == LINE_NO && line2 == LINE_UNKNOWN) { solver_set_line(sstate, line2_index, LINE_YES); diff = min(diff, DIFF_EASY); } if (line2 == LINE_NO && line1 == LINE_UNKNOWN) { solver_set_line(sstate, line1_index, LINE_YES); diff = min(diff, DIFF_EASY); } } /* Deductions that depend on the numbers of lines. * Only bother if both lines are UNKNOWN, otherwise the * easy-mode solver (or deductions above) would have taken * care of it. */ if (line1 != LINE_UNKNOWN || line2 != LINE_UNKNOWN) continue; if (yes == 0 && unknown == 2) { /* Both these unknowns must be identical. If we know * atmostone or atleastone, we can make progress. */ if (is_atmostone(dlines, dline_index)) { solver_set_line(sstate, line1_index, LINE_NO); solver_set_line(sstate, line2_index, LINE_NO); diff = min(diff, DIFF_EASY); } if (is_atleastone(dlines, dline_index)) { solver_set_line(sstate, line1_index, LINE_YES); solver_set_line(sstate, line2_index, LINE_YES); diff = min(diff, DIFF_EASY); } } if (yes == 1) { if (set_atmostone(dlines, dline_index)) diff = min(diff, DIFF_NORMAL); if (unknown == 2) { if (set_atleastone(dlines, dline_index)) diff = min(diff, DIFF_NORMAL); } } /* More advanced deduction that allows propagation along diagonal * chains of faces connected by dots, for example: 3-2-...-2-3 * in square grids. */ if (sstate->diff >= DIFF_TRICKY) { /* If we have atleastone set for this dline, infer * atmostone for each "opposite" dline (that is, each * dline without edges in common with this one). * Again, this test is only worth doing if both these * lines are UNKNOWN. For if one of these lines were YES, * the (yes == 1) test above would kick in instead. */ if (is_atleastone(dlines, dline_index)) { int opp; for (opp = 0; opp < N; opp++) { int opp_dline_index; if (opp == j || opp == j+1 || opp == j-1) continue; if (j == 0 && opp == N-1) continue; if (j == N-1 && opp == 0) continue; opp_dline_index = dline_index_from_dot(g, d, opp); if (set_atmostone(dlines, opp_dline_index)) diff = min(diff, DIFF_NORMAL); } if (yes == 0 && is_atmostone(dlines, dline_index)) { /* This dline has *exactly* one YES and there are no * other YESs. This allows more deductions. */ if (unknown == 3) { /* Third unknown must be YES */ for (opp = 0; opp < N; opp++) { int opp_index; if (opp == j || opp == k) continue; opp_index = d->edges[opp] - g->edges; if (state->lines[opp_index] == LINE_UNKNOWN) { solver_set_line(sstate, opp_index, LINE_YES); diff = min(diff, DIFF_EASY); } } } else if (unknown == 4) { /* Exactly one of opposite UNKNOWNS is YES. We've * already set atmostone, so set atleastone as * well. */ if (dline_set_opp_atleastone(sstate, d, j)) diff = min(diff, DIFF_NORMAL); } } } } } } return diff; } static int linedsf_deductions(solver_state *sstate) { game_state *state = sstate->state; grid *g = state->game_grid; char *dlines = sstate->dlines; int i; int diff = DIFF_MAX; int diff_tmp; /* ------ Face deductions ------ */ /* A fully-general linedsf deduction seems overly complicated * (I suspect the problem is NP-complete, though in practice it might just * be doable because faces are limited in size). * For simplicity, we only consider *pairs* of LINE_UNKNOWNS that are * known to be identical. If setting them both to YES (or NO) would break * the clue, set them to NO (or YES). */ for (i = 0; i < g->num_faces; i++) { int N, yes, no, unknown; int clue; if (sstate->face_solved[i]) continue; clue = state->clues[i]; if (clue < 0) continue; N = g->faces[i].order; yes = sstate->face_yes_count[i]; if (yes + 1 == clue) { if (face_setall_identical(sstate, i, LINE_NO)) diff = min(diff, DIFF_EASY); } no = sstate->face_no_count[i]; if (no + 1 == N - clue) { if (face_setall_identical(sstate, i, LINE_YES)) diff = min(diff, DIFF_EASY); } /* Reload YES count, it might have changed */ yes = sstate->face_yes_count[i]; unknown = N - no - yes; /* Deductions with small number of LINE_UNKNOWNs, based on overall * parity of lines. */ diff_tmp = parity_deductions(sstate, g->faces[i].edges, (clue - yes) % 2, unknown); diff = min(diff, diff_tmp); } /* ------ Dot deductions ------ */ for (i = 0; i < g->num_dots; i++) { grid_dot *d = g->dots + i; int N = d->order; int j; int yes, no, unknown; /* Go through dlines, and do any dline<->linedsf deductions wherever * we find two UNKNOWNS. */ for (j = 0; j < N; j++) { int dline_index = dline_index_from_dot(g, d, j); int line1_index; int line2_index; int can1, can2, inv1, inv2; int j2; line1_index = d->edges[j] - g->edges; if (state->lines[line1_index] != LINE_UNKNOWN) continue; j2 = j + 1; if (j2 == N) j2 = 0; line2_index = d->edges[j2] - g->edges; if (state->lines[line2_index] != LINE_UNKNOWN) continue; /* Infer dline flags from linedsf */ can1 = edsf_canonify(sstate->linedsf, line1_index, &inv1); can2 = edsf_canonify(sstate->linedsf, line2_index, &inv2); if (can1 == can2 && inv1 != inv2) { /* These are opposites, so set dline atmostone/atleastone */ if (set_atmostone(dlines, dline_index)) diff = min(diff, DIFF_NORMAL); if (set_atleastone(dlines, dline_index)) diff = min(diff, DIFF_NORMAL); continue; } /* Infer linedsf from dline flags */ if (is_atmostone(dlines, dline_index) && is_atleastone(dlines, dline_index)) { if (merge_lines(sstate, line1_index, line2_index, 1)) diff = min(diff, DIFF_HARD); } } /* Deductions with small number of LINE_UNKNOWNs, based on overall * parity of lines. */ yes = sstate->dot_yes_count[i]; no = sstate->dot_no_count[i]; unknown = N - yes - no; diff_tmp = parity_deductions(sstate, d->edges, yes % 2, unknown); diff = min(diff, diff_tmp); } /* ------ Edge dsf deductions ------ */ /* If the state of a line is known, deduce the state of its canonical line * too, and vice versa. */ for (i = 0; i < g->num_edges; i++) { int can, inv; enum line_state s; can = edsf_canonify(sstate->linedsf, i, &inv); if (can == i) continue; s = sstate->state->lines[can]; if (s != LINE_UNKNOWN) { if (solver_set_line(sstate, i, inv ? OPP(s) : s)) diff = min(diff, DIFF_EASY); } else { s = sstate->state->lines[i]; if (s != LINE_UNKNOWN) { if (solver_set_line(sstate, can, inv ? OPP(s) : s)) diff = min(diff, DIFF_EASY); } } } return diff; } static int loop_deductions(solver_state *sstate) { int edgecount = 0, clues = 0, satclues = 0, sm1clues = 0; game_state *state = sstate->state; grid *g = state->game_grid; int shortest_chainlen = g->num_dots; int loop_found = FALSE; int dots_connected; int progress = FALSE; int i; /* * Go through the grid and update for all the new edges. * Since merge_dots() is idempotent, the simplest way to * do this is just to update for _all_ the edges. * Also, while we're here, we count the edges. */ for (i = 0; i < g->num_edges; i++) { if (state->lines[i] == LINE_YES) { loop_found |= merge_dots(sstate, i); edgecount++; } } /* * Count the clues, count the satisfied clues, and count the * satisfied-minus-one clues. */ for (i = 0; i < g->num_faces; i++) { int c = state->clues[i]; if (c >= 0) { int o = sstate->face_yes_count[i]; if (o == c) satclues++; else if (o == c-1) sm1clues++; clues++; } } for (i = 0; i < g->num_dots; ++i) { dots_connected = sstate->looplen[dsf_canonify(sstate->dotdsf, i)]; if (dots_connected > 1) shortest_chainlen = min(shortest_chainlen, dots_connected); } assert(sstate->solver_status == SOLVER_INCOMPLETE); if (satclues == clues && shortest_chainlen == edgecount) { sstate->solver_status = SOLVER_SOLVED; /* This discovery clearly counts as progress, even if we haven't * just added any lines or anything */ progress = TRUE; goto finished_loop_deductionsing; } /* * Now go through looking for LINE_UNKNOWN edges which * connect two dots that are already in the same * equivalence class. If we find one, test to see if the * loop it would create is a solution. */ for (i = 0; i < g->num_edges; i++) { grid_edge *e = g->edges + i; int d1 = e->dot1 - g->dots; int d2 = e->dot2 - g->dots; int eqclass, val; if (state->lines[i] != LINE_UNKNOWN) continue; eqclass = dsf_canonify(sstate->dotdsf, d1); if (eqclass != dsf_canonify(sstate->dotdsf, d2)) continue; val = LINE_NO; /* loop is bad until proven otherwise */ /* * This edge would form a loop. Next * question: how long would the loop be? * Would it equal the total number of edges * (plus the one we'd be adding if we added * it)? */ if (sstate->looplen[eqclass] == edgecount + 1) { int sm1_nearby; /* * This edge would form a loop which * took in all the edges in the entire * grid. So now we need to work out * whether it would be a valid solution * to the puzzle, which means we have to * check if it satisfies all the clues. * This means that every clue must be * either satisfied or satisfied-minus- * 1, and also that the number of * satisfied-minus-1 clues must be at * most two and they must lie on either * side of this edge. */ sm1_nearby = 0; if (e->face1) { int f = e->face1 - g->faces; int c = state->clues[f]; if (c >= 0 && sstate->face_yes_count[f] == c - 1) sm1_nearby++; } if (e->face2) { int f = e->face2 - g->faces; int c = state->clues[f]; if (c >= 0 && sstate->face_yes_count[f] == c - 1) sm1_nearby++; } if (sm1clues == sm1_nearby && sm1clues + satclues == clues) { val = LINE_YES; /* loop is good! */ } } /* * Right. Now we know that adding this edge * would form a loop, and we know whether * that loop would be a viable solution or * not. * * If adding this edge produces a solution, * then we know we've found _a_ solution but * we don't know that it's _the_ solution - * if it were provably the solution then * we'd have deduced this edge some time ago * without the need to do loop detection. So * in this state we return SOLVER_AMBIGUOUS, * which has the effect that hitting Solve * on a user-provided puzzle will fill in a * solution but using the solver to * construct new puzzles won't consider this * a reasonable deduction for the user to * make. */ progress = solver_set_line(sstate, i, val); assert(progress == TRUE); if (val == LINE_YES) { sstate->solver_status = SOLVER_AMBIGUOUS; goto finished_loop_deductionsing; } } finished_loop_deductionsing: return progress ? DIFF_EASY : DIFF_MAX; } /* This will return a dynamically allocated solver_state containing the (more) * solved grid */ static solver_state *solve_game_rec(const solver_state *sstate_start) { solver_state *sstate; /* Index of the solver we should call next. */ int i = 0; /* As a speed-optimisation, we avoid re-running solvers that we know * won't make any progress. This happens when a high-difficulty * solver makes a deduction that can only help other high-difficulty * solvers. * For example: if a new 'dline' flag is set by dline_deductions, the * trivial_deductions solver cannot do anything with this information. * If we've already run the trivial_deductions solver (because it's * earlier in the list), there's no point running it again. * * Therefore: if a solver is earlier in the list than "threshold_index", * we don't bother running it if it's difficulty level is less than * "threshold_diff". */ int threshold_diff = 0; int threshold_index = 0; sstate = dup_solver_state(sstate_start); check_caches(sstate); while (i < NUM_SOLVERS) { if (sstate->solver_status == SOLVER_MISTAKE) return sstate; if (sstate->solver_status == SOLVER_SOLVED || sstate->solver_status == SOLVER_AMBIGUOUS) { /* solver finished */ break; } if ((solver_diffs[i] >= threshold_diff || i >= threshold_index) && solver_diffs[i] <= sstate->diff) { /* current_solver is eligible, so use it */ int next_diff = solver_fns[i](sstate); if (next_diff != DIFF_MAX) { /* solver made progress, so use new thresholds and * start again at top of list. */ threshold_diff = next_diff; threshold_index = i; i = 0; continue; } } /* current_solver is ineligible, or failed to make progress, so * go to the next solver in the list */ i++; } if (sstate->solver_status == SOLVER_SOLVED || sstate->solver_status == SOLVER_AMBIGUOUS) { /* s/LINE_UNKNOWN/LINE_NO/g */ array_setall(sstate->state->lines, LINE_UNKNOWN, LINE_NO, sstate->state->game_grid->num_edges); return sstate; } return sstate; } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { char *soln = NULL; solver_state *sstate, *new_sstate; sstate = new_solver_state(state, DIFF_MAX); new_sstate = solve_game_rec(sstate); if (new_sstate->solver_status == SOLVER_SOLVED) { soln = encode_solve_move(new_sstate->state); } else if (new_sstate->solver_status == SOLVER_AMBIGUOUS) { soln = encode_solve_move(new_sstate->state); /**error = "Solver found ambiguous solutions"; */ } else { soln = encode_solve_move(new_sstate->state); /**error = "Solver failed"; */ } free_solver_state(new_sstate); free_solver_state(sstate); return soln; } /* ---------------------------------------------------------------------- * Drawing and mouse-handling */ static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { grid *g = state->game_grid; grid_edge *e; int i; char *ret, buf[80]; char button_char = ' '; enum line_state old_state; button &= ~MOD_MASK; /* Convert mouse-click (x,y) to grid coordinates */ x -= BORDER(ds->tilesize); y -= BORDER(ds->tilesize); x = x * g->tilesize / ds->tilesize; y = y * g->tilesize / ds->tilesize; x += g->lowest_x; y += g->lowest_y; e = grid_nearest_edge(g, x, y); if (e == NULL) return NULL; i = e - g->edges; /* I think it's only possible to play this game with mouse clicks, sorry */ /* Maybe will add mouse drag support some time */ old_state = state->lines[i]; switch (button) { case LEFT_BUTTON: switch (old_state) { case LINE_UNKNOWN: button_char = 'y'; break; case LINE_YES: #ifdef STYLUS_BASED button_char = 'n'; break; #endif case LINE_NO: button_char = 'u'; break; } break; case MIDDLE_BUTTON: button_char = 'u'; break; case RIGHT_BUTTON: switch (old_state) { case LINE_UNKNOWN: button_char = 'n'; break; case LINE_NO: #ifdef STYLUS_BASED button_char = 'y'; break; #endif case LINE_YES: button_char = 'u'; break; } break; default: return NULL; } sprintf(buf, "%d%c", i, (int)button_char); ret = dupstr(buf); return ret; } static game_state *execute_move(const game_state *state, const char *move) { int i; game_state *newstate = dup_game(state); if (move[0] == 'S') { move++; newstate->cheated = TRUE; } while (*move) { i = atoi(move); if (i < 0 || i >= newstate->game_grid->num_edges) goto fail; move += strspn(move, "1234567890"); switch (*(move++)) { case 'y': newstate->lines[i] = LINE_YES; break; case 'n': newstate->lines[i] = LINE_NO; break; case 'u': newstate->lines[i] = LINE_UNKNOWN; break; default: goto fail; } } /* * Check for completion. */ if (check_completion(newstate)) newstate->solved = TRUE; return newstate; fail: free_game(newstate); return NULL; } /* ---------------------------------------------------------------------- * Drawing routines. */ /* Convert from grid coordinates to screen coordinates */ static void grid_to_screen(const game_drawstate *ds, const grid *g, int grid_x, int grid_y, int *x, int *y) { *x = grid_x - g->lowest_x; *y = grid_y - g->lowest_y; *x = *x * ds->tilesize / g->tilesize; *y = *y * ds->tilesize / g->tilesize; *x += BORDER(ds->tilesize); *y += BORDER(ds->tilesize); } /* Returns (into x,y) position of centre of face for rendering the text clue. */ static void face_text_pos(const game_drawstate *ds, const grid *g, grid_face *f, int *xret, int *yret) { int faceindex = f - g->faces; /* * Return the cached position for this face, if we've already * worked it out. */ if (ds->textx[faceindex] >= 0) { *xret = ds->textx[faceindex]; *yret = ds->texty[faceindex]; return; } /* * Otherwise, use the incentre computed by grid.c and convert it * to screen coordinates. */ grid_find_incentre(f); grid_to_screen(ds, g, f->ix, f->iy, &ds->textx[faceindex], &ds->texty[faceindex]); *xret = ds->textx[faceindex]; *yret = ds->texty[faceindex]; } static void face_text_bbox(game_drawstate *ds, grid *g, grid_face *f, int *x, int *y, int *w, int *h) { int xx, yy; face_text_pos(ds, g, f, &xx, &yy); /* There seems to be a certain amount of trial-and-error involved * in working out the correct bounding-box for the text. */ *x = xx - ds->tilesize/4 - 1; *y = yy - ds->tilesize/4 - 3; *w = ds->tilesize/2 + 2; *h = ds->tilesize/2 + 5; } static void game_redraw_clue(drawing *dr, game_drawstate *ds, const game_state *state, int i) { grid *g = state->game_grid; grid_face *f = g->faces + i; int x, y; char c[20]; sprintf(c, "%d", state->clues[i]); face_text_pos(ds, g, f, &x, &y); draw_text(dr, x, y, FONT_VARIABLE, ds->tilesize/2, ALIGN_VCENTRE | ALIGN_HCENTRE, ds->clue_error[i] ? COL_MISTAKE : ds->clue_satisfied[i] ? COL_SATISFIED : COL_FOREGROUND, c); } static void edge_bbox(game_drawstate *ds, grid *g, grid_edge *e, int *x, int *y, int *w, int *h) { int x1 = e->dot1->x; int y1 = e->dot1->y; int x2 = e->dot2->x; int y2 = e->dot2->y; int xmin, xmax, ymin, ymax; grid_to_screen(ds, g, x1, y1, &x1, &y1); grid_to_screen(ds, g, x2, y2, &x2, &y2); /* Allow extra margin for dots, and thickness of lines */ xmin = min(x1, x2) - 2; xmax = max(x1, x2) + 2; ymin = min(y1, y2) - 2; ymax = max(y1, y2) + 2; *x = xmin; *y = ymin; *w = xmax - xmin + 1; *h = ymax - ymin + 1; } static void dot_bbox(game_drawstate *ds, grid *g, grid_dot *d, int *x, int *y, int *w, int *h) { int x1, y1; grid_to_screen(ds, g, d->x, d->y, &x1, &y1); *x = x1 - 2; *y = y1 - 2; *w = 5; *h = 5; } static const int loopy_line_redraw_phases[] = { COL_FAINT, COL_LINEUNKNOWN, COL_FOREGROUND, COL_HIGHLIGHT, COL_MISTAKE }; #define NPHASES lenof(loopy_line_redraw_phases) static void game_redraw_line(drawing *dr, game_drawstate *ds, const game_state *state, int i, int phase) { grid *g = state->game_grid; grid_edge *e = g->edges + i; int x1, x2, y1, y2; int line_colour; if (state->line_errors[i]) line_colour = COL_MISTAKE; else if (state->lines[i] == LINE_UNKNOWN) line_colour = COL_LINEUNKNOWN; else if (state->lines[i] == LINE_NO) line_colour = COL_FAINT; else if (ds->flashing) line_colour = COL_HIGHLIGHT; else line_colour = COL_FOREGROUND; if (line_colour != loopy_line_redraw_phases[phase]) return; /* Convert from grid to screen coordinates */ grid_to_screen(ds, g, e->dot1->x, e->dot1->y, &x1, &y1); grid_to_screen(ds, g, e->dot2->x, e->dot2->y, &x2, &y2); if (line_colour == COL_FAINT) { static int draw_faint_lines = -1; if (draw_faint_lines < 0) { char *env = getenv("LOOPY_FAINT_LINES"); draw_faint_lines = (!env || (env[0] == 'y' || env[0] == 'Y')); } if (draw_faint_lines) draw_line(dr, x1, y1, x2, y2, line_colour); } else { draw_thick_line(dr, 3.0, x1 + 0.5, y1 + 0.5, x2 + 0.5, y2 + 0.5, line_colour); } } static void game_redraw_dot(drawing *dr, game_drawstate *ds, const game_state *state, int i) { grid *g = state->game_grid; grid_dot *d = g->dots + i; int x, y; grid_to_screen(ds, g, d->x, d->y, &x, &y); draw_circle(dr, x, y, 2, COL_FOREGROUND, COL_FOREGROUND); } static int boxes_intersect(int x0, int y0, int w0, int h0, int x1, int y1, int w1, int h1) { /* * Two intervals intersect iff neither is wholly on one side of * the other. Two boxes intersect iff their horizontal and * vertical intervals both intersect. */ return (x0 < x1+w1 && x1 < x0+w0 && y0 < y1+h1 && y1 < y0+h0); } static void game_redraw_in_rect(drawing *dr, game_drawstate *ds, const game_state *state, int x, int y, int w, int h) { grid *g = state->game_grid; int i, phase; int bx, by, bw, bh; clip(dr, x, y, w, h); draw_rect(dr, x, y, w, h, COL_BACKGROUND); for (i = 0; i < g->num_faces; i++) { if (state->clues[i] >= 0) { face_text_bbox(ds, g, &g->faces[i], &bx, &by, &bw, &bh); if (boxes_intersect(x, y, w, h, bx, by, bw, bh)) game_redraw_clue(dr, ds, state, i); } } for (phase = 0; phase < NPHASES; phase++) { for (i = 0; i < g->num_edges; i++) { edge_bbox(ds, g, &g->edges[i], &bx, &by, &bw, &bh); if (boxes_intersect(x, y, w, h, bx, by, bw, bh)) game_redraw_line(dr, ds, state, i, phase); } } for (i = 0; i < g->num_dots; i++) { dot_bbox(ds, g, &g->dots[i], &bx, &by, &bw, &bh); if (boxes_intersect(x, y, w, h, bx, by, bw, bh)) game_redraw_dot(dr, ds, state, i); } unclip(dr); draw_update(dr, x, y, w, h); } 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) { #define REDRAW_OBJECTS_LIMIT 16 /* Somewhat arbitrary tradeoff */ grid *g = state->game_grid; int border = BORDER(ds->tilesize); int i; int flash_changed; int redraw_everything = FALSE; int edges[REDRAW_OBJECTS_LIMIT], nedges = 0; int faces[REDRAW_OBJECTS_LIMIT], nfaces = 0; /* Redrawing is somewhat involved. * * An update can theoretically affect an arbitrary number of edges * (consider, for example, completing or breaking a cycle which doesn't * satisfy all the clues -- we'll switch many edges between error and * normal states). On the other hand, redrawing the whole grid takes a * while, making the game feel sluggish, and many updates are actually * quite well localized. * * This redraw algorithm attempts to cope with both situations gracefully * and correctly. For localized changes, we set a clip rectangle, fill * it with background, and then redraw (a plausible but conservative * guess at) the objects which intersect the rectangle; if several * objects need redrawing, we'll do them individually. However, if lots * of objects are affected, we'll just redraw everything. * * The reason for all of this is that it's just not safe to do the redraw * piecemeal. If you try to draw an antialiased diagonal line over * itself, you get a slightly thicker antialiased diagonal line, which * looks rather ugly after a while. * * So, we take two passes over the grid. The first attempts to work out * what needs doing, and the second actually does it. */ if (!ds->started) { redraw_everything = TRUE; /* * But we must still go through the upcoming loops, so that we * set up stuff in ds correctly for the initial redraw. */ } /* First, trundle through the faces. */ for (i = 0; i < g->num_faces; i++) { grid_face *f = g->faces + i; int sides = f->order; int clue_mistake; int clue_satisfied; int n = state->clues[i]; if (n < 0) continue; clue_mistake = (face_order(state, i, LINE_YES) > n || face_order(state, i, LINE_NO ) > (sides-n)); clue_satisfied = (face_order(state, i, LINE_YES) == n && face_order(state, i, LINE_NO ) == (sides-n)); if (clue_mistake != ds->clue_error[i] || clue_satisfied != ds->clue_satisfied[i]) { ds->clue_error[i] = clue_mistake; ds->clue_satisfied[i] = clue_satisfied; if (nfaces == REDRAW_OBJECTS_LIMIT) redraw_everything = TRUE; else faces[nfaces++] = i; } } /* Work out what the flash state needs to be. */ if (flashtime > 0 && (flashtime <= FLASH_TIME/3 || flashtime >= FLASH_TIME*2/3)) { flash_changed = !ds->flashing; ds->flashing = TRUE; } else { flash_changed = ds->flashing; ds->flashing = FALSE; } /* Now, trundle through the edges. */ for (i = 0; i < g->num_edges; i++) { char new_ds = state->line_errors[i] ? DS_LINE_ERROR : state->lines[i]; if (new_ds != ds->lines[i] || (flash_changed && state->lines[i] == LINE_YES)) { ds->lines[i] = new_ds; if (nedges == REDRAW_OBJECTS_LIMIT) redraw_everything = TRUE; else edges[nedges++] = i; } } /* Pass one is now done. Now we do the actual drawing. */ if (redraw_everything) { int grid_width = g->highest_x - g->lowest_x; int grid_height = g->highest_y - g->lowest_y; int w = grid_width * ds->tilesize / g->tilesize; int h = grid_height * ds->tilesize / g->tilesize; game_redraw_in_rect(dr, ds, state, 0, 0, w + 2*border + 1, h + 2*border + 1); } else { /* Right. Now we roll up our sleeves. */ for (i = 0; i < nfaces; i++) { grid_face *f = g->faces + faces[i]; int x, y, w, h; face_text_bbox(ds, g, f, &x, &y, &w, &h); game_redraw_in_rect(dr, ds, state, x, y, w, h); } for (i = 0; i < nedges; i++) { grid_edge *e = g->edges + edges[i]; int x, y, w, h; edge_bbox(ds, g, e, &x, &y, &w, &h); game_redraw_in_rect(dr, ds, state, x, y, w, h); } } ds->started = TRUE; } static float game_flash_length(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui) { if (!oldstate->solved && newstate->solved && !oldstate->cheated && !newstate->cheated) { return FLASH_TIME; } return 0.0F; } static int game_status(const game_state *state) { return state->solved ? +1 : 0; } static void game_print_size(const game_params *params, float *x, float *y) { int pw, ph; /* * I'll use 7mm "squares" by default. */ game_compute_size(params, 700, &pw, &ph); *x = pw / 100.0F; *y = ph / 100.0F; } static void game_print(drawing *dr, const game_state *state, int tilesize) { int ink = print_mono_colour(dr, 0); int i; game_drawstate ads, *ds = &ads; grid *g = state->game_grid; ds->tilesize = tilesize; ds->textx = snewn(g->num_faces, int); ds->texty = snewn(g->num_faces, int); for (i = 0; i < g->num_faces; i++) ds->textx[i] = ds->texty[i] = -1; for (i = 0; i < g->num_dots; i++) { int x, y; grid_to_screen(ds, g, g->dots[i].x, g->dots[i].y, &x, &y); draw_circle(dr, x, y, ds->tilesize / 15, ink, ink); } /* * Clues. */ for (i = 0; i < g->num_faces; i++) { grid_face *f = g->faces + i; int clue = state->clues[i]; if (clue >= 0) { char c[20]; int x, y; sprintf(c, "%d", state->clues[i]); face_text_pos(ds, g, f, &x, &y); draw_text(dr, x, y, FONT_VARIABLE, ds->tilesize / 2, ALIGN_VCENTRE | ALIGN_HCENTRE, ink, c); } } /* * Lines. */ for (i = 0; i < g->num_edges; i++) { int thickness = (state->lines[i] == LINE_YES) ? 30 : 150; grid_edge *e = g->edges + i; int x1, y1, x2, y2; grid_to_screen(ds, g, e->dot1->x, e->dot1->y, &x1, &y1); grid_to_screen(ds, g, e->dot2->x, e->dot2->y, &x2, &y2); if (state->lines[i] == LINE_YES) { /* (dx, dy) points from (x1, y1) to (x2, y2). * The line is then "fattened" in a perpendicular * direction to create a thin rectangle. */ double d = sqrt(SQ((double)x1 - x2) + SQ((double)y1 - y2)); double dx = (x2 - x1) / d; double dy = (y2 - y1) / d; int points[8]; dx = (dx * ds->tilesize) / thickness; dy = (dy * ds->tilesize) / thickness; points[0] = x1 + (int)dy; points[1] = y1 - (int)dx; points[2] = x1 - (int)dy; points[3] = y1 + (int)dx; points[4] = x2 - (int)dy; points[5] = y2 + (int)dx; points[6] = x2 + (int)dy; points[7] = y2 - (int)dx; draw_polygon(dr, points, 4, ink, ink); } else { /* Draw a dotted line */ int divisions = 6; int j; for (j = 1; j < divisions; j++) { /* Weighted average */ int x = (x1 * (divisions -j) + x2 * j) / divisions; int y = (y1 * (divisions -j) + y2 * j) / divisions; draw_circle(dr, x, y, ds->tilesize / thickness, ink, ink); } } } sfree(ds->textx); sfree(ds->texty); } #ifdef COMBINED #define thegame loopy #endif const struct game thegame = { "Loopy", "games.loopy", "loopy", default_params, game_fetch_preset, 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, 1, solve_game, TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, decode_ui, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, TRUE, FALSE, game_print_size, game_print, FALSE /* wants_statusbar */, FALSE, game_timing_state, 0, /* mouse_priorities */ }; #ifdef STANDALONE_SOLVER /* * Half-hearted standalone solver. It can't output the solution to * anything but a square puzzle, and it can't log the deductions * it makes either. But it can solve square puzzles, and more * importantly it can use its solver to grade the difficulty of * any puzzle you give it. */ #include int main(int argc, char **argv) { game_params *p; game_state *s; char *id = NULL, *desc, *err; int grade = FALSE; int ret, diff; #if 0 /* verbose solver not supported here (yet) */ int really_verbose = FALSE; #endif while (--argc > 0) { char *p = *++argv; #if 0 /* verbose solver not supported here (yet) */ if (!strcmp(p, "-v")) { really_verbose = TRUE; } else #endif 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); /* * When solving an Easy 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 */ for (diff = 0; diff < DIFF_MAX; diff++) { solver_state *sstate_new; solver_state *sstate = new_solver_state((game_state *)s, diff); sstate_new = solve_game_rec(sstate); if (sstate_new->solver_status == SOLVER_MISTAKE) ret = 0; else if (sstate_new->solver_status == SOLVER_SOLVED) ret = 1; else ret = 2; free_solver_state(sstate_new); free_solver_state(sstate); if (ret < 2) break; } if (diff == DIFF_MAX) { if (grade) printf("Difficulty rating: harder than Hard, or ambiguous\n"); else printf("Unable to find a unique solution\n"); } else { if (grade) { if (ret == 0) printf("Difficulty rating: impossible (no solution exists)\n"); else if (ret == 1) printf("Difficulty rating: %s\n", diffnames[diff]); } else { solver_state *sstate_new; solver_state *sstate = new_solver_state((game_state *)s, diff); /* If we supported a verbose solver, we'd set verbosity here */ sstate_new = solve_game_rec(sstate); if (sstate_new->solver_status == SOLVER_MISTAKE) printf("Puzzle is inconsistent\n"); else { assert(sstate_new->solver_status == SOLVER_SOLVED); if (s->grid_type == 0) { fputs(game_text_format(sstate_new->state), stdout); } else { printf("Unable to output non-square grids\n"); } } free_solver_state(sstate_new); free_solver_state(sstate); } } return 0; } #endif /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/magnets.c0000644000175300017530000022461512132232554014240 0ustar simonsimon/* * magnets.c: implementation of janko.at 'magnets puzzle' game. * * http://64.233.179.104/translate_c?hl=en&u=http://www.janko.at/Raetsel/Magnete/Beispiel.htm * * Puzzle definition is just the size, and then the list of + (across then * down) and - (across then down) present, then domino edges. * * An example: * * + 2 0 1 * +-----+ * 1|+ -| |1 * |-+-+ | * 0|-|#| |1 * | +-+-| * 2|+|- +|1 * +-----+ * 1 2 0 - * * 3x3:201,102,120,111,LRTT*BBLR * * 'Zotmeister' examples: * 5x5:.2..1,3..1.,.2..2,2..2.,LRLRTTLRTBBT*BTTBLRBBLRLR * 9x9:3.51...33,.2..23.13,..33.33.2,12...5.3.,**TLRTLR*,*TBLRBTLR,TBLRLRBTT,BLRTLRTBB,LRTB*TBLR,LRBLRBLRT,TTTLRLRTB,BBBTLRTB*,*LRBLRB** * * Janko 6x6 with solution: * 6x6:322223,323132,232223,232223,LRTLRTTTBLRBBBTTLRLRBBLRTTLRTTBBLRBB * * janko 8x8: * 8x8:34131323,23131334,43122323,21332243,LRTLRLRT,LRBTTTTB,LRTBBBBT,TTBTLRTB,BBTBTTBT,TTBTBBTB,BBTBLRBT,LRBLRLRB */ #include #include #include #include #include #include #include "puzzles.h" #ifdef STANDALONE_SOLVER int verbose = 0; #endif enum { COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT, COL_TEXT, COL_ERROR, COL_CURSOR, COL_NEUTRAL, COL_NEGATIVE, COL_POSITIVE, COL_NOT, NCOLOURS }; /* Cell states. */ enum { EMPTY = 0, NEUTRAL = EMPTY, POSITIVE = 1, NEGATIVE = 2 }; #if defined DEBUGGING || defined STANDALONE_SOLVER static const char *cellnames[3] = { "neutral", "positive", "negative" }; #define NAME(w) ( ((w) < 0 || (w) > 2) ? "(out of range)" : cellnames[(w)] ) #endif #define GRID2CHAR(g) ( ((g) >= 0 && (g) <= 2) ? ".+-"[(g)] : '?' ) #define CHAR2GRID(c) ( (c) == '+' ? POSITIVE : (c) == '-' ? NEGATIVE : NEUTRAL ) #define INGRID(s,x,y) ((x) >= 0 && (x) < (s)->w && (y) >= 0 && (y) < (s)->h) #define OPPOSITE(x) ( ((x)*2) % 3 ) /* 0 --> 0, 1 --> 2, 2 --> 4 --> 1 */ #define FLASH_TIME 0.7F /* Macro ickery copied from slant.c */ #define DIFFLIST(A) \ A(EASY,Easy,e) \ A(TRICKY,Tricky,t) #define ENUM(upper,title,lower) DIFF_ ## upper, #define TITLE(upper,title,lower) #title, #define ENCODE(upper,title,lower) #lower #define CONFIG(upper,title,lower) ":" #title enum { DIFFLIST(ENUM) DIFFCOUNT }; static char const *const magnets_diffnames[] = { DIFFLIST(TITLE) "(count)" }; static char const magnets_diffchars[] = DIFFLIST(ENCODE); #define DIFFCONFIG DIFFLIST(CONFIG) /* --------------------------------------------------------------- */ /* Game parameter functions. */ struct game_params { int w, h, diff, stripclues; }; #define DEFAULT_PRESET 2 static const struct game_params magnets_presets[] = { {6, 5, DIFF_EASY, 0}, {6, 5, DIFF_TRICKY, 0}, {6, 5, DIFF_TRICKY, 1}, {8, 7, DIFF_EASY, 0}, {8, 7, DIFF_TRICKY, 0}, {8, 7, DIFF_TRICKY, 1}, {10, 9, DIFF_TRICKY, 0}, {10, 9, DIFF_TRICKY, 1} }; static game_params *default_params(void) { game_params *ret = snew(game_params); *ret = magnets_presets[DEFAULT_PRESET]; return ret; } static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char buf[64]; if (i < 0 || i >= lenof(magnets_presets)) return FALSE; ret = default_params(); *ret = magnets_presets[i]; /* struct copy */ *params = ret; sprintf(buf, "%dx%d %s%s", magnets_presets[i].w, magnets_presets[i].h, magnets_diffnames[magnets_presets[i].diff], magnets_presets[i].stripclues ? ", strip clues" : ""); *name = dupstr(buf); 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 *ret, char const *string) { ret->w = ret->h = atoi(string); while (*string && isdigit((unsigned char) *string)) ++string; if (*string == 'x') { string++; ret->h = atoi(string); while (*string && isdigit((unsigned char)*string)) string++; } ret->diff = DIFF_EASY; if (*string == 'd') { int i; string++; for (i = 0; i < DIFFCOUNT; i++) if (*string == magnets_diffchars[i]) ret->diff = i; if (*string) string++; } ret->stripclues = 0; if (*string == 'S') { string++; ret->stripclues = 1; } } static char *encode_params(const game_params *params, int full) { char buf[256]; sprintf(buf, "%dx%d", params->w, params->h); if (full) sprintf(buf + strlen(buf), "d%c%s", magnets_diffchars[params->diff], params->stripclues ? "S" : ""); return dupstr(buf); } static config_item *game_configure(const game_params *params) { config_item *ret; char buf[64]; ret = snewn(5, config_item); ret[0].name = "Width"; ret[0].type = C_STRING; sprintf(buf, "%d", params->w); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Difficulty"; ret[2].type = C_CHOICES; ret[2].sval = DIFFCONFIG; ret[2].ival = params->diff; ret[3].name = "Strip clues"; ret[3].type = C_BOOLEAN; ret[3].sval = NULL; ret[3].ival = params->stripclues; ret[4].name = NULL; ret[4].type = C_END; ret[4].sval = NULL; ret[4].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); ret->diff = cfg[2].ival; ret->stripclues = cfg[3].ival; return ret; } static char *validate_params(const game_params *params, int full) { if (params->w < 2) return "Width must be at least one"; if (params->h < 2) return "Height must be at least one"; if (params->diff < 0 || params->diff >= DIFFCOUNT) return "Unknown difficulty level"; return NULL; } /* --------------------------------------------------------------- */ /* Game state allocation, deallocation. */ struct game_common { int *dominoes; /* size w*h, dominoes[i] points to other end of domino. */ int *rowcount; /* size 3*h, array of [plus, minus, neutral] counts */ int *colcount; /* size 3*w, ditto */ int refcount; }; #define GS_ERROR 1 #define GS_SET 2 #define GS_NOTPOSITIVE 4 #define GS_NOTNEGATIVE 8 #define GS_NOTNEUTRAL 16 #define GS_MARK 32 #define GS_NOTMASK (GS_NOTPOSITIVE|GS_NOTNEGATIVE|GS_NOTNEUTRAL) #define NOTFLAG(w) ( (w) == NEUTRAL ? GS_NOTNEUTRAL : \ (w) == POSITIVE ? GS_NOTPOSITIVE : \ (w) == NEGATIVE ? GS_NOTNEGATIVE : \ 0 ) #define POSSIBLE(f,w) (!(state->flags[(f)] & NOTFLAG(w))) struct game_state { int w, h, wh; int *grid; /* size w*h, for cell state (pos/neg) */ unsigned int *flags; /* size w*h */ int solved, completed, numbered; struct game_common *common; /* domino layout never changes. */ }; static void clear_state(game_state *ret) { int i; ret->solved = ret->completed = ret->numbered = 0; memset(ret->common->rowcount, 0, ret->h*3*sizeof(int)); memset(ret->common->colcount, 0, ret->w*3*sizeof(int)); for (i = 0; i < ret->wh; i++) { ret->grid[i] = EMPTY; ret->flags[i] = 0; ret->common->dominoes[i] = i; } } static game_state *new_state(int w, int h) { game_state *ret = snew(game_state); memset(ret, 0, sizeof(game_state)); ret->w = w; ret->h = h; ret->wh = w*h; ret->grid = snewn(ret->wh, int); ret->flags = snewn(ret->wh, unsigned int); ret->common = snew(struct game_common); ret->common->refcount = 1; ret->common->dominoes = snewn(ret->wh, int); ret->common->rowcount = snewn(ret->h*3, int); ret->common->colcount = snewn(ret->w*3, int); clear_state(ret); return ret; } static game_state *dup_game(const game_state *src) { game_state *dest = snew(game_state); dest->w = src->w; dest->h = src->h; dest->wh = src->wh; dest->solved = src->solved; dest->completed = src->completed; dest->numbered = src->numbered; dest->common = src->common; dest->common->refcount++; dest->grid = snewn(dest->wh, int); memcpy(dest->grid, src->grid, dest->wh*sizeof(int)); dest->flags = snewn(dest->wh, unsigned int); memcpy(dest->flags, src->flags, dest->wh*sizeof(unsigned int)); return dest; } static void free_game(game_state *state) { state->common->refcount--; if (state->common->refcount == 0) { sfree(state->common->dominoes); sfree(state->common->rowcount); sfree(state->common->colcount); sfree(state->common); } sfree(state->flags); sfree(state->grid); sfree(state); } /* --------------------------------------------------------------- */ /* Game generation and reading. */ /* For a game of size w*h the game description is: * w-sized string of column + numbers (L-R), or '.' for none * semicolon * h-sized string of row + numbers (T-B), or '.' * semicolon * w-sized string of column - numbers (L-R), or '.' * semicolon * h-sized string of row - numbers (T-B), or '.' * semicolon * w*h-sized string of 'L', 'R', 'U', 'D' for domino associations, * or '*' for a black singleton square. * * for a total length of 2w + 2h + wh + 4. */ static char n2c(int num) { /* XXX cloned from singles.c */ if (num == -1) return '.'; if (num < 10) return '0' + num; else if (num < 10+26) return 'a' + num - 10; else return 'A' + num - 10 - 26; return '?'; } static int c2n(char c) { /* XXX cloned from singles.c */ if (isdigit((unsigned char)c)) return (int)(c - '0'); else if (c >= 'a' && c <= 'z') return (int)(c - 'a' + 10); else if (c >= 'A' && c <= 'Z') return (int)(c - 'A' + 10 + 26); return -1; } static const char *readrow(const char *desc, int n, int *array, int off, const char **prob) { int i, num; char c; for (i = 0; i < n; i++) { c = *desc++; if (c == 0) goto badchar; if (c == '.') num = -1; else { num = c2n(c); if (num < 0) goto badchar; } array[i*3+off] = num; } c = *desc++; if (c != ',') goto badchar; return desc; badchar: *prob = (c == 0) ? "Game description too short" : "Game description contained unexpected characters"; return NULL; } static game_state *new_game_int(const game_params *params, const char *desc, const char **prob) { game_state *state = new_state(params->w, params->h); int x, y, idx, *count; char c; *prob = NULL; /* top row, left-to-right */ desc = readrow(desc, state->w, state->common->colcount, POSITIVE, prob); if (*prob) goto done; /* left column, top-to-bottom */ desc = readrow(desc, state->h, state->common->rowcount, POSITIVE, prob); if (*prob) goto done; /* bottom row, left-to-right */ desc = readrow(desc, state->w, state->common->colcount, NEGATIVE, prob); if (*prob) goto done; /* right column, top-to-bottom */ desc = readrow(desc, state->h, state->common->rowcount, NEGATIVE, prob); if (*prob) goto done; /* Add neutral counts (== size - pos - neg) to columns and rows. * Any singleton cells will just be treated as permanently neutral. */ count = state->common->colcount; for (x = 0; x < state->w; x++) { if (count[x*3+POSITIVE] < 0 || count[x*3+NEGATIVE] < 0) count[x*3+NEUTRAL] = -1; else { count[x*3+NEUTRAL] = state->h - count[x*3+POSITIVE] - count[x*3+NEGATIVE]; if (count[x*3+NEUTRAL] < 0) { *prob = "Column counts inconsistent"; goto done; } } } count = state->common->rowcount; for (y = 0; y < state->h; y++) { if (count[y*3+POSITIVE] < 0 || count[y*3+NEGATIVE] < 0) count[y*3+NEUTRAL] = -1; else { count[y*3+NEUTRAL] = state->w - count[y*3+POSITIVE] - count[y*3+NEGATIVE]; if (count[y*3+NEUTRAL] < 0) { *prob = "Row counts inconsistent"; goto done; } } } for (y = 0; y < state->h; y++) { for (x = 0; x < state->w; x++) { idx = y*state->w + x; nextchar: c = *desc++; if (c == 'L') /* this square is LHS of a domino */ state->common->dominoes[idx] = idx+1; else if (c == 'R') /* ... RHS of a domino */ state->common->dominoes[idx] = idx-1; else if (c == 'T') /* ... top of a domino */ state->common->dominoes[idx] = idx+state->w; else if (c == 'B') /* ... bottom of a domino */ state->common->dominoes[idx] = idx-state->w; else if (c == '*') /* singleton */ state->common->dominoes[idx] = idx; else if (c == ',') /* spacer, ignore */ goto nextchar; else goto badchar; } } /* Check dominoes as input are sensibly consistent * (i.e. each end points to the other) */ for (idx = 0; idx < state->wh; idx++) { if (state->common->dominoes[idx] < 0 || state->common->dominoes[idx] > state->wh || state->common->dominoes[state->common->dominoes[idx]] != idx) { *prob = "Domino descriptions inconsistent"; goto done; } if (state->common->dominoes[idx] == idx) { state->grid[idx] = NEUTRAL; state->flags[idx] |= GS_SET; } } /* Success. */ state->numbered = 1; goto done; badchar: *prob = (c == 0) ? "Game description too short" : "Game description contained unexpected characters"; done: if (*prob) { free_game(state); return NULL; } return state; } static char *validate_desc(const game_params *params, const char *desc) { const char *prob; game_state *st = new_game_int(params, desc, &prob); if (!st) return (char*)prob; free_game(st); return NULL; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { const char *prob; game_state *st = new_game_int(params, desc, &prob); assert(st); return st; } static char *generate_desc(game_state *new) { int x, y, idx, other, w = new->w, h = new->h; char *desc = snewn(new->wh + 2*(w + h) + 5, char), *p = desc; for (x = 0; x < w; x++) *p++ = n2c(new->common->colcount[x*3+POSITIVE]); *p++ = ','; for (y = 0; y < h; y++) *p++ = n2c(new->common->rowcount[y*3+POSITIVE]); *p++ = ','; for (x = 0; x < w; x++) *p++ = n2c(new->common->colcount[x*3+NEGATIVE]); *p++ = ','; for (y = 0; y < h; y++) *p++ = n2c(new->common->rowcount[y*3+NEGATIVE]); *p++ = ','; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { idx = y*w + x; other = new->common->dominoes[idx]; if (other == idx) *p++ = '*'; else if (other == idx+1) *p++ = 'L'; else if (other == idx-1) *p++ = 'R'; else if (other == idx+w) *p++ = 'T'; else if (other == idx-w) *p++ = 'B'; else assert(!"mad domino orientation"); } } *p = '\0'; return desc; } static void game_text_hborder(const game_state *state, char **p_r) { char *p = *p_r; int x; *p++ = ' '; *p++ = '+'; for (x = 0; x < state->w*2-1; x++) *p++ = '-'; *p++ = '+'; *p++ = '\n'; *p_r = p; } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { int len, x, y, i; char *ret, *p; len = ((state->w*2)+4) * ((state->h*2)+4) + 2; p = ret = snewn(len, char); /* top row: '+' then column totals for plus. */ *p++ = '+'; for (x = 0; x < state->w; x++) { *p++ = ' '; *p++ = n2c(state->common->colcount[x*3+POSITIVE]); } *p++ = '\n'; /* top border. */ game_text_hborder(state, &p); for (y = 0; y < state->h; y++) { *p++ = n2c(state->common->rowcount[y*3+POSITIVE]); *p++ = '|'; for (x = 0; x < state->w; x++) { i = y*state->w+x; *p++ = state->common->dominoes[i] == i ? '#' : state->grid[i] == POSITIVE ? '+' : state->grid[i] == NEGATIVE ? '-' : state->flags[i] & GS_SET ? '*' : ' '; if (x < (state->w-1)) *p++ = state->common->dominoes[i] == i+1 ? ' ' : '|'; } *p++ = '|'; *p++ = n2c(state->common->rowcount[y*3+NEGATIVE]); *p++ = '\n'; if (y < (state->h-1)) { *p++ = ' '; *p++ = '|'; for (x = 0; x < state->w; x++) { i = y*state->w+x; *p++ = state->common->dominoes[i] == i+state->w ? ' ' : '-'; if (x < (state->w-1)) *p++ = '+'; } *p++ = '|'; *p++ = '\n'; } } /* bottom border. */ game_text_hborder(state, &p); /* bottom row: column totals for minus then '-'. */ *p++ = ' '; for (x = 0; x < state->w; x++) { *p++ = ' '; *p++ = n2c(state->common->colcount[x*3+NEGATIVE]); } *p++ = ' '; *p++ = '-'; *p++ = '\n'; *p++ = '\0'; return ret; } static void game_debug(game_state *state, const char *desc) { char *fmt = game_text_format(state); debug(("%s:\n%s\n", desc, fmt)); sfree(fmt); } enum { ROW, COLUMN }; typedef struct rowcol { int i, di, n, roworcol, num; int *targets; const char *name; } rowcol; static rowcol mkrowcol(const game_state *state, int num, int roworcol) { rowcol rc; rc.roworcol = roworcol; rc.num = num; if (roworcol == ROW) { rc.i = num * state->w; rc.di = 1; rc.n = state->w; rc.targets = &(state->common->rowcount[num*3]); rc.name = "row"; } else if (roworcol == COLUMN) { rc.i = num; rc.di = state->w; rc.n = state->h; rc.targets = &(state->common->colcount[num*3]); rc.name = "column"; } else { assert(!"unknown roworcol"); } return rc; } static int count_rowcol(const game_state *state, int num, int roworcol, int which) { int i, count = 0; rowcol rc = mkrowcol(state, num, roworcol); for (i = 0; i < rc.n; i++, rc.i += rc.di) { if (which < 0) { if (state->grid[rc.i] == EMPTY && !(state->flags[rc.i] & GS_SET)) count++; } else if (state->grid[rc.i] == which) count++; } return count; } static void check_rowcol(game_state *state, int num, int roworcol, int which, int *wrong, int *incomplete) { int count, target = mkrowcol(state, num, roworcol).targets[which]; if (target == -1) return; /* no number to check against. */ count = count_rowcol(state, num, roworcol, which); if (count < target) *incomplete = 1; if (count > target) *wrong = 1; } static int check_completion(game_state *state) { int i, j, x, y, idx, w = state->w, h = state->h; int which = POSITIVE, wrong = 0, incomplete = 0; /* Check row and column counts for magnets. */ for (which = POSITIVE, j = 0; j < 2; which = OPPOSITE(which), j++) { for (i = 0; i < w; i++) check_rowcol(state, i, COLUMN, which, &wrong, &incomplete); for (i = 0; i < h; i++) check_rowcol(state, i, ROW, which, &wrong, &incomplete); } /* Check each domino has been filled, and that we don't have * touching identical terminals. */ for (i = 0; i < state->wh; i++) state->flags[i] &= ~GS_ERROR; for (x = 0; x < w; x++) { for (y = 0; y < h; y++) { idx = y*w + x; if (state->common->dominoes[idx] == idx) continue; /* no domino here */ if (!(state->flags[idx] & GS_SET)) incomplete = 1; which = state->grid[idx]; if (which != NEUTRAL) { #define CHECK(xx,yy) do { \ if (INGRID(state,xx,yy) && \ (state->grid[(yy)*w+(xx)] == which)) { \ wrong = 1; \ state->flags[(yy)*w+(xx)] |= GS_ERROR; \ state->flags[y*w+x] |= GS_ERROR; \ } \ } while(0) CHECK(x,y-1); CHECK(x,y+1); CHECK(x-1,y); CHECK(x+1,y); #undef CHECK } } } return wrong ? -1 : incomplete ? 0 : 1; } static const int dx[4] = {-1, 1, 0, 0}; static const int dy[4] = {0, 0, -1, 1}; static void solve_clearflags(game_state *state) { int i; for (i = 0; i < state->wh; i++) { state->flags[i] &= ~GS_NOTMASK; if (state->common->dominoes[i] != i) state->flags[i] &= ~GS_SET; } } /* Knowing a given cell cannot be a certain colour also tells us * something about the other cell in that domino. */ static int solve_unflag(game_state *state, int i, int which, const char *why, rowcol *rc) { int ii, ret = 0; #if defined DEBUGGING || defined STANDALONE_SOLVER int w = state->w; #endif assert(i >= 0 && i < state->wh); ii = state->common->dominoes[i]; if (ii == i) return 0; if (rc) debug(("solve_unflag: (%d,%d) for %s %d", i%w, i/w, rc->name, rc->num)); if ((state->flags[i] & GS_SET) && (state->grid[i] == which)) { debug(("solve_unflag: (%d,%d) already %s, cannot unflag (for %s).", i%w, i/w, NAME(which), why)); return -1; } if ((state->flags[ii] & GS_SET) && (state->grid[ii] == OPPOSITE(which))) { debug(("solve_unflag: (%d,%d) opposite already %s, cannot unflag (for %s).", ii%w, ii/w, NAME(OPPOSITE(which)), why)); return -1; } if (POSSIBLE(i, which)) { state->flags[i] |= NOTFLAG(which); ret++; debug(("solve_unflag: (%d,%d) CANNOT be %s (%s)", i%w, i/w, NAME(which), why)); } if (POSSIBLE(ii, OPPOSITE(which))) { state->flags[ii] |= NOTFLAG(OPPOSITE(which)); ret++; debug(("solve_unflag: (%d,%d) CANNOT be %s (%s, other half)", ii%w, ii/w, NAME(OPPOSITE(which)), why)); } #ifdef STANDALONE_SOLVER if (verbose && ret) { printf("(%d,%d)", i%w, i/w); if (rc) printf(" in %s %d", rc->name, rc->num); printf(" cannot be %s (%s); opposite (%d,%d) not %s.\n", NAME(which), why, ii%w, ii/w, NAME(OPPOSITE(which))); } #endif return ret; } static int solve_unflag_surrounds(game_state *state, int i, int which) { int x = i%state->w, y = i/state->w, xx, yy, j, ii; assert(INGRID(state, x, y)); for (j = 0; j < 4; j++) { xx = x+dx[j]; yy = y+dy[j]; if (!INGRID(state, xx, yy)) continue; ii = yy*state->w+xx; if (solve_unflag(state, ii, which, "adjacent to set cell", NULL) < 0) return -1; } return 0; } /* Sets a cell to a particular colour, and also perform other * housekeeping around that. */ static int solve_set(game_state *state, int i, int which, const char *why, rowcol *rc) { int ii; #if defined DEBUGGING || defined STANDALONE_SOLVER int w = state->w; #endif ii = state->common->dominoes[i]; if (state->flags[i] & GS_SET) { if (state->grid[i] == which) { return 0; /* was already set and held, do nothing. */ } else { debug(("solve_set: (%d,%d) is held and %s, cannot set to %s", i%w, i/w, NAME(state->grid[i]), NAME(which))); return -1; } } if ((state->flags[ii] & GS_SET) && state->grid[ii] != OPPOSITE(which)) { debug(("solve_set: (%d,%d) opposite is held and %s, cannot set to %s", ii%w, ii/w, NAME(state->grid[ii]), NAME(OPPOSITE(which)))); return -1; } if (!POSSIBLE(i, which)) { debug(("solve_set: (%d,%d) NOT %s, cannot set.", i%w, i/w, NAME(which))); return -1; } if (!POSSIBLE(ii, OPPOSITE(which))) { debug(("solve_set: (%d,%d) NOT %s, cannot set (%d,%d).", ii%w, ii/w, NAME(OPPOSITE(which)), i%w, i/w)); return -1; } #ifdef STANDALONE_SOLVER if (verbose) { printf("(%d,%d)", i%w, i/w); if (rc) printf(" in %s %d", rc->name, rc->num); printf(" set to %s (%s), opposite (%d,%d) set to %s.\n", NAME(which), why, ii%w, ii/w, NAME(OPPOSITE(which))); } #endif if (rc) debug(("solve_set: (%d,%d) for %s %d", i%w, i/w, rc->name, rc->num)); debug(("solve_set: (%d,%d) setting to %s (%s), surrounds first:", i%w, i/w, NAME(which), why)); if (which != NEUTRAL) { if (solve_unflag_surrounds(state, i, which) < 0) return -1; if (solve_unflag_surrounds(state, ii, OPPOSITE(which)) < 0) return -1; } state->grid[i] = which; state->grid[ii] = OPPOSITE(which); state->flags[i] |= GS_SET; state->flags[ii] |= GS_SET; debug(("solve_set: (%d,%d) set to %s (%s)", i%w, i/w, NAME(which), why)); return 1; } /* counts should be int[4]. */ static void solve_counts(game_state *state, rowcol rc, int *counts, int *unset) { int i, j, which; assert(counts); for (i = 0; i < 4; i++) { counts[i] = 0; if (unset) unset[i] = 0; } for (i = rc.i, j = 0; j < rc.n; i += rc.di, j++) { if (state->flags[i] & GS_SET) { assert(state->grid[i] < 3); counts[state->grid[i]]++; } else if (unset) { for (which = 0; which <= 2; which++) { if (POSSIBLE(i, which)) unset[which]++; } } } } static int solve_checkfull(game_state *state, rowcol rc, int *counts) { int starti = rc.i, j, which, didsth = 0, target; int unset[4]; assert(state->numbered); /* only useful (should only be called) if numbered. */ solve_counts(state, rc, counts, unset); for (which = 0; which <= 2; which++) { target = rc.targets[which]; if (target == -1) continue; /*debug(("%s %d for %s: target %d, count %d, unset %d", rc.name, rc.num, NAME(which), target, counts[which], unset[which]));*/ if (target < counts[which]) { debug(("%s %d has too many (%d) %s squares (target %d), impossible!", rc.name, rc.num, counts[which], NAME(which), target)); return -1; } if (target == counts[which]) { /* We have the correct no. of the colour in this row/column * already; unflag all the rest. */ for (rc.i = starti, j = 0; j < rc.n; rc.i += rc.di, j++) { if (state->flags[rc.i] & GS_SET) continue; if (!POSSIBLE(rc.i, which)) continue; if (solve_unflag(state, rc.i, which, "row/col full", &rc) < 0) return -1; didsth = 1; } } else if ((target - counts[which]) == unset[which]) { /* We need all the remaining unset squares for this colour; * set them all. */ for (rc.i = starti, j = 0; j < rc.n; rc.i += rc.di, j++) { if (state->flags[rc.i] & GS_SET) continue; if (!POSSIBLE(rc.i, which)) continue; if (solve_set(state, rc.i, which, "row/col needs all unset", &rc) < 0) return -1; didsth = 1; } } } return didsth; } static int solve_startflags(game_state *state) { int x, y, i; for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { i = y*state->w+x; if (state->common->dominoes[i] == i) continue; if (state->grid[i] != NEUTRAL || state->flags[i] & GS_SET) { if (solve_set(state, i, state->grid[i], "initial set-and-hold", NULL) < 0) return -1; } } } return 0; } typedef int (*rowcolfn)(game_state *state, rowcol rc, int *counts); static int solve_rowcols(game_state *state, rowcolfn fn) { int x, y, didsth = 0, ret; rowcol rc; int counts[4]; for (x = 0; x < state->w; x++) { rc = mkrowcol(state, x, COLUMN); solve_counts(state, rc, counts, NULL); ret = fn(state, rc, counts); if (ret < 0) return ret; didsth += ret; } for (y = 0; y < state->h; y++) { rc = mkrowcol(state, y, ROW); solve_counts(state, rc, counts, NULL); ret = fn(state, rc, counts); if (ret < 0) return ret; didsth += ret; } return didsth; } static int solve_force(game_state *state) { int i, which, didsth = 0; unsigned long f; for (i = 0; i < state->wh; i++) { if (state->flags[i] & GS_SET) continue; if (state->common->dominoes[i] == i) continue; f = state->flags[i] & GS_NOTMASK; which = -1; if (f == (GS_NOTPOSITIVE|GS_NOTNEGATIVE)) which = NEUTRAL; if (f == (GS_NOTPOSITIVE|GS_NOTNEUTRAL)) which = NEGATIVE; if (f == (GS_NOTNEGATIVE|GS_NOTNEUTRAL)) which = POSITIVE; if (which != -1) { if (solve_set(state, i, which, "forced by flags", NULL) < 0) return -1; didsth = 1; } } return didsth; } static int solve_neither(game_state *state) { int i, j, didsth = 0; for (i = 0; i < state->wh; i++) { if (state->flags[i] & GS_SET) continue; j = state->common->dominoes[i]; if (i == j) continue; if (((state->flags[i] & GS_NOTPOSITIVE) && (state->flags[j] & GS_NOTPOSITIVE)) || ((state->flags[i] & GS_NOTNEGATIVE) && (state->flags[j] & GS_NOTNEGATIVE))) { if (solve_set(state, i, NEUTRAL, "neither tile magnet", NULL) < 0) return -1; didsth = 1; } } return didsth; } static int solve_advancedfull(game_state *state, rowcol rc, int *counts) { int i, j, nfound = 0, clearpos = 0, clearneg = 0, ret = 0; /* For this row/col, look for a domino entirely within the row where * both ends can only be + or - (but isn't held). * The +/- counts can thus be decremented by 1 each, and the 'unset' * count by 2. * * Once that's done for all such dominoes (and they're marked), try * and made usual deductions about rest of the row based on new totals. */ if (rc.targets[POSITIVE] == -1 && rc.targets[NEGATIVE] == -1) return 0; /* don't have a target for either colour, nothing to do. */ if ((rc.targets[POSITIVE] >= 0 && counts[POSITIVE] == rc.targets[POSITIVE]) && (rc.targets[NEGATIVE] >= 0 && counts[NEGATIVE] == rc.targets[NEGATIVE])) return 0; /* both colours are full up already, nothing to do. */ for (i = rc.i, j = 0; j < rc.n; i += rc.di, j++) state->flags[i] &= ~GS_MARK; for (i = rc.i, j = 0; j < rc.n; i += rc.di, j++) { if (state->flags[i] & GS_SET) continue; /* We're looking for a domino in our row/col, thus if * dominoes[i] -> i+di we've found one. */ if (state->common->dominoes[i] != i+rc.di) continue; /* We need both squares of this domino to be either + or - * (i.e. both NOTNEUTRAL only). */ if (((state->flags[i] & GS_NOTMASK) != GS_NOTNEUTRAL) || ((state->flags[i+rc.di] & GS_NOTMASK) != GS_NOTNEUTRAL)) continue; debug(("Domino in %s %d at (%d,%d) must be polarised.", rc.name, rc.num, i%state->w, i/state->w)); state->flags[i] |= GS_MARK; state->flags[i+rc.di] |= GS_MARK; nfound++; } if (nfound == 0) return 0; /* nfound is #dominoes we matched, which will all be marked. */ counts[POSITIVE] += nfound; counts[NEGATIVE] += nfound; if (rc.targets[POSITIVE] >= 0 && counts[POSITIVE] == rc.targets[POSITIVE]) { debug(("%s %d has now filled POSITIVE:", rc.name, rc.num)); clearpos = 1; } if (rc.targets[NEGATIVE] >= 0 && counts[NEGATIVE] == rc.targets[NEGATIVE]) { debug(("%s %d has now filled NEGATIVE:", rc.name, rc.num)); clearneg = 1; } if (!clearpos && !clearneg) return 0; for (i = rc.i, j = 0; j < rc.n; i += rc.di, j++) { if (state->flags[i] & GS_SET) continue; if (state->flags[i] & GS_MARK) continue; if (clearpos && !(state->flags[i] & GS_NOTPOSITIVE)) { if (solve_unflag(state, i, POSITIVE, "row/col full (+ve) [tricky]", &rc) < 0) return -1; ret++; } if (clearneg && !(state->flags[i] & GS_NOTNEGATIVE)) { if (solve_unflag(state, i, NEGATIVE, "row/col full (-ve) [tricky]", &rc) < 0) return -1; ret++; } } return ret; } /* If we only have one neutral still to place on a row/column then no dominoes entirely in that row/column can be neutral. */ static int solve_nonneutral(game_state *state, rowcol rc, int *counts) { int i, j, ret = 0; if (rc.targets[NEUTRAL] != counts[NEUTRAL]+1) return 0; for (i = rc.i, j = 0; j < rc.n; i += rc.di, j++) { if (state->flags[i] & GS_SET) continue; if (state->common->dominoes[i] != i+rc.di) continue; if (!(state->flags[i] & GS_NOTNEUTRAL)) { if (solve_unflag(state, i, NEUTRAL, "single neutral in row/col [tricky]", &rc) < 0) return -1; ret++; } } return ret; } /* If we need to fill all unfilled cells with +-, and we need 1 more of * one than the other, and we have a single odd-numbered region of unfilled * cells, that odd-numbered region must start and end with the extra number. */ static int solve_oddlength(game_state *state, rowcol rc, int *counts) { int i, j, ret = 0, extra, tpos, tneg; int start = -1, length = 0, inempty = 0, startodd = -1; /* need zero neutral cells still to find... */ if (rc.targets[NEUTRAL] != counts[NEUTRAL]) return 0; /* ...and #positive and #negative to differ by one. */ tpos = rc.targets[POSITIVE] - counts[POSITIVE]; tneg = rc.targets[NEGATIVE] - counts[NEGATIVE]; if (tpos == tneg+1) extra = POSITIVE; else if (tneg == tpos+1) extra = NEGATIVE; else return 0; for (i = rc.i, j = 0; j < rc.n; i += rc.di, j++) { if (state->flags[i] & GS_SET) { if (inempty) { if (length % 2) { /* we've just finished an odd-length section. */ if (startodd != -1) goto twoodd; startodd = start; } inempty = 0; } } else { if (inempty) length++; else { start = i; length = 1; inempty = 1; } } } if (inempty && (length % 2)) { if (startodd != -1) goto twoodd; startodd = start; } if (startodd != -1) ret = solve_set(state, startodd, extra, "odd-length section start", &rc); return ret; twoodd: debug(("%s %d has >1 odd-length sections, starting at %d,%d and %d,%d.", rc.name, rc.num, startodd%state->w, startodd/state->w, start%state->w, start/state->w)); return 0; } /* Count the number of remaining empty dominoes in any row/col. * If that number is equal to the #remaining positive, * or to the #remaining negative, no empty cells can be neutral. */ static int solve_countdominoes_neutral(game_state *state, rowcol rc, int *counts) { int i, j, ndom = 0, nonn = 0, ret = 0; if ((rc.targets[POSITIVE] == -1) && (rc.targets[NEGATIVE] == -1)) return 0; /* need at least one target to compare. */ for (i = rc.i, j = 0; j < rc.n; i += rc.di, j++) { if (state->flags[i] & GS_SET) continue; assert(state->grid[i] == EMPTY); /* Skip solo cells, or second cell in domino. */ if ((state->common->dominoes[i] == i) || (state->common->dominoes[i] == i-rc.di)) continue; ndom++; } if ((rc.targets[POSITIVE] != -1) && (rc.targets[POSITIVE]-counts[POSITIVE] == ndom)) nonn = 1; if ((rc.targets[NEGATIVE] != -1) && (rc.targets[NEGATIVE]-counts[NEGATIVE] == ndom)) nonn = 1; if (!nonn) return 0; for (i = rc.i, j = 0; j < rc.n; i += rc.di, j++) { if (state->flags[i] & GS_SET) continue; if (!(state->flags[i] & GS_NOTNEUTRAL)) { if (solve_unflag(state, i, NEUTRAL, "all dominoes +/- [tricky]", &rc) < 0) return -1; ret++; } } return ret; } static int solve_domino_count(game_state *state, rowcol rc, int i, int which) { int nposs = 0; /* Skip solo cells or 2nd in domino. */ if ((state->common->dominoes[i] == i) || (state->common->dominoes[i] == i-rc.di)) return 0; if (state->flags[i] & GS_SET) return 0; if (POSSIBLE(i, which)) nposs++; if (state->common->dominoes[i] == i+rc.di) { /* second cell of domino is on our row: test that too. */ if (POSSIBLE(i+rc.di, which)) nposs++; } return nposs; } /* Count number of dominoes we could put each of + and - into. If it is equal * to the #left, any domino we can only put + or - in one cell of must have it. */ static int solve_countdominoes_nonneutral(game_state *state, rowcol rc, int *counts) { int which, w, i, j, ndom = 0, didsth = 0, toset; for (which = POSITIVE, w = 0; w < 2; which = OPPOSITE(which), w++) { if (rc.targets[which] == -1) continue; for (i = rc.i, j = 0; j < rc.n; i += rc.di, j++) { if (solve_domino_count(state, rc, i, which) > 0) ndom++; } if ((rc.targets[which] - counts[which]) != ndom) continue; for (i = rc.i, j = 0; j < rc.n; i += rc.di, j++) { if (solve_domino_count(state, rc, i, which) == 1) { if (POSSIBLE(i, which)) toset = i; else { /* paranoia, should have been checked by solve_domino_count. */ assert(state->common->dominoes[i] == i+rc.di); assert(POSSIBLE(i+rc.di, which)); toset = i+rc.di; } if (solve_set(state, toset, which, "all empty dominoes need +/- [tricky]", &rc) < 0) return -1; didsth++; } } } return didsth; } /* danger, evil macro. can't use the do { ... } while(0) trick because * the continue breaks. */ #define SOLVE_FOR_ROWCOLS(fn) \ ret = solve_rowcols(state, fn); \ if (ret < 0) { debug(("%s said impossible, cannot solve", #fn)); return -1; } \ if (ret > 0) continue static int solve_state(game_state *state, int diff) { int ret; debug(("solve_state, difficulty %s", magnets_diffnames[diff])); solve_clearflags(state); if (solve_startflags(state) < 0) return -1; while (1) { ret = solve_force(state); if (ret > 0) continue; if (ret < 0) return -1; ret = solve_neither(state); if (ret > 0) continue; if (ret < 0) return -1; SOLVE_FOR_ROWCOLS(solve_checkfull); SOLVE_FOR_ROWCOLS(solve_oddlength); if (diff < DIFF_TRICKY) break; SOLVE_FOR_ROWCOLS(solve_advancedfull); SOLVE_FOR_ROWCOLS(solve_nonneutral); SOLVE_FOR_ROWCOLS(solve_countdominoes_neutral); SOLVE_FOR_ROWCOLS(solve_countdominoes_nonneutral); /* more ... */ break; } return check_completion(state); } static char *game_state_diff(const game_state *src, const game_state *dst, int issolve) { char *ret = NULL, buf[80], c; int retlen = 0, x, y, i, k; assert(src->w == dst->w && src->h == dst->h); if (issolve) { ret = sresize(ret, 3, char); ret[0] = 'S'; ret[1] = ';'; ret[2] = '\0'; retlen += 2; } for (x = 0; x < dst->w; x++) { for (y = 0; y < dst->h; y++) { i = y*dst->w+x; if (src->common->dominoes[i] == i) continue; #define APPEND do { \ ret = sresize(ret, retlen + k + 1, char); \ strcpy(ret + retlen, buf); \ retlen += k; \ } while(0) if ((src->grid[i] != dst->grid[i]) || ((src->flags[i] & GS_SET) != (dst->flags[i] & GS_SET))) { if (dst->grid[i] == EMPTY && !(dst->flags[i] & GS_SET)) c = ' '; else c = GRID2CHAR(dst->grid[i]); k = sprintf(buf, "%c%d,%d;", (int)c, x, y); APPEND; } } } debug(("game_state_diff returns %s", ret)); return ret; } static void solve_from_aux(const game_state *state, const char *aux) { int i; assert(strlen(aux) == state->wh); for (i = 0; i < state->wh; i++) { state->grid[i] = CHAR2GRID(aux[i]); state->flags[i] |= GS_SET; } } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { game_state *solved = dup_game(currstate); char *move = NULL; int ret; if (aux && strlen(aux) == state->wh) { solve_from_aux(solved, aux); goto solved; } if (solve_state(solved, DIFFCOUNT) > 0) goto solved; free_game(solved); solved = dup_game(state); ret = solve_state(solved, DIFFCOUNT); if (ret > 0) goto solved; free_game(solved); *error = (ret < 0) ? "Puzzle is impossible." : "Unable to solve puzzle."; return NULL; solved: move = game_state_diff(currstate, solved, 1); free_game(solved); return move; } static int solve_unnumbered(game_state *state) { int i, ret; while (1) { ret = solve_force(state); if (ret > 0) continue; if (ret < 0) return -1; ret = solve_neither(state); if (ret > 0) continue; if (ret < 0) return -1; break; } for (i = 0; i < state->wh; i++) { if (!(state->flags[i] & GS_SET)) return 0; } return 1; } static int lay_dominoes(game_state *state, random_state *rs, int *scratch) { int n, i, ret = 0, nlaid = 0, n_initial_neutral; for (i = 0; i < state->wh; i++) { scratch[i] = i; state->grid[i] = EMPTY; state->flags[i] = (state->common->dominoes[i] == i) ? GS_SET : 0; } shuffle(scratch, state->wh, sizeof(int), rs); n_initial_neutral = (state->wh > 100) ? 5 : (state->wh / 10); for (n = 0; n < state->wh; n++) { /* Find a space ... */ i = scratch[n]; if (state->flags[i] & GS_SET) continue; /* already laid here. */ /* ...and lay a domino if we can. */ debug(("Laying domino at i:%d, (%d,%d)\n", i, i%state->w, i/state->w)); /* The choice of which type of domino to lay here leads to subtle differences * in the sorts of boards that get produced. Too much bias towards magnets * leads to games that are too easy. * * Currently, it lays a small set of dominoes at random as neutral, and * then lays the rest preferring to be magnets -- however, if the * current layout is such that a magnet won't go there, then it lays * another neutral. * * The number of initially neutral dominoes is limited as grids get bigger: * too many neutral dominoes invariably ends up with insoluble puzzle at * this size, and the positioning process means it'll always end up laying * more than the initial 5 anyway. */ /* We should always be able to lay a neutral anywhere. */ assert(!(state->flags[i] & GS_NOTNEUTRAL)); if (n < n_initial_neutral) { debug((" ...laying neutral\n")); ret = solve_set(state, i, NEUTRAL, "layout initial neutral", NULL); } else { debug((" ... preferring magnet\n")); if (!(state->flags[i] & GS_NOTPOSITIVE)) ret = solve_set(state, i, POSITIVE, "layout", NULL); else if (!(state->flags[i] & GS_NOTNEGATIVE)) ret = solve_set(state, i, NEGATIVE, "layout", NULL); else ret = solve_set(state, i, NEUTRAL, "layout", NULL); } if (!ret) { debug(("Unable to lay anything at (%d,%d), giving up.", i%state->w, i/state->w)); ret = -1; break; } nlaid++; ret = solve_unnumbered(state); if (ret == -1) debug(("solve_unnumbered decided impossible.\n")); if (ret != 0) break; } debug(("Laid %d dominoes, total %d dominoes.\n", nlaid, state->wh/2)); game_debug(state, "Final layout"); return ret; } static void gen_game(game_state *new, random_state *rs) { int ret, x, y, val; int *scratch = snewn(new->wh, int); #ifdef STANDALONE_SOLVER if (verbose) printf("Generating new game...\n"); #endif clear_state(new); sfree(new->common->dominoes); /* bit grotty. */ new->common->dominoes = domino_layout(new->w, new->h, rs); do { ret = lay_dominoes(new, rs, scratch); } while(ret == -1); /* for each cell, update colcount/rowcount as appropriate. */ memset(new->common->colcount, 0, new->w*3*sizeof(int)); memset(new->common->rowcount, 0, new->h*3*sizeof(int)); for (x = 0; x < new->w; x++) { for (y = 0; y < new->h; y++) { val = new->grid[y*new->w+x]; new->common->colcount[x*3+val]++; new->common->rowcount[y*3+val]++; } } new->numbered = 1; sfree(scratch); } static void generate_aux(game_state *new, char *aux) { int i; for (i = 0; i < new->wh; i++) aux[i] = GRID2CHAR(new->grid[i]); aux[new->wh] = '\0'; } static int check_difficulty(const game_params *params, game_state *new, random_state *rs) { int *scratch, *grid_correct, slen, i; memset(new->grid, EMPTY, new->wh*sizeof(int)); if (params->diff > DIFF_EASY) { /* If this is too easy, return. */ if (solve_state(new, params->diff-1) > 0) { debug(("Puzzle is too easy.")); return -1; } } if (solve_state(new, params->diff) <= 0) { debug(("Puzzle is not soluble at requested difficulty.")); return -1; } if (!params->stripclues) return 0; /* Copy the correct grid away. */ grid_correct = snewn(new->wh, int); memcpy(grid_correct, new->grid, new->wh*sizeof(int)); /* Create shuffled array of side-clue locations. */ slen = new->w*2 + new->h*2; scratch = snewn(slen, int); for (i = 0; i < slen; i++) scratch[i] = i; shuffle(scratch, slen, sizeof(int), rs); /* For each clue, check whether removing it makes the puzzle unsoluble; * put it back if so. */ for (i = 0; i < slen; i++) { int num = scratch[i], which, roworcol, target, targetn, ret; rowcol rc; /* work out which clue we meant. */ if (num < new->w+new->h) { which = POSITIVE; } else { which = NEGATIVE; num -= new->w+new->h; } if (num < new->w) { roworcol = COLUMN; } else { roworcol = ROW; num -= new->w; } /* num is now the row/column index in question. */ rc = mkrowcol(new, num, roworcol); /* Remove clue, storing original... */ target = rc.targets[which]; targetn = rc.targets[NEUTRAL]; rc.targets[which] = -1; rc.targets[NEUTRAL] = -1; /* ...and see if we can still solve it. */ game_debug(new, "removed clue, new board:"); memset(new->grid, EMPTY, new->wh * sizeof(int)); ret = solve_state(new, params->diff); assert(ret != -1); if (ret == 0 || memcmp(new->grid, grid_correct, new->wh*sizeof(int)) != 0) { /* We made it ambiguous: put clue back. */ debug(("...now impossible/different, put clue back.")); rc.targets[which] = target; rc.targets[NEUTRAL] = targetn; } } sfree(scratch); sfree(grid_correct); return 0; } static char *new_game_desc(const game_params *params, random_state *rs, char **aux_r, int interactive) { game_state *new = new_state(params->w, params->h); char *desc, *aux = snewn(new->wh+1, char); do { gen_game(new, rs); generate_aux(new, aux); } while (check_difficulty(params, new, rs) < 0); /* now we're complete, generate the description string * and an aux_info for the completed game. */ desc = generate_desc(new); free_game(new); *aux_r = aux; return desc; } struct game_ui { int cur_x, cur_y, cur_visible; }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->cur_x = ui->cur_y = 0; ui->cur_visible = 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) { if (!oldstate->completed && newstate->completed) ui->cur_visible = 0; } struct game_drawstate { int tilesize, started, solved; int w, h; unsigned long *what; /* size w*h */ unsigned long *colwhat, *rowwhat; /* size 3*w, 3*h */ }; #define DS_WHICH_MASK 0xf #define DS_ERROR 0x10 #define DS_CURSOR 0x20 #define DS_SET 0x40 #define DS_FULL 0x80 #define DS_NOTPOS 0x100 #define DS_NOTNEG 0x200 #define DS_NOTNEU 0x400 #define DS_FLASH 0x800 #define PREFERRED_TILE_SIZE 32 #define TILE_SIZE (ds->tilesize) #define BORDER (TILE_SIZE / 8) #define COORD(x) ( (x+1) * TILE_SIZE + BORDER ) #define FROMCOORD(x) ( (x - BORDER) / TILE_SIZE - 1 ) static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { int gx = FROMCOORD(x), gy = FROMCOORD(y), idx, curr; char *nullret = NULL, buf[80], movech; enum { CYCLE_MAGNET, CYCLE_NEUTRAL } action; if (IS_CURSOR_MOVE(button)) { move_cursor(button, &ui->cur_x, &ui->cur_y, state->w, state->h, 0); ui->cur_visible = 1; return ""; } else if (IS_CURSOR_SELECT(button)) { if (!ui->cur_visible) { ui->cur_visible = 1; return ""; } action = (button == CURSOR_SELECT) ? CYCLE_MAGNET : CYCLE_NEUTRAL; gx = ui->cur_x; gy = ui->cur_y; } else if (INGRID(state, gx, gy) && (button == LEFT_BUTTON || button == RIGHT_BUTTON)) { if (ui->cur_visible) { ui->cur_visible = 0; nullret = ""; } action = (button == LEFT_BUTTON) ? CYCLE_MAGNET : CYCLE_NEUTRAL; } else return NULL; idx = gy * state->w + gx; if (state->common->dominoes[idx] == idx) return nullret; curr = state->grid[idx]; if (action == CYCLE_MAGNET) { /* ... empty --> positive --> negative --> empty ... */ if (state->grid[idx] == NEUTRAL && state->flags[idx] & GS_SET) return nullret; /* can't cycle a magnet from a neutral. */ movech = (curr == EMPTY) ? '+' : (curr == POSITIVE) ? '-' : ' '; } else if (action == CYCLE_NEUTRAL) { /* ... empty -> neutral -> !neutral --> empty ... */ if (state->grid[idx] != NEUTRAL) return nullret; /* can't cycle through neutral from a magnet. */ /* All of these are grid == EMPTY == NEUTRAL; it twiddles * combinations of flags. */ if (state->flags[idx] & GS_SET) /* neutral */ movech = '?'; else if (state->flags[idx] & GS_NOTNEUTRAL) /* !neutral */ movech = ' '; else movech = '.'; } else { assert(!"unknown action"); movech = 0; /* placate optimiser */ } sprintf(buf, "%c%d,%d", movech, gx, gy); return dupstr(buf); } static game_state *execute_move(const game_state *state, const char *move) { game_state *ret = dup_game(state); int x, y, n, idx, idx2; char c; if (!*move) goto badmove; while (*move) { c = *move++; if (c == 'S') { ret->solved = TRUE; n = 0; } else if (c == '+' || c == '-' || c == '.' || c == ' ' || c == '?') { if ((sscanf(move, "%d,%d%n", &x, &y, &n) != 2) || !INGRID(state, x, y)) goto badmove; idx = y*state->w + x; idx2 = state->common->dominoes[idx]; if (idx == idx2) goto badmove; ret->flags[idx] &= ~GS_NOTMASK; ret->flags[idx2] &= ~GS_NOTMASK; if (c == ' ' || c == '?') { ret->grid[idx] = EMPTY; ret->grid[idx2] = EMPTY; ret->flags[idx] &= ~GS_SET; ret->flags[idx2] &= ~GS_SET; if (c == '?') { ret->flags[idx] |= GS_NOTNEUTRAL; ret->flags[idx2] |= GS_NOTNEUTRAL; } } else { ret->grid[idx] = CHAR2GRID(c); ret->grid[idx2] = OPPOSITE(CHAR2GRID(c)); ret->flags[idx] |= GS_SET; ret->flags[idx2] |= GS_SET; } } else goto badmove; move += n; if (*move == ';') move++; else if (*move) goto badmove; } if (check_completion(ret) == 1) ret->completed = 1; return ret; badmove: free_game(ret); return NULL; } /* ---------------------------------------------------------------------- * 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 = TILE_SIZE * (params->w+2) + 2 * BORDER; *y = TILE_SIZE * (params->h+2) + 2 * BORDER; } 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); for (i = 0; i < 3; i++) { ret[COL_TEXT * 3 + i] = 0.0F; ret[COL_NEGATIVE * 3 + i] = 0.0F; ret[COL_CURSOR * 3 + i] = 0.9F; } ret[COL_POSITIVE * 3 + 0] = 0.8F; ret[COL_POSITIVE * 3 + 1] = 0.0F; ret[COL_POSITIVE * 3 + 2] = 0.0F; ret[COL_NEUTRAL * 3 + 0] = 0.10F; ret[COL_NEUTRAL * 3 + 1] = 0.60F; ret[COL_NEUTRAL * 3 + 2] = 0.10F; ret[COL_ERROR * 3 + 0] = 1.0F; ret[COL_ERROR * 3 + 1] = 0.0F; ret[COL_ERROR * 3 + 2] = 0.0F; ret[COL_NOT * 3 + 0] = 0.2F; ret[COL_NOT * 3 + 1] = 0.2F; ret[COL_NOT * 3 + 2] = 1.0F; *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 = ds->started = ds->solved = 0; ds->w = state->w; ds->h = state->h; ds->what = snewn(state->wh, unsigned long); memset(ds->what, 0, state->wh*sizeof(unsigned long)); ds->colwhat = snewn(state->w*3, unsigned long); memset(ds->colwhat, 0, state->w*3*sizeof(unsigned long)); ds->rowwhat = snewn(state->h*3, unsigned long); memset(ds->rowwhat, 0, state->h*3*sizeof(unsigned long)); return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->colwhat); sfree(ds->rowwhat); sfree(ds->what); sfree(ds); } static void draw_num_col(drawing *dr, game_drawstate *ds, int rowcol, int which, int idx, int colbg, int col, int num) { char buf[32]; int cx, cy, tsz; if (num < 0) return; sprintf(buf, "%d", num); tsz = (strlen(buf) == 1) ? (7*TILE_SIZE/10) : (9*TILE_SIZE/10)/strlen(buf); if (rowcol == ROW) { cx = BORDER; if (which == NEGATIVE) cx += TILE_SIZE * (ds->w+1); cy = BORDER + TILE_SIZE * (idx+1); } else { cx = BORDER + TILE_SIZE * (idx+1); cy = BORDER; if (which == NEGATIVE) cy += TILE_SIZE * (ds->h+1); } draw_rect(dr, cx, cy, TILE_SIZE, TILE_SIZE, colbg); draw_text(dr, cx + TILE_SIZE/2, cy + TILE_SIZE/2, FONT_VARIABLE, tsz, ALIGN_VCENTRE | ALIGN_HCENTRE, col, buf); draw_update(dr, cx, cy, TILE_SIZE, TILE_SIZE); } static void draw_num(drawing *dr, game_drawstate *ds, int rowcol, int which, int idx, unsigned long c, int num) { draw_num_col(dr, ds, rowcol, which, idx, COL_BACKGROUND, (c & DS_ERROR) ? COL_ERROR : COL_TEXT, num); } static void draw_sym(drawing *dr, game_drawstate *ds, int x, int y, int which, int col) { int cx = COORD(x), cy = COORD(y); int ccx = cx + TILE_SIZE/2, ccy = cy + TILE_SIZE/2; int roff = TILE_SIZE/4, rsz = 2*roff+1; int soff = TILE_SIZE/16, ssz = 2*soff+1; if (which == POSITIVE || which == NEGATIVE) { draw_rect(dr, ccx - roff, ccy - soff, rsz, ssz, col); if (which == POSITIVE) draw_rect(dr, ccx - soff, ccy - roff, ssz, rsz, col); } else if (col == COL_NOT) { /* not-a-neutral is a blue question mark. */ char qu[2] = { '?', 0 }; draw_text(dr, ccx, ccy, FONT_VARIABLE, 7*TILE_SIZE/10, ALIGN_VCENTRE | ALIGN_HCENTRE, col, qu); } else { draw_line(dr, ccx - roff, ccy - roff, ccx + roff, ccy + roff, col); draw_line(dr, ccx + roff, ccy - roff, ccx - roff, ccy + roff, col); } } enum { TYPE_L, TYPE_R, TYPE_T, TYPE_B, TYPE_BLANK }; /* NOT responsible for redrawing background or updating. */ static void draw_tile_col(drawing *dr, game_drawstate *ds, int *dominoes, int x, int y, int which, int bg, int fg, int perc) { int cx = COORD(x), cy = COORD(y), i, other, type = TYPE_BLANK; int gutter, radius, coffset; /* gutter is TSZ/16 for 100%, 8*TSZ/16 (TSZ/2) for 0% */ gutter = (TILE_SIZE / 16) + ((100 - perc) * (7*TILE_SIZE / 16))/100; radius = (perc * (TILE_SIZE / 8)) / 100; coffset = gutter + radius; i = y*ds->w + x; other = dominoes[i]; if (other == i) return; else if (other == i+1) type = TYPE_L; else if (other == i-1) type = TYPE_R; else if (other == i+ds->w) type = TYPE_T; else if (other == i-ds->w) type = TYPE_B; else assert(!"mad domino orientation"); /* domino drawing shamelessly stolen from dominosa.c. */ if (type == TYPE_L || type == TYPE_T) draw_circle(dr, cx+coffset, cy+coffset, radius, bg, bg); if (type == TYPE_R || type == TYPE_T) draw_circle(dr, cx+TILE_SIZE-1-coffset, cy+coffset, radius, bg, bg); if (type == TYPE_L || type == TYPE_B) draw_circle(dr, cx+coffset, cy+TILE_SIZE-1-coffset, radius, bg, bg); if (type == TYPE_R || type == TYPE_B) draw_circle(dr, cx+TILE_SIZE-1-coffset, cy+TILE_SIZE-1-coffset, radius, bg, bg); for (i = 0; i < 2; i++) { int x1, y1, x2, y2; x1 = cx + (i ? gutter : coffset); y1 = cy + (i ? coffset : gutter); x2 = cx + TILE_SIZE-1 - (i ? gutter : coffset); y2 = cy + TILE_SIZE-1 - (i ? coffset : gutter); if (type == TYPE_L) x2 = cx + TILE_SIZE; else if (type == TYPE_R) x1 = cx; else if (type == TYPE_T) y2 = cy + TILE_SIZE ; else if (type == TYPE_B) y1 = cy; draw_rect(dr, x1, y1, x2-x1+1, y2-y1+1, bg); } if (fg != -1) draw_sym(dr, ds, x, y, which, fg); } static void draw_tile(drawing *dr, game_drawstate *ds, int *dominoes, int x, int y, unsigned long flags) { int cx = COORD(x), cy = COORD(y), bg, fg, perc = 100; int which = flags & DS_WHICH_MASK; flags &= ~DS_WHICH_MASK; draw_rect(dr, cx, cy, TILE_SIZE, TILE_SIZE, COL_BACKGROUND); if (flags & DS_CURSOR) bg = COL_CURSOR; /* off-white white for cursor */ else if (which == POSITIVE) bg = COL_POSITIVE; else if (which == NEGATIVE) bg = COL_NEGATIVE; else if (flags & DS_SET) bg = COL_NEUTRAL; /* green inner for neutral cells */ else bg = COL_LOWLIGHT; /* light grey for empty cells. */ if (which == EMPTY && !(flags & DS_SET)) { int notwhich = -1; fg = -1; /* don't draw cross unless actually set as neutral. */ if (flags & DS_NOTPOS) notwhich = POSITIVE; if (flags & DS_NOTNEG) notwhich = NEGATIVE; if (flags & DS_NOTNEU) notwhich = NEUTRAL; if (notwhich != -1) { which = notwhich; fg = COL_NOT; } } else fg = (flags & DS_ERROR) ? COL_ERROR : (flags & DS_CURSOR) ? COL_TEXT : COL_BACKGROUND; draw_rect(dr, cx, cy, TILE_SIZE, TILE_SIZE, COL_BACKGROUND); if (flags & DS_FLASH) { int bordercol = COL_HIGHLIGHT; draw_tile_col(dr, ds, dominoes, x, y, which, bordercol, -1, perc); perc = 3*perc/4; } draw_tile_col(dr, ds, dominoes, x, y, which, bg, fg, perc); draw_update(dr, cx, cy, TILE_SIZE, TILE_SIZE); } 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 x, y, w = state->w, h = state->h, which, i, j, flash; unsigned long c = 0; flash = (int)(flashtime * 5 / FLASH_TIME) % 2; if (!ds->started) { /* draw background, corner +-. */ draw_rect(dr, 0, 0, TILE_SIZE * (w+2) + 2 * BORDER, TILE_SIZE * (h+2) + 2 * BORDER, COL_BACKGROUND); draw_sym(dr, ds, -1, -1, POSITIVE, COL_TEXT); draw_sym(dr, ds, state->w, state->h, NEGATIVE, COL_TEXT); draw_update(dr, 0, 0, TILE_SIZE * (ds->w+2) + 2 * BORDER, TILE_SIZE * (ds->h+2) + 2 * BORDER); } /* Draw grid */ for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { int idx = y*w+x; c = state->grid[idx]; if (state->flags[idx] & GS_ERROR) c |= DS_ERROR; if (state->flags[idx] & GS_SET) c |= DS_SET; if (x == ui->cur_x && y == ui->cur_y && ui->cur_visible) c |= DS_CURSOR; if (flash) c |= DS_FLASH; if (state->flags[idx] & GS_NOTPOSITIVE) c |= DS_NOTPOS; if (state->flags[idx] & GS_NOTNEGATIVE) c |= DS_NOTNEG; if (state->flags[idx] & GS_NOTNEUTRAL) c |= DS_NOTNEU; if (ds->what[idx] != c || !ds->started) { draw_tile(dr, ds, state->common->dominoes, x, y, c); ds->what[idx] = c; } } } /* Draw counts around side */ for (which = POSITIVE, j = 0; j < 2; which = OPPOSITE(which), j++) { int target, count; for (i = 0; i < w; i++) { target = state->common->colcount[i*3+which]; count = count_rowcol(state, i, COLUMN, which); c = 0; if ((count > target) || (count < target && !count_rowcol(state, i, COLUMN, -1))) c |= DS_ERROR; if (count == target) c |= DS_FULL; if (c != ds->colwhat[i*3+which] || !ds->started) { draw_num(dr, ds, COLUMN, which, i, c, state->common->colcount[i*3+which]); ds->colwhat[i*3+which] = c; } } for (i = 0; i < h; i++) { target = state->common->rowcount[i*3+which]; count = count_rowcol(state, i, ROW, which); c = 0; if ((count > target) || (count < target && !count_rowcol(state, i, ROW, -1))) c |= DS_ERROR; if (count == target) c |= DS_FULL; if (c != ds->rowwhat[i*3+which] || !ds->started) { draw_num(dr, ds, ROW, which, i, c, state->common->rowcount[i*3+which]); ds->rowwhat[i*3+which] = c; } } } ds->started = 1; } 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->solved && !newstate->solved) return FLASH_TIME; return 0.0F; } static int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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) { int pw, ph; /* * I'll use 6mm squares by default. */ game_compute_size(params, 600, &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->w, h = state->h; int ink = print_mono_colour(dr, 0); int paper = print_mono_colour(dr, 1); int x, y, which, i, j; /* Ick: fake up `ds->tilesize' for macro expansion purposes */ game_drawstate ads, *ds = &ads; game_set_size(dr, ds, NULL, tilesize); ds->w = w; ds->h = h; /* Border. */ print_line_width(dr, TILE_SIZE/12); /* Numbers and +/- for corners. */ draw_sym(dr, ds, -1, -1, POSITIVE, ink); draw_sym(dr, ds, state->w, state->h, NEGATIVE, ink); for (which = POSITIVE, j = 0; j < 2; which = OPPOSITE(which), j++) { for (i = 0; i < w; i++) { draw_num_col(dr, ds, COLUMN, which, i, paper, ink, state->common->colcount[i*3+which]); } for (i = 0; i < h; i++) { draw_num_col(dr, ds, ROW, which, i, paper, ink, state->common->rowcount[i*3+which]); } } /* Dominoes. */ for (x = 0; x < w; x++) { for (y = 0; y < h; y++) { i = y*state->w + x; if (state->common->dominoes[i] == i+1 || state->common->dominoes[i] == i+w) { int dx = state->common->dominoes[i] == i+1 ? 2 : 1; int dy = 3 - dx; int xx, yy; int cx = COORD(x), cy = COORD(y); print_line_width(dr, 0); /* Ink the domino */ for (yy = 0; yy < 2; yy++) for (xx = 0; xx < 2; xx++) draw_circle(dr, cx+xx*dx*TILE_SIZE+(1-2*xx)*3*TILE_SIZE/16, cy+yy*dy*TILE_SIZE+(1-2*yy)*3*TILE_SIZE/16, TILE_SIZE/8, ink, ink); draw_rect(dr, cx + TILE_SIZE/16, cy + 3*TILE_SIZE/16, dx*TILE_SIZE - 2*(TILE_SIZE/16), dy*TILE_SIZE - 6*(TILE_SIZE/16), ink); draw_rect(dr, cx + 3*TILE_SIZE/16, cy + TILE_SIZE/16, dx*TILE_SIZE - 6*(TILE_SIZE/16), dy*TILE_SIZE - 2*(TILE_SIZE/16), ink); /* Un-ink the domino interior */ for (yy = 0; yy < 2; yy++) for (xx = 0; xx < 2; xx++) draw_circle(dr, cx+xx*dx*TILE_SIZE+(1-2*xx)*3*TILE_SIZE/16, cy+yy*dy*TILE_SIZE+(1-2*yy)*3*TILE_SIZE/16, 3*TILE_SIZE/32, paper, paper); draw_rect(dr, cx + 3*TILE_SIZE/32, cy + 3*TILE_SIZE/16, dx*TILE_SIZE - 2*(3*TILE_SIZE/32), dy*TILE_SIZE - 6*(TILE_SIZE/16), paper); draw_rect(dr, cx + 3*TILE_SIZE/16, cy + 3*TILE_SIZE/32, dx*TILE_SIZE - 6*(TILE_SIZE/16), dy*TILE_SIZE - 2*(3*TILE_SIZE/32), paper); } } } /* Grid symbols (solution). */ for (x = 0; x < w; x++) { for (y = 0; y < h; y++) { i = y*state->w + x; if ((state->grid[i] != NEUTRAL) || (state->flags[i] & GS_SET)) draw_sym(dr, ds, x, y, state->grid[i], ink); } } } #ifdef COMBINED #define thegame magnets #endif const struct game thegame = { "Magnets", "games.magnets", "magnets", default_params, game_fetch_preset, 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, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, REQUIRE_RBUTTON, /* flags */ }; #ifdef STANDALONE_SOLVER #include #include const char *quis = NULL; int csv = 0; void usage(FILE *out) { fprintf(out, "usage: %s [-v] [--print] |\n", quis); } void doprint(game_state *state) { char *fmt = game_text_format(state); printf("%s", fmt); sfree(fmt); } static void pnum(int n, int ntot, const char *desc) { printf("%2.1f%% (%d) %s", (double)n*100.0 / (double)ntot, n, desc); } static void start_soak(game_params *p, random_state *rs) { time_t tt_start, tt_now, tt_last; char *aux; game_state *s, *s2; int n = 0, nsolved = 0, nimpossible = 0, ntricky = 0, ret, i; long nn, nn_total = 0, nn_solved = 0, nn_tricky = 0; tt_start = tt_now = time(NULL); if (csv) printf("time, w, h, #generated, #solved, #tricky, #impossible, " "#neutral, #neutral/solved, #neutral/tricky\n"); else printf("Soak-testing a %dx%d grid.\n", p->w, p->h); s = new_state(p->w, p->h); aux = snewn(s->wh+1, char); while (1) { gen_game(s, rs); nn = 0; for (i = 0; i < s->wh; i++) { if (s->grid[i] == NEUTRAL) nn++; } generate_aux(s, aux); memset(s->grid, EMPTY, s->wh * sizeof(int)); s2 = dup_game(s); ret = solve_state(s, DIFFCOUNT); n++; nn_total += nn; if (ret > 0) { nsolved++; nn_solved += nn; if (solve_state(s2, DIFF_EASY) <= 0) { ntricky++; nn_tricky += nn; } } else if (ret < 0) { char *desc = generate_desc(s); solve_from_aux(s, aux); printf("Game considered impossible:\n %dx%d:%s\n", p->w, p->h, desc); sfree(desc); doprint(s); nimpossible++; } free_game(s2); tt_last = time(NULL); if (tt_last > tt_now) { tt_now = tt_last; if (csv) { printf("%d,%d,%d, %d,%d,%d,%d, %ld,%ld,%ld\n", (int)(tt_now - tt_start), p->w, p->h, n, nsolved, ntricky, nimpossible, nn_total, nn_solved, nn_tricky); } else { printf("%d total, %3.1f/s, ", n, (double)n / ((double)tt_now - tt_start)); pnum(nsolved, n, "solved"); printf(", "); pnum(ntricky, n, "tricky"); if (nimpossible > 0) pnum(nimpossible, n, "impossible"); printf("\n"); printf(" overall %3.1f%% neutral (%3.1f%% for solved, %3.1f%% for tricky)\n", (double)(nn_total * 100) / (double)(p->w * p->h * n), (double)(nn_solved * 100) / (double)(p->w * p->h * nsolved), (double)(nn_tricky * 100) / (double)(p->w * p->h * ntricky)); } } } free_game(s); sfree(aux); } int main(int argc, const char *argv[]) { int print = 0, soak = 0, solved = 0, ret; char *id = NULL, *desc, *desc_gen = NULL, *err, *aux = NULL; game_state *s = NULL; game_params *p = NULL; random_state *rs = NULL; time_t seed = time(NULL); setvbuf(stdout, NULL, _IONBF, 0); quis = argv[0]; while (--argc > 0) { char *p = (char*)(*++argv); if (!strcmp(p, "-v") || !strcmp(p, "--verbose")) { verbose = 1; } else if (!strcmp(p, "--csv")) { csv = 1; } else if (!strcmp(p, "-e") || !strcmp(p, "--seed")) { seed = atoi(*++argv); argc--; } else if (!strcmp(p, "-p") || !strcmp(p, "--print")) { print = 1; } else if (!strcmp(p, "-s") || !strcmp(p, "--soak")) { soak = 1; } else if (*p == '-') { fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p); usage(stderr); exit(1); } else { id = p; } } rs = random_new((void*)&seed, sizeof(time_t)); if (!id) { fprintf(stderr, "usage: %s [-v] [--soak] | \n", argv[0]); goto done; } desc = strchr(id, ':'); if (desc) *desc++ = '\0'; p = default_params(); decode_params(p, id); err = validate_params(p, 1); if (err) { fprintf(stderr, "%s: %s", argv[0], err); goto done; } if (soak) { if (desc) { fprintf(stderr, "%s: --soak needs parameters, not description.\n", quis); goto done; } start_soak(p, rs); goto done; } if (!desc) desc = desc_gen = new_game_desc(p, rs, &aux, 0); err = validate_desc(p, desc); if (err) { fprintf(stderr, "%s: %s\nDescription: %s\n", quis, err, desc); goto done; } s = new_game(NULL, p, desc); printf("%s:%s (seed %ld)\n", id, desc, seed); if (aux) { /* We just generated this ourself. */ if (verbose || print) { doprint(s); solve_from_aux(s, aux); solved = 1; } } else { doprint(s); verbose = 1; ret = solve_state(s, DIFFCOUNT); if (ret < 0) printf("Puzzle is impossible.\n"); else if (ret == 0) printf("Puzzle is ambiguous.\n"); else printf("Puzzle was solved.\n"); verbose = 0; solved = 1; } if (solved) doprint(s); done: if (desc_gen) sfree(desc_gen); if (p) free_params(p); if (s) free_game(s); if (rs) random_free(rs); if (aux) sfree(aux); return 0; } #endif /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/malloc.c0000644000175300017530000000173710247372314014053 0ustar simonsimon/* * malloc.c: safe wrappers around malloc, realloc, free, strdup */ #include #include #include "puzzles.h" /* * smalloc should guarantee to return a useful pointer - Halibut * can do nothing except die when it's out of memory anyway. */ void *smalloc(size_t size) { void *p; p = malloc(size); if (!p) fatal("out of memory"); return p; } /* * sfree should guaranteeably deal gracefully with freeing NULL */ void sfree(void *p) { if (p) { free(p); } } /* * srealloc should guaranteeably be able to realloc NULL */ void *srealloc(void *p, size_t size) { void *q; if (p) { q = realloc(p, size); } else { q = malloc(size); } if (!q) fatal("out of memory"); return q; } /* * dupstr is like strdup, but with the never-return-NULL property * of smalloc (and also reliably defined in all environments :-) */ char *dupstr(const char *s) { char *r = smalloc(1+strlen(s)); strcpy(r,s); return r; } puzzles-r9872/map.c0000644000175300017530000026135612132232554013362 0ustar simonsimon/* * map.c: Game involving four-colouring a map. */ /* * TODO: * * - clue marking * - better four-colouring algorithm? */ #include #include #include #include #include #include #include "puzzles.h" /* * In standalone solver mode, `verbose' is a variable which can be * set by command-line option; in debugging mode it's simply always * true. */ #if defined STANDALONE_SOLVER #define SOLVER_DIAGNOSTICS int verbose = FALSE; #elif defined SOLVER_DIAGNOSTICS #define verbose TRUE #endif /* * I don't seriously anticipate wanting to change the number of * colours used in this game, but it doesn't cost much to use a * #define just in case :-) */ #define FOUR 4 #define THREE (FOUR-1) #define FIVE (FOUR+1) #define SIX (FOUR+2) /* * Ghastly run-time configuration option, just for Gareth (again). */ static int flash_type = -1; static float flash_length; /* * 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(EASY,Easy,e) \ A(NORMAL,Normal,n) \ A(HARD,Hard,h) \ A(RECURSE,Unreasonable,u) #define ENUM(upper,title,lower) DIFF_ ## upper, #define TITLE(upper,title,lower) #title, #define ENCODE(upper,title,lower) #lower #define CONFIG(upper,title,lower) ":" #title enum { DIFFLIST(ENUM) DIFFCOUNT }; static char const *const map_diffnames[] = { DIFFLIST(TITLE) }; static char const map_diffchars[] = DIFFLIST(ENCODE); #define DIFFCONFIG DIFFLIST(CONFIG) enum { TE, BE, LE, RE }; /* top/bottom/left/right edges */ enum { COL_BACKGROUND, COL_GRID, COL_0, COL_1, COL_2, COL_3, COL_ERROR, COL_ERRTEXT, NCOLOURS }; struct game_params { int w, h, n, diff; }; struct map { int refcount; int *map; int *graph; int n; int ngraph; int *immutable; int *edgex, *edgey; /* position of a point on each edge */ int *regionx, *regiony; /* position of a point in each region */ }; struct game_state { game_params p; struct map *map; int *colouring, *pencil; int completed, cheated; }; static game_params *default_params(void) { game_params *ret = snew(game_params); #ifdef PORTRAIT_SCREEN ret->w = 16; ret->h = 18; #else ret->w = 20; ret->h = 15; #endif ret->n = 30; ret->diff = DIFF_NORMAL; return ret; } static const struct game_params map_presets[] = { #ifdef PORTRAIT_SCREEN {16, 18, 30, DIFF_EASY}, {16, 18, 30, DIFF_NORMAL}, {16, 18, 30, DIFF_HARD}, {16, 18, 30, DIFF_RECURSE}, {25, 30, 75, DIFF_NORMAL}, {25, 30, 75, DIFF_HARD}, #else {20, 15, 30, DIFF_EASY}, {20, 15, 30, DIFF_NORMAL}, {20, 15, 30, DIFF_HARD}, {20, 15, 30, DIFF_RECURSE}, {30, 25, 75, DIFF_NORMAL}, {30, 25, 75, DIFF_HARD}, #endif }; static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char str[80]; if (i < 0 || i >= lenof(map_presets)) return FALSE; ret = snew(game_params); *ret = map_presets[i]; sprintf(str, "%dx%d, %d regions, %s", ret->w, ret->h, ret->n, map_diffnames[ret->diff]); *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) { char const *p = string; params->w = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; if (*p == 'x') { p++; params->h = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; } else { params->h = params->w; } if (*p == 'n') { p++; params->n = atoi(p); while (*p && (*p == '.' || isdigit((unsigned char)*p))) p++; } else { params->n = params->w * params->h / 8; } if (*p == 'd') { int i; p++; for (i = 0; i < DIFFCOUNT; i++) if (*p == map_diffchars[i]) params->diff = i; if (*p) p++; } } static char *encode_params(const game_params *params, int full) { char ret[400]; sprintf(ret, "%dx%dn%d", params->w, params->h, params->n); if (full) sprintf(ret + strlen(ret), "d%c", map_diffchars[params->diff]); return dupstr(ret); } static config_item *game_configure(const game_params *params) { config_item *ret; char buf[80]; ret = snewn(5, config_item); ret[0].name = "Width"; ret[0].type = C_STRING; sprintf(buf, "%d", params->w); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Regions"; ret[2].type = C_STRING; sprintf(buf, "%d", params->n); ret[2].sval = dupstr(buf); ret[2].ival = 0; ret[3].name = "Difficulty"; ret[3].type = C_CHOICES; ret[3].sval = DIFFCONFIG; ret[3].ival = params->diff; ret[4].name = NULL; ret[4].type = C_END; ret[4].sval = NULL; ret[4].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); ret->n = atoi(cfg[2].sval); ret->diff = cfg[3].ival; return ret; } static char *validate_params(const game_params *params, int full) { if (params->w < 2 || params->h < 2) return "Width and height must be at least two"; if (params->n < 5) return "Must have at least five regions"; if (params->n > params->w * params->h) return "Too many regions to fit in grid"; return NULL; } /* ---------------------------------------------------------------------- * Cumulative frequency table functions. */ /* * Initialise a cumulative frequency table. (Hardly worth writing * this function; all it does is to initialise everything in the * array to zero.) */ static void cf_init(int *table, int n) { int i; for (i = 0; i < n; i++) table[i] = 0; } /* * Increment the count of symbol `sym' by `count'. */ static void cf_add(int *table, int n, int sym, int count) { int bit; bit = 1; while (sym != 0) { if (sym & bit) { table[sym] += count; sym &= ~bit; } bit <<= 1; } table[0] += count; } /* * Cumulative frequency lookup: return the total count of symbols * with value less than `sym'. */ static int cf_clookup(int *table, int n, int sym) { int bit, index, limit, count; if (sym == 0) return 0; assert(0 < sym && sym <= n); count = table[0]; /* start with the whole table size */ bit = 1; while (bit < n) bit <<= 1; limit = n; while (bit > 0) { /* * Find the least number with its lowest set bit in this * position which is greater than or equal to sym. */ index = ((sym + bit - 1) &~ (bit * 2 - 1)) + bit; if (index < limit) { count -= table[index]; limit = index; } bit >>= 1; } return count; } /* * Single frequency lookup: return the count of symbol `sym'. */ static int cf_slookup(int *table, int n, int sym) { int count, bit; assert(0 <= sym && sym < n); count = table[sym]; for (bit = 1; sym+bit < n && !(sym & bit); bit <<= 1) count -= table[sym+bit]; return count; } /* * Return the largest symbol index such that the cumulative * frequency up to that symbol is less than _or equal to_ count. */ static int cf_whichsym(int *table, int n, int count) { int bit, sym, top; assert(count >= 0 && count < table[0]); bit = 1; while (bit < n) bit <<= 1; sym = 0; top = table[0]; while (bit > 0) { if (sym+bit < n) { if (count >= top - table[sym+bit]) sym += bit; else top -= table[sym+bit]; } bit >>= 1; } return sym; } /* ---------------------------------------------------------------------- * Map generation. * * FIXME: this isn't entirely optimal at present, because it * inherently prioritises growing the largest region since there * are more squares adjacent to it. This acts as a destabilising * influence leading to a few large regions and mostly small ones. * It might be better to do it some other way. */ #define WEIGHT_INCREASED 2 /* for increased perimeter */ #define WEIGHT_DECREASED 4 /* for decreased perimeter */ #define WEIGHT_UNCHANGED 3 /* for unchanged perimeter */ /* * Look at a square and decide which colours can be extended into * it. * * If called with index < 0, it adds together one of * WEIGHT_INCREASED, WEIGHT_DECREASED or WEIGHT_UNCHANGED for each * colour that has a valid extension (according to the effect that * it would have on the perimeter of the region being extended) and * returns the overall total. * * If called with index >= 0, it returns one of the possible * colours depending on the value of index, in such a way that the * number of possible inputs which would give rise to a given * return value correspond to the weight of that value. */ static int extend_options(int w, int h, int n, int *map, int x, int y, int index) { int c, i, dx, dy; int col[8]; int total = 0; if (map[y*w+x] >= 0) { assert(index < 0); return 0; /* can't do this square at all */ } /* * Fetch the eight neighbours of this square, in order around * the square. */ for (dy = -1; dy <= +1; dy++) for (dx = -1; dx <= +1; dx++) { int index = (dy < 0 ? 6-dx : dy > 0 ? 2+dx : 2*(1+dx)); if (x+dx >= 0 && x+dx < w && y+dy >= 0 && y+dy < h) col[index] = map[(y+dy)*w+(x+dx)]; else col[index] = -1; } /* * Iterate over each colour that might be feasible. * * FIXME: this routine currently has O(n) running time. We * could turn it into O(FOUR) by only bothering to iterate over * the colours mentioned in the four neighbouring squares. */ for (c = 0; c < n; c++) { int count, neighbours, runs; /* * One of the even indices of col (representing the * orthogonal neighbours of this square) must be equal to * c, or else this square is not adjacent to region c and * obviously cannot become an extension of it at this time. */ neighbours = 0; for (i = 0; i < 8; i += 2) if (col[i] == c) neighbours++; if (!neighbours) continue; /* * Now we know this square is adjacent to region c. The * next question is, would extending it cause the region to * become non-simply-connected? If so, we mustn't do it. * * We determine this by looking around col to see if we can * find more than one separate run of colour c. */ runs = 0; for (i = 0; i < 8; i++) if (col[i] == c && col[(i+1) & 7] != c) runs++; if (runs > 1) continue; assert(runs == 1); /* * This square is a possibility. Determine its effect on * the region's perimeter (computed from the number of * orthogonal neighbours - 1 means a perimeter increase, 3 * a decrease, 2 no change; 4 is impossible because the * region would already not be simply connected) and we're * done. */ assert(neighbours > 0 && neighbours < 4); count = (neighbours == 1 ? WEIGHT_INCREASED : neighbours == 2 ? WEIGHT_UNCHANGED : WEIGHT_DECREASED); total += count; if (index >= 0 && index < count) return c; else index -= count; } assert(index < 0); return total; } static void genmap(int w, int h, int n, int *map, random_state *rs) { int wh = w*h; int x, y, i, k; int *tmp; assert(n <= wh); tmp = snewn(wh, int); /* * Clear the map, and set up `tmp' as a list of grid indices. */ for (i = 0; i < wh; i++) { map[i] = -1; tmp[i] = i; } /* * Place the region seeds by selecting n members from `tmp'. */ k = wh; for (i = 0; i < n; i++) { int j = random_upto(rs, k); map[tmp[j]] = i; tmp[j] = tmp[--k]; } /* * Re-initialise `tmp' as a cumulative frequency table. This * will store the number of possible region colours we can * extend into each square. */ cf_init(tmp, wh); /* * Go through the grid and set up the initial cumulative * frequencies. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) cf_add(tmp, wh, y*w+x, extend_options(w, h, n, map, x, y, -1)); /* * Now repeatedly choose a square we can extend a region into, * and do so. */ while (tmp[0] > 0) { int k = random_upto(rs, tmp[0]); int sq; int colour; int xx, yy; sq = cf_whichsym(tmp, wh, k); k -= cf_clookup(tmp, wh, sq); x = sq % w; y = sq / w; colour = extend_options(w, h, n, map, x, y, k); map[sq] = colour; /* * Re-scan the nine cells around the one we've just * modified. */ for (yy = max(y-1, 0); yy < min(y+2, h); yy++) for (xx = max(x-1, 0); xx < min(x+2, w); xx++) { cf_add(tmp, wh, yy*w+xx, -cf_slookup(tmp, wh, yy*w+xx) + extend_options(w, h, n, map, xx, yy, -1)); } } /* * Finally, go through and normalise the region labels into * order, meaning that indistinguishable maps are actually * identical. */ for (i = 0; i < n; i++) tmp[i] = -1; k = 0; for (i = 0; i < wh; i++) { assert(map[i] >= 0); if (tmp[map[i]] < 0) tmp[map[i]] = k++; map[i] = tmp[map[i]]; } sfree(tmp); } /* ---------------------------------------------------------------------- * Functions to handle graphs. */ /* * Having got a map in a square grid, convert it into a graph * representation. */ static int gengraph(int w, int h, int n, int *map, int *graph) { int i, j, x, y; /* * Start by setting the graph up as an adjacency matrix. We'll * turn it into a list later. */ for (i = 0; i < n*n; i++) graph[i] = 0; /* * Iterate over the map looking for all adjacencies. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) { int v, vx, vy; v = map[y*w+x]; if (x+1 < w && (vx = map[y*w+(x+1)]) != v) graph[v*n+vx] = graph[vx*n+v] = 1; if (y+1 < h && (vy = map[(y+1)*w+x]) != v) graph[v*n+vy] = graph[vy*n+v] = 1; } /* * Turn the matrix into a list. */ for (i = j = 0; i < n*n; i++) if (graph[i]) graph[j++] = i; return j; } static int graph_edge_index(int *graph, int n, int ngraph, int i, int j) { int v = i*n+j; int top, bot, mid; bot = -1; top = ngraph; while (top - bot > 1) { mid = (top + bot) / 2; if (graph[mid] == v) return mid; else if (graph[mid] < v) bot = mid; else top = mid; } return -1; } #define graph_adjacent(graph, n, ngraph, i, j) \ (graph_edge_index((graph), (n), (ngraph), (i), (j)) >= 0) static int graph_vertex_start(int *graph, int n, int ngraph, int i) { int v = i*n; int top, bot, mid; bot = -1; top = ngraph; while (top - bot > 1) { mid = (top + bot) / 2; if (graph[mid] < v) bot = mid; else top = mid; } return top; } /* ---------------------------------------------------------------------- * Generate a four-colouring of a graph. * * FIXME: it would be nice if we could convert this recursion into * pseudo-recursion using some sort of explicit stack array, for * the sake of the Palm port and its limited stack. */ static int fourcolour_recurse(int *graph, int n, int ngraph, int *colouring, int *scratch, random_state *rs) { int nfree, nvert, start, i, j, k, c, ci; int cs[FOUR]; /* * Find the smallest number of free colours in any uncoloured * vertex, and count the number of such vertices. */ nfree = FIVE; /* start off bigger than FOUR! */ nvert = 0; for (i = 0; i < n; i++) if (colouring[i] < 0 && scratch[i*FIVE+FOUR] <= nfree) { if (nfree > scratch[i*FIVE+FOUR]) { nfree = scratch[i*FIVE+FOUR]; nvert = 0; } nvert++; } /* * If there aren't any uncoloured vertices at all, we're done. */ if (nvert == 0) return TRUE; /* we've got a colouring! */ /* * Pick a random vertex in that set. */ j = random_upto(rs, nvert); for (i = 0; i < n; i++) if (colouring[i] < 0 && scratch[i*FIVE+FOUR] == nfree) if (j-- == 0) break; assert(i < n); start = graph_vertex_start(graph, n, ngraph, i); /* * Loop over the possible colours for i, and recurse for each * one. */ ci = 0; for (c = 0; c < FOUR; c++) if (scratch[i*FIVE+c] == 0) cs[ci++] = c; shuffle(cs, ci, sizeof(*cs), rs); while (ci-- > 0) { c = cs[ci]; /* * Fill in this colour. */ colouring[i] = c; /* * Update the scratch space to reflect a new neighbour * of this colour for each neighbour of vertex i. */ for (j = start; j < ngraph && graph[j] < n*(i+1); j++) { k = graph[j] - i*n; if (scratch[k*FIVE+c] == 0) scratch[k*FIVE+FOUR]--; scratch[k*FIVE+c]++; } /* * Recurse. */ if (fourcolour_recurse(graph, n, ngraph, colouring, scratch, rs)) return TRUE; /* got one! */ /* * If that didn't work, clean up and try again with a * different colour. */ for (j = start; j < ngraph && graph[j] < n*(i+1); j++) { k = graph[j] - i*n; scratch[k*FIVE+c]--; if (scratch[k*FIVE+c] == 0) scratch[k*FIVE+FOUR]++; } colouring[i] = -1; } /* * If we reach here, we were unable to find a colouring at all. * (This doesn't necessarily mean the Four Colour Theorem is * violated; it might just mean we've gone down a dead end and * need to back up and look somewhere else. It's only an FCT * violation if we get all the way back up to the top level and * still fail.) */ return FALSE; } static void fourcolour(int *graph, int n, int ngraph, int *colouring, random_state *rs) { int *scratch; int i; /* * For each vertex and each colour, we store the number of * neighbours that have that colour. Also, we store the number * of free colours for the vertex. */ scratch = snewn(n * FIVE, int); for (i = 0; i < n * FIVE; i++) scratch[i] = (i % FIVE == FOUR ? FOUR : 0); /* * Clear the colouring to start with. */ for (i = 0; i < n; i++) colouring[i] = -1; i = fourcolour_recurse(graph, n, ngraph, colouring, scratch, rs); assert(i); /* by the Four Colour Theorem :-) */ sfree(scratch); } /* ---------------------------------------------------------------------- * Non-recursive solver. */ struct solver_scratch { unsigned char *possible; /* bitmap of colours for each region */ int *graph; int n; int ngraph; int *bfsqueue; int *bfscolour; #ifdef SOLVER_DIAGNOSTICS int *bfsprev; #endif int depth; }; static struct solver_scratch *new_scratch(int *graph, int n, int ngraph) { struct solver_scratch *sc; sc = snew(struct solver_scratch); sc->graph = graph; sc->n = n; sc->ngraph = ngraph; sc->possible = snewn(n, unsigned char); sc->depth = 0; sc->bfsqueue = snewn(n, int); sc->bfscolour = snewn(n, int); #ifdef SOLVER_DIAGNOSTICS sc->bfsprev = snewn(n, int); #endif return sc; } static void free_scratch(struct solver_scratch *sc) { sfree(sc->possible); sfree(sc->bfsqueue); sfree(sc->bfscolour); #ifdef SOLVER_DIAGNOSTICS sfree(sc->bfsprev); #endif sfree(sc); } /* * Count the bits in a word. Only needs to cope with FOUR bits. */ static int bitcount(int word) { assert(FOUR <= 4); /* or this needs changing */ word = ((word & 0xA) >> 1) + (word & 0x5); word = ((word & 0xC) >> 2) + (word & 0x3); return word; } #ifdef SOLVER_DIAGNOSTICS static const char colnames[FOUR] = { 'R', 'Y', 'G', 'B' }; #endif static int place_colour(struct solver_scratch *sc, int *colouring, int index, int colour #ifdef SOLVER_DIAGNOSTICS , char *verb #endif ) { int *graph = sc->graph, n = sc->n, ngraph = sc->ngraph; int j, k; if (!(sc->possible[index] & (1 << colour))) { #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("%*scannot place %c in region %d\n", 2*sc->depth, "", colnames[colour], index); #endif return FALSE; /* can't do it */ } sc->possible[index] = 1 << colour; colouring[index] = colour; #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("%*s%s %c in region %d\n", 2*sc->depth, "", verb, colnames[colour], index); #endif /* * Rule out this colour from all the region's neighbours. */ for (j = graph_vertex_start(graph, n, ngraph, index); j < ngraph && graph[j] < n*(index+1); j++) { k = graph[j] - index*n; #ifdef SOLVER_DIAGNOSTICS if (verbose && (sc->possible[k] & (1 << colour))) printf("%*s ruling out %c in region %d\n", 2*sc->depth, "", colnames[colour], k); #endif sc->possible[k] &= ~(1 << colour); } return TRUE; } #ifdef SOLVER_DIAGNOSTICS static char *colourset(char *buf, int set) { int i; char *p = buf; char *sep = ""; for (i = 0; i < FOUR; i++) if (set & (1 << i)) { p += sprintf(p, "%s%c", sep, colnames[i]); sep = ","; } return buf; } #endif /* * Returns 0 for impossible, 1 for success, 2 for failure to * converge (i.e. puzzle is either ambiguous or just too * difficult). */ static int map_solver(struct solver_scratch *sc, int *graph, int n, int ngraph, int *colouring, int difficulty) { int i; if (sc->depth == 0) { /* * Initialise scratch space. */ for (i = 0; i < n; i++) sc->possible[i] = (1 << FOUR) - 1; /* * Place clues. */ for (i = 0; i < n; i++) if (colouring[i] >= 0) { if (!place_colour(sc, colouring, i, colouring[i] #ifdef SOLVER_DIAGNOSTICS , "initial clue:" #endif )) { #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("%*sinitial clue set is inconsistent\n", 2*sc->depth, ""); #endif return 0; /* the clues aren't even consistent! */ } } } /* * Now repeatedly loop until we find nothing further to do. */ while (1) { int done_something = FALSE; if (difficulty < DIFF_EASY) break; /* can't do anything at all! */ /* * Simplest possible deduction: find a region with only one * possible colour. */ for (i = 0; i < n; i++) if (colouring[i] < 0) { int p = sc->possible[i]; if (p == 0) { #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("%*sregion %d has no possible colours left\n", 2*sc->depth, "", i); #endif return 0; /* puzzle is inconsistent */ } if ((p & (p-1)) == 0) { /* p is a power of two */ int c, ret; for (c = 0; c < FOUR; c++) if (p == (1 << c)) break; assert(c < FOUR); ret = place_colour(sc, colouring, i, c #ifdef SOLVER_DIAGNOSTICS , "placing" #endif ); /* * place_colour() can only fail if colour c was not * even a _possibility_ for region i, and we're * pretty sure it was because we checked before * calling place_colour(). So we can safely assert * here rather than having to return a nice * friendly error code. */ assert(ret); done_something = TRUE; } } if (done_something) continue; if (difficulty < DIFF_NORMAL) break; /* can't do anything harder */ /* * Failing that, go up one level. Look for pairs of regions * which (a) both have the same pair of possible colours, * (b) are adjacent to one another, (c) are adjacent to the * same region, and (d) that region still thinks it has one * or both of those possible colours. * * Simplest way to do this is by going through the graph * edge by edge, so that we start with property (b) and * then look for (a) and finally (c) and (d). */ for (i = 0; i < ngraph; i++) { int j1 = graph[i] / n, j2 = graph[i] % n; int j, k, v, v2; #ifdef SOLVER_DIAGNOSTICS int started = FALSE; #endif if (j1 > j2) continue; /* done it already, other way round */ if (colouring[j1] >= 0 || colouring[j2] >= 0) continue; /* they're not undecided */ if (sc->possible[j1] != sc->possible[j2]) continue; /* they don't have the same possibles */ v = sc->possible[j1]; /* * See if v contains exactly two set bits. */ v2 = v & -v; /* find lowest set bit */ v2 = v & ~v2; /* clear it */ if (v2 == 0 || (v2 & (v2-1)) != 0) /* not power of 2 */ continue; /* * We've found regions j1 and j2 satisfying properties * (a) and (b): they have two possible colours between * them, and since they're adjacent to one another they * must use _both_ those colours between them. * Therefore, if they are both adjacent to any other * region then that region cannot be either colour. * * Go through the neighbours of j1 and see if any are * shared with j2. */ for (j = graph_vertex_start(graph, n, ngraph, j1); j < ngraph && graph[j] < n*(j1+1); j++) { k = graph[j] - j1*n; if (graph_adjacent(graph, n, ngraph, k, j2) && (sc->possible[k] & v)) { #ifdef SOLVER_DIAGNOSTICS if (verbose) { char buf[80]; if (!started) printf("%*sadjacent regions %d,%d share colours" " %s\n", 2*sc->depth, "", j1, j2, colourset(buf, v)); started = TRUE; printf("%*s ruling out %s in region %d\n",2*sc->depth, "", colourset(buf, sc->possible[k] & v), k); } #endif sc->possible[k] &= ~v; done_something = TRUE; } } } if (done_something) continue; if (difficulty < DIFF_HARD) break; /* can't do anything harder */ /* * Right; now we get creative. Now we're going to look for * `forcing chains'. A forcing chain is a path through the * graph with the following properties: * * (a) Each vertex on the path has precisely two possible * colours. * * (b) Each pair of vertices which are adjacent on the * path share at least one possible colour in common. * * (c) Each vertex in the middle of the path shares _both_ * of its colours with at least one of its neighbours * (not the same one with both neighbours). * * These together imply that at least one of the possible * colour choices at one end of the path forces _all_ the * rest of the colours along the path. In order to make * real use of this, we need further properties: * * (c) Ruling out some colour C from the vertex at one end * of the path forces the vertex at the other end to * take colour C. * * (d) The two end vertices are mutually adjacent to some * third vertex. * * (e) That third vertex currently has C as a possibility. * * If we can find all of that lot, we can deduce that at * least one of the two ends of the forcing chain has * colour C, and that therefore the mutually adjacent third * vertex does not. * * To find forcing chains, we're going to start a bfs at * each suitable vertex of the graph, once for each of its * two possible colours. */ for (i = 0; i < n; i++) { int c; if (colouring[i] >= 0 || bitcount(sc->possible[i]) != 2) continue; for (c = 0; c < FOUR; c++) if (sc->possible[i] & (1 << c)) { int j, k, gi, origc, currc, head, tail; /* * Try a bfs from this vertex, ruling out * colour c. * * Within this loop, we work in colour bitmaps * rather than actual colours, because * converting back and forth is a needless * computational expense. */ origc = 1 << c; for (j = 0; j < n; j++) { sc->bfscolour[j] = -1; #ifdef SOLVER_DIAGNOSTICS sc->bfsprev[j] = -1; #endif } head = tail = 0; sc->bfsqueue[tail++] = i; sc->bfscolour[i] = sc->possible[i] &~ origc; while (head < tail) { j = sc->bfsqueue[head++]; currc = sc->bfscolour[j]; /* * Try neighbours of j. */ for (gi = graph_vertex_start(graph, n, ngraph, j); gi < ngraph && graph[gi] < n*(j+1); gi++) { k = graph[gi] - j*n; /* * To continue with the bfs in vertex * k, we need k to be * (a) not already visited * (b) have two possible colours * (c) those colours include currc. */ if (sc->bfscolour[k] < 0 && colouring[k] < 0 && bitcount(sc->possible[k]) == 2 && (sc->possible[k] & currc)) { sc->bfsqueue[tail++] = k; sc->bfscolour[k] = sc->possible[k] &~ currc; #ifdef SOLVER_DIAGNOSTICS sc->bfsprev[k] = j; #endif } /* * One other possibility is that k * might be the region in which we can * make a real deduction: if it's * adjacent to i, contains currc as a * possibility, and currc is equal to * the original colour we ruled out. */ if (currc == origc && graph_adjacent(graph, n, ngraph, k, i) && (sc->possible[k] & currc)) { #ifdef SOLVER_DIAGNOSTICS if (verbose) { char buf[80], *sep = ""; int r; printf("%*sforcing chain, colour %s, ", 2*sc->depth, "", colourset(buf, origc)); for (r = j; r != -1; r = sc->bfsprev[r]) { printf("%s%d", sep, r); sep = "-"; } printf("\n%*s ruling out %s in region" " %d\n", 2*sc->depth, "", colourset(buf, origc), k); } #endif sc->possible[k] &= ~origc; done_something = TRUE; } } } assert(tail <= n); } } if (!done_something) break; } /* * See if we've got a complete solution, and return if so. */ for (i = 0; i < n; i++) if (colouring[i] < 0) break; if (i == n) { #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("%*sone solution found\n", 2*sc->depth, ""); #endif return 1; /* success! */ } /* * If recursion is not permissible, we now give up. */ if (difficulty < DIFF_RECURSE) { #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("%*sunable to proceed further without recursion\n", 2*sc->depth, ""); #endif return 2; /* unable to complete */ } /* * Now we've got to do something recursive. So first hunt for a * currently-most-constrained region. */ { int best, bestc; struct solver_scratch *rsc; int *subcolouring, *origcolouring; int ret, subret; int we_already_got_one; best = -1; bestc = FIVE; for (i = 0; i < n; i++) if (colouring[i] < 0) { int p = sc->possible[i]; enum { compile_time_assertion = 1 / (FOUR <= 4) }; int c; /* Count the set bits. */ c = (p & 5) + ((p >> 1) & 5); c = (c & 3) + ((c >> 2) & 3); assert(c > 1); /* or colouring[i] would be >= 0 */ if (c < bestc) { best = i; bestc = c; } } assert(best >= 0); /* or we'd be solved already */ #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("%*srecursing on region %d\n", 2*sc->depth, "", best); #endif /* * Now iterate over the possible colours for this region. */ rsc = new_scratch(graph, n, ngraph); rsc->depth = sc->depth + 1; origcolouring = snewn(n, int); memcpy(origcolouring, colouring, n * sizeof(int)); subcolouring = snewn(n, int); we_already_got_one = FALSE; ret = 0; for (i = 0; i < FOUR; i++) { if (!(sc->possible[best] & (1 << i))) continue; memcpy(rsc->possible, sc->possible, n); memcpy(subcolouring, origcolouring, n * sizeof(int)); place_colour(rsc, subcolouring, best, i #ifdef SOLVER_DIAGNOSTICS , "trying" #endif ); subret = map_solver(rsc, graph, n, ngraph, subcolouring, difficulty); #ifdef SOLVER_DIAGNOSTICS if (verbose) { printf("%*sretracting %c in region %d; found %s\n", 2*sc->depth, "", colnames[i], best, subret == 0 ? "no solutions" : subret == 1 ? "one solution" : "multiple solutions"); } #endif /* * If this possibility turned up more than one valid * solution, or if it turned up one and we already had * one, we're definitely ambiguous. */ if (subret == 2 || (subret == 1 && we_already_got_one)) { ret = 2; break; } /* * If this possibility turned up one valid solution and * it's the first we've seen, copy it into the output. */ if (subret == 1) { memcpy(colouring, subcolouring, n * sizeof(int)); we_already_got_one = TRUE; ret = 1; } /* * Otherwise, this guess led to a contradiction, so we * do nothing. */ } sfree(origcolouring); sfree(subcolouring); free_scratch(rsc); #ifdef SOLVER_DIAGNOSTICS if (verbose && sc->depth == 0) { printf("%*s%s found\n", 2*sc->depth, "", ret == 0 ? "no solutions" : ret == 1 ? "one solution" : "multiple solutions"); } #endif return ret; } } /* ---------------------------------------------------------------------- * Game generation main function. */ static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { struct solver_scratch *sc = NULL; int *map, *graph, ngraph, *colouring, *colouring2, *regions; int i, j, w, h, n, solveret, cfreq[FOUR]; int wh; int mindiff, tries; #ifdef GENERATION_DIAGNOSTICS int x, y; #endif char *ret, buf[80]; int retlen, retsize; w = params->w; h = params->h; n = params->n; wh = w*h; *aux = NULL; map = snewn(wh, int); graph = snewn(n*n, int); colouring = snewn(n, int); colouring2 = snewn(n, int); regions = snewn(n, int); /* * This is the minimum difficulty below which we'll completely * reject a map design. Normally we set this to one below the * requested difficulty, ensuring that we have the right * result. However, for particularly dense maps or maps with * particularly few regions it might not be possible to get the * desired difficulty, so we will eventually drop this down to * -1 to indicate that any old map will do. */ mindiff = params->diff; tries = 50; while (1) { /* * Create the map. */ genmap(w, h, n, map, rs); #ifdef GENERATION_DIAGNOSTICS for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { int v = map[y*w+x]; if (v >= 62) putchar('!'); else if (v >= 36) putchar('a' + v-36); else if (v >= 10) putchar('A' + v-10); else putchar('0' + v); } putchar('\n'); } #endif /* * Convert the map into a graph. */ ngraph = gengraph(w, h, n, map, graph); #ifdef GENERATION_DIAGNOSTICS for (i = 0; i < ngraph; i++) printf("%d-%d\n", graph[i]/n, graph[i]%n); #endif /* * Colour the map. */ fourcolour(graph, n, ngraph, colouring, rs); #ifdef GENERATION_DIAGNOSTICS for (i = 0; i < n; i++) printf("%d: %d\n", i, colouring[i]); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { int v = colouring[map[y*w+x]]; if (v >= 36) putchar('a' + v-36); else if (v >= 10) putchar('A' + v-10); else putchar('0' + v); } putchar('\n'); } #endif /* * Encode the solution as an aux string. */ if (*aux) /* in case we've come round again */ sfree(*aux); retlen = retsize = 0; ret = NULL; for (i = 0; i < n; i++) { int len; if (colouring[i] < 0) continue; len = sprintf(buf, "%s%d:%d", i ? ";" : "S;", colouring[i], i); if (retlen + len >= retsize) { retsize = retlen + len + 256; ret = sresize(ret, retsize, char); } strcpy(ret + retlen, buf); retlen += len; } *aux = ret; /* * Remove the region colours one by one, keeping * solubility. Also ensure that there always remains at * least one region of every colour, so that the user can * drag from somewhere. */ for (i = 0; i < FOUR; i++) cfreq[i] = 0; for (i = 0; i < n; i++) { regions[i] = i; cfreq[colouring[i]]++; } for (i = 0; i < FOUR; i++) if (cfreq[i] == 0) continue; shuffle(regions, n, sizeof(*regions), rs); if (sc) free_scratch(sc); sc = new_scratch(graph, n, ngraph); for (i = 0; i < n; i++) { j = regions[i]; if (cfreq[colouring[j]] == 1) continue; /* can't remove last region of colour */ memcpy(colouring2, colouring, n*sizeof(int)); colouring2[j] = -1; solveret = map_solver(sc, graph, n, ngraph, colouring2, params->diff); assert(solveret >= 0); /* mustn't be impossible! */ if (solveret == 1) { cfreq[colouring[j]]--; colouring[j] = -1; } } #ifdef GENERATION_DIAGNOSTICS for (i = 0; i < n; i++) if (colouring[i] >= 0) { if (i >= 62) putchar('!'); else if (i >= 36) putchar('a' + i-36); else if (i >= 10) putchar('A' + i-10); else putchar('0' + i); printf(": %d\n", colouring[i]); } #endif /* * Finally, check that the puzzle is _at least_ as hard as * required, and indeed that it isn't already solved. * (Calling map_solver with negative difficulty ensures the * latter - if a solver which _does nothing_ can solve it, * it's too easy!) */ memcpy(colouring2, colouring, n*sizeof(int)); if (map_solver(sc, graph, n, ngraph, colouring2, mindiff - 1) == 1) { /* * Drop minimum difficulty if necessary. */ if (mindiff > 0 && (n < 9 || n > 2*wh/3)) { if (tries-- <= 0) mindiff = 0; /* give up and go for Easy */ } continue; } break; } /* * Encode as a game ID. We do this by: * * - first going along the horizontal edges row by row, and * then the vertical edges column by column * - encoding the lengths of runs of edges and runs of * non-edges * - the decoder will reconstitute the region boundaries from * this and automatically number them the same way we did * - then we encode the initial region colours in a Slant-like * fashion (digits 0-3 interspersed with letters giving * lengths of runs of empty spaces). */ retlen = retsize = 0; ret = NULL; { int run, pv; /* * Start with a notional non-edge, so that there'll be an * explicit `a' to distinguish the case where we start with * an edge. */ run = 1; pv = 0; for (i = 0; i < w*(h-1) + (w-1)*h; i++) { int x, y, dx, dy, v; if (i < w*(h-1)) { /* Horizontal edge. */ y = i / w; x = i % w; dx = 0; dy = 1; } else { /* Vertical edge. */ x = (i - w*(h-1)) / h; y = (i - w*(h-1)) % h; dx = 1; dy = 0; } if (retlen + 10 >= retsize) { retsize = retlen + 256; ret = sresize(ret, retsize, char); } v = (map[y*w+x] != map[(y+dy)*w+(x+dx)]); if (pv != v) { ret[retlen++] = 'a'-1 + run; run = 1; pv = v; } else { /* * 'z' is a special case in this encoding. Rather * than meaning a run of 26 and a state switch, it * means a run of 25 and _no_ state switch, because * otherwise there'd be no way to encode runs of * more than 26. */ if (run == 25) { ret[retlen++] = 'z'; run = 0; } run++; } } ret[retlen++] = 'a'-1 + run; ret[retlen++] = ','; run = 0; for (i = 0; i < n; i++) { if (retlen + 10 >= retsize) { retsize = retlen + 256; ret = sresize(ret, retsize, char); } if (colouring[i] < 0) { /* * In _this_ encoding, 'z' is a run of 26, since * there's no implicit state switch after each run. * Confusingly different, but more compact. */ if (run == 26) { ret[retlen++] = 'z'; run = 0; } run++; } else { if (run > 0) ret[retlen++] = 'a'-1 + run; ret[retlen++] = '0' + colouring[i]; run = 0; } } if (run > 0) ret[retlen++] = 'a'-1 + run; ret[retlen] = '\0'; assert(retlen < retsize); } free_scratch(sc); sfree(regions); sfree(colouring2); sfree(colouring); sfree(graph); sfree(map); return ret; } static char *parse_edge_list(const game_params *params, const char **desc, int *map) { int w = params->w, h = params->h, wh = w*h, n = params->n; int i, k, pos, state; const char *p = *desc; dsf_init(map+wh, wh); pos = -1; state = 0; /* * Parse the game description to get the list of edges, and * build up a disjoint set forest as we go (by identifying * pairs of squares whenever the edge list shows a non-edge). */ while (*p && *p != ',') { if (*p < 'a' || *p > 'z') return "Unexpected character in edge list"; if (*p == 'z') k = 25; else k = *p - 'a' + 1; while (k-- > 0) { int x, y, dx, dy; if (pos < 0) { pos++; continue; } else if (pos < w*(h-1)) { /* Horizontal edge. */ y = pos / w; x = pos % w; dx = 0; dy = 1; } else if (pos < 2*wh-w-h) { /* Vertical edge. */ x = (pos - w*(h-1)) / h; y = (pos - w*(h-1)) % h; dx = 1; dy = 0; } else return "Too much data in edge list"; if (!state) dsf_merge(map+wh, y*w+x, (y+dy)*w+(x+dx)); pos++; } if (*p != 'z') state = !state; p++; } assert(pos <= 2*wh-w-h); if (pos < 2*wh-w-h) return "Too little data in edge list"; /* * Now go through again and allocate region numbers. */ pos = 0; for (i = 0; i < wh; i++) map[i] = -1; for (i = 0; i < wh; i++) { k = dsf_canonify(map+wh, i); if (map[k] < 0) map[k] = pos++; map[i] = map[k]; } if (pos != n) return "Edge list defines the wrong number of regions"; *desc = p; return NULL; } static char *validate_desc(const game_params *params, const char *desc) { int w = params->w, h = params->h, wh = w*h, n = params->n; int area; int *map; char *ret; map = snewn(2*wh, int); ret = parse_edge_list(params, &desc, map); sfree(map); if (ret) return ret; if (*desc != ',') return "Expected comma before clue list"; desc++; /* eat comma */ area = 0; while (*desc) { if (*desc >= '0' && *desc < '0'+FOUR) area++; else if (*desc >= 'a' && *desc <= 'z') area += *desc - 'a' + 1; else return "Unexpected character in clue list"; desc++; } if (area < n) return "Too little data in clue list"; else if (area > n) return "Too much data in clue list"; return NULL; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { int w = params->w, h = params->h, wh = w*h, n = params->n; int i, pos; const char *p; game_state *state = snew(game_state); state->p = *params; state->colouring = snewn(n, int); for (i = 0; i < n; i++) state->colouring[i] = -1; state->pencil = snewn(n, int); for (i = 0; i < n; i++) state->pencil[i] = 0; state->completed = state->cheated = FALSE; state->map = snew(struct map); state->map->refcount = 1; state->map->map = snewn(wh*4, int); state->map->graph = snewn(n*n, int); state->map->n = n; state->map->immutable = snewn(n, int); for (i = 0; i < n; i++) state->map->immutable[i] = FALSE; p = desc; { char *ret; ret = parse_edge_list(params, &p, state->map->map); assert(!ret); } /* * Set up the other three quadrants in `map'. */ for (i = wh; i < 4*wh; i++) state->map->map[i] = state->map->map[i % wh]; assert(*p == ','); p++; /* * Now process the clue list. */ pos = 0; while (*p) { if (*p >= '0' && *p < '0'+FOUR) { state->colouring[pos] = *p - '0'; state->map->immutable[pos] = TRUE; pos++; } else { assert(*p >= 'a' && *p <= 'z'); pos += *p - 'a' + 1; } p++; } assert(pos == n); state->map->ngraph = gengraph(w, h, n, state->map->map, state->map->graph); /* * Attempt to smooth out some of the more jagged region * outlines by the judicious use of diagonally divided squares. */ { random_state *rs = random_new(desc, strlen(desc)); int *squares = snewn(wh, int); int done_something; for (i = 0; i < wh; i++) squares[i] = i; shuffle(squares, wh, sizeof(*squares), rs); do { done_something = FALSE; for (i = 0; i < wh; i++) { int y = squares[i] / w, x = squares[i] % w; int c = state->map->map[y*w+x]; int tc, bc, lc, rc; if (x == 0 || x == w-1 || y == 0 || y == h-1) continue; if (state->map->map[TE * wh + y*w+x] != state->map->map[BE * wh + y*w+x]) continue; tc = state->map->map[BE * wh + (y-1)*w+x]; bc = state->map->map[TE * wh + (y+1)*w+x]; lc = state->map->map[RE * wh + y*w+(x-1)]; rc = state->map->map[LE * wh + y*w+(x+1)]; /* * If this square is adjacent on two sides to one * region and on the other two sides to the other * region, and is itself one of the two regions, we can * adjust it so that it's a diagonal. */ if (tc != bc && (tc == c || bc == c)) { if ((lc == tc && rc == bc) || (lc == bc && rc == tc)) { state->map->map[TE * wh + y*w+x] = tc; state->map->map[BE * wh + y*w+x] = bc; state->map->map[LE * wh + y*w+x] = lc; state->map->map[RE * wh + y*w+x] = rc; done_something = TRUE; } } } } while (done_something); sfree(squares); random_free(rs); } /* * Analyse the map to find a canonical line segment * corresponding to each edge, and a canonical point * corresponding to each region. The former are where we'll * eventually put error markers; the latter are where we'll put * per-region flags such as numbers (when in diagnostic mode). */ { int *bestx, *besty, *an, pass; float *ax, *ay, *best; ax = snewn(state->map->ngraph + n, float); ay = snewn(state->map->ngraph + n, float); an = snewn(state->map->ngraph + n, int); bestx = snewn(state->map->ngraph + n, int); besty = snewn(state->map->ngraph + n, int); best = snewn(state->map->ngraph + n, float); for (i = 0; i < state->map->ngraph + n; i++) { bestx[i] = besty[i] = -1; best[i] = (float)(2*(w+h)+1); ax[i] = ay[i] = 0.0F; an[i] = 0; } /* * We make two passes over the map, finding all the line * segments separating regions and all the suitable points * within regions. In the first pass, we compute the * _average_ x and y coordinate of all the points in a * given class; in the second pass, for each such average * point, we find the candidate closest to it and call that * canonical. * * Line segments are considered to have coordinates in * their centre. Thus, at least one coordinate for any line * segment is always something-and-a-half; so we store our * coordinates as twice their normal value. */ for (pass = 0; pass < 2; pass++) { int x, y; for (y = 0; y < h; y++) for (x = 0; x < w; x++) { int ex[4], ey[4], ea[4], eb[4], en = 0; /* * Look for an edge to the right of this * square, an edge below it, and an edge in the * middle of it. Also look to see if the point * at the bottom right of this square is on an * edge (and isn't a place where more than two * regions meet). */ if (x+1 < w) { /* right edge */ ea[en] = state->map->map[RE * wh + y*w+x]; eb[en] = state->map->map[LE * wh + y*w+(x+1)]; ex[en] = (x+1)*2; ey[en] = y*2+1; en++; } if (y+1 < h) { /* bottom edge */ ea[en] = state->map->map[BE * wh + y*w+x]; eb[en] = state->map->map[TE * wh + (y+1)*w+x]; ex[en] = x*2+1; ey[en] = (y+1)*2; en++; } /* diagonal edge */ ea[en] = state->map->map[TE * wh + y*w+x]; eb[en] = state->map->map[BE * wh + y*w+x]; ex[en] = x*2+1; ey[en] = y*2+1; en++; if (x+1 < w && y+1 < h) { /* bottom right corner */ int oct[8], othercol, nchanges; oct[0] = state->map->map[RE * wh + y*w+x]; oct[1] = state->map->map[LE * wh + y*w+(x+1)]; oct[2] = state->map->map[BE * wh + y*w+(x+1)]; oct[3] = state->map->map[TE * wh + (y+1)*w+(x+1)]; oct[4] = state->map->map[LE * wh + (y+1)*w+(x+1)]; oct[5] = state->map->map[RE * wh + (y+1)*w+x]; oct[6] = state->map->map[TE * wh + (y+1)*w+x]; oct[7] = state->map->map[BE * wh + y*w+x]; othercol = -1; nchanges = 0; for (i = 0; i < 8; i++) { if (oct[i] != oct[0]) { if (othercol < 0) othercol = oct[i]; else if (othercol != oct[i]) break; /* three colours at this point */ } if (oct[i] != oct[(i+1) & 7]) nchanges++; } /* * Now if there are exactly two regions at * this point (not one, and not three or * more), and only two changes around the * loop, then this is a valid place to put * an error marker. */ if (i == 8 && othercol >= 0 && nchanges == 2) { ea[en] = oct[0]; eb[en] = othercol; ex[en] = (x+1)*2; ey[en] = (y+1)*2; en++; } /* * If there's exactly _one_ region at this * point, on the other hand, it's a valid * place to put a region centre. */ if (othercol < 0) { ea[en] = eb[en] = oct[0]; ex[en] = (x+1)*2; ey[en] = (y+1)*2; en++; } } /* * Now process the points we've found, one by * one. */ for (i = 0; i < en; i++) { int emin = min(ea[i], eb[i]); int emax = max(ea[i], eb[i]); int gindex; if (emin != emax) { /* Graph edge */ gindex = graph_edge_index(state->map->graph, n, state->map->ngraph, emin, emax); } else { /* Region number */ gindex = state->map->ngraph + emin; } assert(gindex >= 0); if (pass == 0) { /* * In pass 0, accumulate the values * we'll use to compute the average * positions. */ ax[gindex] += ex[i]; ay[gindex] += ey[i]; an[gindex] += 1; } else { /* * In pass 1, work out whether this * point is closer to the average than * the last one we've seen. */ float dx, dy, d; assert(an[gindex] > 0); dx = ex[i] - ax[gindex]; dy = ey[i] - ay[gindex]; d = (float)sqrt(dx*dx + dy*dy); if (d < best[gindex]) { best[gindex] = d; bestx[gindex] = ex[i]; besty[gindex] = ey[i]; } } } } if (pass == 0) { for (i = 0; i < state->map->ngraph + n; i++) if (an[i] > 0) { ax[i] /= an[i]; ay[i] /= an[i]; } } } state->map->edgex = snewn(state->map->ngraph, int); state->map->edgey = snewn(state->map->ngraph, int); memcpy(state->map->edgex, bestx, state->map->ngraph * sizeof(int)); memcpy(state->map->edgey, besty, state->map->ngraph * sizeof(int)); state->map->regionx = snewn(n, int); state->map->regiony = snewn(n, int); memcpy(state->map->regionx, bestx + state->map->ngraph, n*sizeof(int)); memcpy(state->map->regiony, besty + state->map->ngraph, n*sizeof(int)); for (i = 0; i < state->map->ngraph; i++) if (state->map->edgex[i] < 0) { /* Find the other representation of this edge. */ int e = state->map->graph[i]; int iprime = graph_edge_index(state->map->graph, n, state->map->ngraph, e%n, e/n); assert(state->map->edgex[iprime] >= 0); state->map->edgex[i] = state->map->edgex[iprime]; state->map->edgey[i] = state->map->edgey[iprime]; } sfree(ax); sfree(ay); sfree(an); sfree(best); sfree(bestx); sfree(besty); } return state; } static game_state *dup_game(const game_state *state) { game_state *ret = snew(game_state); ret->p = state->p; ret->colouring = snewn(state->p.n, int); memcpy(ret->colouring, state->colouring, state->p.n * sizeof(int)); ret->pencil = snewn(state->p.n, int); memcpy(ret->pencil, state->pencil, state->p.n * sizeof(int)); ret->map = state->map; ret->map->refcount++; ret->completed = state->completed; ret->cheated = state->cheated; return ret; } static void free_game(game_state *state) { if (--state->map->refcount <= 0) { sfree(state->map->map); sfree(state->map->graph); sfree(state->map->immutable); sfree(state->map->edgex); sfree(state->map->edgey); sfree(state->map->regionx); sfree(state->map->regiony); sfree(state->map); } sfree(state->pencil); sfree(state->colouring); sfree(state); } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { if (!aux) { /* * Use the solver. */ int *colouring; struct solver_scratch *sc; int sret; int i; char *ret, buf[80]; int retlen, retsize; colouring = snewn(state->map->n, int); memcpy(colouring, state->colouring, state->map->n * sizeof(int)); sc = new_scratch(state->map->graph, state->map->n, state->map->ngraph); sret = map_solver(sc, state->map->graph, state->map->n, state->map->ngraph, colouring, DIFFCOUNT-1); free_scratch(sc); if (sret != 1) { sfree(colouring); if (sret == 0) *error = "Puzzle is inconsistent"; else *error = "Unable to find a unique solution for this puzzle"; return NULL; } retsize = 64; ret = snewn(retsize, char); strcpy(ret, "S"); retlen = 1; for (i = 0; i < state->map->n; i++) { int len; assert(colouring[i] >= 0); if (colouring[i] == currstate->colouring[i]) continue; assert(!state->map->immutable[i]); len = sprintf(buf, ";%d:%d", colouring[i], i); if (retlen + len >= retsize) { retsize = retlen + len + 256; ret = sresize(ret, retsize, char); } strcpy(ret + retlen, buf); retlen += len; } sfree(colouring); return ret; } return dupstr(aux); } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { return NULL; } struct game_ui { /* * drag_colour: * * - -2 means no drag currently active. * - >=0 means we're dragging a solid colour. * - -1 means we're dragging a blank space, and drag_pencil * might or might not add some pencil-mark stipples to that. */ int drag_colour; int drag_pencil; int dragx, dragy; int show_numbers; int cur_x, cur_y, cur_visible, cur_moved, cur_lastmove; }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->dragx = ui->dragy = -1; ui->drag_colour = -2; ui->drag_pencil = 0; ui->show_numbers = FALSE; ui->cur_x = ui->cur_y = ui->cur_visible = ui->cur_moved = 0; ui->cur_lastmove = 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) { } struct game_drawstate { int tilesize; unsigned long *drawn, *todraw; int started; int dragx, dragy, drag_visible; blitter *bl; }; /* Flags in `drawn'. */ #define ERR_BASE 0x00800000L #define ERR_MASK 0xFF800000L #define PENCIL_T_BASE 0x00080000L #define PENCIL_T_MASK 0x00780000L #define PENCIL_B_BASE 0x00008000L #define PENCIL_B_MASK 0x00078000L #define PENCIL_MASK 0x007F8000L #define SHOW_NUMBERS 0x00004000L #define TILESIZE (ds->tilesize) #define BORDER (TILESIZE) #define COORD(x) ( (x) * TILESIZE + BORDER ) #define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 ) /* * EPSILON_FOO are epsilons added to absolute cursor position by * cursor movement, such that in pathological cases (e.g. a very * small diamond-shaped area) it's relatively easy to select the * region you wanted. */ #define EPSILON_X(button) (((button) == CURSOR_RIGHT) ? +1 : \ ((button) == CURSOR_LEFT) ? -1 : 0) #define EPSILON_Y(button) (((button) == CURSOR_DOWN) ? +1 : \ ((button) == CURSOR_UP) ? -1 : 0) static int region_from_coords(const game_state *state, const game_drawstate *ds, int x, int y) { int w = state->p.w, h = state->p.h, wh = w*h /*, n = state->p.n */; int tx = FROMCOORD(x), ty = FROMCOORD(y); int dx = x - COORD(tx), dy = y - COORD(ty); int quadrant; if (tx < 0 || tx >= w || ty < 0 || ty >= h) return -1; /* border */ quadrant = 2 * (dx > dy) + (TILESIZE - dx > dy); quadrant = (quadrant == 0 ? BE : quadrant == 1 ? LE : quadrant == 2 ? RE : TE); return state->map->map[quadrant * wh + ty*w+tx]; } static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { char *bufp, buf[256]; int alt_button; /* * Enable or disable numeric labels on regions. */ if (button == 'l' || button == 'L') { ui->show_numbers = !ui->show_numbers; return ""; } if (IS_CURSOR_MOVE(button)) { move_cursor(button, &ui->cur_x, &ui->cur_y, state->p.w, state->p.h, 0); ui->cur_visible = 1; ui->cur_moved = 1; ui->cur_lastmove = button; ui->dragx = COORD(ui->cur_x) + TILESIZE/2 + EPSILON_X(button); ui->dragy = COORD(ui->cur_y) + TILESIZE/2 + EPSILON_Y(button); return ""; } if (IS_CURSOR_SELECT(button)) { if (!ui->cur_visible) { ui->dragx = COORD(ui->cur_x) + TILESIZE/2 + EPSILON_X(ui->cur_lastmove); ui->dragy = COORD(ui->cur_y) + TILESIZE/2 + EPSILON_Y(ui->cur_lastmove); ui->cur_visible = 1; return ""; } if (ui->drag_colour == -2) { /* not currently cursor-dragging, start. */ int r = region_from_coords(state, ds, ui->dragx, ui->dragy); if (r >= 0) { ui->drag_colour = state->colouring[r]; ui->drag_pencil = (ui->drag_colour >= 0) ? 0 : state->pencil[r]; } else { ui->drag_colour = -1; ui->drag_pencil = 0; } ui->cur_moved = 0; return ""; } else { /* currently cursor-dragging; drop the colour in the new region. */ x = COORD(ui->cur_x) + TILESIZE/2 + EPSILON_X(ui->cur_lastmove); y = COORD(ui->cur_y) + TILESIZE/2 + EPSILON_Y(ui->cur_lastmove); alt_button = (button == CURSOR_SELECT2) ? 1 : 0; /* Double-select removes current colour. */ if (!ui->cur_moved) ui->drag_colour = -1; goto drag_dropped; } } if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { int r = region_from_coords(state, ds, x, y); if (r >= 0) { ui->drag_colour = state->colouring[r]; ui->drag_pencil = state->pencil[r]; if (ui->drag_colour >= 0) ui->drag_pencil = 0; /* should be already, but double-check */ } else { ui->drag_colour = -1; ui->drag_pencil = 0; } ui->dragx = x; ui->dragy = y; ui->cur_visible = 0; return ""; } if ((button == LEFT_DRAG || button == RIGHT_DRAG) && ui->drag_colour > -2) { ui->dragx = x; ui->dragy = y; return ""; } if ((button == LEFT_RELEASE || button == RIGHT_RELEASE) && ui->drag_colour > -2) { alt_button = (button == RIGHT_RELEASE) ? 1 : 0; goto drag_dropped; } return NULL; drag_dropped: { int r = region_from_coords(state, ds, x, y); int c = ui->drag_colour; int p = ui->drag_pencil; int oldp; /* * Cancel the drag, whatever happens. */ ui->drag_colour = -2; if (r < 0) return ""; /* drag into border; do nothing else */ if (state->map->immutable[r]) return ""; /* can't change this region */ if (state->colouring[r] == c && state->pencil[r] == p) return ""; /* don't _need_ to change this region */ if (alt_button) { if (state->colouring[r] >= 0) { /* Can't pencil on a coloured region */ return ""; } else if (c >= 0) { /* Right-dragging from colour to blank toggles one pencil */ p = state->pencil[r] ^ (1 << c); c = -1; } /* Otherwise, right-dragging from blank to blank is equivalent * to left-dragging. */ } bufp = buf; oldp = state->pencil[r]; if (c != state->colouring[r]) { bufp += sprintf(bufp, ";%c:%d", (int)(c < 0 ? 'C' : '0' + c), r); if (c >= 0) oldp = 0; } if (p != oldp) { int i; for (i = 0; i < FOUR; i++) if ((oldp ^ p) & (1 << i)) bufp += sprintf(bufp, ";p%c:%d", (int)('0' + i), r); } return dupstr(buf+1); /* ignore first semicolon */ } } static game_state *execute_move(const game_state *state, const char *move) { int n = state->p.n; game_state *ret = dup_game(state); int c, k, adv, i; while (*move) { int pencil = FALSE; c = *move; if (c == 'p') { pencil = TRUE; c = *++move; } if ((c == 'C' || (c >= '0' && c < '0'+FOUR)) && sscanf(move+1, ":%d%n", &k, &adv) == 1 && k >= 0 && k < state->p.n) { move += 1 + adv; if (pencil) { if (ret->colouring[k] >= 0) { free_game(ret); return NULL; } if (c == 'C') ret->pencil[k] = 0; else ret->pencil[k] ^= 1 << (c - '0'); } else { ret->colouring[k] = (c == 'C' ? -1 : c - '0'); ret->pencil[k] = 0; } } else if (*move == 'S') { move++; ret->cheated = TRUE; } else { free_game(ret); return NULL; } if (*move && *move != ';') { free_game(ret); return NULL; } if (*move) move++; } /* * Check for completion. */ if (!ret->completed) { int ok = TRUE; for (i = 0; i < n; i++) if (ret->colouring[i] < 0) { ok = FALSE; break; } if (ok) { for (i = 0; i < ret->map->ngraph; i++) { int j = ret->map->graph[i] / n; int k = ret->map->graph[i] % n; if (ret->colouring[j] == ret->colouring[k]) { ok = FALSE; break; } } } if (ok) 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 = params->w * TILESIZE + 2 * BORDER + 1; *y = params->h * TILESIZE + 2 * BORDER + 1; } static void game_set_size(drawing *dr, game_drawstate *ds, const game_params *params, int tilesize) { ds->tilesize = tilesize; assert(!ds->bl); /* set_size is never called twice */ ds->bl = blitter_new(dr, TILESIZE+3, TILESIZE+3); } const float map_colours[FOUR][3] = { #ifdef VIVID_COLOURS /* Use more vivid colours (e.g. on the Pocket PC) */ {0.75F, 0.25F, 0.25F}, {0.3F, 0.7F, 0.3F}, {0.3F, 0.3F, 0.7F}, {0.85F, 0.85F, 0.1F}, #else {0.7F, 0.5F, 0.4F}, {0.8F, 0.7F, 0.4F}, {0.5F, 0.6F, 0.4F}, {0.55F, 0.45F, 0.35F}, #endif }; const int map_hatching[FOUR] = { HATCH_VERT, HATCH_SLASH, HATCH_HORIZ, HATCH_BACKSLASH }; 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; memcpy(ret + COL_0 * 3, map_colours[0], 3 * sizeof(float)); memcpy(ret + COL_1 * 3, map_colours[1], 3 * sizeof(float)); memcpy(ret + COL_2 * 3, map_colours[2], 3 * sizeof(float)); memcpy(ret + COL_3 * 3, map_colours[3], 3 * sizeof(float)); ret[COL_ERROR * 3 + 0] = 1.0F; ret[COL_ERROR * 3 + 1] = 0.0F; ret[COL_ERROR * 3 + 2] = 0.0F; ret[COL_ERRTEXT * 3 + 0] = 1.0F; ret[COL_ERRTEXT * 3 + 1] = 1.0F; ret[COL_ERRTEXT * 3 + 2] = 1.0F; *ncolours = NCOLOURS; return ret; } static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { struct game_drawstate *ds = snew(struct game_drawstate); int i; ds->tilesize = 0; ds->drawn = snewn(state->p.w * state->p.h, unsigned long); for (i = 0; i < state->p.w * state->p.h; i++) ds->drawn[i] = 0xFFFFL; ds->todraw = snewn(state->p.w * state->p.h, unsigned long); ds->started = FALSE; ds->bl = NULL; ds->drag_visible = FALSE; ds->dragx = ds->dragy = -1; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->drawn); sfree(ds->todraw); if (ds->bl) blitter_free(dr, ds->bl); sfree(ds); } static void draw_error(drawing *dr, game_drawstate *ds, int x, int y) { int coords[8]; int yext, xext; /* * Draw a diamond. */ coords[0] = x - TILESIZE*2/5; coords[1] = y; coords[2] = x; coords[3] = y - TILESIZE*2/5; coords[4] = x + TILESIZE*2/5; coords[5] = y; coords[6] = x; coords[7] = y + TILESIZE*2/5; draw_polygon(dr, coords, 4, COL_ERROR, COL_GRID); /* * Draw an exclamation mark in the diamond. This turns out to * look unpleasantly off-centre if done via draw_text, so I do * it by hand on the basis that exclamation marks aren't that * difficult to draw... */ xext = TILESIZE/16; yext = TILESIZE*2/5 - (xext*2+2); draw_rect(dr, x-xext, y-yext, xext*2+1, yext*2+1 - (xext*3), COL_ERRTEXT); draw_rect(dr, x-xext, y+yext-xext*2+1, xext*2+1, xext*2, COL_ERRTEXT); } static void draw_square(drawing *dr, game_drawstate *ds, const game_params *params, struct map *map, int x, int y, unsigned long v) { int w = params->w, h = params->h, wh = w*h; int tv, bv, xo, yo, i, j, oldj; unsigned long errs, pencil, show_numbers; errs = v & ERR_MASK; v &= ~ERR_MASK; pencil = v & PENCIL_MASK; v &= ~PENCIL_MASK; show_numbers = v & SHOW_NUMBERS; v &= ~SHOW_NUMBERS; tv = v / FIVE; bv = v % FIVE; clip(dr, COORD(x), COORD(y), TILESIZE, TILESIZE); /* * Draw the region colour. */ draw_rect(dr, COORD(x), COORD(y), TILESIZE, TILESIZE, (tv == FOUR ? COL_BACKGROUND : COL_0 + tv)); /* * Draw the second region colour, if this is a diagonally * divided square. */ if (map->map[TE * wh + y*w+x] != map->map[BE * wh + y*w+x]) { int coords[6]; coords[0] = COORD(x)-1; coords[1] = COORD(y+1)+1; if (map->map[LE * wh + y*w+x] == map->map[TE * wh + y*w+x]) coords[2] = COORD(x+1)+1; else coords[2] = COORD(x)-1; coords[3] = COORD(y)-1; coords[4] = COORD(x+1)+1; coords[5] = COORD(y+1)+1; draw_polygon(dr, coords, 3, (bv == FOUR ? COL_BACKGROUND : COL_0 + bv), COL_GRID); } /* * Draw `pencil marks'. Currently we arrange these in a square * formation, which means we may be in trouble if the value of * FOUR changes later... */ assert(FOUR == 4); for (yo = 0; yo < 4; yo++) for (xo = 0; xo < 4; xo++) { int te = map->map[TE * wh + y*w+x]; int e, ee, c; e = (yo < xo && yo < 3-xo ? TE : yo > xo && yo > 3-xo ? BE : xo < 2 ? LE : RE); ee = map->map[e * wh + y*w+x]; if (xo != (yo * 2 + 1) % 5) continue; c = yo; if (!(pencil & ((ee == te ? PENCIL_T_BASE : PENCIL_B_BASE) << c))) continue; if (yo == xo && (map->map[TE * wh + y*w+x] != map->map[LE * wh + y*w+x])) continue; /* avoid TL-BR diagonal line */ if (yo == 3-xo && (map->map[TE * wh + y*w+x] != map->map[RE * wh + y*w+x])) continue; /* avoid BL-TR diagonal line */ draw_circle(dr, COORD(x) + (xo+1)*TILESIZE/5, COORD(y) + (yo+1)*TILESIZE/5, TILESIZE/7, COL_0 + c, COL_0 + c); } /* * Draw the grid lines, if required. */ if (x <= 0 || map->map[RE*wh+y*w+(x-1)] != map->map[LE*wh+y*w+x]) draw_rect(dr, COORD(x), COORD(y), 1, TILESIZE, COL_GRID); if (y <= 0 || map->map[BE*wh+(y-1)*w+x] != map->map[TE*wh+y*w+x]) draw_rect(dr, COORD(x), COORD(y), TILESIZE, 1, COL_GRID); if (x <= 0 || y <= 0 || map->map[RE*wh+(y-1)*w+(x-1)] != map->map[TE*wh+y*w+x] || map->map[BE*wh+(y-1)*w+(x-1)] != map->map[LE*wh+y*w+x]) draw_rect(dr, COORD(x), COORD(y), 1, 1, COL_GRID); /* * Draw error markers. */ for (yo = 0; yo < 3; yo++) for (xo = 0; xo < 3; xo++) if (errs & (ERR_BASE << (yo*3+xo))) draw_error(dr, ds, (COORD(x)*2+TILESIZE*xo)/2, (COORD(y)*2+TILESIZE*yo)/2); /* * Draw region numbers, if desired. */ if (show_numbers) { oldj = -1; for (i = 0; i < 2; i++) { j = map->map[(i?BE:TE)*wh+y*w+x]; if (oldj == j) continue; oldj = j; xo = map->regionx[j] - 2*x; yo = map->regiony[j] - 2*y; if (xo >= 0 && xo <= 2 && yo >= 0 && yo <= 2) { char buf[80]; sprintf(buf, "%d", j); draw_text(dr, (COORD(x)*2+TILESIZE*xo)/2, (COORD(y)*2+TILESIZE*yo)/2, FONT_VARIABLE, 3*TILESIZE/5, ALIGN_HCENTRE|ALIGN_VCENTRE, COL_GRID, buf); } } } unclip(dr); draw_update(dr, COORD(x), COORD(y), 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, n = state->p.n; int x, y, i; int flash; if (ds->drag_visible) { blitter_load(dr, ds->bl, ds->dragx, ds->dragy); draw_update(dr, ds->dragx, ds->dragy, TILESIZE + 3, TILESIZE + 3); ds->drag_visible = FALSE; } /* * 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. */ if (!ds->started) { int ww, wh; game_compute_size(&state->p, TILESIZE, &ww, &wh); draw_rect(dr, 0, 0, ww, wh, COL_BACKGROUND); draw_rect(dr, COORD(0), COORD(0), w*TILESIZE+1, h*TILESIZE+1, COL_GRID); draw_update(dr, 0, 0, ww, wh); ds->started = TRUE; } if (flashtime) { if (flash_type == 1) flash = (int)(flashtime * FOUR / flash_length); else flash = 1 + (int)(flashtime * THREE / flash_length); } else flash = -1; /* * Set up the `todraw' array. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) { int tv = state->colouring[state->map->map[TE * wh + y*w+x]]; int bv = state->colouring[state->map->map[BE * wh + y*w+x]]; unsigned long v; if (tv < 0) tv = FOUR; if (bv < 0) bv = FOUR; if (flash >= 0) { if (flash_type == 1) { if (tv == flash) tv = FOUR; if (bv == flash) bv = FOUR; } else if (flash_type == 2) { if (flash % 2) tv = bv = FOUR; } else { if (tv != FOUR) tv = (tv + flash) % FOUR; if (bv != FOUR) bv = (bv + flash) % FOUR; } } v = tv * FIVE + bv; /* * Add pencil marks. */ for (i = 0; i < FOUR; i++) { if (state->colouring[state->map->map[TE * wh + y*w+x]] < 0 && (state->pencil[state->map->map[TE * wh + y*w+x]] & (1<colouring[state->map->map[BE * wh + y*w+x]] < 0 && (state->pencil[state->map->map[BE * wh + y*w+x]] & (1<show_numbers) v |= SHOW_NUMBERS; ds->todraw[y*w+x] = v; } /* * Add error markers to the `todraw' array. */ for (i = 0; i < state->map->ngraph; i++) { int v1 = state->map->graph[i] / n; int v2 = state->map->graph[i] % n; int xo, yo; if (state->colouring[v1] < 0 || state->colouring[v2] < 0) continue; if (state->colouring[v1] != state->colouring[v2]) continue; x = state->map->edgex[i]; y = state->map->edgey[i]; xo = x % 2; x /= 2; yo = y % 2; y /= 2; ds->todraw[y*w+x] |= ERR_BASE << (yo*3+xo); if (xo == 0) { assert(x > 0); ds->todraw[y*w+(x-1)] |= ERR_BASE << (yo*3+2); } if (yo == 0) { assert(y > 0); ds->todraw[(y-1)*w+x] |= ERR_BASE << (2*3+xo); } if (xo == 0 && yo == 0) { assert(x > 0 && y > 0); ds->todraw[(y-1)*w+(x-1)] |= ERR_BASE << (2*3+2); } } /* * Now actually draw everything. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) { unsigned long v = ds->todraw[y*w+x]; if (ds->drawn[y*w+x] != v) { draw_square(dr, ds, &state->p, state->map, x, y, v); ds->drawn[y*w+x] = v; } } /* * Draw the dragged colour blob if any. */ if ((ui->drag_colour > -2) || ui->cur_visible) { int bg, iscur = 0; if (ui->drag_colour >= 0) bg = COL_0 + ui->drag_colour; else if (ui->drag_colour == -1) { bg = COL_BACKGROUND; } else { int r = region_from_coords(state, ds, ui->dragx, ui->dragy); int c = (r < 0) ? -1 : state->colouring[r]; assert(ui->cur_visible); /*bg = COL_GRID;*/ bg = (c < 0) ? COL_BACKGROUND : COL_0 + c; iscur = 1; } ds->dragx = ui->dragx - TILESIZE/2 - 2; ds->dragy = ui->dragy - TILESIZE/2 - 2; blitter_save(dr, ds->bl, ds->dragx, ds->dragy); draw_circle(dr, ui->dragx, ui->dragy, iscur ? TILESIZE/4 : TILESIZE/2, bg, COL_GRID); for (i = 0; i < FOUR; i++) if (ui->drag_pencil & (1 << i)) draw_circle(dr, ui->dragx + ((i*4+2)%10-3) * TILESIZE/10, ui->dragy + (i*2-3) * TILESIZE/10, TILESIZE/8, COL_0 + i, COL_0 + i); draw_update(dr, ds->dragx, ds->dragy, TILESIZE + 3, TILESIZE + 3); ds->drag_visible = TRUE; } } 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) { if (flash_type < 0) { char *env = getenv("MAP_ALTERNATIVE_FLASH"); if (env) flash_type = atoi(env); else flash_type = 0; flash_length = (flash_type == 1 ? 0.50F : 0.30F); } return flash_length; } else return 0.0F; } static int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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) { int pw, ph; /* * I'll use 4mm squares by default, I think. Simplest way to * compute this size is to compute the pixel puzzle size at a * given tile size and then scale. */ game_compute_size(params, 400, &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->p.w, h = state->p.h, wh = w*h, n = state->p.n; int ink, c[FOUR], i; int x, y, r; int *coords, ncoords, coordsize; /* Ick: fake up `ds->tilesize' for macro expansion purposes */ struct { int tilesize; } ads, *ds = &ads; /* We can't call game_set_size() here because we don't want a blitter */ ads.tilesize = tilesize; ink = print_mono_colour(dr, 0); for (i = 0; i < FOUR; i++) c[i] = print_rgb_hatched_colour(dr, map_colours[i][0], map_colours[i][1], map_colours[i][2], map_hatching[i]); coordsize = 0; coords = NULL; print_line_width(dr, TILESIZE / 16); /* * Draw a single filled polygon around each region. */ for (r = 0; r < n; r++) { int octants[8], lastdir, d1, d2, ox, oy; /* * Start by finding a point on the region boundary. Any * point will do. To do this, we'll search for a square * containing the region and then decide which corner of it * to use. */ x = w; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { if (state->map->map[wh*0+y*w+x] == r || state->map->map[wh*1+y*w+x] == r || state->map->map[wh*2+y*w+x] == r || state->map->map[wh*3+y*w+x] == r) break; } if (x < w) break; } assert(y < h && x < w); /* we must have found one somewhere */ /* * This is the first square in lexicographic order which * contains part of this region. Therefore, one of the top * two corners of the square must be what we're after. The * only case in which it isn't the top left one is if the * square is diagonally divided and the region is in the * bottom right half. */ if (state->map->map[wh*TE+y*w+x] != r && state->map->map[wh*LE+y*w+x] != r) x++; /* could just as well have done y++ */ /* * Now we have a point on the region boundary. Trace around * the region until we come back to this point, * accumulating coordinates for a polygon draw operation as * we go. */ lastdir = -1; ox = x; oy = y; ncoords = 0; do { /* * There are eight possible directions we could head in * from here. We identify them by octant numbers, and * we also use octant numbers to identify the spaces * between them: * * 6 7 0 * \ 7|0 / * \ | / * 6 \|/ 1 * 5-----+-----1 * 5 /|\ 2 * / | \ * / 4|3 \ * 4 3 2 */ octants[0] = x0 ? state->map->map[wh*LE+(y-1)*w+x] : -1; octants[1] = x0 ? state->map->map[wh*BE+(y-1)*w+x] : -1; octants[2] = xmap->map[wh*TE+y*w+x] : -1; octants[3] = xmap->map[wh*LE+y*w+x] : -1; octants[4] = x>0 && ymap->map[wh*RE+y*w+(x-1)] : -1; octants[5] = x>0 && ymap->map[wh*TE+y*w+(x-1)] : -1; octants[6] = x>0 && y>0 ? state->map->map[wh*BE+(y-1)*w+(x-1)] :-1; octants[7] = x>0 && y>0 ? state->map->map[wh*RE+(y-1)*w+(x-1)] :-1; d1 = d2 = -1; for (i = 0; i < 8; i++) if ((octants[i] == r) ^ (octants[(i+1)%8] == r)) { assert(d2 == -1); if (d1 == -1) d1 = i; else d2 = i; } assert(d1 != -1 && d2 != -1); if (d1 == lastdir) d1 = d2; /* * Now we're heading in direction d1. Save the current * coordinates. */ if (ncoords + 2 > coordsize) { coordsize += 128; coords = sresize(coords, coordsize, int); } coords[ncoords++] = COORD(x); coords[ncoords++] = COORD(y); /* * Compute the new coordinates. */ x += (d1 % 4 == 3 ? 0 : d1 < 4 ? +1 : -1); y += (d1 % 4 == 1 ? 0 : d1 > 1 && d1 < 5 ? +1 : -1); assert(x >= 0 && x <= w && y >= 0 && y <= h); lastdir = d1 ^ 4; } while (x != ox || y != oy); draw_polygon(dr, coords, ncoords/2, state->colouring[r] >= 0 ? c[state->colouring[r]] : -1, ink); } sfree(coords); } #ifdef COMBINED #define thegame map #endif const struct game thegame = { "Map", "games.map", "map", default_params, game_fetch_preset, 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, FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, decode_ui, game_changed_state, interpret_move, execute_move, 20, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, TRUE, TRUE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, 0, /* flags */ }; #ifdef STANDALONE_SOLVER int main(int argc, char **argv) { game_params *p; game_state *s; char *id = NULL, *desc, *err; int grade = FALSE; int ret, diff, really_verbose = FALSE; struct solver_scratch *sc; int i; while (--argc > 0) { char *p = *++argv; if (!strcmp(p, "-v")) { really_verbose = 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); sc = new_scratch(s->map->graph, s->map->n, s->map->ngraph); /* * When solving an Easy 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 */ for (diff = 0; diff < DIFFCOUNT; diff++) { for (i = 0; i < s->map->n; i++) if (!s->map->immutable[i]) s->colouring[i] = -1; ret = map_solver(sc, s->map->graph, s->map->n, s->map->ngraph, s->colouring, diff); if (ret < 2) break; } if (diff == DIFFCOUNT) { if (grade) printf("Difficulty rating: harder than Hard, or ambiguous\n"); else printf("Unable to find a unique solution\n"); } else { if (grade) { if (ret == 0) printf("Difficulty rating: impossible (no solution exists)\n"); else if (ret == 1) printf("Difficulty rating: %s\n", map_diffnames[diff]); } else { verbose = really_verbose; for (i = 0; i < s->map->n; i++) if (!s->map->immutable[i]) s->colouring[i] = -1; ret = map_solver(sc, s->map->graph, s->map->n, s->map->ngraph, s->colouring, diff); if (ret == 0) printf("Puzzle is inconsistent\n"); else { int col = 0; for (i = 0; i < s->map->n; i++) { printf("%5d <- %c%c", i, colnames[s->colouring[i]], (col < 6 && i+1 < s->map->n ? ' ' : '\n')); if (++col == 7) col = 0; } } } } return 0; } #endif /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/maxflow.c0000644000175300017530000002373510323523700014253 0ustar simonsimon/* * Edmonds-Karp algorithm for finding a maximum flow and minimum * cut in a network. Almost identical to the Ford-Fulkerson * algorithm, but apparently using breadth-first search to find the * _shortest_ augmenting path is a good way to guarantee * termination and ensure the time complexity is not dependent on * the actual value of the maximum flow. I don't understand why * that should be, but it's claimed on the Internet that it's been * proved, and that's good enough for me. I prefer BFS to DFS * anyway :-) */ #include #include #include #include "maxflow.h" #include "puzzles.h" /* for snewn/sfree */ int maxflow_with_scratch(void *scratch, int nv, int source, int sink, int ne, const int *edges, const int *backedges, const int *capacity, int *flow, int *cut) { int *todo = (int *)scratch; int *prev = todo + nv; int *firstedge = todo + 2*nv; int *firstbackedge = todo + 3*nv; int i, j, head, tail, from, to; int totalflow; /* * Scan the edges array to find the index of the first edge * from each node. */ j = 0; for (i = 0; i < ne; i++) while (j <= edges[2*i]) firstedge[j++] = i; while (j < nv) firstedge[j++] = ne; assert(j == nv); /* * Scan the backedges array to find the index of the first edge * _to_ each node. */ j = 0; for (i = 0; i < ne; i++) while (j <= edges[2*backedges[i]+1]) firstbackedge[j++] = i; while (j < nv) firstbackedge[j++] = ne; assert(j == nv); /* * Start the flow off at zero on every edge. */ for (i = 0; i < ne; i++) flow[i] = 0; totalflow = 0; /* * Repeatedly look for an augmenting path, and follow it. */ while (1) { /* * Set up the prev array. */ for (i = 0; i < nv; i++) prev[i] = -1; /* * Initialise the to-do list for BFS. */ head = tail = 0; todo[tail++] = source; /* * Now do the BFS loop. */ while (head < tail && prev[sink] <= 0) { from = todo[head++]; /* * Try all the forward edges out of node `from'. For a * forward edge to be valid, it must have flow * currently less than its capacity. */ for (i = firstedge[from]; i < ne && edges[2*i] == from; i++) { to = edges[2*i+1]; if (to == source || prev[to] >= 0) continue; if (capacity[i] >= 0 && flow[i] >= capacity[i]) continue; /* * This is a valid augmenting edge. Visit node `to'. */ prev[to] = 2*i; todo[tail++] = to; } /* * Try all the backward edges into node `from'. For a * backward edge to be valid, it must have flow * currently greater than zero. */ for (i = firstbackedge[from]; j = backedges[i], i < ne && edges[2*j+1]==from; i++) { to = edges[2*j]; if (to == source || prev[to] >= 0) continue; if (flow[j] <= 0) continue; /* * This is a valid augmenting edge. Visit node `to'. */ prev[to] = 2*j+1; todo[tail++] = to; } } /* * If prev[sink] is non-null, we have found an augmenting * path. */ if (prev[sink] >= 0) { int max; /* * Work backwards along the path figuring out the * maximum flow we can add. */ to = sink; max = -1; while (to != source) { int spare; /* * Find the edge we're currently moving along. */ i = prev[to]; from = edges[i]; assert(from != to); /* * Determine the spare capacity of this edge. */ if (i & 1) spare = flow[i / 2]; /* backward edge */ else if (capacity[i / 2] >= 0) spare = capacity[i / 2] - flow[i / 2]; /* forward edge */ else spare = -1; /* unlimited forward edge */ assert(spare != 0); if (max < 0 || (spare >= 0 && spare < max)) max = spare; to = from; } /* * Fail an assertion if max is still < 0, i.e. there is * an entirely unlimited path from source to sink. Also * max should not _be_ zero, because by construction * this _should_ be an augmenting path. */ assert(max > 0); /* * Now work backwards along the path again, this time * actually adjusting the flow. */ to = sink; while (to != source) { /* * Find the edge we're currently moving along. */ i = prev[to]; from = edges[i]; assert(from != to); /* * Adjust the edge. */ if (i & 1) flow[i / 2] -= max; /* backward edge */ else flow[i / 2] += max; /* forward edge */ to = from; } /* * And adjust the overall flow counter. */ totalflow += max; continue; } /* * If we reach here, we have failed to find an augmenting * path, which means we're done. Output the `cut' array if * required, and leave. */ if (cut) { for (i = 0; i < nv; i++) { if (i == source || prev[i] >= 0) cut[i] = 0; else cut[i] = 1; } } return totalflow; } } int maxflow_scratch_size(int nv) { return (nv * 4) * sizeof(int); } void maxflow_setup_backedges(int ne, const int *edges, int *backedges) { int i, n; for (i = 0; i < ne; i++) backedges[i] = i; /* * We actually can't use the C qsort() function, because we'd * need to pass `edges' as a context parameter to its * comparator function. So instead I'm forced to implement my * own sorting algorithm internally, which is a pest. I'll use * heapsort, because I like it. */ #define LESS(i,j) ( (edges[2*(i)+1] < edges[2*(j)+1]) || \ (edges[2*(i)+1] == edges[2*(j)+1] && \ edges[2*(i)] < edges[2*(j)]) ) #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) /* * Phase 1: build the heap. We want the _largest_ element at * the top. */ n = 0; while (n < ne) { n++; /* * Swap element n with its parent repeatedly to preserve * the heap property. */ i = n-1; while (i > 0) { int p = PARENT(i); if (LESS(backedges[p], backedges[i])) { SWAP(backedges[p], backedges[i]); i = p; } else break; } } /* * Phase 2: repeatedly remove the largest element and stick it * at the top of the array. */ while (n > 0) { /* * The largest element is at position 0. Put it at the top, * and swap the arbitrary element from that position into * position 0. */ n--; SWAP(backedges[0], backedges[n]); /* * 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 >= n) break; /* we've hit bottom */ if (rc >= n) { /* * Special case: there is only one child to check. */ if (LESS(backedges[i], backedges[lc])) SWAP(backedges[i], backedges[lc]); /* _Now_ we've hit bottom. */ break; } else { /* * The common case: there are two children and we * must check them both. */ if (LESS(backedges[i], backedges[lc]) || LESS(backedges[i], backedges[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 (LESS(backedges[lc], backedges[rc])) { SWAP(backedges[i], backedges[rc]); i = rc; } else { SWAP(backedges[i], backedges[lc]); i = lc; } } else { /* This element is in the right place; we're done. */ break; } } } } #undef LESS #undef PARENT #undef LCHILD #undef RCHILD #undef SWAP } int maxflow(int nv, int source, int sink, int ne, const int *edges, const int *capacity, int *flow, int *cut) { void *scratch; int *backedges; int size; int ret; /* * Allocate the space. */ size = ne * sizeof(int) + maxflow_scratch_size(nv); backedges = smalloc(size); if (!backedges) return -1; scratch = backedges + ne; /* * Set up the backedges array. */ maxflow_setup_backedges(ne, edges, backedges); /* * Call the main function. */ ret = maxflow_with_scratch(scratch, nv, source, sink, ne, edges, backedges, capacity, flow, cut); /* * Free the scratch space. */ sfree(backedges); /* * And we're done. */ return ret; } #ifdef TESTMODE #define MAXEDGES 256 #define MAXVERTICES 128 #define ADDEDGE(i,j) do{edges[ne*2] = (i); edges[ne*2+1] = (j); ne++;}while(0) int compare_edge(const void *av, const void *bv) { const int *a = (const int *)av; const int *b = (const int *)bv; if (a[0] < b[0]) return -1; else if (a[0] > b[0]) return +1; else if (a[1] < b[1]) return -1; else if (a[1] > b[1]) return +1; else return 0; } int main(void) { int edges[MAXEDGES*2], ne, nv; int capacity[MAXEDGES], flow[MAXEDGES], cut[MAXVERTICES]; int source, sink, p, q, i, j, ret; /* * Use this algorithm to find a maximal complete matching in a * bipartite graph. */ ne = 0; nv = 0; source = nv++; p = nv; nv += 5; q = nv; nv += 5; sink = nv++; for (i = 0; i < 5; i++) { capacity[ne] = 1; ADDEDGE(source, p+i); } for (i = 0; i < 5; i++) { capacity[ne] = 1; ADDEDGE(q+i, sink); } j = ne; capacity[ne] = 1; ADDEDGE(p+0,q+0); capacity[ne] = 1; ADDEDGE(p+1,q+0); capacity[ne] = 1; ADDEDGE(p+1,q+1); capacity[ne] = 1; ADDEDGE(p+2,q+1); capacity[ne] = 1; ADDEDGE(p+2,q+2); capacity[ne] = 1; ADDEDGE(p+3,q+2); capacity[ne] = 1; ADDEDGE(p+3,q+3); capacity[ne] = 1; ADDEDGE(p+4,q+3); /* capacity[ne] = 1; ADDEDGE(p+2,q+4); */ qsort(edges, ne, 2*sizeof(int), compare_edge); ret = maxflow(nv, source, sink, ne, edges, capacity, flow, cut); printf("ret = %d\n", ret); for (i = 0; i < ne; i++) printf("flow %d: %d -> %d\n", flow[i], edges[2*i], edges[2*i+1]); for (i = 0; i < nv; i++) if (cut[i] == 0) printf("difficult set includes %d\n", i); return 0; } #endif puzzles-r9872/midend.c0000644000175300017530000016715512131530672014050 0ustar simonsimon/* * midend.c: general middle fragment sitting between the * platform-specific front end and game-specific back end. * Maintains a move list, takes care of Undo and Redo commands, and * processes standard keystrokes for undo/redo/new/quit. */ #include #include #include #include #include #include "puzzles.h" enum { DEF_PARAMS, DEF_SEED, DEF_DESC }; /* for midend_game_id_int */ enum { NEWGAME, MOVE, SOLVE, RESTART };/* for midend_state_entry.movetype */ #define special(type) ( (type) != MOVE ) struct midend_state_entry { game_state *state; char *movestr; int movetype; }; struct midend { frontend *frontend; random_state *random; const game *ourgame; game_params **presets; char **preset_names, **preset_encodings; int npresets, presetsize; /* * `desc' and `privdesc' deserve a comment. * * `desc' is the game description as presented to the user when * they ask for Game -> Specific. `privdesc', if non-NULL, is a * different game description used to reconstruct the initial * game_state when de-serialising. If privdesc is NULL, `desc' * is used for both. * * For almost all games, `privdesc' is NULL and never used. The * exception (as usual) is Mines: the initial game state has no * squares open at all, but after the first click `desc' is * rewritten to describe a game state with an initial click and * thus a bunch of squares open. If we used that desc to * serialise and deserialise, then the initial game state after * deserialisation would look unlike the initial game state * beforehand, and worse still execute_move() might fail on the * attempted first click. So `privdesc' is also used in this * case, to provide a game description describing the same * fixed mine layout _but_ no initial click. (These game IDs * may also be typed directly into Mines if you like.) */ char *desc, *privdesc, *seedstr; char *aux_info; enum { GOT_SEED, GOT_DESC, GOT_NOTHING } genmode; int nstates, statesize, statepos; struct midend_state_entry *states; game_params *params, *curparams; game_drawstate *drawstate; game_ui *ui; game_state *oldstate; float anim_time, anim_pos; float flash_time, flash_pos; int dir; int timing; float elapsed; char *laststatus; drawing *drawing; int pressed_mouse_button; int preferred_tilesize, tilesize, winwidth, winheight; void (*game_id_change_notify_function)(void *); void *game_id_change_notify_ctx; }; #define ensure(me) do { \ if ((me)->nstates >= (me)->statesize) { \ (me)->statesize = (me)->nstates + 128; \ (me)->states = sresize((me)->states, (me)->statesize, \ struct midend_state_entry); \ } \ } while (0) void midend_reset_tilesize(midend *me) { me->preferred_tilesize = me->ourgame->preferred_tilesize; { /* * Allow an environment-based override for the default tile * size by defining a variable along the lines of * `NET_TILESIZE=15'. */ char buf[80], *e; int j, k, ts; sprintf(buf, "%s_TILESIZE", me->ourgame->name); for (j = k = 0; buf[j]; j++) if (!isspace((unsigned char)buf[j])) buf[k++] = toupper((unsigned char)buf[j]); buf[k] = '\0'; if ((e = getenv(buf)) != NULL && sscanf(e, "%d", &ts) == 1 && ts > 0) me->preferred_tilesize = ts; } } midend *midend_new(frontend *fe, const game *ourgame, const drawing_api *drapi, void *drhandle) { midend *me = snew(midend); void *randseed; int randseedsize; get_random_seed(&randseed, &randseedsize); me->frontend = fe; me->ourgame = ourgame; me->random = random_new(randseed, randseedsize); me->nstates = me->statesize = me->statepos = 0; me->states = NULL; me->params = ourgame->default_params(); me->game_id_change_notify_function = NULL; me->game_id_change_notify_ctx = NULL; /* * Allow environment-based changing of the default settings by * defining a variable along the lines of `NET_DEFAULT=25x25w' * in which the value is an encoded parameter string. */ { char buf[80], *e; int j, k; sprintf(buf, "%s_DEFAULT", me->ourgame->name); for (j = k = 0; buf[j]; j++) if (!isspace((unsigned char)buf[j])) buf[k++] = toupper((unsigned char)buf[j]); buf[k] = '\0'; if ((e = getenv(buf)) != NULL) me->ourgame->decode_params(me->params, e); } me->curparams = NULL; me->desc = me->privdesc = NULL; me->seedstr = NULL; me->aux_info = NULL; me->genmode = GOT_NOTHING; me->drawstate = NULL; me->oldstate = NULL; me->presets = NULL; me->preset_names = NULL; me->preset_encodings = NULL; me->npresets = me->presetsize = 0; me->anim_time = me->anim_pos = 0.0F; me->flash_time = me->flash_pos = 0.0F; me->dir = 0; me->ui = NULL; me->pressed_mouse_button = 0; me->laststatus = NULL; me->timing = FALSE; me->elapsed = 0.0F; me->tilesize = me->winwidth = me->winheight = 0; if (drapi) me->drawing = drawing_new(drapi, me, drhandle); else me->drawing = NULL; midend_reset_tilesize(me); sfree(randseed); return me; } const game *midend_which_game(midend *me) { return me->ourgame; } static void midend_purge_states(midend *me) { while (me->nstates > me->statepos) { me->ourgame->free_game(me->states[--me->nstates].state); if (me->states[me->nstates].movestr) sfree(me->states[me->nstates].movestr); } } static void midend_free_game(midend *me) { while (me->nstates > 0) { me->nstates--; me->ourgame->free_game(me->states[me->nstates].state); sfree(me->states[me->nstates].movestr); } if (me->drawstate) me->ourgame->free_drawstate(me->drawing, me->drawstate); } void midend_free(midend *me) { int i; midend_free_game(me); if (me->drawing) drawing_free(me->drawing); random_free(me->random); sfree(me->states); sfree(me->desc); sfree(me->privdesc); sfree(me->seedstr); sfree(me->aux_info); me->ourgame->free_params(me->params); if (me->npresets) { for (i = 0; i < me->npresets; i++) { sfree(me->presets[i]); sfree(me->preset_names[i]); sfree(me->preset_encodings[i]); } sfree(me->presets); sfree(me->preset_names); sfree(me->preset_encodings); } if (me->ui) me->ourgame->free_ui(me->ui); if (me->curparams) me->ourgame->free_params(me->curparams); sfree(me->laststatus); sfree(me); } static void midend_size_new_drawstate(midend *me) { /* * Don't even bother, if we haven't worked out our tile size * anyway yet. */ if (me->tilesize > 0) { me->ourgame->compute_size(me->params, me->tilesize, &me->winwidth, &me->winheight); me->ourgame->set_size(me->drawing, me->drawstate, me->params, me->tilesize); } } void midend_size(midend *me, int *x, int *y, int user_size) { int min, max; int rx, ry; /* * We can't set the size on the same drawstate twice. So if * we've already sized one drawstate, we must throw it away and * create a new one. */ if (me->drawstate && me->tilesize > 0) { me->ourgame->free_drawstate(me->drawing, me->drawstate); me->drawstate = me->ourgame->new_drawstate(me->drawing, me->states[0].state); } /* * Find the tile size that best fits within the given space. If * `user_size' is TRUE, we must actually find the _largest_ such * tile size, in order to get as close to the user's explicit * request as possible; otherwise, we bound above at the game's * preferred tile size, so that the game gets what it wants * provided that this doesn't break the constraint from the * front-end (which is likely to be a screen size or similar). */ if (user_size) { max = 1; do { max *= 2; me->ourgame->compute_size(me->params, max, &rx, &ry); } while (rx <= *x && ry <= *y); } else max = me->preferred_tilesize + 1; min = 1; /* * Now binary-search between min and max. We're looking for a * boundary rather than a value: the point at which tile sizes * stop fitting within the given dimensions. Thus, we stop when * max and min differ by exactly 1. */ while (max - min > 1) { int mid = (max + min) / 2; me->ourgame->compute_size(me->params, mid, &rx, &ry); if (rx <= *x && ry <= *y) min = mid; else max = mid; } /* * Now `min' is a valid size, and `max' isn't. So use `min'. */ me->tilesize = min; if (user_size) /* If the user requested a change in size, make it permanent. */ me->preferred_tilesize = me->tilesize; midend_size_new_drawstate(me); *x = me->winwidth; *y = me->winheight; } int midend_tilesize(midend *me) { return me->tilesize; } void midend_set_params(midend *me, game_params *params) { me->ourgame->free_params(me->params); me->params = me->ourgame->dup_params(params); } game_params *midend_get_params(midend *me) { return me->ourgame->dup_params(me->params); } static void midend_set_timer(midend *me) { me->timing = (me->ourgame->is_timed && me->ourgame->timing_state(me->states[me->statepos-1].state, me->ui)); if (me->timing || me->flash_time || me->anim_time) activate_timer(me->frontend); else deactivate_timer(me->frontend); } void midend_force_redraw(midend *me) { if (me->drawstate) me->ourgame->free_drawstate(me->drawing, me->drawstate); me->drawstate = me->ourgame->new_drawstate(me->drawing, me->states[0].state); midend_size_new_drawstate(me); midend_redraw(me); } void midend_new_game(midend *me) { midend_free_game(me); assert(me->nstates == 0); if (me->genmode == GOT_DESC) { me->genmode = GOT_NOTHING; } else { random_state *rs; if (me->genmode == GOT_SEED) { me->genmode = GOT_NOTHING; } else { /* * Generate a new random seed. 15 digits comes to about * 48 bits, which should be more than enough. * * I'll avoid putting a leading zero on the number, * just in case it confuses anybody who thinks it's * processed as an integer rather than a string. */ char newseed[16]; int i; newseed[15] = '\0'; newseed[0] = '1' + (char)random_upto(me->random, 9); for (i = 1; i < 15; i++) newseed[i] = '0' + (char)random_upto(me->random, 10); sfree(me->seedstr); me->seedstr = dupstr(newseed); if (me->curparams) me->ourgame->free_params(me->curparams); me->curparams = me->ourgame->dup_params(me->params); } sfree(me->desc); sfree(me->privdesc); sfree(me->aux_info); me->aux_info = NULL; rs = random_new(me->seedstr, strlen(me->seedstr)); /* * If this midend has been instantiated without providing a * drawing API, it is non-interactive. This means that it's * being used for bulk game generation, and hence we should * pass the non-interactive flag to new_desc. */ me->desc = me->ourgame->new_desc(me->curparams, rs, &me->aux_info, (me->drawing != NULL)); me->privdesc = NULL; random_free(rs); } ensure(me); /* * It might seem a bit odd that we're using me->params to * create the initial game state, rather than me->curparams * which is better tailored to this specific game and which we * always know. * * It's supposed to be an invariant in the midend that * me->params and me->curparams differ in no aspect that is * important after generation (i.e. after new_desc()). By * deliberately passing the _less_ specific of these two * parameter sets, we provoke play-time misbehaviour in the * case where a game has failed to encode a play-time parameter * in the non-full version of encode_params(). */ me->states[me->nstates].state = me->ourgame->new_game(me, me->params, me->desc); /* * As part of our commitment to self-testing, test the aux * string to make sure nothing ghastly went wrong. */ if (me->ourgame->can_solve && me->aux_info) { game_state *s; char *msg, *movestr; msg = NULL; movestr = me->ourgame->solve(me->states[0].state, me->states[0].state, me->aux_info, &msg); assert(movestr && !msg); s = me->ourgame->execute_move(me->states[0].state, movestr); assert(s); me->ourgame->free_game(s); sfree(movestr); } me->states[me->nstates].movestr = NULL; me->states[me->nstates].movetype = NEWGAME; me->nstates++; me->statepos = 1; me->drawstate = me->ourgame->new_drawstate(me->drawing, me->states[0].state); midend_size_new_drawstate(me); me->elapsed = 0.0F; if (me->ui) me->ourgame->free_ui(me->ui); me->ui = me->ourgame->new_ui(me->states[0].state); midend_set_timer(me); me->pressed_mouse_button = 0; if (me->game_id_change_notify_function) me->game_id_change_notify_function(me->game_id_change_notify_ctx); } int midend_can_undo(midend *me) { return (me->statepos > 1); } int midend_can_redo(midend *me) { return (me->statepos < me->nstates); } static int midend_undo(midend *me) { if (me->statepos > 1) { if (me->ui) me->ourgame->changed_state(me->ui, me->states[me->statepos-1].state, me->states[me->statepos-2].state); me->statepos--; me->dir = -1; return 1; } else return 0; } static int midend_redo(midend *me) { if (me->statepos < me->nstates) { if (me->ui) me->ourgame->changed_state(me->ui, me->states[me->statepos-1].state, me->states[me->statepos].state); me->statepos++; me->dir = +1; return 1; } else return 0; } static void midend_finish_move(midend *me) { float flashtime; /* * We do not flash if the later of the two states is special. * This covers both forward Solve moves and backward (undone) * Restart moves. */ if ((me->oldstate || me->statepos > 1) && ((me->dir > 0 && !special(me->states[me->statepos-1].movetype)) || (me->dir < 0 && me->statepos < me->nstates && !special(me->states[me->statepos].movetype)))) { flashtime = me->ourgame->flash_length(me->oldstate ? me->oldstate : me->states[me->statepos-2].state, me->states[me->statepos-1].state, me->oldstate ? me->dir : +1, me->ui); if (flashtime > 0) { me->flash_pos = 0.0F; me->flash_time = flashtime; } } if (me->oldstate) me->ourgame->free_game(me->oldstate); me->oldstate = NULL; me->anim_pos = me->anim_time = 0; me->dir = 0; midend_set_timer(me); } void midend_stop_anim(midend *me) { if (me->oldstate || me->anim_time != 0) { midend_finish_move(me); midend_redraw(me); } } void midend_restart_game(midend *me) { game_state *s; midend_stop_anim(me); assert(me->statepos >= 1); if (me->statepos == 1) return; /* no point doing anything at all! */ /* * During restart, we reconstruct the game from the (public) * game description rather than from states[0], because that * way Mines gets slightly more sensible behaviour (restart * goes to _after_ the first click so you don't have to * remember where you clicked). */ s = me->ourgame->new_game(me, me->params, me->desc); /* * Now enter the restarted state as the next move. */ midend_stop_anim(me); midend_purge_states(me); ensure(me); me->states[me->nstates].state = s; me->states[me->nstates].movestr = dupstr(me->desc); me->states[me->nstates].movetype = RESTART; me->statepos = ++me->nstates; if (me->ui) me->ourgame->changed_state(me->ui, me->states[me->statepos-2].state, me->states[me->statepos-1].state); me->anim_time = 0.0; midend_finish_move(me); midend_redraw(me); midend_set_timer(me); } static int midend_really_process_key(midend *me, int x, int y, int button) { game_state *oldstate = me->ourgame->dup_game(me->states[me->statepos - 1].state); int type = MOVE, gottype = FALSE, ret = 1; float anim_time; game_state *s; char *movestr; movestr = me->ourgame->interpret_move(me->states[me->statepos-1].state, me->ui, me->drawstate, x, y, button); if (!movestr) { if (button == 'n' || button == 'N' || button == '\x0E') { midend_stop_anim(me); midend_new_game(me); midend_redraw(me); goto done; /* never animate */ } else if (button == 'u' || button == 'u' || button == '\x1A' || button == '\x1F') { midend_stop_anim(me); type = me->states[me->statepos-1].movetype; gottype = TRUE; if (!midend_undo(me)) goto done; } else if (button == 'r' || button == 'R' || button == '\x12' || button == '\x19') { midend_stop_anim(me); if (!midend_redo(me)) goto done; } else if (button == '\x13' && me->ourgame->can_solve) { if (midend_solve(me)) goto done; } else if (button == 'q' || button == 'Q' || button == '\x11') { ret = 0; goto done; } else goto done; } else { if (!*movestr) s = me->states[me->statepos-1].state; else { s = me->ourgame->execute_move(me->states[me->statepos-1].state, movestr); assert(s != NULL); } if (s == me->states[me->statepos-1].state) { /* * make_move() is allowed to return its input state to * indicate that although no move has been made, the UI * state has been updated and a redraw is called for. */ midend_redraw(me); midend_set_timer(me); goto done; } else if (s) { midend_stop_anim(me); midend_purge_states(me); ensure(me); assert(movestr != NULL); me->states[me->nstates].state = s; me->states[me->nstates].movestr = movestr; me->states[me->nstates].movetype = MOVE; me->statepos = ++me->nstates; me->dir = +1; if (me->ui) me->ourgame->changed_state(me->ui, me->states[me->statepos-2].state, me->states[me->statepos-1].state); } else { goto done; } } if (!gottype) type = me->states[me->statepos-1].movetype; /* * See if this move requires an animation. */ if (special(type) && !(type == SOLVE && (me->ourgame->flags & SOLVE_ANIMATES))) { anim_time = 0; } else { anim_time = me->ourgame->anim_length(oldstate, me->states[me->statepos-1].state, me->dir, me->ui); } me->oldstate = oldstate; oldstate = NULL; if (anim_time > 0) { me->anim_time = anim_time; } else { me->anim_time = 0.0; midend_finish_move(me); } me->anim_pos = 0.0; midend_redraw(me); midend_set_timer(me); done: if (oldstate) me->ourgame->free_game(oldstate); return ret; } int midend_process_key(midend *me, int x, int y, int button) { int ret = 1; /* * Harmonise mouse drag and release messages. * * Some front ends might accidentally switch from sending, say, * RIGHT_DRAG messages to sending LEFT_DRAG, half way through a * drag. (This can happen on the Mac, for example, since * RIGHT_DRAG is usually done using Command+drag, and if the * user accidentally releases Command half way through the drag * then there will be trouble.) * * It would be an O(number of front ends) annoyance to fix this * in the front ends, but an O(number of back ends) annoyance * to have each game capable of dealing with it. Therefore, we * fix it _here_ in the common midend code so that it only has * to be done once. * * The possible ways in which things can go screwy in the front * end are: * * - in a system containing multiple physical buttons button * presses can inadvertently overlap. We can see ABab (caps * meaning button-down and lowercase meaning button-up) when * the user had semantically intended AaBb. * * - in a system where one button is simulated by means of a * modifier key and another button, buttons can mutate * between press and release (possibly during drag). So we * can see Ab instead of Aa. * * Definite requirements are: * * - button _presses_ must never be invented or destroyed. If * the user presses two buttons in succession, the button * presses must be transferred to the backend unchanged. So * if we see AaBb , that's fine; if we see ABab (the button * presses inadvertently overlapped) we must somehow * `correct' it to AaBb. * * - every mouse action must end up looking like a press, zero * or more drags, then a release. This allows back ends to * make the _assumption_ that incoming mouse data will be * sane in this regard, and not worry about the details. * * So my policy will be: * * - treat any button-up as a button-up for the currently * pressed button, or ignore it if there is no currently * pressed button. * * - treat any drag as a drag for the currently pressed * button, or ignore it if there is no currently pressed * button. * * - if we see a button-down while another button is currently * pressed, invent a button-up for the first one and then * pass the button-down through as before. * * 2005-05-31: An addendum to the above. Some games might want * a `priority order' among buttons, such that if one button is * pressed while another is down then a fixed one of the * buttons takes priority no matter what order they're pressed * in. Mines, in particular, wants to treat a left+right click * like a left click for the benefit of users of other * implementations. So the last of the above points is modified * in the presence of an (optional) button priority order. * * A further addition: we translate certain keyboard presses to * cursor key 'select' buttons, so that a) frontends don't have * to translate these themselves (like they do for CURSOR_UP etc), * and b) individual games don't have to hard-code button presses * of '\n' etc for keyboard-based cursors. The choice of buttons * here could eventually be controlled by a runtime configuration * option. */ if (IS_MOUSE_DRAG(button) || IS_MOUSE_RELEASE(button)) { if (me->pressed_mouse_button) { if (IS_MOUSE_DRAG(button)) { button = me->pressed_mouse_button + (LEFT_DRAG - LEFT_BUTTON); } else { button = me->pressed_mouse_button + (LEFT_RELEASE - LEFT_BUTTON); } } else return ret; /* ignore it */ } else if (IS_MOUSE_DOWN(button) && me->pressed_mouse_button) { /* * If the new button has lower priority than the old one, * don't bother doing this. */ if (me->ourgame->flags & BUTTON_BEATS(me->pressed_mouse_button, button)) return ret; /* just ignore it */ /* * Fabricate a button-up for the previously pressed button. */ ret = ret && midend_really_process_key (me, x, y, (me->pressed_mouse_button + (LEFT_RELEASE - LEFT_BUTTON))); } /* * Translate keyboard presses to cursor selection. */ if (button == '\n' || button == '\r') button = CURSOR_SELECT; if (button == ' ') button = CURSOR_SELECT2; /* * Normalise both backspace characters (8 and 127) to \b. Easier * to do this once, here, than to require all front ends to * carefully generate the same one - now each front end can * generate whichever is easiest. */ if (button == '\177') button = '\b'; /* * Now send on the event we originally received. */ ret = ret && midend_really_process_key(me, x, y, button); /* * And update the currently pressed button. */ if (IS_MOUSE_RELEASE(button)) me->pressed_mouse_button = 0; else if (IS_MOUSE_DOWN(button)) me->pressed_mouse_button = button; return ret; } void midend_redraw(midend *me) { assert(me->drawing); if (me->statepos > 0 && me->drawstate) { start_draw(me->drawing); if (me->oldstate && me->anim_time > 0 && me->anim_pos < me->anim_time) { assert(me->dir != 0); me->ourgame->redraw(me->drawing, me->drawstate, me->oldstate, me->states[me->statepos-1].state, me->dir, me->ui, me->anim_pos, me->flash_pos); } else { me->ourgame->redraw(me->drawing, me->drawstate, NULL, me->states[me->statepos-1].state, +1 /*shrug*/, me->ui, 0.0, me->flash_pos); } end_draw(me->drawing); } } /* * Nasty hacky function used to implement the --redo option in * gtk.c. Only used for generating the puzzles' icons. */ void midend_freeze_timer(midend *me, float tprop) { me->anim_pos = me->anim_time * tprop; midend_redraw(me); deactivate_timer(me->frontend); } void midend_timer(midend *me, float tplus) { int need_redraw = (me->anim_time > 0 || me->flash_time > 0); me->anim_pos += tplus; if (me->anim_pos >= me->anim_time || me->anim_time == 0 || !me->oldstate) { if (me->anim_time > 0) midend_finish_move(me); } me->flash_pos += tplus; if (me->flash_pos >= me->flash_time || me->flash_time == 0) { me->flash_pos = me->flash_time = 0; } if (need_redraw) midend_redraw(me); if (me->timing) { float oldelapsed = me->elapsed; me->elapsed += tplus; if ((int)oldelapsed != (int)me->elapsed) status_bar(me->drawing, me->laststatus ? me->laststatus : ""); } midend_set_timer(me); } float *midend_colours(midend *me, int *ncolours) { float *ret; ret = me->ourgame->colours(me->frontend, ncolours); { int i; /* * Allow environment-based overrides for the standard * colours by defining variables along the lines of * `NET_COLOUR_4=6000c0'. */ for (i = 0; i < *ncolours; i++) { char buf[80], *e; unsigned int r, g, b; int j, k; sprintf(buf, "%s_COLOUR_%d", me->ourgame->name, i); for (j = k = 0; buf[j]; j++) if (!isspace((unsigned char)buf[j])) buf[k++] = toupper((unsigned char)buf[j]); buf[k] = '\0'; if ((e = getenv(buf)) != NULL && sscanf(e, "%2x%2x%2x", &r, &g, &b) == 3) { ret[i*3 + 0] = r / 255.0F; ret[i*3 + 1] = g / 255.0F; ret[i*3 + 2] = b / 255.0F; } } } return ret; } int midend_num_presets(midend *me) { if (!me->npresets) { char *name; game_params *preset; while (me->ourgame->fetch_preset(me->npresets, &name, &preset)) { if (me->presetsize <= me->npresets) { me->presetsize = me->npresets + 10; me->presets = sresize(me->presets, me->presetsize, game_params *); me->preset_names = sresize(me->preset_names, me->presetsize, char *); me->preset_encodings = sresize(me->preset_encodings, me->presetsize, char *); } me->presets[me->npresets] = preset; me->preset_names[me->npresets] = name; me->preset_encodings[me->npresets] = me->ourgame->encode_params(preset, TRUE);; me->npresets++; } } { /* * Allow environment-based extensions to the preset list by * defining a variable along the lines of `SOLO_PRESETS=2x3 * Advanced:2x3da'. Colon-separated list of items, * alternating between textual titles in the menu and * encoded parameter strings. */ char buf[80], *e, *p; int j, k; sprintf(buf, "%s_PRESETS", me->ourgame->name); for (j = k = 0; buf[j]; j++) if (!isspace((unsigned char)buf[j])) buf[k++] = toupper((unsigned char)buf[j]); buf[k] = '\0'; if ((e = getenv(buf)) != NULL) { p = e = dupstr(e); while (*p) { char *name, *val; game_params *preset; name = p; while (*p && *p != ':') p++; if (*p) *p++ = '\0'; val = p; while (*p && *p != ':') p++; if (*p) *p++ = '\0'; preset = me->ourgame->default_params(); me->ourgame->decode_params(preset, val); if (me->ourgame->validate_params(preset, TRUE)) { /* Drop this one from the list. */ me->ourgame->free_params(preset); continue; } if (me->presetsize <= me->npresets) { me->presetsize = me->npresets + 10; me->presets = sresize(me->presets, me->presetsize, game_params *); me->preset_names = sresize(me->preset_names, me->presetsize, char *); me->preset_encodings = sresize(me->preset_encodings, me->presetsize, char *); } me->presets[me->npresets] = preset; me->preset_names[me->npresets] = dupstr(name); me->preset_encodings[me->npresets] = me->ourgame->encode_params(preset, TRUE); me->npresets++; } sfree(e); } } return me->npresets; } void midend_fetch_preset(midend *me, int n, char **name, game_params **params) { assert(n >= 0 && n < me->npresets); *name = me->preset_names[n]; *params = me->presets[n]; } int midend_which_preset(midend *me) { char *encoding = me->ourgame->encode_params(me->params, TRUE); int i, ret; ret = -1; for (i = 0; i < me->npresets; i++) if (!strcmp(encoding, me->preset_encodings[i])) { ret = i; break; } sfree(encoding); return ret; } int midend_wants_statusbar(midend *me) { return me->ourgame->wants_statusbar; } void midend_request_id_changes(midend *me, void (*notify)(void *), void *ctx) { me->game_id_change_notify_function = notify; me->game_id_change_notify_ctx = ctx; } void midend_supersede_game_desc(midend *me, char *desc, char *privdesc) { sfree(me->desc); sfree(me->privdesc); me->desc = dupstr(desc); me->privdesc = privdesc ? dupstr(privdesc) : NULL; if (me->game_id_change_notify_function) me->game_id_change_notify_function(me->game_id_change_notify_ctx); } config_item *midend_get_config(midend *me, int which, char **wintitle) { char *titlebuf, *parstr, *rest; config_item *ret; char sep; assert(wintitle); titlebuf = snewn(40 + strlen(me->ourgame->name), char); switch (which) { case CFG_SETTINGS: sprintf(titlebuf, "%s configuration", me->ourgame->name); *wintitle = titlebuf; return me->ourgame->configure(me->params); case CFG_SEED: case CFG_DESC: if (!me->curparams) { sfree(titlebuf); return NULL; } sprintf(titlebuf, "%s %s selection", me->ourgame->name, which == CFG_SEED ? "random" : "game"); *wintitle = titlebuf; ret = snewn(2, config_item); ret[0].type = C_STRING; if (which == CFG_SEED) ret[0].name = "Game random seed"; else ret[0].name = "Game ID"; ret[0].ival = 0; /* * For CFG_DESC the text going in here will be a string * encoding of the restricted parameters, plus a colon, * plus the game description. For CFG_SEED it will be the * full parameters, plus a hash, plus the random seed data. * Either of these is a valid full game ID (although only * the former is likely to persist across many code * changes). */ parstr = me->ourgame->encode_params(me->curparams, which == CFG_SEED); assert(parstr); if (which == CFG_DESC) { rest = me->desc ? me->desc : ""; sep = ':'; } else { rest = me->seedstr ? me->seedstr : ""; sep = '#'; } ret[0].sval = snewn(strlen(parstr) + strlen(rest) + 2, char); sprintf(ret[0].sval, "%s%c%s", parstr, sep, rest); sfree(parstr); ret[1].type = C_END; ret[1].name = ret[1].sval = NULL; ret[1].ival = 0; return ret; } assert(!"We shouldn't be here"); return NULL; } static char *midend_game_id_int(midend *me, char *id, int defmode) { char *error, *par, *desc, *seed; game_params *newcurparams, *newparams, *oldparams1, *oldparams2; int free_params; seed = strchr(id, '#'); desc = strchr(id, ':'); if (desc && (!seed || desc < seed)) { /* * We have a colon separating parameters from game * description. So `par' now points to the parameters * string, and `desc' to the description string. */ *desc++ = '\0'; par = id; seed = NULL; } else if (seed && (!desc || seed < desc)) { /* * We have a hash separating parameters from random seed. * So `par' now points to the parameters string, and `seed' * to the seed string. */ *seed++ = '\0'; par = id; desc = NULL; } else { /* * We only have one string. Depending on `defmode', we take * it to be either parameters, seed or description. */ if (defmode == DEF_SEED) { seed = id; par = desc = NULL; } else if (defmode == DEF_DESC) { desc = id; par = seed = NULL; } else { par = id; seed = desc = NULL; } } /* * We must be reasonably careful here not to modify anything in * `me' until we have finished validating things. This function * must either return an error and do nothing to the midend, or * return success and do everything; nothing in between is * acceptable. */ newcurparams = newparams = oldparams1 = oldparams2 = NULL; if (par) { newcurparams = me->ourgame->dup_params(me->params); me->ourgame->decode_params(newcurparams, par); error = me->ourgame->validate_params(newcurparams, desc == NULL); if (error) { me->ourgame->free_params(newcurparams); return error; } oldparams1 = me->curparams; /* * Now filter only the persistent parts of this state into * the long-term params structure, unless we've _only_ * received a params string in which case the whole lot is * persistent. */ oldparams2 = me->params; if (seed || desc) { char *tmpstr; newparams = me->ourgame->dup_params(me->params); tmpstr = me->ourgame->encode_params(newcurparams, FALSE); me->ourgame->decode_params(newparams, tmpstr); sfree(tmpstr); } else { newparams = me->ourgame->dup_params(newcurparams); } free_params = TRUE; } else { newcurparams = me->curparams; newparams = me->params; free_params = FALSE; } if (desc) { error = me->ourgame->validate_desc(newparams, desc); if (error) { if (free_params) { if (newcurparams) me->ourgame->free_params(newcurparams); if (newparams) me->ourgame->free_params(newparams); } return error; } } /* * Now we've got past all possible error points. Update the * midend itself. */ me->params = newparams; me->curparams = newcurparams; if (oldparams1) me->ourgame->free_params(oldparams1); if (oldparams2) me->ourgame->free_params(oldparams2); sfree(me->desc); sfree(me->privdesc); me->desc = me->privdesc = NULL; sfree(me->seedstr); me->seedstr = NULL; if (desc) { me->desc = dupstr(desc); me->genmode = GOT_DESC; sfree(me->aux_info); me->aux_info = NULL; } if (seed) { me->seedstr = dupstr(seed); me->genmode = GOT_SEED; } return NULL; } char *midend_game_id(midend *me, char *id) { return midend_game_id_int(me, id, DEF_PARAMS); } char *midend_get_game_id(midend *me) { char *parstr, *ret; parstr = me->ourgame->encode_params(me->curparams, FALSE); assert(parstr); assert(me->desc); ret = snewn(strlen(parstr) + strlen(me->desc) + 2, char); sprintf(ret, "%s:%s", parstr, me->desc); sfree(parstr); return ret; } char *midend_get_random_seed(midend *me) { char *parstr, *ret; if (!me->seedstr) return NULL; parstr = me->ourgame->encode_params(me->curparams, TRUE); assert(parstr); ret = snewn(strlen(parstr) + strlen(me->seedstr) + 2, char); sprintf(ret, "%s#%s", parstr, me->seedstr); sfree(parstr); return ret; } char *midend_set_config(midend *me, int which, config_item *cfg) { char *error; game_params *params; switch (which) { case CFG_SETTINGS: params = me->ourgame->custom_params(cfg); error = me->ourgame->validate_params(params, TRUE); if (error) { me->ourgame->free_params(params); return error; } me->ourgame->free_params(me->params); me->params = params; break; case CFG_SEED: case CFG_DESC: error = midend_game_id_int(me, cfg[0].sval, (which == CFG_SEED ? DEF_SEED : DEF_DESC)); if (error) return error; break; } return NULL; } int midend_can_format_as_text_now(midend *me) { if (me->ourgame->can_format_as_text_ever) return me->ourgame->can_format_as_text_now(me->params); else return FALSE; } char *midend_text_format(midend *me) { if (me->ourgame->can_format_as_text_ever && me->statepos > 0 && me->ourgame->can_format_as_text_now(me->params)) return me->ourgame->text_format(me->states[me->statepos-1].state); else return NULL; } char *midend_solve(midend *me) { game_state *s; char *msg, *movestr; if (!me->ourgame->can_solve) return "This game does not support the Solve operation"; if (me->statepos < 1) return "No game set up to solve"; /* _shouldn't_ happen! */ msg = NULL; movestr = me->ourgame->solve(me->states[0].state, me->states[me->statepos-1].state, me->aux_info, &msg); if (!movestr) { if (!msg) msg = "Solve operation failed"; /* _shouldn't_ happen, but can */ return msg; } s = me->ourgame->execute_move(me->states[me->statepos-1].state, movestr); assert(s); /* * Now enter the solved state as the next move. */ midend_stop_anim(me); midend_purge_states(me); ensure(me); me->states[me->nstates].state = s; me->states[me->nstates].movestr = movestr; me->states[me->nstates].movetype = SOLVE; me->statepos = ++me->nstates; if (me->ui) me->ourgame->changed_state(me->ui, me->states[me->statepos-2].state, me->states[me->statepos-1].state); me->dir = +1; if (me->ourgame->flags & SOLVE_ANIMATES) { me->oldstate = me->ourgame->dup_game(me->states[me->statepos-2].state); me->anim_time = me->ourgame->anim_length(me->states[me->statepos-2].state, me->states[me->statepos-1].state, +1, me->ui); me->anim_pos = 0.0; } else { me->anim_time = 0.0; midend_finish_move(me); } if (me->drawing) midend_redraw(me); midend_set_timer(me); return NULL; } int midend_status(midend *me) { /* * We should probably never be called when the state stack has no * states on it at all - ideally, midends should never be left in * that state for long enough to get put down and forgotten about. * But if we are, I think we return _true_ - pedantically speaking * a midend in that state is 'vacuously solved', and more * practically, a user whose midend has been left in that state * probably _does_ want the 'new game' option to be prominent. */ if (me->statepos == 0) return +1; return me->ourgame->status(me->states[me->statepos-1].state); } char *midend_rewrite_statusbar(midend *me, char *text) { /* * An important special case is that we are occasionally called * with our own laststatus, to update the timer. */ if (me->laststatus != text) { sfree(me->laststatus); me->laststatus = dupstr(text); } if (me->ourgame->is_timed) { char timebuf[100], *ret; int min, sec; sec = (int)me->elapsed; min = sec / 60; sec %= 60; sprintf(timebuf, "[%d:%02d] ", min, sec); ret = snewn(strlen(timebuf) + strlen(text) + 1, char); strcpy(ret, timebuf); strcat(ret, text); return ret; } else { return dupstr(text); } } #define SERIALISE_MAGIC "Simon Tatham's Portable Puzzle Collection" #define SERIALISE_VERSION "1" void midend_serialise(midend *me, void (*write)(void *ctx, void *buf, int len), void *wctx) { int i; /* * Each line of the save file contains three components. First * exactly 8 characters of header word indicating what type of * data is contained on the line; then a colon followed by a * decimal integer giving the length of the main string on the * line; then a colon followed by the string itself (exactly as * many bytes as previously specified, no matter what they * contain). Then a newline (of reasonably flexible form). */ #define wr(h,s) do { \ char hbuf[80]; \ char *str = (s); \ sprintf(hbuf, "%-8.8s:%d:", (h), (int)strlen(str)); \ write(wctx, hbuf, strlen(hbuf)); \ write(wctx, str, strlen(str)); \ write(wctx, "\n", 1); \ } while (0) /* * Magic string identifying the file, and version number of the * file format. */ wr("SAVEFILE", SERIALISE_MAGIC); wr("VERSION", SERIALISE_VERSION); /* * The game name. (Copied locally to avoid const annoyance.) */ { char *s = dupstr(me->ourgame->name); wr("GAME", s); sfree(s); } /* * The current long-term parameters structure, in full. */ if (me->params) { char *s = me->ourgame->encode_params(me->params, TRUE); wr("PARAMS", s); sfree(s); } /* * The current short-term parameters structure, in full. */ if (me->curparams) { char *s = me->ourgame->encode_params(me->curparams, TRUE); wr("CPARAMS", s); sfree(s); } /* * The current game description, the privdesc, and the random seed. */ if (me->seedstr) wr("SEED", me->seedstr); if (me->desc) wr("DESC", me->desc); if (me->privdesc) wr("PRIVDESC", me->privdesc); /* * The game's aux_info. We obfuscate this to prevent spoilers * (people are likely to run `head' or similar on a saved game * file simply to find out what it is, and don't necessarily * want to be told the answer to the puzzle!) */ if (me->aux_info) { unsigned char *s1; char *s2; int len; len = strlen(me->aux_info); s1 = snewn(len, unsigned char); memcpy(s1, me->aux_info, len); obfuscate_bitmap(s1, len*8, FALSE); s2 = bin2hex(s1, len); wr("AUXINFO", s2); sfree(s2); sfree(s1); } /* * Any required serialisation of the game_ui. */ if (me->ui) { char *s = me->ourgame->encode_ui(me->ui); if (s) { wr("UI", s); sfree(s); } } /* * The game time, if it's a timed game. */ if (me->ourgame->is_timed) { char buf[80]; sprintf(buf, "%g", me->elapsed); wr("TIME", buf); } /* * The length of, and position in, the states list. */ { char buf[80]; sprintf(buf, "%d", me->nstates); wr("NSTATES", buf); sprintf(buf, "%d", me->statepos); wr("STATEPOS", buf); } /* * For each state after the initial one (which we know is * constructed from either privdesc or desc), enough * information for execute_move() to reconstruct it from the * previous one. */ for (i = 1; i < me->nstates; i++) { assert(me->states[i].movetype != NEWGAME); /* only state 0 */ switch (me->states[i].movetype) { case MOVE: wr("MOVE", me->states[i].movestr); break; case SOLVE: wr("SOLVE", me->states[i].movestr); break; case RESTART: wr("RESTART", me->states[i].movestr); break; } } #undef wr } /* * This function returns NULL on success, or an error message. */ char *midend_deserialise(midend *me, int (*read)(void *ctx, void *buf, int len), void *rctx) { int nstates = 0, statepos = -1, gotstates = 0; int started = FALSE; int i; char *val = NULL; /* Initially all errors give the same report */ char *ret = "Data does not appear to be a saved game file"; /* * We construct all the new state in local variables while we * check its sanity. Only once we have finished reading the * serialised data and detected no errors at all do we start * modifying stuff in the midend passed in. */ char *seed = NULL, *parstr = NULL, *desc = NULL, *privdesc = NULL; char *auxinfo = NULL, *uistr = NULL, *cparstr = NULL; float elapsed = 0.0F; game_params *params = NULL, *cparams = NULL; game_ui *ui = NULL; struct midend_state_entry *states = NULL; /* * Loop round and round reading one key/value pair at a time * from the serialised stream, until we have enough game states * to finish. */ while (nstates <= 0 || statepos < 0 || gotstates < nstates-1) { char key[9], c; int len; do { if (!read(rctx, key, 1)) { /* unexpected EOF */ goto cleanup; } } while (key[0] == '\r' || key[0] == '\n'); if (!read(rctx, key+1, 8)) { /* unexpected EOF */ goto cleanup; } if (key[8] != ':') { if (started) ret = "Data was incorrectly formatted for a saved game file"; goto cleanup; } len = strcspn(key, ": "); assert(len <= 8); key[len] = '\0'; len = 0; while (1) { if (!read(rctx, &c, 1)) { /* unexpected EOF */ goto cleanup; } if (c == ':') { break; } else if (c >= '0' && c <= '9') { len = (len * 10) + (c - '0'); } else { if (started) ret = "Data was incorrectly formatted for a" " saved game file"; goto cleanup; } } val = snewn(len+1, char); if (!read(rctx, val, len)) { if (started) goto cleanup; } val[len] = '\0'; if (!started) { if (strcmp(key, "SAVEFILE") || strcmp(val, SERIALISE_MAGIC)) { /* ret already has the right message in it */ goto cleanup; } /* Now most errors are this one, unless otherwise specified */ ret = "Saved data ended unexpectedly"; started = TRUE; } else { if (!strcmp(key, "VERSION")) { if (strcmp(val, SERIALISE_VERSION)) { ret = "Cannot handle this version of the saved game" " file format"; goto cleanup; } } else if (!strcmp(key, "GAME")) { if (strcmp(val, me->ourgame->name)) { ret = "Save file is from a different game"; goto cleanup; } } else if (!strcmp(key, "PARAMS")) { sfree(parstr); parstr = val; val = NULL; } else if (!strcmp(key, "CPARAMS")) { sfree(cparstr); cparstr = val; val = NULL; } else if (!strcmp(key, "SEED")) { sfree(seed); seed = val; val = NULL; } else if (!strcmp(key, "DESC")) { sfree(desc); desc = val; val = NULL; } else if (!strcmp(key, "PRIVDESC")) { sfree(privdesc); privdesc = val; val = NULL; } else if (!strcmp(key, "AUXINFO")) { unsigned char *tmp; int len = strlen(val) / 2; /* length in bytes */ tmp = hex2bin(val, len); obfuscate_bitmap(tmp, len*8, TRUE); sfree(auxinfo); auxinfo = snewn(len + 1, char); memcpy(auxinfo, tmp, len); auxinfo[len] = '\0'; sfree(tmp); } else if (!strcmp(key, "UI")) { sfree(uistr); uistr = val; val = NULL; } else if (!strcmp(key, "TIME")) { elapsed = (float)atof(val); } else if (!strcmp(key, "NSTATES")) { nstates = atoi(val); if (nstates <= 0) { ret = "Number of states in save file was negative"; goto cleanup; } if (states) { ret = "Two state counts provided in save file"; goto cleanup; } states = snewn(nstates, struct midend_state_entry); for (i = 0; i < nstates; i++) { states[i].state = NULL; states[i].movestr = NULL; states[i].movetype = NEWGAME; } } else if (!strcmp(key, "STATEPOS")) { statepos = atoi(val); } else if (!strcmp(key, "MOVE")) { gotstates++; states[gotstates].movetype = MOVE; states[gotstates].movestr = val; val = NULL; } else if (!strcmp(key, "SOLVE")) { gotstates++; states[gotstates].movetype = SOLVE; states[gotstates].movestr = val; val = NULL; } else if (!strcmp(key, "RESTART")) { gotstates++; states[gotstates].movetype = RESTART; states[gotstates].movestr = val; val = NULL; } } sfree(val); val = NULL; } params = me->ourgame->default_params(); me->ourgame->decode_params(params, parstr); if (me->ourgame->validate_params(params, TRUE)) { ret = "Long-term parameters in save file are invalid"; goto cleanup; } cparams = me->ourgame->default_params(); me->ourgame->decode_params(cparams, cparstr); if (me->ourgame->validate_params(cparams, FALSE)) { ret = "Short-term parameters in save file are invalid"; goto cleanup; } if (seed && me->ourgame->validate_params(cparams, TRUE)) { /* * The seed's no use with this version, but we can perfectly * well use the rest of the data. */ sfree(seed); seed = NULL; } if (!desc) { ret = "Game description in save file is missing"; goto cleanup; } else if (me->ourgame->validate_desc(params, desc)) { ret = "Game description in save file is invalid"; goto cleanup; } if (privdesc && me->ourgame->validate_desc(params, privdesc)) { ret = "Game private description in save file is invalid"; goto cleanup; } if (statepos < 0 || statepos >= nstates) { ret = "Game position in save file is out of range"; } states[0].state = me->ourgame->new_game(me, params, privdesc ? privdesc : desc); for (i = 1; i < nstates; i++) { assert(states[i].movetype != NEWGAME); switch (states[i].movetype) { case MOVE: case SOLVE: states[i].state = me->ourgame->execute_move(states[i-1].state, states[i].movestr); if (states[i].state == NULL) { ret = "Save file contained an invalid move"; goto cleanup; } break; case RESTART: if (me->ourgame->validate_desc(params, states[i].movestr)) { ret = "Save file contained an invalid restart move"; goto cleanup; } states[i].state = me->ourgame->new_game(me, params, states[i].movestr); break; } } ui = me->ourgame->new_ui(states[0].state); me->ourgame->decode_ui(ui, uistr); /* * Now we've run out of possible error conditions, so we're * ready to start overwriting the real data in the current * midend. We'll do this by swapping things with the local * variables, so that the same cleanup code will free the old * stuff. */ { char *tmp; tmp = me->desc; me->desc = desc; desc = tmp; tmp = me->privdesc; me->privdesc = privdesc; privdesc = tmp; tmp = me->seedstr; me->seedstr = seed; seed = tmp; tmp = me->aux_info; me->aux_info = auxinfo; auxinfo = tmp; } me->genmode = GOT_NOTHING; me->statesize = nstates; nstates = me->nstates; me->nstates = me->statesize; { struct midend_state_entry *tmp; tmp = me->states; me->states = states; states = tmp; } me->statepos = statepos; { game_params *tmp; tmp = me->params; me->params = params; params = tmp; tmp = me->curparams; me->curparams = cparams; cparams = tmp; } me->oldstate = NULL; me->anim_time = me->anim_pos = me->flash_time = me->flash_pos = 0.0F; me->dir = 0; { game_ui *tmp; tmp = me->ui; me->ui = ui; ui = tmp; } me->elapsed = elapsed; me->pressed_mouse_button = 0; midend_set_timer(me); if (me->drawstate) me->ourgame->free_drawstate(me->drawing, me->drawstate); me->drawstate = me->ourgame->new_drawstate(me->drawing, me->states[me->statepos-1].state); midend_size_new_drawstate(me); ret = NULL; /* success! */ cleanup: sfree(val); sfree(seed); sfree(parstr); sfree(cparstr); sfree(desc); sfree(privdesc); sfree(auxinfo); sfree(uistr); if (params) me->ourgame->free_params(params); if (cparams) me->ourgame->free_params(cparams); if (ui) me->ourgame->free_ui(ui); if (states) { int i; for (i = 0; i < nstates; i++) { if (states[i].state) me->ourgame->free_game(states[i].state); sfree(states[i].movestr); } sfree(states); } return ret; } /* * This function examines a saved game file just far enough to * determine which game type it contains. It returns NULL on success * and the game name string in 'name' (which will be dynamically * allocated and should be caller-freed), or an error message on * failure. */ char *identify_game(char **name, int (*read)(void *ctx, void *buf, int len), void *rctx) { int nstates = 0, statepos = -1, gotstates = 0; int started = FALSE; char *val = NULL; /* Initially all errors give the same report */ char *ret = "Data does not appear to be a saved game file"; *name = NULL; /* * Loop round and round reading one key/value pair at a time from * the serialised stream, until we've found the game name. */ while (nstates <= 0 || statepos < 0 || gotstates < nstates-1) { char key[9], c; int len; do { if (!read(rctx, key, 1)) { /* unexpected EOF */ goto cleanup; } } while (key[0] == '\r' || key[0] == '\n'); if (!read(rctx, key+1, 8)) { /* unexpected EOF */ goto cleanup; } if (key[8] != ':') { if (started) ret = "Data was incorrectly formatted for a saved game file"; goto cleanup; } len = strcspn(key, ": "); assert(len <= 8); key[len] = '\0'; len = 0; while (1) { if (!read(rctx, &c, 1)) { /* unexpected EOF */ goto cleanup; } if (c == ':') { break; } else if (c >= '0' && c <= '9') { len = (len * 10) + (c - '0'); } else { if (started) ret = "Data was incorrectly formatted for a" " saved game file"; goto cleanup; } } val = snewn(len+1, char); if (!read(rctx, val, len)) { if (started) goto cleanup; } val[len] = '\0'; if (!started) { if (strcmp(key, "SAVEFILE") || strcmp(val, SERIALISE_MAGIC)) { /* ret already has the right message in it */ goto cleanup; } /* Now most errors are this one, unless otherwise specified */ ret = "Saved data ended unexpectedly"; started = TRUE; } else { if (!strcmp(key, "VERSION")) { if (strcmp(val, SERIALISE_VERSION)) { ret = "Cannot handle this version of the saved game" " file format"; goto cleanup; } } else if (!strcmp(key, "GAME")) { *name = dupstr(val); ret = NULL; goto cleanup; } } sfree(val); val = NULL; } cleanup: sfree(val); return ret; } char *midend_print_puzzle(midend *me, document *doc, int with_soln) { game_state *soln = NULL; if (me->statepos < 1) return "No game set up to print";/* _shouldn't_ happen! */ if (with_soln) { char *msg, *movestr; if (!me->ourgame->can_solve) return "This game does not support the Solve operation"; msg = "Solve operation failed";/* game _should_ overwrite on error */ movestr = me->ourgame->solve(me->states[0].state, me->states[me->statepos-1].state, me->aux_info, &msg); if (!movestr) return msg; soln = me->ourgame->execute_move(me->states[me->statepos-1].state, movestr); assert(soln); sfree(movestr); } else soln = NULL; /* * This call passes over ownership of the two game_states and * the game_params. Hence we duplicate the ones we want to * keep, and we don't have to bother freeing soln if it was * non-NULL. */ document_add_puzzle(doc, me->ourgame, me->ourgame->dup_params(me->curparams), me->ourgame->dup_game(me->states[0].state), soln); return NULL; } puzzles-r9872/mines.c0000644000175300017530000024604512132232554013716 0ustar simonsimon/* * mines.c: Minesweeper clone with sophisticated grid generation. * * Still TODO: * * - think about configurably supporting question marks. Once, * that is, we've thought about configurability in general! */ #include #include #include #include #include #include #include "tree234.h" #include "puzzles.h" enum { COL_BACKGROUND, COL_BACKGROUND2, COL_1, COL_2, COL_3, COL_4, COL_5, COL_6, COL_7, COL_8, COL_MINE, COL_BANG, COL_CROSS, COL_FLAG, COL_FLAGBASE, COL_QUERY, COL_HIGHLIGHT, COL_LOWLIGHT, COL_WRONGNUMBER, COL_CURSOR, NCOLOURS }; #define PREFERRED_TILE_SIZE 20 #define TILE_SIZE (ds->tilesize) #ifdef SMALL_SCREEN #define BORDER 8 #else #define BORDER (TILE_SIZE * 3 / 2) #endif #define HIGHLIGHT_WIDTH (TILE_SIZE / 10) #define OUTER_HIGHLIGHT_WIDTH (BORDER / 10) #define COORD(x) ( (x) * TILE_SIZE + BORDER ) #define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 ) #define FLASH_FRAME 0.13F struct game_params { int w, h, n; int unique; }; struct mine_layout { /* * This structure is shared between all the game_states for a * given instance of the puzzle, so we reference-count it. */ int refcount; char *mines; /* * If we haven't yet actually generated the mine layout, here's * all the data we will need to do so. */ int n, unique; random_state *rs; midend *me; /* to give back the new game desc */ }; struct game_state { int w, h, n, dead, won; int used_solve; struct mine_layout *layout; /* real mine positions */ signed char *grid; /* player knowledge */ /* * Each item in the `grid' array is one of the following values: * * - 0 to 8 mean the square is open and has a surrounding mine * count. * * - -1 means the square is marked as a mine. * * - -2 means the square is unknown. * * - -3 means the square is marked with a question mark * (FIXME: do we even want to bother with this?). * * - 64 means the square has had a mine revealed when the game * was lost. * * - 65 means the square had a mine revealed and this was the * one the player hits. * * - 66 means the square has a crossed-out mine because the * player had incorrectly marked it. */ }; static game_params *default_params(void) { game_params *ret = snew(game_params); ret->w = ret->h = 9; ret->n = 10; ret->unique = TRUE; return ret; } static const struct game_params mines_presets[] = { {9, 9, 10, TRUE}, {9, 9, 35, TRUE}, {16, 16, 40, TRUE}, {16, 16, 99, TRUE}, #ifndef SMALL_SCREEN {30, 16, 99, TRUE}, {30, 16, 170, TRUE}, #endif }; static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char str[80]; if (i < 0 || i >= lenof(mines_presets)) return FALSE; ret = snew(game_params); *ret = mines_presets[i]; sprintf(str, "%dx%d, %d mines", ret->w, ret->h, ret->n); *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) { char const *p = string; params->w = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; if (*p == 'x') { p++; params->h = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; } else { params->h = params->w; } if (*p == 'n') { p++; params->n = atoi(p); while (*p && (*p == '.' || isdigit((unsigned char)*p))) p++; } else { params->n = params->w * params->h / 10; } while (*p) { if (*p == 'a') { p++; params->unique = FALSE; } else p++; /* skip any other gunk */ } } static char *encode_params(const game_params *params, int full) { char ret[400]; int len; len = sprintf(ret, "%dx%d", params->w, params->h); /* * Mine count is a generation-time parameter, since it can be * deduced from the mine bitmap! */ if (full) len += sprintf(ret+len, "n%d", params->n); if (full && !params->unique) ret[len++] = 'a'; assert(len < lenof(ret)); ret[len] = '\0'; return dupstr(ret); } static config_item *game_configure(const game_params *params) { config_item *ret; char buf[80]; ret = snewn(5, config_item); ret[0].name = "Width"; ret[0].type = C_STRING; sprintf(buf, "%d", params->w); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Mines"; ret[2].type = C_STRING; sprintf(buf, "%d", params->n); ret[2].sval = dupstr(buf); ret[2].ival = 0; ret[3].name = "Ensure solubility"; ret[3].type = C_BOOLEAN; ret[3].sval = NULL; ret[3].ival = params->unique; ret[4].name = NULL; ret[4].type = C_END; ret[4].sval = NULL; ret[4].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); ret->n = atoi(cfg[2].sval); if (strchr(cfg[2].sval, '%')) ret->n = ret->n * (ret->w * ret->h) / 100; ret->unique = cfg[3].ival; return ret; } static char *validate_params(const game_params *params, int full) { /* * Lower limit on grid size: each dimension must be at least 3. * 1 is theoretically workable if rather boring, but 2 is a * real problem: there is often _no_ way to generate a uniquely * solvable 2xn Mines grid. You either run into two mines * blocking the way and no idea what's behind them, or one mine * and no way to know which of the two rows it's in. If the * mine count is even you can create a soluble grid by packing * all the mines at one end (so what when you hit a two-mine * wall there are only as many covered squares left as there * are mines); but if it's odd, you are doomed, because you * _have_ to have a gap somewhere which you can't determine the * position of. */ if (full && params->unique && (params->w <= 2 || params->h <= 2)) return "Width and height must both be greater than two"; if (params->n > params->w * params->h - 9) return "Too many mines for grid size"; /* * FIXME: Need more constraints here. Not sure what the * sensible limits for Minesweeper actually are. The limits * probably ought to change, however, depending on uniqueness. */ return NULL; } /* ---------------------------------------------------------------------- * Minesweeper solver, used to ensure the generated grids are * solvable without having to take risks. */ /* * Count the bits in a word. Only needs to cope with 16 bits. */ static int bitcount16(int inword) { unsigned int word = inword; word = ((word & 0xAAAA) >> 1) + (word & 0x5555); word = ((word & 0xCCCC) >> 2) + (word & 0x3333); word = ((word & 0xF0F0) >> 4) + (word & 0x0F0F); word = ((word & 0xFF00) >> 8) + (word & 0x00FF); return (int)word; } /* * We use a tree234 to store a large number of small localised * sets, each with a mine count. We also keep some of those sets * linked together into a to-do list. */ struct set { short x, y, mask, mines; int todo; struct set *prev, *next; }; static int setcmp(void *av, void *bv) { struct set *a = (struct set *)av; struct set *b = (struct set *)bv; if (a->y < b->y) return -1; else if (a->y > b->y) return +1; else if (a->x < b->x) return -1; else if (a->x > b->x) return +1; else if (a->mask < b->mask) return -1; else if (a->mask > b->mask) return +1; else return 0; } struct setstore { tree234 *sets; struct set *todo_head, *todo_tail; }; static struct setstore *ss_new(void) { struct setstore *ss = snew(struct setstore); ss->sets = newtree234(setcmp); ss->todo_head = ss->todo_tail = NULL; return ss; } /* * Take two input sets, in the form (x,y,mask). Munge the first by * taking either its intersection with the second or its difference * with the second. Return the new mask part of the first set. */ static int setmunge(int x1, int y1, int mask1, int x2, int y2, int mask2, int diff) { /* * Adjust the second set so that it has the same x,y * coordinates as the first. */ if (abs(x2-x1) >= 3 || abs(y2-y1) >= 3) { mask2 = 0; } else { while (x2 > x1) { mask2 &= ~(4|32|256); mask2 <<= 1; x2--; } while (x2 < x1) { mask2 &= ~(1|8|64); mask2 >>= 1; x2++; } while (y2 > y1) { mask2 &= ~(64|128|256); mask2 <<= 3; y2--; } while (y2 < y1) { mask2 &= ~(1|2|4); mask2 >>= 3; y2++; } } /* * Invert the second set if `diff' is set (we're after A &~ B * rather than A & B). */ if (diff) mask2 ^= 511; /* * Now all that's left is a logical AND. */ return mask1 & mask2; } static void ss_add_todo(struct setstore *ss, struct set *s) { if (s->todo) return; /* already on it */ #ifdef SOLVER_DIAGNOSTICS printf("adding set on todo list: %d,%d %03x %d\n", s->x, s->y, s->mask, s->mines); #endif s->prev = ss->todo_tail; if (s->prev) s->prev->next = s; else ss->todo_head = s; ss->todo_tail = s; s->next = NULL; s->todo = TRUE; } static void ss_add(struct setstore *ss, int x, int y, int mask, int mines) { struct set *s; assert(mask != 0); /* * Normalise so that x and y are genuinely the bounding * rectangle. */ while (!(mask & (1|8|64))) mask >>= 1, x++; while (!(mask & (1|2|4))) mask >>= 3, y++; /* * Create a set structure and add it to the tree. */ s = snew(struct set); s->x = x; s->y = y; s->mask = mask; s->mines = mines; s->todo = FALSE; if (add234(ss->sets, s) != s) { /* * This set already existed! Free it and return. */ sfree(s); return; } /* * We've added a new set to the tree, so put it on the todo * list. */ ss_add_todo(ss, s); } static void ss_remove(struct setstore *ss, struct set *s) { struct set *next = s->next, *prev = s->prev; #ifdef SOLVER_DIAGNOSTICS printf("removing set %d,%d %03x\n", s->x, s->y, s->mask); #endif /* * Remove s from the todo list. */ if (prev) prev->next = next; else if (s == ss->todo_head) ss->todo_head = next; if (next) next->prev = prev; else if (s == ss->todo_tail) ss->todo_tail = prev; s->todo = FALSE; /* * Remove s from the tree. */ del234(ss->sets, s); /* * Destroy the actual set structure. */ sfree(s); } /* * Return a dynamically allocated list of all the sets which * overlap a provided input set. */ static struct set **ss_overlap(struct setstore *ss, int x, int y, int mask) { struct set **ret = NULL; int nret = 0, retsize = 0; int xx, yy; for (xx = x-3; xx < x+3; xx++) for (yy = y-3; yy < y+3; yy++) { struct set stmp, *s; int pos; /* * Find the first set with these top left coordinates. */ stmp.x = xx; stmp.y = yy; stmp.mask = 0; if (findrelpos234(ss->sets, &stmp, NULL, REL234_GE, &pos)) { while ((s = index234(ss->sets, pos)) != NULL && s->x == xx && s->y == yy) { /* * This set potentially overlaps the input one. * Compute the intersection to see if they * really overlap, and add it to the list if * so. */ if (setmunge(x, y, mask, s->x, s->y, s->mask, FALSE)) { /* * There's an overlap. */ if (nret >= retsize) { retsize = nret + 32; ret = sresize(ret, retsize, struct set *); } ret[nret++] = s; } pos++; } } } ret = sresize(ret, nret+1, struct set *); ret[nret] = NULL; return ret; } /* * Get an element from the head of the set todo list. */ static struct set *ss_todo(struct setstore *ss) { if (ss->todo_head) { struct set *ret = ss->todo_head; ss->todo_head = ret->next; if (ss->todo_head) ss->todo_head->prev = NULL; else ss->todo_tail = NULL; ret->next = ret->prev = NULL; ret->todo = FALSE; return ret; } else { return NULL; } } struct squaretodo { int *next; int head, tail; }; static void std_add(struct squaretodo *std, int i) { if (std->tail >= 0) std->next[std->tail] = i; else std->head = i; std->tail = i; std->next[i] = -1; } typedef int (*open_cb)(void *, int, int); static void known_squares(int w, int h, struct squaretodo *std, signed char *grid, open_cb open, void *openctx, int x, int y, int mask, int mine) { int xx, yy, bit; bit = 1; for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++) { if (mask & bit) { int i = (y + yy) * w + (x + xx); /* * It's possible that this square is _already_ * known, in which case we don't try to add it to * the list twice. */ if (grid[i] == -2) { if (mine) { grid[i] = -1; /* and don't open it! */ } else { grid[i] = open(openctx, x + xx, y + yy); assert(grid[i] != -1); /* *bang* */ } std_add(std, i); } } bit <<= 1; } } /* * This is data returned from the `perturb' function. It details * which squares have become mines and which have become clear. The * solver is (of course) expected to honourably not use that * knowledge directly, but to efficently adjust its internal data * structures and proceed based on only the information it * legitimately has. */ struct perturbation { int x, y; int delta; /* +1 == become a mine; -1 == cleared */ }; struct perturbations { int n; struct perturbation *changes; }; /* * Main solver entry point. You give it a grid of existing * knowledge (-1 for a square known to be a mine, 0-8 for empty * squares with a given number of neighbours, -2 for completely * unknown), plus a function which you can call to open new squares * once you're confident of them. It fills in as much more of the * grid as it can. * * Return value is: * * - -1 means deduction stalled and nothing could be done * - 0 means deduction succeeded fully * - >0 means deduction succeeded but some number of perturbation * steps were required; the exact return value is the number of * perturb calls. */ typedef struct perturbations *(*perturb_cb) (void *, signed char *, int, int, int); static int minesolve(int w, int h, int n, signed char *grid, open_cb open, perturb_cb perturb, void *ctx, random_state *rs) { struct setstore *ss = ss_new(); struct set **list; struct squaretodo astd, *std = &astd; int x, y, i, j; int nperturbs = 0; /* * Set up a linked list of squares with known contents, so that * we can process them one by one. */ std->next = snewn(w*h, int); std->head = std->tail = -1; /* * Initialise that list with all known squares in the input * grid. */ for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { i = y*w+x; if (grid[i] != -2) std_add(std, i); } } /* * Main deductive loop. */ while (1) { int done_something = FALSE; struct set *s; /* * If there are any known squares on the todo list, process * them and construct a set for each. */ while (std->head != -1) { i = std->head; #ifdef SOLVER_DIAGNOSTICS printf("known square at %d,%d [%d]\n", i%w, i/w, grid[i]); #endif std->head = std->next[i]; if (std->head == -1) std->tail = -1; x = i % w; y = i / w; if (grid[i] >= 0) { int dx, dy, mines, bit, val; #ifdef SOLVER_DIAGNOSTICS printf("creating set around this square\n"); #endif /* * Empty square. Construct the set of non-known squares * around this one, and determine its mine count. */ mines = grid[i]; bit = 1; val = 0; for (dy = -1; dy <= +1; dy++) { for (dx = -1; dx <= +1; dx++) { #ifdef SOLVER_DIAGNOSTICS printf("grid %d,%d = %d\n", x+dx, y+dy, grid[i+dy*w+dx]); #endif if (x+dx < 0 || x+dx >= w || y+dy < 0 || y+dy >= h) /* ignore this one */; else if (grid[i+dy*w+dx] == -1) mines--; else if (grid[i+dy*w+dx] == -2) val |= bit; bit <<= 1; } } if (val) ss_add(ss, x-1, y-1, val, mines); } /* * Now, whether the square is empty or full, we must * find any set which contains it and replace it with * one which does not. */ { #ifdef SOLVER_DIAGNOSTICS printf("finding sets containing known square %d,%d\n", x, y); #endif list = ss_overlap(ss, x, y, 1); for (j = 0; list[j]; j++) { int newmask, newmines; s = list[j]; /* * Compute the mask for this set minus the * newly known square. */ newmask = setmunge(s->x, s->y, s->mask, x, y, 1, TRUE); /* * Compute the new mine count. */ newmines = s->mines - (grid[i] == -1); /* * Insert the new set into the collection, * unless it's been whittled right down to * nothing. */ if (newmask) ss_add(ss, s->x, s->y, newmask, newmines); /* * Destroy the old one; it is actually obsolete. */ ss_remove(ss, s); } sfree(list); } /* * Marking a fresh square as known certainly counts as * doing something. */ done_something = TRUE; } /* * Now pick a set off the to-do list and attempt deductions * based on it. */ if ((s = ss_todo(ss)) != NULL) { #ifdef SOLVER_DIAGNOSTICS printf("set to do: %d,%d %03x %d\n", s->x, s->y, s->mask, s->mines); #endif /* * Firstly, see if this set has a mine count of zero or * of its own cardinality. */ if (s->mines == 0 || s->mines == bitcount16(s->mask)) { /* * If so, we can immediately mark all the squares * in the set as known. */ #ifdef SOLVER_DIAGNOSTICS printf("easy\n"); #endif known_squares(w, h, std, grid, open, ctx, s->x, s->y, s->mask, (s->mines != 0)); /* * Having done that, we need do nothing further * with this set; marking all the squares in it as * known will eventually eliminate it, and will * also permit further deductions about anything * that overlaps it. */ continue; } /* * Failing that, we now search through all the sets * which overlap this one. */ list = ss_overlap(ss, s->x, s->y, s->mask); for (j = 0; list[j]; j++) { struct set *s2 = list[j]; int swing, s2wing, swc, s2wc; /* * Find the non-overlapping parts s2-s and s-s2, * and their cardinalities. * * I'm going to refer to these parts as `wings' * surrounding the central part common to both * sets. The `s wing' is s-s2; the `s2 wing' is * s2-s. */ swing = setmunge(s->x, s->y, s->mask, s2->x, s2->y, s2->mask, TRUE); s2wing = setmunge(s2->x, s2->y, s2->mask, s->x, s->y, s->mask, TRUE); swc = bitcount16(swing); s2wc = bitcount16(s2wing); /* * If one set has more mines than the other, and * the number of extra mines is equal to the * cardinality of that set's wing, then we can mark * every square in the wing as a known mine, and * every square in the other wing as known clear. */ if (swc == s->mines - s2->mines || s2wc == s2->mines - s->mines) { known_squares(w, h, std, grid, open, ctx, s->x, s->y, swing, (swc == s->mines - s2->mines)); known_squares(w, h, std, grid, open, ctx, s2->x, s2->y, s2wing, (s2wc == s2->mines - s->mines)); continue; } /* * Failing that, see if one set is a subset of the * other. If so, we can divide up the mine count of * the larger set between the smaller set and its * complement, even if neither smaller set ends up * being immediately clearable. */ if (swc == 0 && s2wc != 0) { /* s is a subset of s2. */ assert(s2->mines > s->mines); ss_add(ss, s2->x, s2->y, s2wing, s2->mines - s->mines); } else if (s2wc == 0 && swc != 0) { /* s2 is a subset of s. */ assert(s->mines > s2->mines); ss_add(ss, s->x, s->y, swing, s->mines - s2->mines); } } sfree(list); /* * In this situation we have definitely done * _something_, even if it's only reducing the size of * our to-do list. */ done_something = TRUE; } else if (n >= 0) { /* * We have nothing left on our todo list, which means * all localised deductions have failed. Our next step * is to resort to global deduction based on the total * mine count. This is computationally expensive * compared to any of the above deductions, which is * why we only ever do it when all else fails, so that * hopefully it won't have to happen too often. * * If you pass n<0 into this solver, that informs it * that you do not know the total mine count, so it * won't even attempt these deductions. */ int minesleft, squaresleft; int nsets, setused[10], cursor; /* * Start by scanning the current grid state to work out * how many unknown squares we still have, and how many * mines are to be placed in them. */ squaresleft = 0; minesleft = n; for (i = 0; i < w*h; i++) { if (grid[i] == -1) minesleft--; else if (grid[i] == -2) squaresleft++; } #ifdef SOLVER_DIAGNOSTICS printf("global deduction time: squaresleft=%d minesleft=%d\n", squaresleft, minesleft); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { int v = grid[y*w+x]; if (v == -1) putchar('*'); else if (v == -2) putchar('?'); else if (v == 0) putchar('-'); else putchar('0' + v); } putchar('\n'); } #endif /* * If there _are_ no unknown squares, we have actually * finished. */ if (squaresleft == 0) { assert(minesleft == 0); break; } /* * First really simple case: if there are no more mines * left, or if there are exactly as many mines left as * squares to play them in, then it's all easy. */ if (minesleft == 0 || minesleft == squaresleft) { for (i = 0; i < w*h; i++) if (grid[i] == -2) known_squares(w, h, std, grid, open, ctx, i % w, i / w, 1, minesleft != 0); continue; /* now go back to main deductive loop */ } /* * Failing that, we have to do some _real_ work. * Ideally what we do here is to try every single * combination of the currently available sets, in an * attempt to find a disjoint union (i.e. a set of * squares with a known mine count between them) such * that the remaining unknown squares _not_ contained * in that union either contain no mines or are all * mines. * * Actually enumerating all 2^n possibilities will get * a bit slow for large n, so I artificially cap this * recursion at n=10 to avoid too much pain. */ nsets = count234(ss->sets); if (nsets <= lenof(setused)) { /* * Doing this with actual recursive function calls * would get fiddly because a load of local * variables from this function would have to be * passed down through the recursion. So instead * I'm going to use a virtual recursion within this * function. The way this works is: * * - we have an array `setused', such that * setused[n] is 0 or 1 depending on whether set * n is currently in the union we are * considering. * * - we have a value `cursor' which indicates how * much of `setused' we have so far filled in. * It's conceptually the recursion depth. * * We begin by setting `cursor' to zero. Then: * * - if cursor can advance, we advance it by one. * We set the value in `setused' that it went * past to 1 if that set is disjoint from * anything else currently in `setused', or to 0 * otherwise. * * - If cursor cannot advance because it has * reached the end of the setused list, then we * have a maximal disjoint union. Check to see * whether its mine count has any useful * properties. If so, mark all the squares not * in the union as known and terminate. * * - If cursor has reached the end of setused and * the algorithm _hasn't_ terminated, back * cursor up to the nearest 1, turn it into a 0 * and advance cursor just past it. * * - If we attempt to back up to the nearest 1 and * there isn't one at all, then we have gone * through all disjoint unions of sets in the * list and none of them has been helpful, so we * give up. */ struct set *sets[lenof(setused)]; for (i = 0; i < nsets; i++) sets[i] = index234(ss->sets, i); cursor = 0; while (1) { if (cursor < nsets) { int ok = TRUE; /* See if any existing set overlaps this one. */ for (i = 0; i < cursor; i++) if (setused[i] && setmunge(sets[cursor]->x, sets[cursor]->y, sets[cursor]->mask, sets[i]->x, sets[i]->y, sets[i]->mask, FALSE)) { ok = FALSE; break; } if (ok) { /* * We're adding this set to our union, * so adjust minesleft and squaresleft * appropriately. */ minesleft -= sets[cursor]->mines; squaresleft -= bitcount16(sets[cursor]->mask); } setused[cursor++] = ok; } else { #ifdef SOLVER_DIAGNOSTICS printf("trying a set combination with %d %d\n", squaresleft, minesleft); #endif /* SOLVER_DIAGNOSTICS */ /* * We've reached the end. See if we've got * anything interesting. */ if (squaresleft > 0 && (minesleft == 0 || minesleft == squaresleft)) { /* * We have! There is at least one * square not contained within the set * union we've just found, and we can * deduce that either all such squares * are mines or all are not (depending * on whether minesleft==0). So now all * we have to do is actually go through * the grid, find those squares, and * mark them. */ for (i = 0; i < w*h; i++) if (grid[i] == -2) { int outside = TRUE; y = i / w; x = i % w; for (j = 0; j < nsets; j++) if (setused[j] && setmunge(sets[j]->x, sets[j]->y, sets[j]->mask, x, y, 1, FALSE)) { outside = FALSE; break; } if (outside) known_squares(w, h, std, grid, open, ctx, x, y, 1, minesleft != 0); } done_something = TRUE; break; /* return to main deductive loop */ } /* * If we reach here, then this union hasn't * done us any good, so move on to the * next. Backtrack cursor to the nearest 1, * change it to a 0 and continue. */ while (--cursor >= 0 && !setused[cursor]); if (cursor >= 0) { assert(setused[cursor]); /* * We're removing this set from our * union, so re-increment minesleft and * squaresleft. */ minesleft += sets[cursor]->mines; squaresleft += bitcount16(sets[cursor]->mask); setused[cursor++] = 0; } else { /* * We've backtracked all the way to the * start without finding a single 1, * which means that our virtual * recursion is complete and nothing * helped. */ break; } } } } } if (done_something) continue; #ifdef SOLVER_DIAGNOSTICS /* * Dump the current known state of the grid. */ printf("solver ran out of steam, ret=%d, grid:\n", nperturbs); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { int v = grid[y*w+x]; if (v == -1) putchar('*'); else if (v == -2) putchar('?'); else if (v == 0) putchar('-'); else putchar('0' + v); } putchar('\n'); } { struct set *s; for (i = 0; (s = index234(ss->sets, i)) != NULL; i++) printf("remaining set: %d,%d %03x %d\n", s->x, s->y, s->mask, s->mines); } #endif /* * Now we really are at our wits' end as far as solving * this grid goes. Our only remaining option is to call * a perturb function and ask it to modify the grid to * make it easier. */ if (perturb) { struct perturbations *ret; struct set *s; nperturbs++; /* * Choose a set at random from the current selection, * and ask the perturb function to either fill or empty * it. * * If we have no sets at all, we must give up. */ if (count234(ss->sets) == 0) { #ifdef SOLVER_DIAGNOSTICS printf("perturbing on entire unknown set\n"); #endif ret = perturb(ctx, grid, 0, 0, 0); } else { s = index234(ss->sets, random_upto(rs, count234(ss->sets))); #ifdef SOLVER_DIAGNOSTICS printf("perturbing on set %d,%d %03x\n", s->x, s->y, s->mask); #endif ret = perturb(ctx, grid, s->x, s->y, s->mask); } if (ret) { assert(ret->n > 0); /* otherwise should have been NULL */ /* * A number of squares have been fiddled with, and * the returned structure tells us which. Adjust * the mine count in any set which overlaps one of * those squares, and put them back on the to-do * list. Also, if the square itself is marked as a * known non-mine, put it back on the squares-to-do * list. */ for (i = 0; i < ret->n; i++) { #ifdef SOLVER_DIAGNOSTICS printf("perturbation %s mine at %d,%d\n", ret->changes[i].delta > 0 ? "added" : "removed", ret->changes[i].x, ret->changes[i].y); #endif if (ret->changes[i].delta < 0 && grid[ret->changes[i].y*w+ret->changes[i].x] != -2) { std_add(std, ret->changes[i].y*w+ret->changes[i].x); } list = ss_overlap(ss, ret->changes[i].x, ret->changes[i].y, 1); for (j = 0; list[j]; j++) { list[j]->mines += ret->changes[i].delta; ss_add_todo(ss, list[j]); } sfree(list); } /* * Now free the returned data. */ sfree(ret->changes); sfree(ret); #ifdef SOLVER_DIAGNOSTICS /* * Dump the current known state of the grid. */ printf("state after perturbation:\n"); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { int v = grid[y*w+x]; if (v == -1) putchar('*'); else if (v == -2) putchar('?'); else if (v == 0) putchar('-'); else putchar('0' + v); } putchar('\n'); } { struct set *s; for (i = 0; (s = index234(ss->sets, i)) != NULL; i++) printf("remaining set: %d,%d %03x %d\n", s->x, s->y, s->mask, s->mines); } #endif /* * And now we can go back round the deductive loop. */ continue; } } /* * If we get here, even that didn't work (either we didn't * have a perturb function or it returned failure), so we * give up entirely. */ break; } /* * See if we've got any unknown squares left. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) if (grid[y*w+x] == -2) { nperturbs = -1; /* failed to complete */ break; } /* * Free the set list and square-todo list. */ { struct set *s; while ((s = delpos234(ss->sets, 0)) != NULL) sfree(s); freetree234(ss->sets); sfree(ss); sfree(std->next); } return nperturbs; } /* ---------------------------------------------------------------------- * Grid generator which uses the above solver. */ struct minectx { char *grid; int w, h; int sx, sy; int allow_big_perturbs; random_state *rs; }; static int mineopen(void *vctx, int x, int y) { struct minectx *ctx = (struct minectx *)vctx; int i, j, n; assert(x >= 0 && x < ctx->w && y >= 0 && y < ctx->h); if (ctx->grid[y * ctx->w + x]) return -1; /* *bang* */ n = 0; for (i = -1; i <= +1; i++) { if (x + i < 0 || x + i >= ctx->w) continue; for (j = -1; j <= +1; j++) { if (y + j < 0 || y + j >= ctx->h) continue; if (i == 0 && j == 0) continue; if (ctx->grid[(y+j) * ctx->w + (x+i)]) n++; } } return n; } /* Structure used internally to mineperturb(). */ struct square { int x, y, type, random; }; static int squarecmp(const void *av, const void *bv) { const struct square *a = (const struct square *)av; const struct square *b = (const struct square *)bv; if (a->type < b->type) return -1; else if (a->type > b->type) return +1; else if (a->random < b->random) return -1; else if (a->random > b->random) return +1; else if (a->y < b->y) return -1; else if (a->y > b->y) return +1; else if (a->x < b->x) return -1; else if (a->x > b->x) return +1; return 0; } /* * Normally this function is passed an (x,y,mask) set description. * On occasions, though, there is no _localised_ set being used, * and the set being perturbed is supposed to be the entirety of * the unreachable area. This is signified by the special case * mask==0: in this case, anything labelled -2 in the grid is part * of the set. * * Allowing perturbation in this special case appears to make it * guaranteeably possible to generate a workable grid for any mine * density, but they tend to be a bit boring, with mines packed * densely into far corners of the grid and the remainder being * less dense than one might like. Therefore, to improve overall * grid quality I disable this feature for the first few attempts, * and fall back to it after no useful grid has been generated. */ static struct perturbations *mineperturb(void *vctx, signed char *grid, int setx, int sety, int mask) { struct minectx *ctx = (struct minectx *)vctx; struct square *sqlist; int x, y, dx, dy, i, n, nfull, nempty; struct square **tofill, **toempty, **todo; int ntofill, ntoempty, ntodo, dtodo, dset; struct perturbations *ret; int *setlist; if (!mask && !ctx->allow_big_perturbs) return NULL; /* * Make a list of all the squares in the grid which we can * possibly use. This list should be in preference order, which * means * * - first, unknown squares on the boundary of known space * - next, unknown squares beyond that boundary * - as a very last resort, known squares, but not within one * square of the starting position. * * Each of these sections needs to be shuffled independently. * We do this by preparing list of all squares and then sorting * it with a random secondary key. */ sqlist = snewn(ctx->w * ctx->h, struct square); n = 0; for (y = 0; y < ctx->h; y++) for (x = 0; x < ctx->w; x++) { /* * If this square is too near the starting position, * don't put it on the list at all. */ if (abs(y - ctx->sy) <= 1 && abs(x - ctx->sx) <= 1) continue; /* * If this square is in the input set, also don't put * it on the list! */ if ((mask == 0 && grid[y*ctx->w+x] == -2) || (x >= setx && x < setx + 3 && y >= sety && y < sety + 3 && mask & (1 << ((y-sety)*3+(x-setx))))) continue; sqlist[n].x = x; sqlist[n].y = y; if (grid[y*ctx->w+x] != -2) { sqlist[n].type = 3; /* known square */ } else { /* * Unknown square. Examine everything around it and * see if it borders on any known squares. If it * does, it's class 1, otherwise it's 2. */ sqlist[n].type = 2; for (dy = -1; dy <= +1; dy++) for (dx = -1; dx <= +1; dx++) if (x+dx >= 0 && x+dx < ctx->w && y+dy >= 0 && y+dy < ctx->h && grid[(y+dy)*ctx->w+(x+dx)] != -2) { sqlist[n].type = 1; break; } } /* * Finally, a random number to cause qsort to * shuffle within each group. */ sqlist[n].random = random_bits(ctx->rs, 31); n++; } qsort(sqlist, n, sizeof(struct square), squarecmp); /* * Now count up the number of full and empty squares in the set * we've been provided. */ nfull = nempty = 0; if (mask) { for (dy = 0; dy < 3; dy++) for (dx = 0; dx < 3; dx++) if (mask & (1 << (dy*3+dx))) { assert(setx+dx <= ctx->w); assert(sety+dy <= ctx->h); if (ctx->grid[(sety+dy)*ctx->w+(setx+dx)]) nfull++; else nempty++; } } else { for (y = 0; y < ctx->h; y++) for (x = 0; x < ctx->w; x++) if (grid[y*ctx->w+x] == -2) { if (ctx->grid[y*ctx->w+x]) nfull++; else nempty++; } } /* * Now go through our sorted list until we find either `nfull' * empty squares, or `nempty' full squares; these will be * swapped with the appropriate squares in the set to either * fill or empty the set while keeping the same number of mines * overall. */ ntofill = ntoempty = 0; if (mask) { tofill = snewn(9, struct square *); toempty = snewn(9, struct square *); } else { tofill = snewn(ctx->w * ctx->h, struct square *); toempty = snewn(ctx->w * ctx->h, struct square *); } for (i = 0; i < n; i++) { struct square *sq = &sqlist[i]; if (ctx->grid[sq->y * ctx->w + sq->x]) toempty[ntoempty++] = sq; else tofill[ntofill++] = sq; if (ntofill == nfull || ntoempty == nempty) break; } /* * If we haven't found enough empty squares outside the set to * empty it into _or_ enough full squares outside it to fill it * up with, we'll have to settle for doing only a partial job. * In this case we choose to always _fill_ the set (because * this case will tend to crop up when we're working with very * high mine densities and the only way to get a solvable grid * is going to be to pack most of the mines solidly around the * edges). So now our job is to make a list of the empty * squares in the set, and shuffle that list so that we fill a * random selection of them. */ if (ntofill != nfull && ntoempty != nempty) { int k; assert(ntoempty != 0); setlist = snewn(ctx->w * ctx->h, int); i = 0; if (mask) { for (dy = 0; dy < 3; dy++) for (dx = 0; dx < 3; dx++) if (mask & (1 << (dy*3+dx))) { assert(setx+dx <= ctx->w); assert(sety+dy <= ctx->h); if (!ctx->grid[(sety+dy)*ctx->w+(setx+dx)]) setlist[i++] = (sety+dy)*ctx->w+(setx+dx); } } else { for (y = 0; y < ctx->h; y++) for (x = 0; x < ctx->w; x++) if (grid[y*ctx->w+x] == -2) { if (!ctx->grid[y*ctx->w+x]) setlist[i++] = y*ctx->w+x; } } assert(i > ntoempty); /* * Now pick `ntoempty' items at random from the list. */ for (k = 0; k < ntoempty; k++) { int index = k + random_upto(ctx->rs, i - k); int tmp; tmp = setlist[k]; setlist[k] = setlist[index]; setlist[index] = tmp; } } else setlist = NULL; /* * Now we're pretty much there. We need to either * (a) put a mine in each of the empty squares in the set, and * take one out of each square in `toempty' * (b) take a mine out of each of the full squares in the set, * and put one in each square in `tofill' * depending on which one we've found enough squares to do. * * So we start by constructing our list of changes to return to * the solver, so that it can update its data structures * efficiently rather than having to rescan the whole grid. */ ret = snew(struct perturbations); if (ntofill == nfull) { todo = tofill; ntodo = ntofill; dtodo = +1; dset = -1; sfree(toempty); } else { /* * (We also fall into this case if we've constructed a * setlist.) */ todo = toempty; ntodo = ntoempty; dtodo = -1; dset = +1; sfree(tofill); } ret->n = 2 * ntodo; ret->changes = snewn(ret->n, struct perturbation); for (i = 0; i < ntodo; i++) { ret->changes[i].x = todo[i]->x; ret->changes[i].y = todo[i]->y; ret->changes[i].delta = dtodo; } /* now i == ntodo */ if (setlist) { int j; assert(todo == toempty); for (j = 0; j < ntoempty; j++) { ret->changes[i].x = setlist[j] % ctx->w; ret->changes[i].y = setlist[j] / ctx->w; ret->changes[i].delta = dset; i++; } sfree(setlist); } else if (mask) { for (dy = 0; dy < 3; dy++) for (dx = 0; dx < 3; dx++) if (mask & (1 << (dy*3+dx))) { int currval = (ctx->grid[(sety+dy)*ctx->w+(setx+dx)] ? +1 : -1); if (dset == -currval) { ret->changes[i].x = setx + dx; ret->changes[i].y = sety + dy; ret->changes[i].delta = dset; i++; } } } else { for (y = 0; y < ctx->h; y++) for (x = 0; x < ctx->w; x++) if (grid[y*ctx->w+x] == -2) { int currval = (ctx->grid[y*ctx->w+x] ? +1 : -1); if (dset == -currval) { ret->changes[i].x = x; ret->changes[i].y = y; ret->changes[i].delta = dset; i++; } } } assert(i == ret->n); sfree(sqlist); sfree(todo); /* * Having set up the precise list of changes we're going to * make, we now simply make them and return. */ for (i = 0; i < ret->n; i++) { int delta; x = ret->changes[i].x; y = ret->changes[i].y; delta = ret->changes[i].delta; /* * Check we're not trying to add an existing mine or remove * an absent one. */ assert((delta < 0) ^ (ctx->grid[y*ctx->w+x] == 0)); /* * Actually make the change. */ ctx->grid[y*ctx->w+x] = (delta > 0); /* * Update any numbers already present in the grid. */ for (dy = -1; dy <= +1; dy++) for (dx = -1; dx <= +1; dx++) if (x+dx >= 0 && x+dx < ctx->w && y+dy >= 0 && y+dy < ctx->h && grid[(y+dy)*ctx->w+(x+dx)] != -2) { if (dx == 0 && dy == 0) { /* * The square itself is marked as known in * the grid. Mark it as a mine if it's a * mine, or else work out its number. */ if (delta > 0) { grid[y*ctx->w+x] = -1; } else { int dx2, dy2, minecount = 0; for (dy2 = -1; dy2 <= +1; dy2++) for (dx2 = -1; dx2 <= +1; dx2++) if (x+dx2 >= 0 && x+dx2 < ctx->w && y+dy2 >= 0 && y+dy2 < ctx->h && ctx->grid[(y+dy2)*ctx->w+(x+dx2)]) minecount++; grid[y*ctx->w+x] = minecount; } } else { if (grid[(y+dy)*ctx->w+(x+dx)] >= 0) grid[(y+dy)*ctx->w+(x+dx)] += delta; } } } #ifdef GENERATION_DIAGNOSTICS { int yy, xx; printf("grid after perturbing:\n"); for (yy = 0; yy < ctx->h; yy++) { for (xx = 0; xx < ctx->w; xx++) { int v = ctx->grid[yy*ctx->w+xx]; if (yy == ctx->sy && xx == ctx->sx) { assert(!v); putchar('S'); } else if (v) { putchar('*'); } else { putchar('-'); } } putchar('\n'); } printf("\n"); } #endif return ret; } static char *minegen(int w, int h, int n, int x, int y, int unique, random_state *rs) { char *ret = snewn(w*h, char); int success; int ntries = 0; do { success = FALSE; ntries++; memset(ret, 0, w*h); /* * Start by placing n mines, none of which is at x,y or within * one square of it. */ { int *tmp = snewn(w*h, int); int i, j, k, nn; /* * Write down the list of possible mine locations. */ k = 0; for (i = 0; i < h; i++) for (j = 0; j < w; j++) if (abs(i - y) > 1 || abs(j - x) > 1) tmp[k++] = i*w+j; /* * Now pick n off the list at random. */ nn = n; while (nn-- > 0) { i = random_upto(rs, k); ret[tmp[i]] = 1; tmp[i] = tmp[--k]; } sfree(tmp); } #ifdef GENERATION_DIAGNOSTICS { int yy, xx; printf("grid after initial generation:\n"); for (yy = 0; yy < h; yy++) { for (xx = 0; xx < w; xx++) { int v = ret[yy*w+xx]; if (yy == y && xx == x) { assert(!v); putchar('S'); } else if (v) { putchar('*'); } else { putchar('-'); } } putchar('\n'); } printf("\n"); } #endif /* * Now set up a results grid to run the solver in, and a * context for the solver to open squares. Then run the solver * repeatedly; if the number of perturb steps ever goes up or * it ever returns -1, give up completely. * * We bypass this bit if we're not after a unique grid. */ if (unique) { signed char *solvegrid = snewn(w*h, signed char); struct minectx actx, *ctx = &actx; int solveret, prevret = -2; ctx->grid = ret; ctx->w = w; ctx->h = h; ctx->sx = x; ctx->sy = y; ctx->rs = rs; ctx->allow_big_perturbs = (ntries > 100); while (1) { memset(solvegrid, -2, w*h); solvegrid[y*w+x] = mineopen(ctx, x, y); assert(solvegrid[y*w+x] == 0); /* by deliberate arrangement */ solveret = minesolve(w, h, n, solvegrid, mineopen, mineperturb, ctx, rs); if (solveret < 0 || (prevret >= 0 && solveret >= prevret)) { success = FALSE; break; } else if (solveret == 0) { success = TRUE; break; } } sfree(solvegrid); } else { success = TRUE; } } while (!success); return ret; } static char *describe_layout(char *grid, int area, int x, int y, int obfuscate) { char *ret, *p; unsigned char *bmp; int i; /* * Set up the mine bitmap and obfuscate it. */ bmp = snewn((area + 7) / 8, unsigned char); memset(bmp, 0, (area + 7) / 8); for (i = 0; i < area; i++) { if (grid[i]) bmp[i / 8] |= 0x80 >> (i % 8); } if (obfuscate) obfuscate_bitmap(bmp, area, FALSE); /* * Now encode the resulting bitmap in hex. We can work to * nibble rather than byte granularity, since the obfuscation * function guarantees to return a bit string of the same * length as its input. */ ret = snewn((area+3)/4 + 100, char); p = ret + sprintf(ret, "%d,%d,%s", x, y, obfuscate ? "m" : "u"); /* 'm' == masked */ for (i = 0; i < (area+3)/4; i++) { int v = bmp[i/2]; if (i % 2 == 0) v >>= 4; *p++ = "0123456789abcdef"[v & 0xF]; } *p = '\0'; sfree(bmp); return ret; } static char *new_mine_layout(int w, int h, int n, int x, int y, int unique, random_state *rs, char **game_desc) { char *grid; #ifdef TEST_OBFUSCATION static int tested_obfuscation = FALSE; if (!tested_obfuscation) { /* * A few simple test vectors for the obfuscator. * * First test: the 28-bit stream 1234567. This divides up * into 1234 and 567[0]. The SHA of 56 70 30 (appending * "0") is 15ce8ab946640340bbb99f3f48fd2c45d1a31d30. Thus, * we XOR the 16-bit string 15CE into the input 1234 to get * 07FA. Next, we SHA that with "0": the SHA of 07 FA 30 is * 3370135c5e3da4fed937adc004a79533962b6391. So we XOR the * 12-bit string 337 into the input 567 to get 650. Thus * our output is 07FA650. */ { unsigned char bmp1[] = "\x12\x34\x56\x70"; obfuscate_bitmap(bmp1, 28, FALSE); printf("test 1 encode: %s\n", memcmp(bmp1, "\x07\xfa\x65\x00", 4) ? "failed" : "passed"); obfuscate_bitmap(bmp1, 28, TRUE); printf("test 1 decode: %s\n", memcmp(bmp1, "\x12\x34\x56\x70", 4) ? "failed" : "passed"); } /* * Second test: a long string to make sure we switch from * one SHA to the next correctly. My input string this time * is simply fifty bytes of zeroes. */ { unsigned char bmp2[50]; unsigned char bmp2a[50]; memset(bmp2, 0, 50); memset(bmp2a, 0, 50); obfuscate_bitmap(bmp2, 50 * 8, FALSE); /* * SHA of twenty-five zero bytes plus "0" is * b202c07b990c01f6ff2d544707f60e506019b671. SHA of * twenty-five zero bytes plus "1" is * fcb1d8b5a2f6b592fe6780b36aa9d65dd7aa6db9. Thus our * first half becomes * b202c07b990c01f6ff2d544707f60e506019b671fcb1d8b5a2. * * SHA of that lot plus "0" is * 10b0af913db85d37ca27f52a9f78bba3a80030db. SHA of the * same string plus "1" is * 3d01d8df78e76d382b8106f480135a1bc751d725. So the * second half becomes * 10b0af913db85d37ca27f52a9f78bba3a80030db3d01d8df78. */ printf("test 2 encode: %s\n", memcmp(bmp2, "\xb2\x02\xc0\x7b\x99\x0c\x01\xf6\xff\x2d\x54" "\x47\x07\xf6\x0e\x50\x60\x19\xb6\x71\xfc\xb1\xd8" "\xb5\xa2\x10\xb0\xaf\x91\x3d\xb8\x5d\x37\xca\x27" "\xf5\x2a\x9f\x78\xbb\xa3\xa8\x00\x30\xdb\x3d\x01" "\xd8\xdf\x78", 50) ? "failed" : "passed"); obfuscate_bitmap(bmp2, 50 * 8, TRUE); printf("test 2 decode: %s\n", memcmp(bmp2, bmp2a, 50) ? "failed" : "passed"); } } #endif grid = minegen(w, h, n, x, y, unique, rs); if (game_desc) *game_desc = describe_layout(grid, w * h, x, y, TRUE); return grid; } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { /* * We generate the coordinates of an initial click even if they * aren't actually used. This has the effect of harmonising the * random number usage between interactive and batch use: if * you use `mines --generate' with an explicit random seed, you * should get exactly the same results as if you type the same * random seed into the interactive game and click in the same * initial location. (Of course you won't get the same grid if * you click in a _different_ initial location, but there's * nothing to be done about that.) */ int x = random_upto(rs, params->w); int y = random_upto(rs, params->h); if (!interactive) { /* * For batch-generated grids, pre-open one square. */ char *grid; char *desc; grid = new_mine_layout(params->w, params->h, params->n, x, y, params->unique, rs, &desc); sfree(grid); return desc; } else { char *rsdesc, *desc; rsdesc = random_state_encode(rs); desc = snewn(strlen(rsdesc) + 100, char); sprintf(desc, "r%d,%c,%s", params->n, (char)(params->unique ? 'u' : 'a'), rsdesc); sfree(rsdesc); return desc; } } static char *validate_desc(const game_params *params, const char *desc) { int wh = params->w * params->h; int x, y; if (*desc == 'r') { desc++; if (!*desc || !isdigit((unsigned char)*desc)) return "No initial mine count in game description"; while (*desc && isdigit((unsigned char)*desc)) desc++; /* skip over mine count */ if (*desc != ',') return "No ',' after initial x-coordinate in game description"; desc++; if (*desc != 'u' && *desc != 'a') return "No uniqueness specifier in game description"; desc++; if (*desc != ',') return "No ',' after uniqueness specifier in game description"; /* now ignore the rest */ } else { if (*desc && isdigit((unsigned char)*desc)) { x = atoi(desc); if (x < 0 || x >= params->w) return "Initial x-coordinate was out of range"; while (*desc && isdigit((unsigned char)*desc)) desc++; /* skip over x coordinate */ if (*desc != ',') return "No ',' after initial x-coordinate in game description"; desc++; /* eat comma */ if (!*desc || !isdigit((unsigned char)*desc)) return "No initial y-coordinate in game description"; y = atoi(desc); if (y < 0 || y >= params->h) return "Initial y-coordinate was out of range"; while (*desc && isdigit((unsigned char)*desc)) desc++; /* skip over y coordinate */ if (*desc != ',') return "No ',' after initial y-coordinate in game description"; desc++; /* eat comma */ } /* eat `m' for `masked' or `u' for `unmasked', if present */ if (*desc == 'm' || *desc == 'u') desc++; /* now just check length of remainder */ if (strlen(desc) != (wh+3)/4) return "Game description is wrong length"; } return NULL; } static int open_square(game_state *state, int x, int y) { int w = state->w, h = state->h; int xx, yy, nmines, ncovered; if (!state->layout->mines) { /* * We have a preliminary game in which the mine layout * hasn't been generated yet. Generate it based on the * initial click location. */ char *desc, *privdesc; state->layout->mines = new_mine_layout(w, h, state->layout->n, x, y, state->layout->unique, state->layout->rs, &desc); /* * Find the trailing substring of the game description * corresponding to just the mine layout; we will use this * as our second `private' game ID for serialisation. */ privdesc = desc; while (*privdesc && isdigit((unsigned char)*privdesc)) privdesc++; if (*privdesc == ',') privdesc++; while (*privdesc && isdigit((unsigned char)*privdesc)) privdesc++; if (*privdesc == ',') privdesc++; assert(*privdesc == 'm'); midend_supersede_game_desc(state->layout->me, desc, privdesc); sfree(desc); random_free(state->layout->rs); state->layout->rs = NULL; } if (state->layout->mines[y*w+x]) { /* * The player has landed on a mine. Bad luck. Expose the * mine that killed them, but not the rest (in case they * want to Undo and carry on playing). */ state->dead = TRUE; state->grid[y*w+x] = 65; return -1; } /* * Otherwise, the player has opened a safe square. Mark it to-do. */ state->grid[y*w+x] = -10; /* `todo' value internal to this func */ /* * Now go through the grid finding all `todo' values and * opening them. Every time one of them turns out to have no * neighbouring mines, we add all its unopened neighbours to * the list as well. * * FIXME: We really ought to be able to do this better than * using repeated N^2 scans of the grid. */ while (1) { int done_something = FALSE; for (yy = 0; yy < h; yy++) for (xx = 0; xx < w; xx++) if (state->grid[yy*w+xx] == -10) { int dx, dy, v; assert(!state->layout->mines[yy*w+xx]); v = 0; for (dx = -1; dx <= +1; dx++) for (dy = -1; dy <= +1; dy++) if (xx+dx >= 0 && xx+dx < state->w && yy+dy >= 0 && yy+dy < state->h && state->layout->mines[(yy+dy)*w+(xx+dx)]) v++; state->grid[yy*w+xx] = v; if (v == 0) { for (dx = -1; dx <= +1; dx++) for (dy = -1; dy <= +1; dy++) if (xx+dx >= 0 && xx+dx < state->w && yy+dy >= 0 && yy+dy < state->h && state->grid[(yy+dy)*w+(xx+dx)] == -2) state->grid[(yy+dy)*w+(xx+dx)] = -10; } done_something = TRUE; } if (!done_something) break; } /* * Finally, scan the grid and see if exactly as many squares * are still covered as there are mines. If so, set the `won' * flag and fill in mine markers on all covered squares. */ nmines = ncovered = 0; for (yy = 0; yy < h; yy++) for (xx = 0; xx < w; xx++) { if (state->grid[yy*w+xx] < 0) ncovered++; if (state->layout->mines[yy*w+xx]) nmines++; } assert(ncovered >= nmines); if (ncovered == nmines) { for (yy = 0; yy < h; yy++) for (xx = 0; xx < w; xx++) { if (state->grid[yy*w+xx] < 0) state->grid[yy*w+xx] = -1; } state->won = TRUE; } return 0; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { game_state *state = snew(game_state); int i, wh, x, y, masked; unsigned char *bmp; state->w = params->w; state->h = params->h; state->n = params->n; state->dead = state->won = FALSE; state->used_solve = FALSE; wh = state->w * state->h; state->layout = snew(struct mine_layout); memset(state->layout, 0, sizeof(struct mine_layout)); state->layout->refcount = 1; state->grid = snewn(wh, signed char); memset(state->grid, -2, wh); if (*desc == 'r') { desc++; state->layout->n = atoi(desc); while (*desc && isdigit((unsigned char)*desc)) desc++; /* skip over mine count */ if (*desc) desc++; /* eat comma */ if (*desc == 'a') state->layout->unique = FALSE; else state->layout->unique = TRUE; desc++; if (*desc) desc++; /* eat comma */ state->layout->mines = NULL; state->layout->rs = random_state_decode(desc); state->layout->me = me; } else { state->layout->rs = NULL; state->layout->me = NULL; state->layout->mines = snewn(wh, char); if (*desc && isdigit((unsigned char)*desc)) { x = atoi(desc); while (*desc && isdigit((unsigned char)*desc)) desc++; /* skip over x coordinate */ if (*desc) desc++; /* eat comma */ y = atoi(desc); while (*desc && isdigit((unsigned char)*desc)) desc++; /* skip over y coordinate */ if (*desc) desc++; /* eat comma */ } else { x = y = -1; } if (*desc == 'm') { masked = TRUE; desc++; } else { if (*desc == 'u') desc++; /* * We permit game IDs to be entered by hand without the * masking transformation. */ masked = FALSE; } bmp = snewn((wh + 7) / 8, unsigned char); memset(bmp, 0, (wh + 7) / 8); for (i = 0; i < (wh+3)/4; i++) { int c = desc[i]; int v; assert(c != 0); /* validate_desc should have caught */ if (c >= '0' && c <= '9') v = c - '0'; else if (c >= 'a' && c <= 'f') v = c - 'a' + 10; else if (c >= 'A' && c <= 'F') v = c - 'A' + 10; else v = 0; bmp[i / 2] |= v << (4 * (1 - (i % 2))); } if (masked) obfuscate_bitmap(bmp, wh, TRUE); memset(state->layout->mines, 0, wh); for (i = 0; i < wh; i++) { if (bmp[i / 8] & (0x80 >> (i % 8))) state->layout->mines[i] = 1; } if (x >= 0 && y >= 0) open_square(state, x, y); sfree(bmp); } return state; } static game_state *dup_game(const game_state *state) { game_state *ret = snew(game_state); ret->w = state->w; ret->h = state->h; ret->n = state->n; ret->dead = state->dead; ret->won = state->won; ret->used_solve = state->used_solve; ret->layout = state->layout; ret->layout->refcount++; ret->grid = snewn(ret->w * ret->h, signed char); memcpy(ret->grid, state->grid, ret->w * ret->h); return ret; } static void free_game(game_state *state) { if (--state->layout->refcount <= 0) { sfree(state->layout->mines); if (state->layout->rs) random_free(state->layout->rs); sfree(state->layout); } sfree(state->grid); sfree(state); } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { if (!state->layout->mines) { *error = "Game has not been started yet"; return NULL; } return dupstr("S"); } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { char *ret; int x, y; ret = snewn((state->w + 1) * state->h + 1, char); for (y = 0; y < state->h; y++) { for (x = 0; x < state->w; x++) { int v = state->grid[y*state->w+x]; if (v == 0) v = '-'; else if (v >= 1 && v <= 8) v = '0' + v; else if (v == -1) v = '*'; else if (v == -2 || v == -3) v = '?'; else if (v >= 64) v = '!'; ret[y * (state->w+1) + x] = v; } ret[y * (state->w+1) + state->w] = '\n'; } ret[(state->w + 1) * state->h] = '\0'; return ret; } struct game_ui { int hx, hy, hradius; /* for mouse-down highlights */ int validradius; int flash_is_death; int deaths, completed; int cur_x, cur_y, cur_visible; }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->hx = ui->hy = -1; ui->hradius = ui->validradius = 0; ui->deaths = 0; ui->completed = FALSE; ui->flash_is_death = FALSE; /* *shrug* */ ui->cur_x = ui->cur_y = ui->cur_visible = 0; return ui; } static void free_ui(game_ui *ui) { sfree(ui); } static char *encode_ui(const game_ui *ui) { char buf[80]; /* * The deaths counter and completion status need preserving * across a serialisation. */ sprintf(buf, "D%d", ui->deaths); if (ui->completed) strcat(buf, "C"); return dupstr(buf); } static void decode_ui(game_ui *ui, const char *encoding) { int p= 0; sscanf(encoding, "D%d%n", &ui->deaths, &p); if (encoding[p] == 'C') ui->completed = TRUE; } static void game_changed_state(game_ui *ui, const game_state *oldstate, const game_state *newstate) { if (newstate->won) ui->completed = TRUE; } struct game_drawstate { int w, h, started, tilesize, bg; signed char *grid; /* * Items in this `grid' array have all the same values as in * the game_state grid, and in addition: * * - -10 means the tile was drawn `specially' as a result of a * flash, so it will always need redrawing. * * - -22 and -23 mean the tile is highlighted for a possible * click. */ int cur_x, cur_y; /* -1, -1 for no cursor displayed. */ }; static char *interpret_move(const game_state *from, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { int cx, cy; char buf[256]; if (from->dead || from->won) return NULL; /* no further moves permitted */ cx = FROMCOORD(x); cy = FROMCOORD(y); if (IS_CURSOR_MOVE(button)) { move_cursor(button, &ui->cur_x, &ui->cur_y, from->w, from->h, 0); ui->cur_visible = 1; return ""; } if (IS_CURSOR_SELECT(button)) { int v = from->grid[ui->cur_y * from->w + ui->cur_x]; if (!ui->cur_visible) { ui->cur_visible = 1; return ""; } if (button == CURSOR_SELECT2) { /* As for RIGHT_BUTTON; only works on covered square. */ if (v != -2 && v != -1) return NULL; sprintf(buf, "F%d,%d", ui->cur_x, ui->cur_y); return dupstr(buf); } /* Otherwise, treat as LEFT_BUTTON, for a single square. */ if (v == -2 || v == -3) { if (from->layout->mines && from->layout->mines[ui->cur_y * from->w + ui->cur_x]) ui->deaths++; sprintf(buf, "O%d,%d", ui->cur_x, ui->cur_y); return dupstr(buf); } cx = ui->cur_x; cy = ui->cur_y; ui->validradius = 1; goto uncover; } if (button == LEFT_BUTTON || button == LEFT_DRAG || button == MIDDLE_BUTTON || button == MIDDLE_DRAG) { if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h) return NULL; /* * Mouse-downs and mouse-drags just cause highlighting * updates. */ ui->hx = cx; ui->hy = cy; ui->hradius = (from->grid[cy*from->w+cx] >= 0 ? 1 : 0); if (button == LEFT_BUTTON) ui->validradius = ui->hradius; else if (button == MIDDLE_BUTTON) ui->validradius = 1; ui->cur_visible = 0; return ""; } if (button == RIGHT_BUTTON) { if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h) return NULL; /* * Right-clicking only works on a covered square, and it * toggles between -1 (marked as mine) and -2 (not marked * as mine). * * FIXME: question marks. */ if (from->grid[cy * from->w + cx] != -2 && from->grid[cy * from->w + cx] != -1) return NULL; sprintf(buf, "F%d,%d", cx, cy); return dupstr(buf); } if (button == LEFT_RELEASE || button == MIDDLE_RELEASE) { ui->hx = ui->hy = -1; ui->hradius = 0; /* * At this stage we must never return NULL: we have adjusted * the ui, so at worst we return "". */ if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h) return ""; /* * Left-clicking on a covered square opens a tile. Not * permitted if the tile is marked as a mine, for safety. * (Unmark it and _then_ open it.) */ if (button == LEFT_RELEASE && (from->grid[cy * from->w + cx] == -2 || from->grid[cy * from->w + cx] == -3) && ui->validradius == 0) { /* Check if you've killed yourself. */ if (from->layout->mines && from->layout->mines[cy * from->w + cx]) ui->deaths++; sprintf(buf, "O%d,%d", cx, cy); return dupstr(buf); } goto uncover; } return NULL; uncover: { /* * Left-clicking or middle-clicking on an uncovered tile: * first we check to see if the number of mine markers * surrounding the tile is equal to its mine count, and if * so then we open all other surrounding squares. */ if (from->grid[cy * from->w + cx] > 0 && ui->validradius == 1) { int dy, dx, n; /* Count mine markers. */ n = 0; for (dy = -1; dy <= +1; dy++) for (dx = -1; dx <= +1; dx++) if (cx+dx >= 0 && cx+dx < from->w && cy+dy >= 0 && cy+dy < from->h) { if (from->grid[(cy+dy)*from->w+(cx+dx)] == -1) n++; } if (n == from->grid[cy * from->w + cx]) { /* * Now see if any of the squares we're clearing * contains a mine (which will happen iff you've * incorrectly marked the mines around the clicked * square). If so, we open _just_ those squares, to * reveal as little additional information as we * can. */ char *p = buf; char *sep = ""; for (dy = -1; dy <= +1; dy++) for (dx = -1; dx <= +1; dx++) if (cx+dx >= 0 && cx+dx < from->w && cy+dy >= 0 && cy+dy < from->h) { if (from->grid[(cy+dy)*from->w+(cx+dx)] != -1 && from->layout->mines && from->layout->mines[(cy+dy)*from->w+(cx+dx)]) { p += sprintf(p, "%sO%d,%d", sep, cx+dx, cy+dy); sep = ";"; } } if (p > buf) { ui->deaths++; } else { sprintf(buf, "C%d,%d", cx, cy); } return dupstr(buf); } } return ""; } } static game_state *execute_move(const game_state *from, const char *move) { int cy, cx; game_state *ret; if (!strcmp(move, "S")) { int yy, xx; ret = dup_game(from); if (!ret->dead) { /* * If the player is still alive at the moment of pressing * Solve, expose the entire grid as if it were a completed * solution. */ for (yy = 0; yy < ret->h; yy++) for (xx = 0; xx < ret->w; xx++) { if (ret->layout->mines[yy*ret->w+xx]) { ret->grid[yy*ret->w+xx] = -1; } else { int dx, dy, v; v = 0; for (dx = -1; dx <= +1; dx++) for (dy = -1; dy <= +1; dy++) if (xx+dx >= 0 && xx+dx < ret->w && yy+dy >= 0 && yy+dy < ret->h && ret->layout->mines[(yy+dy)*ret->w+(xx+dx)]) v++; ret->grid[yy*ret->w+xx] = v; } } } else { /* * If the player pressed Solve _after dying_, show a full * corrections grid in the style of standard Minesweeper. * Players who don't like Mines's behaviour on death of * only showing the mine that killed you (so that in case * of a typo you can undo and carry on without the rest of * the grid being spoiled) can use this to get the display * that ordinary Minesweeper would have given them. */ for (yy = 0; yy < ret->h; yy++) for (xx = 0; xx < ret->w; xx++) { int pos = yy*ret->w+xx; if ((ret->grid[pos] == -2 || ret->grid[pos] == -3) && ret->layout->mines[pos]) { ret->grid[pos] = 64; } else if (ret->grid[pos] == -1 && !ret->layout->mines[pos]) { ret->grid[pos] = 66; } } } ret->used_solve = TRUE; return ret; } else { ret = dup_game(from); while (*move) { if (move[0] == 'F' && sscanf(move+1, "%d,%d", &cx, &cy) == 2 && cx >= 0 && cx < from->w && cy >= 0 && cy < from->h) { ret->grid[cy * from->w + cx] ^= (-2 ^ -1); } else if (move[0] == 'O' && sscanf(move+1, "%d,%d", &cx, &cy) == 2 && cx >= 0 && cx < from->w && cy >= 0 && cy < from->h) { open_square(ret, cx, cy); } else if (move[0] == 'C' && sscanf(move+1, "%d,%d", &cx, &cy) == 2 && cx >= 0 && cx < from->w && cy >= 0 && cy < from->h) { int dx, dy; for (dy = -1; dy <= +1; dy++) for (dx = -1; dx <= +1; dx++) if (cx+dx >= 0 && cx+dx < ret->w && cy+dy >= 0 && cy+dy < ret->h && (ret->grid[(cy+dy)*ret->w+(cx+dx)] == -2 || ret->grid[(cy+dy)*ret->w+(cx+dx)] == -3)) open_square(ret, cx+dx, cy+dy); } else { free_game(ret); return NULL; } while (*move && *move != ';') move++; if (*move) move++; } 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 = BORDER * 2 + TILE_SIZE * params->w; *y = BORDER * 2 + TILE_SIZE * params->h; } 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_BACKGROUND2 * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 19.0F / 20.0F; ret[COL_BACKGROUND2 * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 19.0F / 20.0F; ret[COL_BACKGROUND2 * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 19.0F / 20.0F; ret[COL_1 * 3 + 0] = 0.0F; ret[COL_1 * 3 + 1] = 0.0F; ret[COL_1 * 3 + 2] = 1.0F; ret[COL_2 * 3 + 0] = 0.0F; ret[COL_2 * 3 + 1] = 0.5F; ret[COL_2 * 3 + 2] = 0.0F; ret[COL_3 * 3 + 0] = 1.0F; ret[COL_3 * 3 + 1] = 0.0F; ret[COL_3 * 3 + 2] = 0.0F; ret[COL_4 * 3 + 0] = 0.0F; ret[COL_4 * 3 + 1] = 0.0F; ret[COL_4 * 3 + 2] = 0.5F; ret[COL_5 * 3 + 0] = 0.5F; ret[COL_5 * 3 + 1] = 0.0F; ret[COL_5 * 3 + 2] = 0.0F; ret[COL_6 * 3 + 0] = 0.0F; ret[COL_6 * 3 + 1] = 0.5F; ret[COL_6 * 3 + 2] = 0.5F; ret[COL_7 * 3 + 0] = 0.0F; ret[COL_7 * 3 + 1] = 0.0F; ret[COL_7 * 3 + 2] = 0.0F; ret[COL_8 * 3 + 0] = 0.5F; ret[COL_8 * 3 + 1] = 0.5F; ret[COL_8 * 3 + 2] = 0.5F; ret[COL_MINE * 3 + 0] = 0.0F; ret[COL_MINE * 3 + 1] = 0.0F; ret[COL_MINE * 3 + 2] = 0.0F; ret[COL_BANG * 3 + 0] = 1.0F; ret[COL_BANG * 3 + 1] = 0.0F; ret[COL_BANG * 3 + 2] = 0.0F; ret[COL_CROSS * 3 + 0] = 1.0F; ret[COL_CROSS * 3 + 1] = 0.0F; ret[COL_CROSS * 3 + 2] = 0.0F; ret[COL_FLAG * 3 + 0] = 1.0F; ret[COL_FLAG * 3 + 1] = 0.0F; ret[COL_FLAG * 3 + 2] = 0.0F; ret[COL_FLAGBASE * 3 + 0] = 0.0F; ret[COL_FLAGBASE * 3 + 1] = 0.0F; ret[COL_FLAGBASE * 3 + 2] = 0.0F; ret[COL_QUERY * 3 + 0] = 0.0F; ret[COL_QUERY * 3 + 1] = 0.0F; ret[COL_QUERY * 3 + 2] = 0.0F; ret[COL_HIGHLIGHT * 3 + 0] = 1.0F; ret[COL_HIGHLIGHT * 3 + 1] = 1.0F; ret[COL_HIGHLIGHT * 3 + 2] = 1.0F; ret[COL_LOWLIGHT * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 2.0F / 3.0F; ret[COL_LOWLIGHT * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 2.0F / 3.0F; ret[COL_LOWLIGHT * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 2.0F / 3.0F; ret[COL_WRONGNUMBER * 3 + 0] = 1.0F; ret[COL_WRONGNUMBER * 3 + 1] = 0.6F; ret[COL_WRONGNUMBER * 3 + 2] = 0.6F; /* Red tinge to a light colour, for the cursor. */ ret[COL_CURSOR * 3 + 0] = ret[COL_HIGHLIGHT * 3 + 0]; ret[COL_CURSOR * 3 + 1] = ret[COL_HIGHLIGHT * 3 + 0] / 2.0F; ret[COL_CURSOR * 3 + 2] = ret[COL_HIGHLIGHT * 3 + 0] / 2.0F; *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->w = state->w; ds->h = state->h; ds->started = FALSE; ds->tilesize = 0; /* not decided yet */ ds->grid = snewn(ds->w * ds->h, signed char); ds->bg = -1; ds->cur_x = ds->cur_y = -1; memset(ds->grid, -99, ds->w * ds->h); 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 bg) { if (v < 0) { int coords[12]; int hl = 0; if (v == -22 || v == -23) { v += 20; /* * Omit the highlights in this case. */ draw_rect(dr, x, y, TILE_SIZE, TILE_SIZE, bg == COL_BACKGROUND ? COL_BACKGROUND2 : bg); draw_line(dr, x, y, x + TILE_SIZE - 1, y, COL_LOWLIGHT); draw_line(dr, x, y, x, y + TILE_SIZE - 1, COL_LOWLIGHT); } else { /* * Draw highlights to indicate the square is covered. */ coords[0] = x + TILE_SIZE - 1; coords[1] = y + TILE_SIZE - 1; coords[2] = x + TILE_SIZE - 1; coords[3] = y; coords[4] = x; coords[5] = y + TILE_SIZE - 1; draw_polygon(dr, coords, 3, COL_LOWLIGHT ^ hl, COL_LOWLIGHT ^ hl); coords[0] = x; coords[1] = y; draw_polygon(dr, coords, 3, COL_HIGHLIGHT ^ hl, COL_HIGHLIGHT ^ hl); draw_rect(dr, x + HIGHLIGHT_WIDTH, y + HIGHLIGHT_WIDTH, TILE_SIZE - 2*HIGHLIGHT_WIDTH, TILE_SIZE - 2*HIGHLIGHT_WIDTH, bg); } if (v == -1) { /* * Draw a flag. */ #define SETCOORD(n, dx, dy) do { \ coords[(n)*2+0] = x + (int)(TILE_SIZE * (dx)); \ coords[(n)*2+1] = y + (int)(TILE_SIZE * (dy)); \ } while (0) SETCOORD(0, 0.6F, 0.35F); SETCOORD(1, 0.6F, 0.7F); SETCOORD(2, 0.8F, 0.8F); SETCOORD(3, 0.25F, 0.8F); SETCOORD(4, 0.55F, 0.7F); SETCOORD(5, 0.55F, 0.35F); draw_polygon(dr, coords, 6, COL_FLAGBASE, COL_FLAGBASE); SETCOORD(0, 0.6F, 0.2F); SETCOORD(1, 0.6F, 0.5F); SETCOORD(2, 0.2F, 0.35F); draw_polygon(dr, coords, 3, COL_FLAG, COL_FLAG); #undef SETCOORD } else if (v == -3) { /* * Draw a question mark. */ draw_text(dr, x + TILE_SIZE / 2, y + TILE_SIZE / 2, FONT_VARIABLE, TILE_SIZE * 6 / 8, ALIGN_VCENTRE | ALIGN_HCENTRE, COL_QUERY, "?"); } } else { /* * Clear the square to the background colour, and draw thin * grid lines along the top and left. * * Exception is that for value 65 (mine we've just trodden * on), we clear the square to COL_BANG. */ if (v & 32) { bg = COL_WRONGNUMBER; v &= ~32; } draw_rect(dr, x, y, TILE_SIZE, TILE_SIZE, (v == 65 ? COL_BANG : bg == COL_BACKGROUND ? COL_BACKGROUND2 : bg)); draw_line(dr, x, y, x + TILE_SIZE - 1, y, COL_LOWLIGHT); draw_line(dr, x, y, x, y + TILE_SIZE - 1, COL_LOWLIGHT); if (v > 0 && v <= 8) { /* * Mark a number. */ char str[2]; str[0] = v + '0'; str[1] = '\0'; draw_text(dr, x + TILE_SIZE / 2, y + TILE_SIZE / 2, FONT_VARIABLE, TILE_SIZE * 7 / 8, ALIGN_VCENTRE | ALIGN_HCENTRE, (COL_1 - 1) + v, str); } else if (v >= 64) { /* * Mark a mine. */ { int cx = x + TILE_SIZE / 2; int cy = y + TILE_SIZE / 2; int r = TILE_SIZE / 2 - 3; draw_circle(dr, cx, cy, 5*r/6, COL_MINE, COL_MINE); draw_rect(dr, cx - r/6, cy - r, 2*(r/6)+1, 2*r+1, COL_MINE); draw_rect(dr, cx - r, cy - r/6, 2*r+1, 2*(r/6)+1, COL_MINE); draw_rect(dr, cx-r/3, cy-r/3, r/3, r/4, COL_HIGHLIGHT); } if (v == 66) { /* * Cross through the mine. */ int dx; for (dx = -1; dx <= +1; dx++) { draw_line(dr, x + 3 + dx, y + 2, x + TILE_SIZE - 3 + dx, y + TILE_SIZE - 2, COL_CROSS); draw_line(dr, x + TILE_SIZE - 3 + dx, y + 2, x + 3 + dx, y + TILE_SIZE - 2, COL_CROSS); } } } } draw_update(dr, x, y, TILE_SIZE, TILE_SIZE); } 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 x, y; int mines, markers, bg; int cx = -1, cy = -1, cmoved; if (flashtime) { int frame = (int)(flashtime / FLASH_FRAME); if (frame % 2) bg = (ui->flash_is_death ? COL_BACKGROUND : COL_LOWLIGHT); else bg = (ui->flash_is_death ? COL_BANG : COL_HIGHLIGHT); } else bg = COL_BACKGROUND; if (!ds->started) { int coords[10]; draw_rect(dr, 0, 0, TILE_SIZE * state->w + 2 * BORDER, TILE_SIZE * state->h + 2 * BORDER, COL_BACKGROUND); draw_update(dr, 0, 0, TILE_SIZE * state->w + 2 * BORDER, TILE_SIZE * state->h + 2 * BORDER); /* * Recessed area containing the whole puzzle. */ coords[0] = COORD(state->w) + OUTER_HIGHLIGHT_WIDTH - 1; coords[1] = COORD(state->h) + OUTER_HIGHLIGHT_WIDTH - 1; coords[2] = COORD(state->w) + OUTER_HIGHLIGHT_WIDTH - 1; coords[3] = COORD(0) - OUTER_HIGHLIGHT_WIDTH; coords[4] = coords[2] - TILE_SIZE; coords[5] = coords[3] + TILE_SIZE; coords[8] = COORD(0) - OUTER_HIGHLIGHT_WIDTH; coords[9] = COORD(state->h) + OUTER_HIGHLIGHT_WIDTH - 1; coords[6] = coords[8] + TILE_SIZE; coords[7] = coords[9] - TILE_SIZE; draw_polygon(dr, coords, 5, COL_HIGHLIGHT, COL_HIGHLIGHT); coords[1] = COORD(0) - OUTER_HIGHLIGHT_WIDTH; coords[0] = COORD(0) - OUTER_HIGHLIGHT_WIDTH; draw_polygon(dr, coords, 5, COL_LOWLIGHT, COL_LOWLIGHT); ds->started = TRUE; } if (ui->cur_visible) cx = ui->cur_x; if (ui->cur_visible) cy = ui->cur_y; cmoved = (cx != ds->cur_x || cy != ds->cur_y); /* * Now draw the tiles. Also in this loop, count up the number * of mines and mine markers. */ mines = markers = 0; for (y = 0; y < ds->h; y++) for (x = 0; x < ds->w; x++) { int v = state->grid[y*ds->w+x], cc = 0; if (v == -1) markers++; if (state->layout->mines && state->layout->mines[y*ds->w+x]) mines++; if (v >= 0 && v <= 8) { /* * Count up the flags around this tile, and if * there are too _many_, highlight the tile. */ int dx, dy, flags = 0; for (dy = -1; dy <= +1; dy++) for (dx = -1; dx <= +1; dx++) { int nx = x+dx, ny = y+dy; if (nx >= 0 && nx < ds->w && ny >= 0 && ny < ds->h && state->grid[ny*ds->w+nx] == -1) flags++; } if (flags > v) v |= 32; } if ((v == -2 || v == -3) && (abs(x-ui->hx) <= ui->hradius && abs(y-ui->hy) <= ui->hradius)) v -= 20; if (cmoved && /* if cursor has moved, force redraw of curr and prev pos */ ((x == cx && y == cy) || (x == ds->cur_x && y == ds->cur_y))) cc = 1; if (ds->grid[y*ds->w+x] != v || bg != ds->bg || cc) { draw_tile(dr, ds, COORD(x), COORD(y), v, (x == cx && y == cy) ? COL_CURSOR : bg); ds->grid[y*ds->w+x] = v; } } ds->bg = bg; ds->cur_x = cx; ds->cur_y = cy; if (!state->layout->mines) mines = state->layout->n; /* * Update the status bar. */ { char statusbar[512]; if (state->dead) { sprintf(statusbar, "DEAD!"); } else if (state->won) { if (state->used_solve) sprintf(statusbar, "Auto-solved."); else sprintf(statusbar, "COMPLETED!"); } else { sprintf(statusbar, "Marked: %d / %d", markers, mines); } if (ui->deaths) sprintf(statusbar + strlen(statusbar), " Deaths: %d", ui->deaths); status_bar(dr, statusbar); } } 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->used_solve || newstate->used_solve) return 0.0F; if (dir > 0 && !oldstate->dead && !oldstate->won) { if (newstate->dead) { ui->flash_is_death = TRUE; return 3 * FLASH_FRAME; } if (newstate->won) { ui->flash_is_death = FALSE; return 2 * FLASH_FRAME; } } return 0.0F; } static int game_status(const game_state *state) { /* * We report the game as lost only if the player has used the * Solve function to reveal all the mines. Otherwise, we assume * they'll undo and continue play. */ return state->won ? (state->used_solve ? -1 : +1) : 0; } static int game_timing_state(const game_state *state, game_ui *ui) { if (state->dead || state->won || ui->completed || !state->layout->mines) return FALSE; 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 mines #endif const struct game thegame = { "Mines", "games.mines", "mines", default_params, game_fetch_preset, 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, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, FALSE, FALSE, game_print_size, game_print, TRUE, /* wants_statusbar */ TRUE, game_timing_state, BUTTON_BEATS(LEFT_BUTTON, RIGHT_BUTTON) | REQUIRE_RBUTTON, }; #ifdef STANDALONE_OBFUSCATOR /* * Vaguely useful stand-alone program which translates between * obfuscated and clear Mines game descriptions. Pass in a game * description on the command line, and if it's clear it will be * obfuscated and vice versa. The output text should also be a * valid game ID describing the same game. Like this: * * $ ./mineobfusc 9x9:4,4,mb071b49fbd1cb6a0d5868 * 9x9:4,4,004000007c00010022080 * $ ./mineobfusc 9x9:4,4,004000007c00010022080 * 9x9:4,4,mb071b49fbd1cb6a0d5868 */ int main(int argc, char **argv) { game_params *p; game_state *s; char *id = NULL, *desc, *err; int y, x; while (--argc > 0) { char *p = *++argv; if (*p == '-') { fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p); return 1; } else { id = p; } } if (!id) { fprintf(stderr, "usage: %s \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); x = atoi(desc); while (*desc && *desc != ',') desc++; if (*desc) desc++; y = atoi(desc); while (*desc && *desc != ',') desc++; if (*desc) desc++; printf("%s:%s\n", id, describe_layout(s->layout->mines, p->w * p->h, x, y, (*desc != 'm'))); return 0; } #endif /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/misc.c0000644000175300017530000002521111365054120013522 0ustar simonsimon/* * misc.c: Miscellaneous helpful functions. */ #include #include #include #include #include "puzzles.h" void free_cfg(config_item *cfg) { config_item *i; for (i = cfg; i->type != C_END; i++) if (i->type == C_STRING) sfree(i->sval); sfree(cfg); } /* * The Mines (among others) game descriptions contain the location of every * mine, and can therefore be used to cheat. * * It would be pointless to attempt to _prevent_ this form of * cheating by encrypting the description, since Mines is * open-source so anyone can find out the encryption key. However, * I think it is worth doing a bit of gentle obfuscation to prevent * _accidental_ spoilers: if you happened to note that the game ID * starts with an F, for example, you might be unable to put the * knowledge of those mines out of your mind while playing. So, * just as discussions of film endings are rot13ed to avoid * spoiling it for people who don't want to be told, we apply a * keyless, reversible, but visually completely obfuscatory masking * function to the mine bitmap. */ void obfuscate_bitmap(unsigned char *bmp, int bits, int decode) { int bytes, firsthalf, secondhalf; struct step { unsigned char *seedstart; int seedlen; unsigned char *targetstart; int targetlen; } steps[2]; int i, j; /* * My obfuscation algorithm is similar in concept to the OAEP * encoding used in some forms of RSA. Here's a specification * of it: * * + We have a `masking function' which constructs a stream of * pseudorandom bytes from a seed of some number of input * bytes. * * + We pad out our input bit stream to a whole number of * bytes by adding up to 7 zero bits on the end. (In fact * the bitmap passed as input to this function will already * have had this done in practice.) * * + We divide the _byte_ stream exactly in half, rounding the * half-way position _down_. So an 81-bit input string, for * example, rounds up to 88 bits or 11 bytes, and then * dividing by two gives 5 bytes in the first half and 6 in * the second half. * * + We generate a mask from the second half of the bytes, and * XOR it over the first half. * * + We generate a mask from the (encoded) first half of the * bytes, and XOR it over the second half. Any null bits at * the end which were added as padding are cleared back to * zero even if this operation would have made them nonzero. * * To de-obfuscate, the steps are precisely the same except * that the final two are reversed. * * Finally, our masking function. Given an input seed string of * bytes, the output mask consists of concatenating the SHA-1 * hashes of the seed string and successive decimal integers, * starting from 0. */ bytes = (bits + 7) / 8; firsthalf = bytes / 2; secondhalf = bytes - firsthalf; steps[decode ? 1 : 0].seedstart = bmp + firsthalf; steps[decode ? 1 : 0].seedlen = secondhalf; steps[decode ? 1 : 0].targetstart = bmp; steps[decode ? 1 : 0].targetlen = firsthalf; steps[decode ? 0 : 1].seedstart = bmp; steps[decode ? 0 : 1].seedlen = firsthalf; steps[decode ? 0 : 1].targetstart = bmp + firsthalf; steps[decode ? 0 : 1].targetlen = secondhalf; for (i = 0; i < 2; i++) { SHA_State base, final; unsigned char digest[20]; char numberbuf[80]; int digestpos = 20, counter = 0; SHA_Init(&base); SHA_Bytes(&base, steps[i].seedstart, steps[i].seedlen); for (j = 0; j < steps[i].targetlen; j++) { if (digestpos >= 20) { sprintf(numberbuf, "%d", counter++); final = base; SHA_Bytes(&final, numberbuf, strlen(numberbuf)); SHA_Final(&final, digest); digestpos = 0; } steps[i].targetstart[j] ^= digest[digestpos++]; } /* * Mask off the pad bits in the final byte after both steps. */ if (bits % 8) bmp[bits / 8] &= 0xFF & (0xFF00 >> (bits % 8)); } } /* err, yeah, these two pretty much rely on unsigned char being 8 bits. * Platforms where this is not the case probably have bigger problems * than just making these two work, though... */ char *bin2hex(const unsigned char *in, int inlen) { char *ret = snewn(inlen*2 + 1, char), *p = ret; int i; for (i = 0; i < inlen*2; i++) { int v = in[i/2]; if (i % 2 == 0) v >>= 4; *p++ = "0123456789abcdef"[v & 0xF]; } *p = '\0'; return ret; } unsigned char *hex2bin(const char *in, int outlen) { unsigned char *ret = snewn(outlen, unsigned char); int i; memset(ret, 0, outlen*sizeof(unsigned char)); for (i = 0; i < outlen*2; i++) { int c = in[i]; int v; assert(c != 0); if (c >= '0' && c <= '9') v = c - '0'; else if (c >= 'a' && c <= 'f') v = c - 'a' + 10; else if (c >= 'A' && c <= 'F') v = c - 'A' + 10; else v = 0; ret[i / 2] |= v << (4 * (1 - (i % 2))); } return ret; } void game_mkhighlight_specific(frontend *fe, float *ret, int background, int highlight, int lowlight) { float max; int i; /* * Drop the background colour so that the highlight is * noticeably brighter than it while still being under 1. */ max = ret[background*3]; for (i = 1; i < 3; i++) if (ret[background*3+i] > max) max = ret[background*3+i]; if (max * 1.2F > 1.0F) { for (i = 0; i < 3; i++) ret[background*3+i] /= (max * 1.2F); } for (i = 0; i < 3; i++) { if (highlight >= 0) ret[highlight * 3 + i] = ret[background * 3 + i] * 1.2F; if (lowlight >= 0) ret[lowlight * 3 + i] = ret[background * 3 + i] * 0.8F; } } void game_mkhighlight(frontend *fe, float *ret, int background, int highlight, int lowlight) { frontend_default_colour(fe, &ret[background * 3]); game_mkhighlight_specific(fe, ret, background, highlight, lowlight); } static void memswap(void *av, void *bv, int size) { char tmpbuf[512]; char *a = av, *b = bv; while (size > 0) { int thislen = min(size, sizeof(tmpbuf)); memcpy(tmpbuf, a, thislen); memcpy(a, b, thislen); memcpy(b, tmpbuf, thislen); a += thislen; b += thislen; size -= thislen; } } void shuffle(void *array, int nelts, int eltsize, random_state *rs) { char *carray = (char *)array; int i; for (i = nelts; i-- > 1 ;) { int j = random_upto(rs, i+1); if (j != i) memswap(carray + eltsize * i, carray + eltsize * j, eltsize); } } void draw_rect_outline(drawing *dr, int x, int y, int w, int h, int colour) { int x0 = x, x1 = x+w-1, y0 = y, y1 = y+h-1; int coords[8]; coords[0] = x0; coords[1] = y0; coords[2] = x0; coords[3] = y1; coords[4] = x1; coords[5] = y1; coords[6] = x1; coords[7] = y0; draw_polygon(dr, coords, 4, -1, colour); } void draw_rect_corners(drawing *dr, int cx, int cy, int r, int col) { draw_line(dr, cx - r, cy - r, cx - r, cy - r/2, col); draw_line(dr, cx - r, cy - r, cx - r/2, cy - r, col); draw_line(dr, cx - r, cy + r, cx - r, cy + r/2, col); draw_line(dr, cx - r, cy + r, cx - r/2, cy + r, col); draw_line(dr, cx + r, cy - r, cx + r, cy - r/2, col); draw_line(dr, cx + r, cy - r, cx + r/2, cy - r, col); draw_line(dr, cx + r, cy + r, cx + r, cy + r/2, col); draw_line(dr, cx + r, cy + r, cx + r/2, cy + r, col); } void move_cursor(int button, int *x, int *y, int maxw, int maxh, int wrap) { int dx = 0, dy = 0; switch (button) { case CURSOR_UP: dy = -1; break; case CURSOR_DOWN: dy = 1; break; case CURSOR_RIGHT: dx = 1; break; case CURSOR_LEFT: dx = -1; break; default: return; } if (wrap) { *x = (*x + dx + maxw) % maxw; *y = (*y + dy + maxh) % maxh; } else { *x = min(max(*x+dx, 0), maxw - 1); *y = min(max(*y+dy, 0), maxh - 1); } } /* Used in netslide.c and sixteen.c for cursor movement around edge. */ int c2pos(int w, int h, int cx, int cy) { if (cy == -1) return cx; /* top row, 0 .. w-1 (->) */ else if (cx == w) return w + cy; /* R col, w .. w+h -1 (v) */ else if (cy == h) return w + h + (w-cx-1); /* bottom row, w+h .. w+h+w-1 (<-) */ else if (cx == -1) return w + h + w + (h-cy-1); /* L col, w+h+w .. w+h+w+h-1 (^) */ assert(!"invalid cursor pos!"); return -1; /* not reached */ } int c2diff(int w, int h, int cx, int cy, int button) { int diff = 0; assert(IS_CURSOR_MOVE(button)); /* Obvious moves around edge. */ if (cy == -1) diff = (button == CURSOR_RIGHT) ? +1 : (button == CURSOR_LEFT) ? -1 : diff; if (cy == h) diff = (button == CURSOR_RIGHT) ? -1 : (button == CURSOR_LEFT) ? +1 : diff; if (cx == -1) diff = (button == CURSOR_UP) ? +1 : (button == CURSOR_DOWN) ? -1 : diff; if (cx == w) diff = (button == CURSOR_UP) ? -1 : (button == CURSOR_DOWN) ? +1 : diff; if (button == CURSOR_LEFT && cx == w && (cy == 0 || cy == h-1)) diff = (cy == 0) ? -1 : +1; if (button == CURSOR_RIGHT && cx == -1 && (cy == 0 || cy == h-1)) diff = (cy == 0) ? +1 : -1; if (button == CURSOR_DOWN && cy == -1 && (cx == 0 || cx == w-1)) diff = (cx == 0) ? -1 : +1; if (button == CURSOR_UP && cy == h && (cx == 0 || cx == w-1)) diff = (cx == 0) ? +1 : -1; debug(("cx,cy = %d,%d; w%d h%d, diff = %d", cx, cy, w, h, diff)); return diff; } void pos2c(int w, int h, int pos, int *cx, int *cy) { int max = w+h+w+h; pos = (pos + max) % max; if (pos < w) { *cx = pos; *cy = -1; return; } pos -= w; if (pos < h) { *cx = w; *cy = pos; return; } pos -= h; if (pos < w) { *cx = w-pos-1; *cy = h; return; } pos -= w; if (pos < h) { *cx = -1; *cy = h-pos-1; return; } assert(!"invalid pos, huh?"); /* limited by % above! */ } void draw_text_outline(drawing *dr, int x, int y, int fonttype, int fontsize, int align, int text_colour, int outline_colour, char *text) { if (outline_colour > -1) { draw_text(dr, x-1, y, fonttype, fontsize, align, outline_colour, text); draw_text(dr, x+1, y, fonttype, fontsize, align, outline_colour, text); draw_text(dr, x, y-1, fonttype, fontsize, align, outline_colour, text); draw_text(dr, x, y+1, fonttype, fontsize, align, outline_colour, text); } draw_text(dr, x, y, fonttype, fontsize, align, text_colour, text); } /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/nestedvm.c0000644000175300017530000002342011465315421014421 0ustar simonsimon/* * nestedvm.c: NestedVM front end for my puzzle collection. */ #include #include #include #include #include #include #include #include #include "puzzles.h" extern void _pause(); extern int _call_java(int cmd, int arg1, int arg2, int arg3); void fatal(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); } struct frontend { // TODO kill unneeded members! midend *me; int timer_active; struct timeval last_time; config_item *cfg; int cfg_which, cfgret; int ox, oy, w, h; }; static frontend *_fe; void get_random_seed(void **randseed, int *randseedsize) { struct timeval *tvp = snew(struct timeval); gettimeofday(tvp, NULL); *randseed = (void *)tvp; *randseedsize = sizeof(struct timeval); } void frontend_default_colour(frontend *fe, float *output) { output[0] = output[1]= output[2] = 0.8f; } void nestedvm_status_bar(void *handle, char *text) { _call_java(4,0,(int)text,0); } void nestedvm_start_draw(void *handle) { frontend *fe = (frontend *)handle; _call_java(5, 0, fe->w, fe->h); _call_java(4, 1, fe->ox, fe->oy); } void nestedvm_clip(void *handle, int x, int y, int w, int h) { frontend *fe = (frontend *)handle; _call_java(5, w, h, 0); _call_java(4, 3, x + fe->ox, y + fe->oy); } void nestedvm_unclip(void *handle) { frontend *fe = (frontend *)handle; _call_java(4, 4, fe->ox, fe->oy); } void nestedvm_draw_text(void *handle, int x, int y, int fonttype, int fontsize, int align, int colour, char *text) { frontend *fe = (frontend *)handle; _call_java(5, x + fe->ox, y + fe->oy, (fonttype == FONT_FIXED ? 0x10 : 0x0) | align); _call_java(7, fontsize, colour, (int)text); } void nestedvm_draw_rect(void *handle, int x, int y, int w, int h, int colour) { frontend *fe = (frontend *)handle; _call_java(5, w, h, colour); _call_java(4, 5, x + fe->ox, y + fe->oy); } void nestedvm_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour) { frontend *fe = (frontend *)handle; _call_java(5, x2 + fe->ox, y2 + fe->oy, colour); _call_java(4, 6, x1 + fe->ox, y1 + fe->oy); } void nestedvm_draw_poly(void *handle, int *coords, int npoints, int fillcolour, int outlinecolour) { frontend *fe = (frontend *)handle; int i; _call_java(4, 7, npoints, 0); for (i = 0; i < npoints; i++) { _call_java(6, i, coords[i*2] + fe->ox, coords[i*2+1] + fe->oy); } _call_java(4, 8, outlinecolour, fillcolour); } void nestedvm_draw_circle(void *handle, int cx, int cy, int radius, int fillcolour, int outlinecolour) { frontend *fe = (frontend *)handle; _call_java(5, cx+fe->ox, cy+fe->oy, radius); _call_java(4, 9, outlinecolour, fillcolour); } struct blitter { int handle, w, h, x, y; }; blitter *nestedvm_blitter_new(void *handle, int w, int h) { blitter *bl = snew(blitter); bl->handle = -1; bl->w = w; bl->h = h; return bl; } void nestedvm_blitter_free(void *handle, blitter *bl) { if (bl->handle != -1) _call_java(4, 11, bl->handle, 0); sfree(bl); } void nestedvm_blitter_save(void *handle, blitter *bl, int x, int y) { frontend *fe = (frontend *)handle; if (bl->handle == -1) bl->handle = _call_java(4,10,bl->w, bl->h); bl->x = x; bl->y = y; _call_java(8, bl->handle, x + fe->ox, y + fe->oy); } void nestedvm_blitter_load(void *handle, blitter *bl, int x, int y) { frontend *fe = (frontend *)handle; assert(bl->handle != -1); if (x == BLITTER_FROMSAVED && y == BLITTER_FROMSAVED) { x = bl->x; y = bl->y; } _call_java(9, bl->handle, x + fe->ox, y + fe->oy); } void nestedvm_end_draw(void *handle) { _call_java(4,2,0,0); } char *nestedvm_text_fallback(void *handle, const char *const *strings, int nstrings) { /* * We assume Java can cope with any UTF-8 likely to be emitted * by a puzzle. */ return dupstr(strings[0]); } const struct drawing_api nestedvm_drawing = { nestedvm_draw_text, nestedvm_draw_rect, nestedvm_draw_line, nestedvm_draw_poly, nestedvm_draw_circle, NULL, // draw_update, nestedvm_clip, nestedvm_unclip, nestedvm_start_draw, nestedvm_end_draw, nestedvm_status_bar, nestedvm_blitter_new, nestedvm_blitter_free, nestedvm_blitter_save, nestedvm_blitter_load, NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */ NULL, NULL, /* line_width, line_dotted */ nestedvm_text_fallback, }; int jcallback_key_event(int x, int y, int keyval) { frontend *fe = (frontend *)_fe; if (fe->ox == -1) return 1; if (keyval >= 0 && !midend_process_key(fe->me, x - fe->ox, y - fe->oy, keyval)) return 42; return 1; } int jcallback_resize(int width, int height) { frontend *fe = (frontend *)_fe; int x, y; x = width; y = height; midend_size(fe->me, &x, &y, TRUE); fe->ox = (width - x) / 2; fe->oy = (height - y) / 2; fe->w = x; fe->h = y; midend_force_redraw(fe->me); return 0; } int jcallback_timer_func() { frontend *fe = (frontend *)_fe; if (fe->timer_active) { struct timeval now; float elapsed; gettimeofday(&now, NULL); elapsed = ((now.tv_usec - fe->last_time.tv_usec) * 0.000001F + (now.tv_sec - fe->last_time.tv_sec)); midend_timer(fe->me, elapsed); /* may clear timer_active */ fe->last_time = now; } return fe->timer_active; } void deactivate_timer(frontend *fe) { if (fe->timer_active) _call_java(4, 13, 0, 0); fe->timer_active = FALSE; } void activate_timer(frontend *fe) { if (!fe->timer_active) { _call_java(4, 12, 0, 0); gettimeofday(&fe->last_time, NULL); } fe->timer_active = TRUE; } void jcallback_config_ok() { frontend *fe = (frontend *)_fe; char *err; err = midend_set_config(fe->me, fe->cfg_which, fe->cfg); if (err) _call_java(2, (int) "Error", (int)err, 1); else { fe->cfgret = TRUE; } } void jcallback_config_set_string(int item_ptr, int char_ptr) { config_item *i = (config_item *)item_ptr; char* newval = (char*) char_ptr; sfree(i->sval); i->sval = dupstr(newval); free(newval); } void jcallback_config_set_boolean(int item_ptr, int selected) { config_item *i = (config_item *)item_ptr; i->ival = selected != 0 ? TRUE : FALSE; } void jcallback_config_set_choice(int item_ptr, int selected) { config_item *i = (config_item *)item_ptr; i->ival = selected; } static int get_config(frontend *fe, int which) { char *title; config_item *i; fe->cfg = midend_get_config(fe->me, which, &title); fe->cfg_which = which; fe->cfgret = FALSE; _call_java(10, (int)title, 0, 0); for (i = fe->cfg; i->type != C_END; i++) { _call_java(5, (int)i, i->type, (int)i->name); _call_java(11, (int)i->sval, i->ival, 0); } _call_java(12,0,0,0); free_cfg(fe->cfg); return fe->cfgret; } int jcallback_menu_key_event(int key) { frontend *fe = (frontend *)_fe; if (!midend_process_key(fe->me, 0, 0, key)) return 42; return 0; } static void resize_fe(frontend *fe) { int x, y; x = INT_MAX; y = INT_MAX; midend_size(fe->me, &x, &y, FALSE); _call_java(3, x, y, 0); } int jcallback_preset_event(int ptr_game_params) { frontend *fe = (frontend *)_fe; game_params *params = (game_params *)ptr_game_params; midend_set_params(fe->me, params); midend_new_game(fe->me); resize_fe(fe); _call_java(13, midend_which_preset(fe->me), 0, 0); return 0; } int jcallback_solve_event() { frontend *fe = (frontend *)_fe; char *msg; msg = midend_solve(fe->me); if (msg) _call_java(2, (int) "Error", (int)msg, 1); return 0; } int jcallback_restart_event() { frontend *fe = (frontend *)_fe; midend_restart_game(fe->me); return 0; } int jcallback_config_event(int which) { frontend *fe = (frontend *)_fe; _call_java(13, midend_which_preset(fe->me), 0, 0); if (!get_config(fe, which)) return 0; midend_new_game(fe->me); resize_fe(fe); _call_java(13, midend_which_preset(fe->me), 0, 0); return 0; } int jcallback_about_event() { char titlebuf[256]; char textbuf[1024]; sprintf(titlebuf, "About %.200s", thegame.name); sprintf(textbuf, "%.200s\n\n" "from Simon Tatham's Portable Puzzle Collection\n\n" "%.500s", thegame.name, ver); _call_java(2, (int)&titlebuf, (int)&textbuf, 0); return 0; } int main(int argc, char **argv) { int i, n; float* colours; _fe = snew(frontend); _fe->timer_active = FALSE; _fe->me = midend_new(_fe, &thegame, &nestedvm_drawing, _fe); if (argc > 1) midend_game_id(_fe->me, argv[1]); /* ignore failure */ midend_new_game(_fe->me); if ((n = midend_num_presets(_fe->me)) > 0) { int i; for (i = 0; i < n; i++) { char *name; game_params *params; midend_fetch_preset(_fe->me, i, &name, ¶ms); _call_java(1, (int)name, (int)params, 0); } } colours = midend_colours(_fe->me, &n); _fe->ox = -1; _call_java(0, (int)thegame.name, (thegame.can_configure ? 1 : 0) | (midend_wants_statusbar(_fe->me) ? 2 : 0) | (thegame.can_solve ? 4 : 0), n); for (i = 0; i < n; i++) { _call_java(1024+ i, (int)(colours[i*3] * 0xFF), (int)(colours[i*3+1] * 0xFF), (int)(colours[i*3+2] * 0xFF)); } resize_fe(_fe); _call_java(13, midend_which_preset(_fe->me), 0, 0); // Now pause the vm. The VM will be call()ed when // an input event occurs. _pause(); // shut down when the VM is resumed. deactivate_timer(_fe); midend_free(_fe->me); return 0; } puzzles-r9872/net.c0000644000175300017530000026202312132232554013363 0ustar simonsimon/* * net.c: Net game. */ #include #include #include #include #include #include #include "puzzles.h" #include "tree234.h" /* * The standard user interface for Net simply has left- and * right-button mouse clicks in a square rotate it one way or the * other. We also provide, by #ifdef, a separate interface based on * rotational dragging motions. I initially developed this for the * Mac on the basis that it might work better than the click * interface with only one mouse button available, but in fact * found it to be quite strange and unintuitive. Apparently it * works better on stylus-driven platforms such as Palm and * PocketPC, though, so we enable it by default there. */ #ifdef STYLUS_BASED #define USE_DRAGGING #endif #define MATMUL(xr,yr,m,x,y) do { \ float rx, ry, xx = (x), yy = (y), *mat = (m); \ rx = mat[0] * xx + mat[2] * yy; \ ry = mat[1] * xx + mat[3] * yy; \ (xr) = rx; (yr) = ry; \ } while (0) /* Direction and other bitfields */ #define R 0x01 #define U 0x02 #define L 0x04 #define D 0x08 #define LOCKED 0x10 #define ACTIVE 0x20 /* Rotations: Anticlockwise, Clockwise, Flip, general rotate */ #define A(x) ( (((x) & 0x07) << 1) | (((x) & 0x08) >> 3) ) #define C(x) ( (((x) & 0x0E) >> 1) | (((x) & 0x01) << 3) ) #define F(x) ( (((x) & 0x0C) >> 2) | (((x) & 0x03) << 2) ) #define ROT(x, n) ( ((n)&3) == 0 ? (x) : \ ((n)&3) == 1 ? A(x) : \ ((n)&3) == 2 ? F(x) : C(x) ) /* X and Y displacements */ #define X(x) ( (x) == R ? +1 : (x) == L ? -1 : 0 ) #define Y(x) ( (x) == D ? +1 : (x) == U ? -1 : 0 ) /* Bit count */ #define COUNT(x) ( (((x) & 0x08) >> 3) + (((x) & 0x04) >> 2) + \ (((x) & 0x02) >> 1) + ((x) & 0x01) ) #define PREFERRED_TILE_SIZE 32 #define TILE_SIZE (ds->tilesize) #define TILE_BORDER 1 #ifdef SMALL_SCREEN #define WINDOW_OFFSET 4 #else #define WINDOW_OFFSET 16 #endif #define ROTATE_TIME 0.13F #define FLASH_FRAME 0.07F /* Transform physical coords to game coords using game_drawstate ds */ #define GX(x) (((x) + ds->org_x) % ds->width) #define GY(y) (((y) + ds->org_y) % ds->height) /* ...and game coords to physical coords */ #define RX(x) (((x) + ds->width - ds->org_x) % ds->width) #define RY(y) (((y) + ds->height - ds->org_y) % ds->height) enum { COL_BACKGROUND, COL_LOCKED, COL_BORDER, COL_WIRE, COL_ENDPOINT, COL_POWERED, COL_BARRIER, NCOLOURS }; struct game_params { int width; int height; int wrapping; int unique; float barrier_probability; }; struct game_state { int width, height, wrapping, completed; int last_rotate_x, last_rotate_y, last_rotate_dir; int used_solve; unsigned char *tiles; unsigned char *barriers; }; #define OFFSETWH(x2,y2,x1,y1,dir,width,height) \ ( (x2) = ((x1) + width + X((dir))) % width, \ (y2) = ((y1) + height + Y((dir))) % height) #define OFFSET(x2,y2,x1,y1,dir,state) \ OFFSETWH(x2,y2,x1,y1,dir,(state)->width,(state)->height) #define index(state, a, x, y) ( a[(y) * (state)->width + (x)] ) #define tile(state, x, y) index(state, (state)->tiles, x, y) #define barrier(state, x, y) index(state, (state)->barriers, x, y) struct xyd { int x, y, direction; }; static int xyd_cmp(const void *av, const void *bv) { const struct xyd *a = (const struct xyd *)av; const struct xyd *b = (const struct xyd *)bv; if (a->x < b->x) return -1; if (a->x > b->x) return +1; if (a->y < b->y) return -1; if (a->y > b->y) return +1; if (a->direction < b->direction) return -1; if (a->direction > b->direction) return +1; return 0; } static int xyd_cmp_nc(void *av, void *bv) { return xyd_cmp(av, bv); } static struct xyd *new_xyd(int x, int y, int direction) { struct xyd *xyd = snew(struct xyd); xyd->x = x; xyd->y = y; xyd->direction = direction; return xyd; } /* ---------------------------------------------------------------------- * Manage game parameters. */ static game_params *default_params(void) { game_params *ret = snew(game_params); ret->width = 5; ret->height = 5; ret->wrapping = FALSE; ret->unique = TRUE; ret->barrier_probability = 0.0; return ret; } static const struct game_params net_presets[] = { {5, 5, FALSE, TRUE, 0.0}, {7, 7, FALSE, TRUE, 0.0}, {9, 9, FALSE, TRUE, 0.0}, {11, 11, FALSE, TRUE, 0.0}, #ifndef SMALL_SCREEN {13, 11, FALSE, TRUE, 0.0}, #endif {5, 5, TRUE, TRUE, 0.0}, {7, 7, TRUE, TRUE, 0.0}, {9, 9, TRUE, TRUE, 0.0}, {11, 11, TRUE, TRUE, 0.0}, #ifndef SMALL_SCREEN {13, 11, TRUE, TRUE, 0.0}, #endif }; static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char str[80]; if (i < 0 || i >= lenof(net_presets)) return FALSE; ret = snew(game_params); *ret = net_presets[i]; sprintf(str, "%dx%d%s", ret->width, ret->height, ret->wrapping ? " wrapping" : ""); *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 *ret, char const *string) { char const *p = string; ret->width = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; if (*p == 'x') { p++; ret->height = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; } else { ret->height = ret->width; } while (*p) { if (*p == 'w') { p++; ret->wrapping = TRUE; } else if (*p == 'b') { p++; ret->barrier_probability = (float)atof(p); while (*p && (*p == '.' || isdigit((unsigned char)*p))) p++; } else if (*p == 'a') { p++; ret->unique = FALSE; } else p++; /* skip any other gunk */ } } static char *encode_params(const game_params *params, int full) { char ret[400]; int len; len = sprintf(ret, "%dx%d", params->width, params->height); if (params->wrapping) ret[len++] = 'w'; if (full && params->barrier_probability) len += sprintf(ret+len, "b%g", params->barrier_probability); if (full && !params->unique) ret[len++] = 'a'; assert(len < lenof(ret)); ret[len] = '\0'; return dupstr(ret); } static config_item *game_configure(const game_params *params) { config_item *ret; char buf[80]; ret = snewn(6, config_item); ret[0].name = "Width"; ret[0].type = C_STRING; sprintf(buf, "%d", params->width); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->height); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Walls wrap around"; ret[2].type = C_BOOLEAN; ret[2].sval = NULL; ret[2].ival = params->wrapping; ret[3].name = "Barrier probability"; ret[3].type = C_STRING; sprintf(buf, "%g", params->barrier_probability); ret[3].sval = dupstr(buf); ret[3].ival = 0; ret[4].name = "Ensure unique solution"; ret[4].type = C_BOOLEAN; ret[4].sval = NULL; ret[4].ival = params->unique; ret[5].name = NULL; ret[5].type = C_END; ret[5].sval = NULL; ret[5].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->width = atoi(cfg[0].sval); ret->height = atoi(cfg[1].sval); ret->wrapping = cfg[2].ival; ret->barrier_probability = (float)atof(cfg[3].sval); ret->unique = cfg[4].ival; return ret; } static char *validate_params(const game_params *params, int full) { if (params->width <= 0 || params->height <= 0) return "Width and height must both be greater than zero"; if (params->width <= 1 && params->height <= 1) return "At least one of width and height must be greater than one"; if (params->barrier_probability < 0) return "Barrier probability may not be negative"; if (params->barrier_probability > 1) return "Barrier probability may not be greater than 1"; /* * Specifying either grid dimension as 2 in a wrapping puzzle * makes it actually impossible to ensure a unique puzzle * solution. * * Proof: * * Without loss of generality, let us assume the puzzle _width_ * is 2, so we can conveniently discuss rows without having to * say `rows/columns' all the time. (The height may be 2 as * well, but that doesn't matter.) * * In each row, there are two edges between tiles: the inner * edge (running down the centre of the grid) and the outer * edge (the identified left and right edges of the grid). * * Lemma: In any valid 2xn puzzle there must be at least one * row in which _exactly one_ of the inner edge and outer edge * is connected. * * Proof: No row can have _both_ inner and outer edges * connected, because this would yield a loop. So the only * other way to falsify the lemma is for every row to have * _neither_ the inner nor outer edge connected. But this * means there is no connection at all between the left and * right columns of the puzzle, so there are two disjoint * subgraphs, which is also disallowed. [] * * Given such a row, it is always possible to make the * disconnected edge connected and the connected edge * disconnected without changing the state of any other edge. * (This is easily seen by case analysis on the various tiles: * left-pointing and right-pointing endpoints can be exchanged, * likewise T-pieces, and a corner piece can select its * horizontal connectivity independently of its vertical.) This * yields a distinct valid solution. * * Thus, for _every_ row in which exactly one of the inner and * outer edge is connected, there are two valid states for that * row, and hence the total number of solutions of the puzzle * is at least 2^(number of such rows), and in particular is at * least 2 since there must be at least one such row. [] */ if (full && params->unique && params->wrapping && (params->width == 2 || params->height == 2)) return "No wrapping puzzle with a width or height of 2 can have" " a unique solution"; return NULL; } /* ---------------------------------------------------------------------- * Solver used to assure solution uniqueness during generation. */ /* * Test cases I used while debugging all this were * * ./net --generate 1 13x11w#12300 * which expands under the non-unique grid generation rules to * 13x11w:5eaade1bd222664436d5e2965c12656b1129dd825219e3274d558d5eb2dab5da18898e571d5a2987be79746bd95726c597447d6da96188c513add829da7681da954db113d3cd244 * and has two ambiguous areas. * * An even better one is * 13x11w#507896411361192 * which expands to * 13x11w:b7125b1aec598eb31bd58d82572bc11494e5dee4e8db2bdd29b88d41a16bdd996d2996ddec8c83741a1e8674e78328ba71737b8894a9271b1cd1399453d1952e43951d9b712822e * and has an ambiguous area _and_ a situation where loop avoidance * is a necessary deductive technique. * * Then there's * 48x25w#820543338195187 * becoming * 48x25w:255989d14cdd185deaa753a93821a12edc1ab97943ac127e2685d7b8b3c48861b2192416139212b316eddd35de43714ebc7628d753db32e596284d9ec52c5a7dc1b4c811a655117d16dc28921b2b4161352cab1d89d18bc836b8b891d55ea4622a1251861b5bc9a8aa3e5bcd745c95229ca6c3b5e21d5832d397e917325793d7eb442dc351b2db2a52ba8e1651642275842d8871d5534aabc6d5b741aaa2d48ed2a7dbbb3151ddb49d5b9a7ed1ab98ee75d613d656dbba347bc514c84556b43a9bc65a3256ead792488b862a9d2a8a39b4255a4949ed7dbd79443292521265896b4399c95ede89d7c8c797a6a57791a849adea489359a158aa12e5dacce862b8333b7ebea7d344d1a3c53198864b73a9dedde7b663abb1b539e1e8853b1b7edb14a2a17ebaae4dbe63598a2e7e9a2dbdad415bc1d8cb88cbab5a8c82925732cd282e641ea3bd7d2c6e776de9117a26be86deb7c82c89524b122cb9397cd1acd2284e744ea62b9279bae85479ababe315c3ac29c431333395b24e6a1e3c43a2da42d4dce84aadd5b154aea555eaddcbd6e527d228c19388d9b424d94214555a7edbdeebe569d4a56dc51a86bd9963e377bb74752bd5eaa5761ba545e297b62a1bda46ab4aee423ad6c661311783cc18786d4289236563cb4a75ec67d481c14814994464cd1b87396dee63e5ab6e952cc584baa1d4c47cb557ec84dbb63d487c8728118673a166846dd3a4ebc23d6cb9c5827d96b4556e91899db32b517eda815ae271a8911bd745447121dc8d321557bc2a435ebec1bbac35b1a291669451174e6aa2218a4a9c5a6ca31ebc45d84e3a82c121e9ced7d55e9a * which has a spot (far right) where slightly more complex loop * avoidance is required. */ struct todo { unsigned char *marked; int *buffer; int buflen; int head, tail; }; static struct todo *todo_new(int maxsize) { struct todo *todo = snew(struct todo); todo->marked = snewn(maxsize, unsigned char); memset(todo->marked, 0, maxsize); todo->buflen = maxsize + 1; todo->buffer = snewn(todo->buflen, int); todo->head = todo->tail = 0; return todo; } static void todo_free(struct todo *todo) { sfree(todo->marked); sfree(todo->buffer); sfree(todo); } static void todo_add(struct todo *todo, int index) { if (todo->marked[index]) return; /* already on the list */ todo->marked[index] = TRUE; todo->buffer[todo->tail++] = index; if (todo->tail == todo->buflen) todo->tail = 0; } static int todo_get(struct todo *todo) { int ret; if (todo->head == todo->tail) return -1; /* list is empty */ ret = todo->buffer[todo->head++]; if (todo->head == todo->buflen) todo->head = 0; todo->marked[ret] = FALSE; return ret; } static int net_solver(int w, int h, unsigned char *tiles, unsigned char *barriers, int wrapping) { unsigned char *tilestate; unsigned char *edgestate; int *deadends; int *equivalence; struct todo *todo; int i, j, x, y; int area; int done_something; /* * Set up the solver's data structures. */ /* * tilestate stores the possible orientations of each tile. * There are up to four of these, so we'll index the array in * fours. tilestate[(y * w + x) * 4] and its three successive * members give the possible orientations, clearing to 255 from * the end as things are ruled out. * * In this loop we also count up the area of the grid (which is * not _necessarily_ equal to w*h, because there might be one * or more blank squares present. This will never happen in a * grid generated _by_ this program, but it's worth keeping the * solver as general as possible.) */ tilestate = snewn(w * h * 4, unsigned char); area = 0; for (i = 0; i < w*h; i++) { tilestate[i * 4] = tiles[i] & 0xF; for (j = 1; j < 4; j++) { if (tilestate[i * 4 + j - 1] == 255 || A(tilestate[i * 4 + j - 1]) == tilestate[i * 4]) tilestate[i * 4 + j] = 255; else tilestate[i * 4 + j] = A(tilestate[i * 4 + j - 1]); } if (tiles[i] != 0) area++; } /* * edgestate stores the known state of each edge. It is 0 for * unknown, 1 for open (connected) and 2 for closed (not * connected). * * In principle we need only worry about each edge once each, * but in fact it's easier to track each edge twice so that we * can reference it from either side conveniently. Also I'm * going to allocate _five_ bytes per tile, rather than the * obvious four, so that I can index edgestate[(y*w+x) * 5 + d] * where d is 1,2,4,8 and they never overlap. */ edgestate = snewn((w * h - 1) * 5 + 9, unsigned char); memset(edgestate, 0, (w * h - 1) * 5 + 9); /* * deadends tracks which edges have dead ends on them. It is * indexed by tile and direction: deadends[(y*w+x) * 5 + d] * tells you whether heading out of tile (x,y) in direction d * can reach a limited amount of the grid. Values are area+1 * (no dead end known) or less than that (can reach _at most_ * this many other tiles by heading this way out of this tile). */ deadends = snewn((w * h - 1) * 5 + 9, int); for (i = 0; i < (w * h - 1) * 5 + 9; i++) deadends[i] = area+1; /* * equivalence tracks which sets of tiles are known to be * connected to one another, so we can avoid creating loops by * linking together tiles which are already linked through * another route. * * This is a disjoint set forest structure: equivalence[i] * contains the index of another member of the equivalence * class containing i, or contains i itself for precisely one * member in each such class. To find a representative member * of the equivalence class containing i, you keep replacing i * with equivalence[i] until it stops changing; then you go * _back_ along the same path and point everything on it * directly at the representative member so as to speed up * future searches. Then you test equivalence between tiles by * finding the representative of each tile and seeing if * they're the same; and you create new equivalence (merge * classes) by finding the representative of each tile and * setting equivalence[one]=the_other. */ equivalence = snew_dsf(w * h); /* * On a non-wrapping grid, we instantly know that all the edges * round the edge are closed. */ if (!wrapping) { for (i = 0; i < w; i++) { edgestate[i * 5 + 2] = edgestate[((h-1) * w + i) * 5 + 8] = 2; } for (i = 0; i < h; i++) { edgestate[(i * w + w-1) * 5 + 1] = edgestate[(i * w) * 5 + 4] = 2; } } /* * If we have barriers available, we can mark those edges as * closed too. */ if (barriers) { for (y = 0; y < h; y++) for (x = 0; x < w; x++) { int d; for (d = 1; d <= 8; d += d) { if (barriers[y*w+x] & d) { int x2, y2; /* * In principle the barrier list should already * contain each barrier from each side, but * let's not take chances with our internal * consistency. */ OFFSETWH(x2, y2, x, y, d, w, h); edgestate[(y*w+x) * 5 + d] = 2; edgestate[(y2*w+x2) * 5 + F(d)] = 2; } } } } /* * Since most deductions made by this solver are local (the * exception is loop avoidance, where joining two tiles * together on one side of the grid can theoretically permit a * fresh deduction on the other), we can address the scaling * problem inherent in iterating repeatedly over the entire * grid by instead working with a to-do list. */ todo = todo_new(w * h); /* * Main deductive loop. */ done_something = TRUE; /* prevent instant termination! */ while (1) { int index; /* * Take a tile index off the todo list and process it. */ index = todo_get(todo); if (index == -1) { /* * If we have run out of immediate things to do, we * have no choice but to scan the whole grid for * longer-range things we've missed. Hence, I now add * every square on the grid back on to the to-do list. * I also set `done_something' to FALSE at this point; * if we later come back here and find it still FALSE, * we will know we've scanned the entire grid without * finding anything new to do, and we can terminate. */ if (!done_something) break; for (i = 0; i < w*h; i++) todo_add(todo, i); done_something = FALSE; index = todo_get(todo); } y = index / w; x = index % w; { int d, ourclass = dsf_canonify(equivalence, y*w+x); int deadendmax[9]; deadendmax[1] = deadendmax[2] = deadendmax[4] = deadendmax[8] = 0; for (i = j = 0; i < 4 && tilestate[(y*w+x) * 4 + i] != 255; i++) { int valid; int nnondeadends, nondeadends[4], deadendtotal; int nequiv, equiv[5]; int val = tilestate[(y*w+x) * 4 + i]; valid = TRUE; nnondeadends = deadendtotal = 0; equiv[0] = ourclass; nequiv = 1; for (d = 1; d <= 8; d += d) { /* * Immediately rule out this orientation if it * conflicts with any known edge. */ if ((edgestate[(y*w+x) * 5 + d] == 1 && !(val & d)) || (edgestate[(y*w+x) * 5 + d] == 2 && (val & d))) valid = FALSE; if (val & d) { /* * Count up the dead-end statistics. */ if (deadends[(y*w+x) * 5 + d] <= area) { deadendtotal += deadends[(y*w+x) * 5 + d]; } else { nondeadends[nnondeadends++] = d; } /* * Ensure we aren't linking to any tiles, * through edges not already known to be * open, which create a loop. */ if (edgestate[(y*w+x) * 5 + d] == 0) { int c, k, x2, y2; OFFSETWH(x2, y2, x, y, d, w, h); c = dsf_canonify(equivalence, y2*w+x2); for (k = 0; k < nequiv; k++) if (c == equiv[k]) break; if (k == nequiv) equiv[nequiv++] = c; else valid = FALSE; } } } if (nnondeadends == 0) { /* * If this orientation links together dead-ends * with a total area of less than the entire * grid, it is invalid. * * (We add 1 to deadendtotal because of the * tile itself, of course; one tile linking * dead ends of size 2 and 3 forms a subnetwork * with a total area of 6, not 5.) */ if (deadendtotal > 0 && deadendtotal+1 < area) valid = FALSE; } else if (nnondeadends == 1) { /* * If this orientation links together one or * more dead-ends with precisely one * non-dead-end, then we may have to mark that * non-dead-end as a dead end going the other * way. However, it depends on whether all * other orientations share the same property. */ deadendtotal++; if (deadendmax[nondeadends[0]] < deadendtotal) deadendmax[nondeadends[0]] = deadendtotal; } else { /* * If this orientation links together two or * more non-dead-ends, then we can rule out the * possibility of putting in new dead-end * markings in those directions. */ int k; for (k = 0; k < nnondeadends; k++) deadendmax[nondeadends[k]] = area+1; } if (valid) tilestate[(y*w+x) * 4 + j++] = val; #ifdef SOLVER_DIAGNOSTICS else printf("ruling out orientation %x at %d,%d\n", val, x, y); #endif } assert(j > 0); /* we can't lose _all_ possibilities! */ if (j < i) { done_something = TRUE; /* * We have ruled out at least one tile orientation. * Make sure the rest are blanked. */ while (j < 4) tilestate[(y*w+x) * 4 + j++] = 255; } /* * Now go through the tile orientations again and see * if we've deduced anything new about any edges. */ { int a, o; a = 0xF; o = 0; for (i = 0; i < 4 && tilestate[(y*w+x) * 4 + i] != 255; i++) { a &= tilestate[(y*w+x) * 4 + i]; o |= tilestate[(y*w+x) * 4 + i]; } for (d = 1; d <= 8; d += d) if (edgestate[(y*w+x) * 5 + d] == 0) { int x2, y2, d2; OFFSETWH(x2, y2, x, y, d, w, h); d2 = F(d); if (a & d) { /* This edge is open in all orientations. */ #ifdef SOLVER_DIAGNOSTICS printf("marking edge %d,%d:%d open\n", x, y, d); #endif edgestate[(y*w+x) * 5 + d] = 1; edgestate[(y2*w+x2) * 5 + d2] = 1; dsf_merge(equivalence, y*w+x, y2*w+x2); done_something = TRUE; todo_add(todo, y2*w+x2); } else if (!(o & d)) { /* This edge is closed in all orientations. */ #ifdef SOLVER_DIAGNOSTICS printf("marking edge %d,%d:%d closed\n", x, y, d); #endif edgestate[(y*w+x) * 5 + d] = 2; edgestate[(y2*w+x2) * 5 + d2] = 2; done_something = TRUE; todo_add(todo, y2*w+x2); } } } /* * Now check the dead-end markers and see if any of * them has lowered from the real ones. */ for (d = 1; d <= 8; d += d) { int x2, y2, d2; OFFSETWH(x2, y2, x, y, d, w, h); d2 = F(d); if (deadendmax[d] > 0 && deadends[(y2*w+x2) * 5 + d2] > deadendmax[d]) { #ifdef SOLVER_DIAGNOSTICS printf("setting dead end value %d,%d:%d to %d\n", x2, y2, d2, deadendmax[d]); #endif deadends[(y2*w+x2) * 5 + d2] = deadendmax[d]; done_something = TRUE; todo_add(todo, y2*w+x2); } } } } /* * Mark all completely determined tiles as locked. */ j = TRUE; for (i = 0; i < w*h; i++) { if (tilestate[i * 4 + 1] == 255) { assert(tilestate[i * 4 + 0] != 255); tiles[i] = tilestate[i * 4] | LOCKED; } else { tiles[i] &= ~LOCKED; j = FALSE; } } /* * Free up working space. */ todo_free(todo); sfree(tilestate); sfree(edgestate); sfree(deadends); sfree(equivalence); return j; } /* ---------------------------------------------------------------------- * Randomly select a new game description. */ /* * Function to randomly perturb an ambiguous section in a grid, to * attempt to ensure unique solvability. */ static void perturb(int w, int h, unsigned char *tiles, int wrapping, random_state *rs, int startx, int starty, int startd) { struct xyd *perimeter, *perim2, *loop[2], looppos[2]; int nperim, perimsize, nloop[2], loopsize[2]; int x, y, d, i; /* * We know that the tile at (startx,starty) is part of an * ambiguous section, and we also know that its neighbour in * direction startd is fully specified. We begin by tracing all * the way round the ambiguous area. */ nperim = perimsize = 0; perimeter = NULL; x = startx; y = starty; d = startd; #ifdef PERTURB_DIAGNOSTICS printf("perturb %d,%d:%d\n", x, y, d); #endif do { int x2, y2, d2; if (nperim >= perimsize) { perimsize = perimsize * 3 / 2 + 32; perimeter = sresize(perimeter, perimsize, struct xyd); } perimeter[nperim].x = x; perimeter[nperim].y = y; perimeter[nperim].direction = d; nperim++; #ifdef PERTURB_DIAGNOSTICS printf("perimeter: %d,%d:%d\n", x, y, d); #endif /* * First, see if we can simply turn left from where we are * and find another locked square. */ d2 = A(d); OFFSETWH(x2, y2, x, y, d2, w, h); if ((!wrapping && (abs(x2-x) > 1 || abs(y2-y) > 1)) || (tiles[y2*w+x2] & LOCKED)) { d = d2; } else { /* * Failing that, step left into the new square and look * in front of us. */ x = x2; y = y2; OFFSETWH(x2, y2, x, y, d, w, h); if ((wrapping || (abs(x2-x) <= 1 && abs(y2-y) <= 1)) && !(tiles[y2*w+x2] & LOCKED)) { /* * And failing _that_, we're going to have to step * forward into _that_ square and look right at the * same locked square as we started with. */ x = x2; y = y2; d = C(d); } } } while (x != startx || y != starty || d != startd); /* * Our technique for perturbing this ambiguous area is to * search round its edge for a join we can make: that is, an * edge on the perimeter which is (a) not currently connected, * and (b) connecting it would not yield a full cross on either * side. Then we make that join, search round the network to * find the loop thus constructed, and sever the loop at a * randomly selected other point. */ perim2 = snewn(nperim, struct xyd); memcpy(perim2, perimeter, nperim * sizeof(struct xyd)); /* Shuffle the perimeter, so as to search it without directional bias. */ shuffle(perim2, nperim, sizeof(*perim2), rs); for (i = 0; i < nperim; i++) { int x2, y2; x = perim2[i].x; y = perim2[i].y; d = perim2[i].direction; OFFSETWH(x2, y2, x, y, d, w, h); if (!wrapping && (abs(x2-x) > 1 || abs(y2-y) > 1)) continue; /* can't link across non-wrapping border */ if (tiles[y*w+x] & d) continue; /* already linked in this direction! */ if (((tiles[y*w+x] | d) & 15) == 15) continue; /* can't turn this tile into a cross */ if (((tiles[y2*w+x2] | F(d)) & 15) == 15) continue; /* can't turn other tile into a cross */ /* * We've found the point at which we're going to make a new * link. */ #ifdef PERTURB_DIAGNOSTICS printf("linking %d,%d:%d\n", x, y, d); #endif tiles[y*w+x] |= d; tiles[y2*w+x2] |= F(d); break; } sfree(perim2); if (i == nperim) { sfree(perimeter); return; /* nothing we can do! */ } /* * Now we've constructed a new link, we need to find the entire * loop of which it is a part. * * In principle, this involves doing a complete search round * the network. However, I anticipate that in the vast majority * of cases the loop will be quite small, so what I'm going to * do is make _two_ searches round the network in parallel, one * keeping its metaphorical hand on the left-hand wall while * the other keeps its hand on the right. As soon as one of * them gets back to its starting point, I abandon the other. */ for (i = 0; i < 2; i++) { loopsize[i] = nloop[i] = 0; loop[i] = NULL; looppos[i].x = x; looppos[i].y = y; looppos[i].direction = d; } while (1) { for (i = 0; i < 2; i++) { int x2, y2, j; x = looppos[i].x; y = looppos[i].y; d = looppos[i].direction; OFFSETWH(x2, y2, x, y, d, w, h); /* * Add this path segment to the loop, unless it exactly * reverses the previous one on the loop in which case * we take it away again. */ #ifdef PERTURB_DIAGNOSTICS printf("looppos[%d] = %d,%d:%d\n", i, x, y, d); #endif if (nloop[i] > 0 && loop[i][nloop[i]-1].x == x2 && loop[i][nloop[i]-1].y == y2 && loop[i][nloop[i]-1].direction == F(d)) { #ifdef PERTURB_DIAGNOSTICS printf("removing path segment %d,%d:%d from loop[%d]\n", x2, y2, F(d), i); #endif nloop[i]--; } else { if (nloop[i] >= loopsize[i]) { loopsize[i] = loopsize[i] * 3 / 2 + 32; loop[i] = sresize(loop[i], loopsize[i], struct xyd); } #ifdef PERTURB_DIAGNOSTICS printf("adding path segment %d,%d:%d to loop[%d]\n", x, y, d, i); #endif loop[i][nloop[i]++] = looppos[i]; } #ifdef PERTURB_DIAGNOSTICS printf("tile at new location is %x\n", tiles[y2*w+x2] & 0xF); #endif d = F(d); for (j = 0; j < 4; j++) { if (i == 0) d = A(d); else d = C(d); #ifdef PERTURB_DIAGNOSTICS printf("trying dir %d\n", d); #endif if (tiles[y2*w+x2] & d) { looppos[i].x = x2; looppos[i].y = y2; looppos[i].direction = d; break; } } assert(j < 4); assert(nloop[i] > 0); if (looppos[i].x == loop[i][0].x && looppos[i].y == loop[i][0].y && looppos[i].direction == loop[i][0].direction) { #ifdef PERTURB_DIAGNOSTICS printf("loop %d finished tracking\n", i); #endif /* * Having found our loop, we now sever it at a * randomly chosen point - absolutely any will do - * which is not the one we joined it at to begin * with. Conveniently, the one we joined it at is * loop[i][0], so we just avoid that one. */ j = random_upto(rs, nloop[i]-1) + 1; x = loop[i][j].x; y = loop[i][j].y; d = loop[i][j].direction; OFFSETWH(x2, y2, x, y, d, w, h); tiles[y*w+x] &= ~d; tiles[y2*w+x2] &= ~F(d); break; } } if (i < 2) break; } sfree(loop[0]); sfree(loop[1]); /* * Finally, we must mark the entire disputed section as locked, * to prevent the perturb function being called on it multiple * times. * * To do this, we _sort_ the perimeter of the area. The * existing xyd_cmp function will arrange things into columns * for us, in such a way that each column has the edges in * vertical order. Then we can work down each column and fill * in all the squares between an up edge and a down edge. */ qsort(perimeter, nperim, sizeof(struct xyd), xyd_cmp); x = y = -1; for (i = 0; i <= nperim; i++) { if (i == nperim || perimeter[i].x > x) { /* * Fill in everything from the last Up edge to the * bottom of the grid, if necessary. */ if (x != -1) { while (y < h) { #ifdef PERTURB_DIAGNOSTICS printf("resolved: locking tile %d,%d\n", x, y); #endif tiles[y * w + x] |= LOCKED; y++; } x = y = -1; } if (i == nperim) break; x = perimeter[i].x; y = 0; } if (perimeter[i].direction == U) { x = perimeter[i].x; y = perimeter[i].y; } else if (perimeter[i].direction == D) { /* * Fill in everything from the last Up edge to here. */ assert(x == perimeter[i].x && y <= perimeter[i].y); while (y <= perimeter[i].y) { #ifdef PERTURB_DIAGNOSTICS printf("resolved: locking tile %d,%d\n", x, y); #endif tiles[y * w + x] |= LOCKED; y++; } x = y = -1; } } sfree(perimeter); } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { tree234 *possibilities, *barriertree; int w, h, x, y, cx, cy, nbarriers; unsigned char *tiles, *barriers; char *desc, *p; w = params->width; h = params->height; cx = w / 2; cy = h / 2; tiles = snewn(w * h, unsigned char); barriers = snewn(w * h, unsigned char); begin_generation: memset(tiles, 0, w * h); memset(barriers, 0, w * h); /* * Construct the unshuffled grid. * * To do this, we simply start at the centre point, repeatedly * choose a random possibility out of the available ways to * extend a used square into an unused one, and do it. After * extending the third line out of a square, we remove the * fourth from the possibilities list to avoid any full-cross * squares (which would make the game too easy because they * only have one orientation). * * The slightly worrying thing is the avoidance of full-cross * squares. Can this cause our unsophisticated construction * algorithm to paint itself into a corner, by getting into a * situation where there are some unreached squares and the * only way to reach any of them is to extend a T-piece into a * full cross? * * Answer: no it can't, and here's a proof. * * Any contiguous group of such unreachable squares must be * surrounded on _all_ sides by T-pieces pointing away from the * group. (If not, then there is a square which can be extended * into one of the `unreachable' ones, and so it wasn't * unreachable after all.) In particular, this implies that * each contiguous group of unreachable squares must be * rectangular in shape (any deviation from that yields a * non-T-piece next to an `unreachable' square). * * So we have a rectangle of unreachable squares, with T-pieces * forming a solid border around the rectangle. The corners of * that border must be connected (since every tile connects all * the lines arriving in it), and therefore the border must * form a closed loop around the rectangle. * * But this can't have happened in the first place, since we * _know_ we've avoided creating closed loops! Hence, no such * situation can ever arise, and the naive grid construction * algorithm will guaranteeably result in a complete grid * containing no unreached squares, no full crosses _and_ no * closed loops. [] */ possibilities = newtree234(xyd_cmp_nc); if (cx+1 < w) add234(possibilities, new_xyd(cx, cy, R)); if (cy-1 >= 0) add234(possibilities, new_xyd(cx, cy, U)); if (cx-1 >= 0) add234(possibilities, new_xyd(cx, cy, L)); if (cy+1 < h) add234(possibilities, new_xyd(cx, cy, D)); while (count234(possibilities) > 0) { int i; struct xyd *xyd; int x1, y1, d1, x2, y2, d2, d; /* * Extract a randomly chosen possibility from the list. */ i = random_upto(rs, count234(possibilities)); xyd = delpos234(possibilities, i); x1 = xyd->x; y1 = xyd->y; d1 = xyd->direction; sfree(xyd); OFFSET(x2, y2, x1, y1, d1, params); d2 = F(d1); #ifdef GENERATION_DIAGNOSTICS printf("picked (%d,%d,%c) <-> (%d,%d,%c)\n", x1, y1, "0RU3L567D9abcdef"[d1], x2, y2, "0RU3L567D9abcdef"[d2]); #endif /* * Make the connection. (We should be moving to an as yet * unused tile.) */ index(params, tiles, x1, y1) |= d1; assert(index(params, tiles, x2, y2) == 0); index(params, tiles, x2, y2) |= d2; /* * If we have created a T-piece, remove its last * possibility. */ if (COUNT(index(params, tiles, x1, y1)) == 3) { struct xyd xyd1, *xydp; xyd1.x = x1; xyd1.y = y1; xyd1.direction = 0x0F ^ index(params, tiles, x1, y1); xydp = find234(possibilities, &xyd1, NULL); if (xydp) { #ifdef GENERATION_DIAGNOSTICS printf("T-piece; removing (%d,%d,%c)\n", xydp->x, xydp->y, "0RU3L567D9abcdef"[xydp->direction]); #endif del234(possibilities, xydp); sfree(xydp); } } /* * Remove all other possibilities that were pointing at the * tile we've just moved into. */ for (d = 1; d < 0x10; d <<= 1) { int x3, y3, d3; struct xyd xyd1, *xydp; OFFSET(x3, y3, x2, y2, d, params); d3 = F(d); xyd1.x = x3; xyd1.y = y3; xyd1.direction = d3; xydp = find234(possibilities, &xyd1, NULL); if (xydp) { #ifdef GENERATION_DIAGNOSTICS printf("Loop avoidance; removing (%d,%d,%c)\n", xydp->x, xydp->y, "0RU3L567D9abcdef"[xydp->direction]); #endif del234(possibilities, xydp); sfree(xydp); } } /* * Add new possibilities to the list for moving _out_ of * the tile we have just moved into. */ for (d = 1; d < 0x10; d <<= 1) { int x3, y3; if (d == d2) continue; /* we've got this one already */ if (!params->wrapping) { if (d == U && y2 == 0) continue; if (d == D && y2 == h-1) continue; if (d == L && x2 == 0) continue; if (d == R && x2 == w-1) continue; } OFFSET(x3, y3, x2, y2, d, params); if (index(params, tiles, x3, y3)) continue; /* this would create a loop */ #ifdef GENERATION_DIAGNOSTICS printf("New frontier; adding (%d,%d,%c)\n", x2, y2, "0RU3L567D9abcdef"[d]); #endif add234(possibilities, new_xyd(x2, y2, d)); } } /* Having done that, we should have no possibilities remaining. */ assert(count234(possibilities) == 0); freetree234(possibilities); if (params->unique) { int prevn = -1; /* * Run the solver to check unique solubility. */ while (!net_solver(w, h, tiles, NULL, params->wrapping)) { int n = 0; /* * We expect (in most cases) that most of the grid will * be uniquely specified already, and the remaining * ambiguous sections will be small and separate. So * our strategy is to find each individual such * section, and perform a perturbation on the network * in that area. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) { if (x+1 < w && ((tiles[y*w+x] ^ tiles[y*w+x+1]) & LOCKED)) { n++; if (tiles[y*w+x] & LOCKED) perturb(w, h, tiles, params->wrapping, rs, x+1, y, L); else perturb(w, h, tiles, params->wrapping, rs, x, y, R); } if (y+1 < h && ((tiles[y*w+x] ^ tiles[(y+1)*w+x]) & LOCKED)) { n++; if (tiles[y*w+x] & LOCKED) perturb(w, h, tiles, params->wrapping, rs, x, y+1, U); else perturb(w, h, tiles, params->wrapping, rs, x, y, D); } } /* * Now n counts the number of ambiguous sections we * have fiddled with. If we haven't managed to decrease * it from the last time we ran the solver, give up and * regenerate the entire grid. */ if (prevn != -1 && prevn <= n) goto begin_generation; /* (sorry) */ prevn = n; } /* * The solver will have left a lot of LOCKED bits lying * around in the tiles array. Remove them. */ for (x = 0; x < w*h; x++) tiles[x] &= ~LOCKED; } /* * Now compute a list of the possible barrier locations. */ barriertree = newtree234(xyd_cmp_nc); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { if (!(index(params, tiles, x, y) & R) && (params->wrapping || x < w-1)) add234(barriertree, new_xyd(x, y, R)); if (!(index(params, tiles, x, y) & D) && (params->wrapping || y < h-1)) add234(barriertree, new_xyd(x, y, D)); } } /* * Save the unshuffled grid in aux. */ { char *solution; int i; solution = snewn(w * h + 1, char); for (i = 0; i < w * h; i++) solution[i] = "0123456789abcdef"[tiles[i] & 0xF]; solution[w*h] = '\0'; *aux = solution; } /* * Now shuffle the grid. * * In order to avoid accidentally generating an already-solved * grid, we will reshuffle as necessary to ensure that at least * one edge has a mismatched connection. * * This can always be done, since validate_params() enforces a * grid area of at least 2 and our generator never creates * either type of rotationally invariant tile (cross and * blank). Hence there must be at least one edge separating * distinct tiles, and it must be possible to find orientations * of those tiles such that one tile is trying to connect * through that edge and the other is not. * * (We could be more subtle, and allow the shuffle to generate * a grid in which all tiles match up locally and the only * criterion preventing the grid from being already solved is * connectedness. However, that would take more effort, and * it's easier to simply make sure every grid is _obviously_ * not solved.) */ while (1) { int mismatches; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { int orig = index(params, tiles, x, y); int rot = random_upto(rs, 4); index(params, tiles, x, y) = ROT(orig, rot); } } mismatches = 0; /* * I can't even be bothered to check for mismatches across * a wrapping edge, so I'm just going to enforce that there * must be a mismatch across a non-wrapping edge, which is * still always possible. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) { if (x+1 < w && ((ROT(index(params, tiles, x, y), 2) ^ index(params, tiles, x+1, y)) & L)) mismatches++; if (y+1 < h && ((ROT(index(params, tiles, x, y), 2) ^ index(params, tiles, x, y+1)) & U)) mismatches++; } if (mismatches > 0) break; } /* * And now choose barrier locations. (We carefully do this * _after_ shuffling, so that changing the barrier rate in the * params while keeping the random seed the same will give the * same shuffled grid and _only_ change the barrier locations. * Also the way we choose barrier locations, by repeatedly * choosing one possibility from the list until we have enough, * is designed to ensure that raising the barrier rate while * keeping the seed the same will provide a superset of the * previous barrier set - i.e. if you ask for 10 barriers, and * then decide that's still too hard and ask for 20, you'll get * the original 10 plus 10 more, rather than getting 20 new * ones and the chance of remembering your first 10.) */ nbarriers = (int)(params->barrier_probability * count234(barriertree)); assert(nbarriers >= 0 && nbarriers <= count234(barriertree)); while (nbarriers > 0) { int i; struct xyd *xyd; int x1, y1, d1, x2, y2, d2; /* * Extract a randomly chosen barrier from the list. */ i = random_upto(rs, count234(barriertree)); xyd = delpos234(barriertree, i); assert(xyd != NULL); x1 = xyd->x; y1 = xyd->y; d1 = xyd->direction; sfree(xyd); OFFSET(x2, y2, x1, y1, d1, params); d2 = F(d1); index(params, barriers, x1, y1) |= d1; index(params, barriers, x2, y2) |= d2; nbarriers--; } /* * Clean up the rest of the barrier list. */ { struct xyd *xyd; while ( (xyd = delpos234(barriertree, 0)) != NULL) sfree(xyd); freetree234(barriertree); } /* * Finally, encode the grid into a string game description. * * My syntax is extremely simple: each square is encoded as a * hex digit in which bit 0 means a connection on the right, * bit 1 means up, bit 2 left and bit 3 down. (i.e. the same * encoding as used internally). Each digit is followed by * optional barrier indicators: `v' means a vertical barrier to * the right of it, and `h' means a horizontal barrier below * it. */ desc = snewn(w * h * 3 + 1, char); p = desc; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { *p++ = "0123456789abcdef"[index(params, tiles, x, y)]; if ((params->wrapping || x < w-1) && (index(params, barriers, x, y) & R)) *p++ = 'v'; if ((params->wrapping || y < h-1) && (index(params, barriers, x, y) & D)) *p++ = 'h'; } } assert(p - desc <= w*h*3); *p = '\0'; sfree(tiles); sfree(barriers); return desc; } static char *validate_desc(const game_params *params, const char *desc) { int w = params->width, h = params->height; int i; for (i = 0; i < w*h; i++) { if (*desc >= '0' && *desc <= '9') /* OK */; else if (*desc >= 'a' && *desc <= 'f') /* OK */; else if (*desc >= 'A' && *desc <= 'F') /* OK */; else if (!*desc) return "Game description shorter than expected"; else return "Game description contained unexpected character"; desc++; while (*desc == 'h' || *desc == 'v') desc++; } if (*desc) return "Game description longer than expected"; return NULL; } /* ---------------------------------------------------------------------- * Construct an initial game state, given a description and parameters. */ static game_state *new_game(midend *me, const game_params *params, const char *desc) { game_state *state; int w, h, x, y; assert(params->width > 0 && params->height > 0); assert(params->width > 1 || params->height > 1); /* * Create a blank game state. */ state = snew(game_state); w = state->width = params->width; h = state->height = params->height; state->wrapping = params->wrapping; state->last_rotate_dir = state->last_rotate_x = state->last_rotate_y = 0; state->completed = state->used_solve = FALSE; state->tiles = snewn(state->width * state->height, unsigned char); memset(state->tiles, 0, state->width * state->height); state->barriers = snewn(state->width * state->height, unsigned char); memset(state->barriers, 0, state->width * state->height); /* * Parse the game description into the grid. */ for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { if (*desc >= '0' && *desc <= '9') tile(state, x, y) = *desc - '0'; else if (*desc >= 'a' && *desc <= 'f') tile(state, x, y) = *desc - 'a' + 10; else if (*desc >= 'A' && *desc <= 'F') tile(state, x, y) = *desc - 'A' + 10; if (*desc) desc++; while (*desc == 'h' || *desc == 'v') { int x2, y2, d1, d2; if (*desc == 'v') d1 = R; else d1 = D; OFFSET(x2, y2, x, y, d1, state); d2 = F(d1); barrier(state, x, y) |= d1; barrier(state, x2, y2) |= d2; desc++; } } } /* * Set up border barriers if this is a non-wrapping game. */ if (!state->wrapping) { for (x = 0; x < state->width; x++) { barrier(state, x, 0) |= U; barrier(state, x, state->height-1) |= D; } for (y = 0; y < state->height; y++) { barrier(state, 0, y) |= L; barrier(state, state->width-1, y) |= R; } } else { /* * We check whether this is de-facto a non-wrapping game * despite the parameters, in case we were passed the * description of a non-wrapping game. This is so that we * can change some aspects of the UI behaviour. */ state->wrapping = FALSE; for (x = 0; x < state->width; x++) if (!(barrier(state, x, 0) & U) || !(barrier(state, x, state->height-1) & D)) state->wrapping = TRUE; for (y = 0; y < state->height; y++) if (!(barrier(state, 0, y) & L) || !(barrier(state, state->width-1, y) & R)) state->wrapping = TRUE; } return state; } static game_state *dup_game(const game_state *state) { game_state *ret; ret = snew(game_state); ret->width = state->width; ret->height = state->height; ret->wrapping = state->wrapping; ret->completed = state->completed; ret->used_solve = state->used_solve; ret->last_rotate_dir = state->last_rotate_dir; ret->last_rotate_x = state->last_rotate_x; ret->last_rotate_y = state->last_rotate_y; ret->tiles = snewn(state->width * state->height, unsigned char); memcpy(ret->tiles, state->tiles, state->width * state->height); ret->barriers = snewn(state->width * state->height, unsigned char); memcpy(ret->barriers, state->barriers, state->width * state->height); return ret; } static void free_game(game_state *state) { sfree(state->tiles); sfree(state->barriers); sfree(state); } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { unsigned char *tiles; char *ret; int retlen, retsize; int i; tiles = snewn(state->width * state->height, unsigned char); if (!aux) { /* * Run the internal solver on the provided grid. This might * not yield a complete solution. */ memcpy(tiles, state->tiles, state->width * state->height); net_solver(state->width, state->height, tiles, state->barriers, state->wrapping); } else { for (i = 0; i < state->width * state->height; i++) { int c = aux[i]; if (c >= '0' && c <= '9') tiles[i] = c - '0'; else if (c >= 'a' && c <= 'f') tiles[i] = c - 'a' + 10; else if (c >= 'A' && c <= 'F') tiles[i] = c - 'A' + 10; tiles[i] |= LOCKED; } } /* * Now construct a string which can be passed to execute_move() * to transform the current grid into the solved one. */ retsize = 256; ret = snewn(retsize, char); retlen = 0; ret[retlen++] = 'S'; for (i = 0; i < state->width * state->height; i++) { int from = currstate->tiles[i], to = tiles[i]; int ft = from & (R|L|U|D), tt = to & (R|L|U|D); int x = i % state->width, y = i / state->width; int chr = '\0'; char buf[80], *p = buf; if (from == to) continue; /* nothing needs doing at all */ /* * To transform this tile into the desired tile: first * unlock the tile if it's locked, then rotate it if * necessary, then lock it if necessary. */ if (from & LOCKED) p += sprintf(p, ";L%d,%d", x, y); if (tt == A(ft)) chr = 'A'; else if (tt == C(ft)) chr = 'C'; else if (tt == F(ft)) chr = 'F'; else { assert(tt == ft); chr = '\0'; } if (chr) p += sprintf(p, ";%c%d,%d", chr, x, y); if (to & LOCKED) p += sprintf(p, ";L%d,%d", x, y); if (p > buf) { if (retlen + (p - buf) >= retsize) { retsize = retlen + (p - buf) + 512; ret = sresize(ret, retsize, char); } memcpy(ret+retlen, buf, p - buf); retlen += p - buf; } } assert(retlen < retsize); ret[retlen] = '\0'; ret = sresize(ret, retlen+1, char); sfree(tiles); return ret; } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { return NULL; } /* ---------------------------------------------------------------------- * Utility routine. */ /* * Compute which squares are reachable from the centre square, as a * quick visual aid to determining how close the game is to * completion. This is also a simple way to tell if the game _is_ * completed - just call this function and see whether every square * is marked active. */ static unsigned char *compute_active(const game_state *state, int cx, int cy) { unsigned char *active; tree234 *todo; struct xyd *xyd; active = snewn(state->width * state->height, unsigned char); memset(active, 0, state->width * state->height); /* * We only store (x,y) pairs in todo, but it's easier to reuse * xyd_cmp and just store direction 0 every time. */ todo = newtree234(xyd_cmp_nc); index(state, active, cx, cy) = ACTIVE; add234(todo, new_xyd(cx, cy, 0)); while ( (xyd = delpos234(todo, 0)) != NULL) { int x1, y1, d1, x2, y2, d2; x1 = xyd->x; y1 = xyd->y; sfree(xyd); for (d1 = 1; d1 < 0x10; d1 <<= 1) { OFFSET(x2, y2, x1, y1, d1, state); d2 = F(d1); /* * If the next tile in this direction is connected to * us, and there isn't a barrier in the way, and it * isn't already marked active, then mark it active and * add it to the to-examine list. */ if ((tile(state, x1, y1) & d1) && (tile(state, x2, y2) & d2) && !(barrier(state, x1, y1) & d1) && !index(state, active, x2, y2)) { index(state, active, x2, y2) = ACTIVE; add234(todo, new_xyd(x2, y2, 0)); } } } /* Now we expect the todo list to have shrunk to zero size. */ assert(count234(todo) == 0); freetree234(todo); return active; } struct game_ui { int org_x, org_y; /* origin */ int cx, cy; /* source tile (game coordinates) */ int cur_x, cur_y; int cur_visible; random_state *rs; /* used for jumbling */ #ifdef USE_DRAGGING int dragtilex, dragtiley, dragstartx, dragstarty, dragged; #endif }; static game_ui *new_ui(const game_state *state) { void *seed; int seedsize; game_ui *ui = snew(game_ui); ui->org_x = ui->org_y = 0; ui->cur_x = ui->cx = state->width / 2; ui->cur_y = ui->cy = state->height / 2; ui->cur_visible = FALSE; get_random_seed(&seed, &seedsize); ui->rs = random_new(seed, seedsize); sfree(seed); return ui; } static void free_ui(game_ui *ui) { random_free(ui->rs); sfree(ui); } static char *encode_ui(const game_ui *ui) { char buf[120]; /* * We preserve the origin and centre-point coordinates over a * serialise. */ sprintf(buf, "O%d,%d;C%d,%d", ui->org_x, ui->org_y, ui->cx, ui->cy); return dupstr(buf); } static void decode_ui(game_ui *ui, const char *encoding) { sscanf(encoding, "O%d,%d;C%d,%d", &ui->org_x, &ui->org_y, &ui->cx, &ui->cy); } static void game_changed_state(game_ui *ui, const game_state *oldstate, const game_state *newstate) { } struct game_drawstate { int started; int width, height; int org_x, org_y; int tilesize; unsigned char *visible; }; /* ---------------------------------------------------------------------- * Process a move. */ static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { char *nullret; int tx = -1, ty = -1, dir = 0; int shift = button & MOD_SHFT, ctrl = button & MOD_CTRL; enum { NONE, ROTATE_LEFT, ROTATE_180, ROTATE_RIGHT, TOGGLE_LOCK, JUMBLE, MOVE_ORIGIN, MOVE_SOURCE, MOVE_ORIGIN_AND_SOURCE, MOVE_CURSOR } action; button &= ~MOD_MASK; nullret = NULL; action = NONE; if (button == LEFT_BUTTON || button == MIDDLE_BUTTON || #ifdef USE_DRAGGING button == LEFT_DRAG || button == LEFT_RELEASE || button == RIGHT_DRAG || button == RIGHT_RELEASE || #endif button == RIGHT_BUTTON) { if (ui->cur_visible) { ui->cur_visible = FALSE; nullret = ""; } /* * The button must have been clicked on a valid tile. */ x -= WINDOW_OFFSET + TILE_BORDER; y -= WINDOW_OFFSET + TILE_BORDER; if (x < 0 || y < 0) return nullret; tx = x / TILE_SIZE; ty = y / TILE_SIZE; if (tx >= state->width || ty >= state->height) return nullret; /* Transform from physical to game coords */ tx = (tx + ui->org_x) % state->width; ty = (ty + ui->org_y) % state->height; if (x % TILE_SIZE >= TILE_SIZE - TILE_BORDER || y % TILE_SIZE >= TILE_SIZE - TILE_BORDER) return nullret; #ifdef USE_DRAGGING if (button == MIDDLE_BUTTON #ifdef STYLUS_BASED || button == RIGHT_BUTTON /* with a stylus, `right-click' locks */ #endif ) { /* * Middle button never drags: it only toggles the lock. */ action = TOGGLE_LOCK; } else if (button == LEFT_BUTTON #ifndef STYLUS_BASED || button == RIGHT_BUTTON /* (see above) */ #endif ) { /* * Otherwise, we note down the start point for a drag. */ ui->dragtilex = tx; ui->dragtiley = ty; ui->dragstartx = x % TILE_SIZE; ui->dragstarty = y % TILE_SIZE; ui->dragged = FALSE; return nullret; /* no actual action */ } else if (button == LEFT_DRAG #ifndef STYLUS_BASED || button == RIGHT_DRAG #endif ) { /* * Find the new drag point and see if it necessitates a * rotation. */ int x0,y0, xA,yA, xC,yC, xF,yF; int mx, my; int d0, dA, dC, dF, dmin; tx = ui->dragtilex; ty = ui->dragtiley; mx = x - (ui->dragtilex * TILE_SIZE); my = y - (ui->dragtiley * TILE_SIZE); x0 = ui->dragstartx; y0 = ui->dragstarty; xA = ui->dragstarty; yA = TILE_SIZE-1 - ui->dragstartx; xF = TILE_SIZE-1 - ui->dragstartx; yF = TILE_SIZE-1 - ui->dragstarty; xC = TILE_SIZE-1 - ui->dragstarty; yC = ui->dragstartx; d0 = (mx-x0)*(mx-x0) + (my-y0)*(my-y0); dA = (mx-xA)*(mx-xA) + (my-yA)*(my-yA); dF = (mx-xF)*(mx-xF) + (my-yF)*(my-yF); dC = (mx-xC)*(mx-xC) + (my-yC)*(my-yC); dmin = min(min(d0,dA),min(dF,dC)); if (d0 == dmin) { return nullret; } else if (dF == dmin) { action = ROTATE_180; ui->dragstartx = xF; ui->dragstarty = yF; ui->dragged = TRUE; } else if (dA == dmin) { action = ROTATE_LEFT; ui->dragstartx = xA; ui->dragstarty = yA; ui->dragged = TRUE; } else /* dC == dmin */ { action = ROTATE_RIGHT; ui->dragstartx = xC; ui->dragstarty = yC; ui->dragged = TRUE; } } else if (button == LEFT_RELEASE #ifndef STYLUS_BASED || button == RIGHT_RELEASE #endif ) { if (!ui->dragged) { /* * There was a click but no perceptible drag: * revert to single-click behaviour. */ tx = ui->dragtilex; ty = ui->dragtiley; if (button == LEFT_RELEASE) action = ROTATE_LEFT; else action = ROTATE_RIGHT; } else return nullret; /* no action */ } #else /* USE_DRAGGING */ action = (button == LEFT_BUTTON ? ROTATE_LEFT : button == RIGHT_BUTTON ? ROTATE_RIGHT : TOGGLE_LOCK); #endif /* USE_DRAGGING */ } else if (IS_CURSOR_MOVE(button)) { switch (button) { case CURSOR_UP: dir = U; break; case CURSOR_DOWN: dir = D; break; case CURSOR_LEFT: dir = L; break; case CURSOR_RIGHT: dir = R; break; default: return nullret; } if (shift && ctrl) action = MOVE_ORIGIN_AND_SOURCE; else if (shift) action = MOVE_ORIGIN; else if (ctrl) action = MOVE_SOURCE; else action = MOVE_CURSOR; } else if (button == 'a' || button == 's' || button == 'd' || button == 'A' || button == 'S' || button == 'D' || button == 'f' || button == 'F' || IS_CURSOR_SELECT(button)) { tx = ui->cur_x; ty = ui->cur_y; if (button == 'a' || button == 'A' || button == CURSOR_SELECT) action = ROTATE_LEFT; else if (button == 's' || button == 'S' || button == CURSOR_SELECT2) action = TOGGLE_LOCK; else if (button == 'd' || button == 'D') action = ROTATE_RIGHT; else if (button == 'f' || button == 'F') action = ROTATE_180; ui->cur_visible = TRUE; } else if (button == 'j' || button == 'J') { /* XXX should we have some mouse control for this? */ action = JUMBLE; } else return nullret; /* * The middle button locks or unlocks a tile. (A locked tile * cannot be turned, and is visually marked as being locked. * This is a convenience for the player, so that once they are * sure which way round a tile goes, they can lock it and thus * avoid forgetting later on that they'd already done that one; * and the locking also prevents them turning the tile by * accident. If they change their mind, another middle click * unlocks it.) */ if (action == TOGGLE_LOCK) { char buf[80]; sprintf(buf, "L%d,%d", tx, ty); return dupstr(buf); } else if (action == ROTATE_LEFT || action == ROTATE_RIGHT || action == ROTATE_180) { char buf[80]; /* * The left and right buttons have no effect if clicked on a * locked tile. */ if (tile(state, tx, ty) & LOCKED) return nullret; /* * Otherwise, turn the tile one way or the other. Left button * turns anticlockwise; right button turns clockwise. */ sprintf(buf, "%c%d,%d", (int)(action == ROTATE_LEFT ? 'A' : action == ROTATE_RIGHT ? 'C' : 'F'), tx, ty); return dupstr(buf); } else if (action == JUMBLE) { /* * Jumble all unlocked tiles to random orientations. */ int jx, jy, maxlen; char *ret, *p; /* * Maximum string length assumes no int can be converted to * decimal and take more than 11 digits! */ maxlen = state->width * state->height * 25 + 3; ret = snewn(maxlen, char); p = ret; *p++ = 'J'; for (jy = 0; jy < state->height; jy++) { for (jx = 0; jx < state->width; jx++) { if (!(tile(state, jx, jy) & LOCKED)) { int rot = random_upto(ui->rs, 4); if (rot) { p += sprintf(p, ";%c%d,%d", "AFC"[rot-1], jx, jy); } } } } *p++ = '\0'; assert(p - ret < maxlen); ret = sresize(ret, p - ret, char); return ret; } else if (action == MOVE_ORIGIN || action == MOVE_SOURCE || action == MOVE_ORIGIN_AND_SOURCE || action == MOVE_CURSOR) { assert(dir != 0); if (action == MOVE_ORIGIN || action == MOVE_ORIGIN_AND_SOURCE) { if (state->wrapping) { OFFSET(ui->org_x, ui->org_y, ui->org_x, ui->org_y, dir, state); } else return nullret; /* disallowed for non-wrapping grids */ } if (action == MOVE_SOURCE || action == MOVE_ORIGIN_AND_SOURCE) { OFFSET(ui->cx, ui->cy, ui->cx, ui->cy, dir, state); } if (action == MOVE_CURSOR) { OFFSET(ui->cur_x, ui->cur_y, ui->cur_x, ui->cur_y, dir, state); ui->cur_visible = TRUE; } return ""; } else { return NULL; } } static game_state *execute_move(const game_state *from, const char *move) { game_state *ret; int tx = -1, ty = -1, n, noanim, orig; ret = dup_game(from); if (move[0] == 'J' || move[0] == 'S') { if (move[0] == 'S') ret->used_solve = TRUE; move++; if (*move == ';') move++; noanim = TRUE; } else noanim = FALSE; ret->last_rotate_dir = 0; /* suppress animation */ ret->last_rotate_x = ret->last_rotate_y = 0; while (*move) { if ((move[0] == 'A' || move[0] == 'C' || move[0] == 'F' || move[0] == 'L') && sscanf(move+1, "%d,%d%n", &tx, &ty, &n) >= 2 && tx >= 0 && tx < from->width && ty >= 0 && ty < from->height) { orig = tile(ret, tx, ty); if (move[0] == 'A') { tile(ret, tx, ty) = A(orig); if (!noanim) ret->last_rotate_dir = +1; } else if (move[0] == 'F') { tile(ret, tx, ty) = F(orig); if (!noanim) ret->last_rotate_dir = +2; /* + for sake of argument */ } else if (move[0] == 'C') { tile(ret, tx, ty) = C(orig); if (!noanim) ret->last_rotate_dir = -1; } else { assert(move[0] == 'L'); tile(ret, tx, ty) ^= LOCKED; } move += 1 + n; if (*move == ';') move++; } else { free_game(ret); return NULL; } } if (!noanim) { if (tx == -1 || ty == -1) { free_game(ret); return NULL; } ret->last_rotate_x = tx; ret->last_rotate_y = ty; } /* * Check whether the game has been completed. * * For this purpose it doesn't matter where the source square * is, because we can start from anywhere and correctly * determine whether the game is completed. */ { unsigned char *active = compute_active(ret, 0, 0); int x1, y1; int complete = TRUE; for (x1 = 0; x1 < ret->width; x1++) for (y1 = 0; y1 < ret->height; y1++) if ((tile(ret, x1, y1) & 0xF) && !index(ret, active, x1, y1)) { complete = FALSE; goto break_label; /* break out of two loops at once */ } break_label: sfree(active); if (complete) ret->completed = TRUE; } return ret; } /* ---------------------------------------------------------------------- * Routines for drawing the game position on the screen. */ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { game_drawstate *ds = snew(game_drawstate); ds->started = FALSE; ds->width = state->width; ds->height = state->height; ds->org_x = ds->org_y = -1; ds->visible = snewn(state->width * state->height, unsigned char); ds->tilesize = 0; /* undecided yet */ memset(ds->visible, 0xFF, state->width * state->height); return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->visible); sfree(ds); } static void game_compute_size(const game_params *params, int tilesize, int *x, int *y) { *x = WINDOW_OFFSET * 2 + tilesize * params->width + TILE_BORDER; *y = WINDOW_OFFSET * 2 + tilesize * params->height + TILE_BORDER; } 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; ret = snewn(NCOLOURS * 3, float); *ncolours = NCOLOURS; /* * Basic background colour is whatever the front end thinks is * a sensible default. */ frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); /* * Wires are black. */ ret[COL_WIRE * 3 + 0] = 0.0F; ret[COL_WIRE * 3 + 1] = 0.0F; ret[COL_WIRE * 3 + 2] = 0.0F; /* * Powered wires and powered endpoints are cyan. */ ret[COL_POWERED * 3 + 0] = 0.0F; ret[COL_POWERED * 3 + 1] = 1.0F; ret[COL_POWERED * 3 + 2] = 1.0F; /* * Barriers are red. */ ret[COL_BARRIER * 3 + 0] = 1.0F; ret[COL_BARRIER * 3 + 1] = 0.0F; ret[COL_BARRIER * 3 + 2] = 0.0F; /* * Unpowered endpoints are blue. */ ret[COL_ENDPOINT * 3 + 0] = 0.0F; ret[COL_ENDPOINT * 3 + 1] = 0.0F; ret[COL_ENDPOINT * 3 + 2] = 1.0F; /* * Tile borders are a darker grey than the background. */ ret[COL_BORDER * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0]; ret[COL_BORDER * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1]; ret[COL_BORDER * 3 + 2] = 0.5F * ret[COL_BACKGROUND * 3 + 2]; /* * Locked tiles are a grey in between those two. */ ret[COL_LOCKED * 3 + 0] = 0.75F * ret[COL_BACKGROUND * 3 + 0]; ret[COL_LOCKED * 3 + 1] = 0.75F * ret[COL_BACKGROUND * 3 + 1]; ret[COL_LOCKED * 3 + 2] = 0.75F * ret[COL_BACKGROUND * 3 + 2]; return ret; } static void draw_filled_line(drawing *dr, int x1, int y1, int x2, int y2, int colour) { draw_line(dr, x1-1, y1, x2-1, y2, COL_WIRE); draw_line(dr, x1+1, y1, x2+1, y2, COL_WIRE); draw_line(dr, x1, y1-1, x2, y2-1, COL_WIRE); draw_line(dr, x1, y1+1, x2, y2+1, COL_WIRE); draw_line(dr, x1, y1, x2, y2, colour); } static void draw_rect_coords(drawing *dr, int x1, int y1, int x2, int y2, int colour) { int mx = (x1 < x2 ? x1 : x2); int my = (y1 < y2 ? y1 : y2); int dx = (x2 + x1 - 2*mx + 1); int dy = (y2 + y1 - 2*my + 1); draw_rect(dr, mx, my, dx, dy, colour); } /* * draw_barrier_corner() and draw_barrier() are passed physical coords */ static void draw_barrier_corner(drawing *dr, game_drawstate *ds, int x, int y, int dx, int dy, int phase) { int bx = WINDOW_OFFSET + TILE_SIZE * x; int by = WINDOW_OFFSET + TILE_SIZE * y; int x1, y1; x1 = (dx > 0 ? TILE_SIZE+TILE_BORDER-1 : 0); y1 = (dy > 0 ? TILE_SIZE+TILE_BORDER-1 : 0); if (phase == 0) { draw_rect_coords(dr, bx+x1+dx, by+y1, bx+x1-TILE_BORDER*dx, by+y1-(TILE_BORDER-1)*dy, COL_WIRE); draw_rect_coords(dr, bx+x1, by+y1+dy, bx+x1-(TILE_BORDER-1)*dx, by+y1-TILE_BORDER*dy, COL_WIRE); } else { draw_rect_coords(dr, bx+x1, by+y1, bx+x1-(TILE_BORDER-1)*dx, by+y1-(TILE_BORDER-1)*dy, COL_BARRIER); } } static void draw_barrier(drawing *dr, game_drawstate *ds, int x, int y, int dir, int phase) { int bx = WINDOW_OFFSET + TILE_SIZE * x; int by = WINDOW_OFFSET + TILE_SIZE * y; int x1, y1, w, h; x1 = (X(dir) > 0 ? TILE_SIZE : X(dir) == 0 ? TILE_BORDER : 0); y1 = (Y(dir) > 0 ? TILE_SIZE : Y(dir) == 0 ? TILE_BORDER : 0); w = (X(dir) ? TILE_BORDER : TILE_SIZE - TILE_BORDER); h = (Y(dir) ? TILE_BORDER : TILE_SIZE - TILE_BORDER); if (phase == 0) { draw_rect(dr, bx+x1-X(dir), by+y1-Y(dir), w, h, COL_WIRE); } else { draw_rect(dr, bx+x1, by+y1, w, h, COL_BARRIER); } } /* * draw_tile() is passed physical coordinates */ static void draw_tile(drawing *dr, const game_state *state, game_drawstate *ds, int x, int y, int tile, int src, float angle, int cursor) { int bx = WINDOW_OFFSET + TILE_SIZE * x; int by = WINDOW_OFFSET + TILE_SIZE * y; float matrix[4]; float cx, cy, ex, ey, tx, ty; int dir, col, phase; /* * When we draw a single tile, we must draw everything up to * and including the borders around the tile. This means that * if the neighbouring tiles have connections to those borders, * we must draw those connections on the borders themselves. */ clip(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER); /* * So. First blank the tile out completely: draw a big * rectangle in border colour, and a smaller rectangle in * background colour to fill it in. */ draw_rect(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER, COL_BORDER); draw_rect(dr, bx+TILE_BORDER, by+TILE_BORDER, TILE_SIZE-TILE_BORDER, TILE_SIZE-TILE_BORDER, tile & LOCKED ? COL_LOCKED : COL_BACKGROUND); /* * Draw an inset outline rectangle as a cursor, in whichever of * COL_LOCKED and COL_BACKGROUND we aren't currently drawing * in. */ if (cursor) { draw_line(dr, bx+TILE_SIZE/8, by+TILE_SIZE/8, bx+TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8, tile & LOCKED ? COL_BACKGROUND : COL_LOCKED); draw_line(dr, bx+TILE_SIZE/8, by+TILE_SIZE/8, bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE/8, tile & LOCKED ? COL_BACKGROUND : COL_LOCKED); draw_line(dr, bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE/8, bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8, tile & LOCKED ? COL_BACKGROUND : COL_LOCKED); draw_line(dr, bx+TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8, bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8, tile & LOCKED ? COL_BACKGROUND : COL_LOCKED); } /* * Set up the rotation matrix. */ matrix[0] = (float)cos(angle * PI / 180.0); matrix[1] = (float)-sin(angle * PI / 180.0); matrix[2] = (float)sin(angle * PI / 180.0); matrix[3] = (float)cos(angle * PI / 180.0); /* * Draw the wires. */ cx = cy = TILE_BORDER + (TILE_SIZE-TILE_BORDER) / 2.0F - 0.5F; col = (tile & ACTIVE ? COL_POWERED : COL_WIRE); for (dir = 1; dir < 0x10; dir <<= 1) { if (tile & dir) { ex = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * X(dir); ey = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * Y(dir); MATMUL(tx, ty, matrix, ex, ey); draw_filled_line(dr, bx+(int)cx, by+(int)cy, bx+(int)(cx+tx), by+(int)(cy+ty), COL_WIRE); } } for (dir = 1; dir < 0x10; dir <<= 1) { if (tile & dir) { ex = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * X(dir); ey = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * Y(dir); MATMUL(tx, ty, matrix, ex, ey); draw_line(dr, bx+(int)cx, by+(int)cy, bx+(int)(cx+tx), by+(int)(cy+ty), col); } } /* * Draw the box in the middle. We do this in blue if the tile * is an unpowered endpoint, in cyan if the tile is a powered * endpoint, in black if the tile is the centrepiece, and * otherwise not at all. */ col = -1; if (src) col = COL_WIRE; else if (COUNT(tile) == 1) { col = (tile & ACTIVE ? COL_POWERED : COL_ENDPOINT); } if (col >= 0) { int i, points[8]; points[0] = +1; points[1] = +1; points[2] = +1; points[3] = -1; points[4] = -1; points[5] = -1; points[6] = -1; points[7] = +1; for (i = 0; i < 8; i += 2) { ex = (TILE_SIZE * 0.24F) * points[i]; ey = (TILE_SIZE * 0.24F) * points[i+1]; MATMUL(tx, ty, matrix, ex, ey); points[i] = bx+(int)(cx+tx); points[i+1] = by+(int)(cy+ty); } draw_polygon(dr, points, 4, col, COL_WIRE); } /* * Draw the points on the border if other tiles are connected * to us. */ for (dir = 1; dir < 0x10; dir <<= 1) { int dx, dy, px, py, lx, ly, vx, vy, ox, oy; dx = X(dir); dy = Y(dir); ox = x + dx; oy = y + dy; if (ox < 0 || ox >= state->width || oy < 0 || oy >= state->height) continue; if (!(tile(state, GX(ox), GY(oy)) & F(dir))) continue; px = bx + (int)(dx>0 ? TILE_SIZE + TILE_BORDER - 1 : dx<0 ? 0 : cx); py = by + (int)(dy>0 ? TILE_SIZE + TILE_BORDER - 1 : dy<0 ? 0 : cy); lx = dx * (TILE_BORDER-1); ly = dy * (TILE_BORDER-1); vx = (dy ? 1 : 0); vy = (dx ? 1 : 0); if (angle == 0.0 && (tile & dir)) { /* * If we are fully connected to the other tile, we must * draw right across the tile border. (We can use our * own ACTIVE state to determine what colour to do this * in: if we are fully connected to the other tile then * the two ACTIVE states will be the same.) */ draw_rect_coords(dr, px-vx, py-vy, px+lx+vx, py+ly+vy, COL_WIRE); draw_rect_coords(dr, px, py, px+lx, py+ly, (tile & ACTIVE) ? COL_POWERED : COL_WIRE); } else { /* * The other tile extends into our border, but isn't * actually connected to us. Just draw a single black * dot. */ draw_rect_coords(dr, px, py, px, py, COL_WIRE); } } /* * Draw barrier corners, and then barriers. */ for (phase = 0; phase < 2; phase++) { for (dir = 1; dir < 0x10; dir <<= 1) { int x1, y1, corner = FALSE; /* * If at least one barrier terminates at the corner * between dir and A(dir), draw a barrier corner. */ if (barrier(state, GX(x), GY(y)) & (dir | A(dir))) { corner = TRUE; } else { /* * Only count barriers terminating at this corner * if they're physically next to the corner. (That * is, if they've wrapped round from the far side * of the screen, they don't count.) */ x1 = x + X(dir); y1 = y + Y(dir); if (x1 >= 0 && x1 < state->width && y1 >= 0 && y1 < state->height && (barrier(state, GX(x1), GY(y1)) & A(dir))) { corner = TRUE; } else { x1 = x + X(A(dir)); y1 = y + Y(A(dir)); if (x1 >= 0 && x1 < state->width && y1 >= 0 && y1 < state->height && (barrier(state, GX(x1), GY(y1)) & dir)) corner = TRUE; } } if (corner) { /* * At least one barrier terminates here. Draw a * corner. */ draw_barrier_corner(dr, ds, x, y, X(dir)+X(A(dir)), Y(dir)+Y(A(dir)), phase); } } for (dir = 1; dir < 0x10; dir <<= 1) if (barrier(state, GX(x), GY(y)) & dir) draw_barrier(dr, ds, x, y, dir, phase); } unclip(dr); draw_update(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER); } static void game_redraw(drawing *dr, game_drawstate *ds, const game_state *oldstate, const game_state *state, int dir, const game_ui *ui, float t, float ft) { int x, y, tx, ty, frame, last_rotate_dir, moved_origin = FALSE; unsigned char *active; float angle = 0.0; /* * Clear the screen, and draw the exterior barrier lines, if * this is our first call or if the origin has changed. */ if (!ds->started || ui->org_x != ds->org_x || ui->org_y != ds->org_y) { int phase; ds->started = TRUE; draw_rect(dr, 0, 0, WINDOW_OFFSET * 2 + TILE_SIZE * state->width + TILE_BORDER, WINDOW_OFFSET * 2 + TILE_SIZE * state->height + TILE_BORDER, COL_BACKGROUND); ds->org_x = ui->org_x; ds->org_y = ui->org_y; moved_origin = TRUE; draw_update(dr, 0, 0, WINDOW_OFFSET*2 + TILE_SIZE*state->width + TILE_BORDER, WINDOW_OFFSET*2 + TILE_SIZE*state->height + TILE_BORDER); for (phase = 0; phase < 2; phase++) { for (x = 0; x < ds->width; x++) { if (x+1 < ds->width) { if (barrier(state, GX(x), GY(0)) & R) draw_barrier_corner(dr, ds, x, -1, +1, +1, phase); if (barrier(state, GX(x), GY(ds->height-1)) & R) draw_barrier_corner(dr, ds, x, ds->height, +1, -1, phase); } if (barrier(state, GX(x), GY(0)) & U) { draw_barrier_corner(dr, ds, x, -1, -1, +1, phase); draw_barrier_corner(dr, ds, x, -1, +1, +1, phase); draw_barrier(dr, ds, x, -1, D, phase); } if (barrier(state, GX(x), GY(ds->height-1)) & D) { draw_barrier_corner(dr, ds, x, ds->height, -1, -1, phase); draw_barrier_corner(dr, ds, x, ds->height, +1, -1, phase); draw_barrier(dr, ds, x, ds->height, U, phase); } } for (y = 0; y < ds->height; y++) { if (y+1 < ds->height) { if (barrier(state, GX(0), GY(y)) & D) draw_barrier_corner(dr, ds, -1, y, +1, +1, phase); if (barrier(state, GX(ds->width-1), GY(y)) & D) draw_barrier_corner(dr, ds, ds->width, y, -1, +1, phase); } if (barrier(state, GX(0), GY(y)) & L) { draw_barrier_corner(dr, ds, -1, y, +1, -1, phase); draw_barrier_corner(dr, ds, -1, y, +1, +1, phase); draw_barrier(dr, ds, -1, y, R, phase); } if (barrier(state, GX(ds->width-1), GY(y)) & R) { draw_barrier_corner(dr, ds, ds->width, y, -1, -1, phase); draw_barrier_corner(dr, ds, ds->width, y, -1, +1, phase); draw_barrier(dr, ds, ds->width, y, L, phase); } } } } tx = ty = -1; last_rotate_dir = dir==-1 ? oldstate->last_rotate_dir : state->last_rotate_dir; if (oldstate && (t < ROTATE_TIME) && last_rotate_dir) { /* * We're animating a single tile rotation. Find the turning * tile. */ tx = (dir==-1 ? oldstate->last_rotate_x : state->last_rotate_x); ty = (dir==-1 ? oldstate->last_rotate_y : state->last_rotate_y); angle = last_rotate_dir * dir * 90.0F * (t / ROTATE_TIME); state = oldstate; } frame = -1; if (ft > 0) { /* * We're animating a completion flash. Find which frame * we're at. */ frame = (int)(ft / FLASH_FRAME); } /* * Draw any tile which differs from the way it was last drawn. */ active = compute_active(state, ui->cx, ui->cy); for (x = 0; x < ds->width; x++) for (y = 0; y < ds->height; y++) { unsigned char c = tile(state, GX(x), GY(y)) | index(state, active, GX(x), GY(y)); int is_src = GX(x) == ui->cx && GY(y) == ui->cy; int is_anim = GX(x) == tx && GY(y) == ty; int is_cursor = ui->cur_visible && GX(x) == ui->cur_x && GY(y) == ui->cur_y; /* * In a completion flash, we adjust the LOCKED bit * depending on our distance from the centre point and * the frame number. */ if (frame >= 0) { int rcx = RX(ui->cx), rcy = RY(ui->cy); int xdist, ydist, dist; xdist = (x < rcx ? rcx - x : x - rcx); ydist = (y < rcy ? rcy - y : y - rcy); dist = (xdist > ydist ? xdist : ydist); if (frame >= dist && frame < dist+4) { int lock = (frame - dist) & 1; lock = lock ? LOCKED : 0; c = (c &~ LOCKED) | lock; } } if (moved_origin || index(state, ds->visible, x, y) != c || index(state, ds->visible, x, y) == 0xFF || is_src || is_anim || is_cursor) { draw_tile(dr, state, ds, x, y, c, is_src, (is_anim ? angle : 0.0F), is_cursor); if (is_src || is_anim || is_cursor) index(state, ds->visible, x, y) = 0xFF; else index(state, ds->visible, x, y) = c; } } /* * Update the status bar. */ { char statusbuf[256]; int i, n, n2, a; n = state->width * state->height; for (i = a = n2 = 0; i < n; i++) { if (active[i]) a++; if (state->tiles[i] & 0xF) n2++; } sprintf(statusbuf, "%sActive: %d/%d", (state->used_solve ? "Auto-solved. " : state->completed ? "COMPLETED! " : ""), a, n2); status_bar(dr, statusbuf); } sfree(active); } static float game_anim_length(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui) { int last_rotate_dir; /* * Don't animate if last_rotate_dir is zero. */ last_rotate_dir = dir==-1 ? oldstate->last_rotate_dir : newstate->last_rotate_dir; if (last_rotate_dir) return ROTATE_TIME; return 0.0F; } static float game_flash_length(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui) { /* * If the game has just been completed, we display a completion * flash. */ if (!oldstate->completed && newstate->completed && !oldstate->used_solve && !newstate->used_solve) { int size = 0; if (size < newstate->width) size = newstate->width; if (size < newstate->height) size = newstate->height; return FLASH_FRAME * (size+4); } return 0.0F; } static int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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) { int pw, ph; /* * I'll use 8mm squares by default. */ game_compute_size(params, 800, &pw, &ph); *x = pw / 100.0F; *y = ph / 100.0F; } static void draw_diagram(drawing *dr, game_drawstate *ds, int x, int y, int topleft, int v, int drawlines, int ink) { int tx, ty, cx, cy, r, br, k, thick; tx = WINDOW_OFFSET + TILE_SIZE * x; ty = WINDOW_OFFSET + TILE_SIZE * y; /* * Find our centre point. */ if (topleft) { cx = tx + (v & L ? TILE_SIZE / 4 : TILE_SIZE / 6); cy = ty + (v & U ? TILE_SIZE / 4 : TILE_SIZE / 6); r = TILE_SIZE / 8; br = TILE_SIZE / 32; } else { cx = tx + TILE_SIZE / 2; cy = ty + TILE_SIZE / 2; r = TILE_SIZE / 2; br = TILE_SIZE / 8; } thick = r / 20; /* * Draw the square block if we have an endpoint. */ if (v == 1 || v == 2 || v == 4 || v == 8) draw_rect(dr, cx - br, cy - br, br*2, br*2, ink); /* * Draw each radial line. */ if (drawlines) { for (k = 1; k < 16; k *= 2) if (v & k) { int x1 = min(cx, cx + (r-thick) * X(k)); int x2 = max(cx, cx + (r-thick) * X(k)); int y1 = min(cy, cy + (r-thick) * Y(k)); int y2 = max(cy, cy + (r-thick) * Y(k)); draw_rect(dr, x1 - thick, y1 - thick, (x2 - x1) + 2*thick, (y2 - y1) + 2*thick, ink); } } } static void game_print(drawing *dr, const game_state *state, int tilesize) { int w = state->width, h = state->height; 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, TILE_SIZE / (state->wrapping ? 128 : 12)); draw_rect_outline(dr, WINDOW_OFFSET, WINDOW_OFFSET, TILE_SIZE * w, TILE_SIZE * h, ink); /* * Grid. */ print_line_width(dr, TILE_SIZE / 128); for (x = 1; x < w; x++) draw_line(dr, WINDOW_OFFSET + TILE_SIZE * x, WINDOW_OFFSET, WINDOW_OFFSET + TILE_SIZE * x, WINDOW_OFFSET + TILE_SIZE * h, ink); for (y = 1; y < h; y++) draw_line(dr, WINDOW_OFFSET, WINDOW_OFFSET + TILE_SIZE * y, WINDOW_OFFSET + TILE_SIZE * w, WINDOW_OFFSET + TILE_SIZE * y, ink); /* * Barriers. */ for (y = 0; y <= h; y++) for (x = 0; x <= w; x++) { int b = barrier(state, x % w, y % h); if (x < w && (b & U)) draw_rect(dr, WINDOW_OFFSET + TILE_SIZE * x - TILE_SIZE/24, WINDOW_OFFSET + TILE_SIZE * y - TILE_SIZE/24, TILE_SIZE + TILE_SIZE/24 * 2, TILE_SIZE/24 * 2, ink); if (y < h && (b & L)) draw_rect(dr, WINDOW_OFFSET + TILE_SIZE * x - TILE_SIZE/24, WINDOW_OFFSET + TILE_SIZE * y - TILE_SIZE/24, TILE_SIZE/24 * 2, TILE_SIZE + TILE_SIZE/24 * 2, ink); } /* * Grid contents. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) { int vx, v = tile(state, x, y); int locked = v & LOCKED; v &= 0xF; /* * Rotate into a standard orientation for the top left * corner diagram. */ vx = v; while (vx != 0 && vx != 15 && vx != 1 && vx != 9 && vx != 13 && vx != 5) vx = A(vx); /* * Draw the top left corner diagram. */ draw_diagram(dr, ds, x, y, TRUE, vx, TRUE, ink); /* * Draw the real solution diagram, if we're doing so. */ draw_diagram(dr, ds, x, y, FALSE, v, locked, ink); } } #ifdef COMBINED #define thegame net #endif const struct game thegame = { "Net", "games.net", "net", default_params, game_fetch_preset, 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, FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, decode_ui, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, TRUE, FALSE, game_print_size, game_print, TRUE, /* wants_statusbar */ FALSE, game_timing_state, 0, /* flags */ }; puzzles-r9872/netslide.c0000644000175300017530000015647212132232554014416 0ustar simonsimon/* * netslide.c: cross between Net and Sixteen, courtesy of Richard * Boulton. */ #include #include #include #include #include #include #include "puzzles.h" #include "tree234.h" #define MATMUL(xr,yr,m,x,y) do { \ float rx, ry, xx = (x), yy = (y), *mat = (m); \ rx = mat[0] * xx + mat[2] * yy; \ ry = mat[1] * xx + mat[3] * yy; \ (xr) = rx; (yr) = ry; \ } while (0) /* Direction and other bitfields */ #define R 0x01 #define U 0x02 #define L 0x04 #define D 0x08 #define FLASHING 0x10 #define ACTIVE 0x20 /* Corner flags go in the barriers array */ #define RU 0x10 #define UL 0x20 #define LD 0x40 #define DR 0x80 /* Get tile at given coordinate */ #define T(state, x, y) ( (y) * (state)->width + (x) ) /* Rotations: Anticlockwise, Clockwise, Flip, general rotate */ #define A(x) ( (((x) & 0x07) << 1) | (((x) & 0x08) >> 3) ) #define C(x) ( (((x) & 0x0E) >> 1) | (((x) & 0x01) << 3) ) #define F(x) ( (((x) & 0x0C) >> 2) | (((x) & 0x03) << 2) ) #define ROT(x, n) ( ((n)&3) == 0 ? (x) : \ ((n)&3) == 1 ? A(x) : \ ((n)&3) == 2 ? F(x) : C(x) ) /* X and Y displacements */ #define X(x) ( (x) == R ? +1 : (x) == L ? -1 : 0 ) #define Y(x) ( (x) == D ? +1 : (x) == U ? -1 : 0 ) /* Bit count */ #define COUNT(x) ( (((x) & 0x08) >> 3) + (((x) & 0x04) >> 2) + \ (((x) & 0x02) >> 1) + ((x) & 0x01) ) #define PREFERRED_TILE_SIZE 48 #define TILE_SIZE (ds->tilesize) #define BORDER TILE_SIZE #define TILE_BORDER 1 #define WINDOW_OFFSET 0 #define ANIM_TIME 0.13F #define FLASH_FRAME 0.07F enum { COL_BACKGROUND, COL_FLASHING, COL_BORDER, COL_WIRE, COL_ENDPOINT, COL_POWERED, COL_BARRIER, COL_LOWLIGHT, COL_TEXT, NCOLOURS }; struct game_params { int width; int height; int wrapping; float barrier_probability; int movetarget; }; struct game_state { int width, height, cx, cy, wrapping, completed; int used_solve; int move_count, movetarget; /* position (row or col number, starting at 0) of last move. */ int last_move_row, last_move_col; /* direction of last move: +1 or -1 */ int last_move_dir; unsigned char *tiles; unsigned char *barriers; }; #define OFFSET(x2,y2,x1,y1,dir,state) \ ( (x2) = ((x1) + (state)->width + X((dir))) % (state)->width, \ (y2) = ((y1) + (state)->height + Y((dir))) % (state)->height) #define index(state, a, x, y) ( a[(y) * (state)->width + (x)] ) #define tile(state, x, y) index(state, (state)->tiles, x, y) #define barrier(state, x, y) index(state, (state)->barriers, x, y) struct xyd { int x, y, direction; }; static int xyd_cmp(void *av, void *bv) { struct xyd *a = (struct xyd *)av; struct xyd *b = (struct xyd *)bv; if (a->x < b->x) return -1; if (a->x > b->x) return +1; if (a->y < b->y) return -1; if (a->y > b->y) return +1; if (a->direction < b->direction) return -1; if (a->direction > b->direction) return +1; return 0; } static struct xyd *new_xyd(int x, int y, int direction) { struct xyd *xyd = snew(struct xyd); xyd->x = x; xyd->y = y; xyd->direction = direction; return xyd; } static void slide_col(game_state *state, int dir, int col); static void slide_col_int(int w, int h, unsigned char *tiles, int dir, int col); static void slide_row(game_state *state, int dir, int row); static void slide_row_int(int w, int h, unsigned char *tiles, int dir, int row); /* ---------------------------------------------------------------------- * Manage game parameters. */ static game_params *default_params(void) { game_params *ret = snew(game_params); ret->width = 3; ret->height = 3; ret->wrapping = FALSE; ret->barrier_probability = 1.0; ret->movetarget = 0; return ret; } static const struct { int x, y, wrap, bprob; const char* desc; } netslide_presets[] = { {3, 3, FALSE, 1, " easy"}, {3, 3, FALSE, 0, " medium"}, {3, 3, TRUE, 0, " hard"}, {4, 4, FALSE, 1, " easy"}, {4, 4, FALSE, 0, " medium"}, {4, 4, TRUE, 0, " hard"}, {5, 5, FALSE, 1, " easy"}, {5, 5, FALSE, 0, " medium"}, {5, 5, TRUE, 0, " hard"}, }; static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char str[80]; if (i < 0 || i >= lenof(netslide_presets)) return FALSE; ret = snew(game_params); ret->width = netslide_presets[i].x; ret->height = netslide_presets[i].y; ret->wrapping = netslide_presets[i].wrap; ret->barrier_probability = (float)netslide_presets[i].bprob; ret->movetarget = 0; sprintf(str, "%dx%d%s", ret->width, ret->height, netslide_presets[i].desc); *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 *ret, char const *string) { char const *p = string; ret->wrapping = FALSE; ret->barrier_probability = 0.0; ret->movetarget = 0; ret->width = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; if (*p == 'x') { p++; ret->height = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; if ( (ret->wrapping = (*p == 'w')) != 0 ) p++; if (*p == 'b') { ret->barrier_probability = (float)atof(++p); while (*p && (isdigit((unsigned char)*p) || *p == '.')) p++; } if (*p == 'm') { ret->movetarget = atoi(++p); } } else { ret->height = ret->width; } } static char *encode_params(const game_params *params, int full) { char ret[400]; int len; len = sprintf(ret, "%dx%d", params->width, params->height); if (params->wrapping) ret[len++] = 'w'; if (full && params->barrier_probability) len += sprintf(ret+len, "b%g", params->barrier_probability); /* Shuffle limit is part of the limited parameters, because we have to * provide the target move count. */ if (params->movetarget) len += sprintf(ret+len, "m%d", params->movetarget); assert(len < lenof(ret)); ret[len] = '\0'; return dupstr(ret); } static config_item *game_configure(const game_params *params) { config_item *ret; char buf[80]; ret = snewn(6, config_item); ret[0].name = "Width"; ret[0].type = C_STRING; sprintf(buf, "%d", params->width); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->height); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Walls wrap around"; ret[2].type = C_BOOLEAN; ret[2].sval = NULL; ret[2].ival = params->wrapping; ret[3].name = "Barrier probability"; ret[3].type = C_STRING; sprintf(buf, "%g", params->barrier_probability); ret[3].sval = dupstr(buf); ret[3].ival = 0; ret[4].name = "Number of shuffling moves"; ret[4].type = C_STRING; sprintf(buf, "%d", params->movetarget); ret[4].sval = dupstr(buf); ret[4].ival = 0; ret[5].name = NULL; ret[5].type = C_END; ret[5].sval = NULL; ret[5].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->width = atoi(cfg[0].sval); ret->height = atoi(cfg[1].sval); ret->wrapping = cfg[2].ival; ret->barrier_probability = (float)atof(cfg[3].sval); ret->movetarget = atoi(cfg[4].sval); return ret; } static char *validate_params(const game_params *params, int full) { if (params->width <= 1 || params->height <= 1) return "Width and height must both be greater than one"; if (params->barrier_probability < 0) return "Barrier probability may not be negative"; if (params->barrier_probability > 1) return "Barrier probability may not be greater than 1"; return NULL; } /* ---------------------------------------------------------------------- * Randomly select a new game description. */ static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { tree234 *possibilities, *barriertree; int w, h, x, y, cx, cy, nbarriers; unsigned char *tiles, *barriers; char *desc, *p; w = params->width; h = params->height; tiles = snewn(w * h, unsigned char); memset(tiles, 0, w * h); barriers = snewn(w * h, unsigned char); memset(barriers, 0, w * h); cx = w / 2; cy = h / 2; /* * Construct the unshuffled grid. * * To do this, we simply start at the centre point, repeatedly * choose a random possibility out of the available ways to * extend a used square into an unused one, and do it. After * extending the third line out of a square, we remove the * fourth from the possibilities list to avoid any full-cross * squares (which would make the game too easy because they * only have one orientation). * * The slightly worrying thing is the avoidance of full-cross * squares. Can this cause our unsophisticated construction * algorithm to paint itself into a corner, by getting into a * situation where there are some unreached squares and the * only way to reach any of them is to extend a T-piece into a * full cross? * * Answer: no it can't, and here's a proof. * * Any contiguous group of such unreachable squares must be * surrounded on _all_ sides by T-pieces pointing away from the * group. (If not, then there is a square which can be extended * into one of the `unreachable' ones, and so it wasn't * unreachable after all.) In particular, this implies that * each contiguous group of unreachable squares must be * rectangular in shape (any deviation from that yields a * non-T-piece next to an `unreachable' square). * * So we have a rectangle of unreachable squares, with T-pieces * forming a solid border around the rectangle. The corners of * that border must be connected (since every tile connects all * the lines arriving in it), and therefore the border must * form a closed loop around the rectangle. * * But this can't have happened in the first place, since we * _know_ we've avoided creating closed loops! Hence, no such * situation can ever arise, and the naive grid construction * algorithm will guaranteeably result in a complete grid * containing no unreached squares, no full crosses _and_ no * closed loops. [] */ possibilities = newtree234(xyd_cmp); if (cx+1 < w) add234(possibilities, new_xyd(cx, cy, R)); if (cy-1 >= 0) add234(possibilities, new_xyd(cx, cy, U)); if (cx-1 >= 0) add234(possibilities, new_xyd(cx, cy, L)); if (cy+1 < h) add234(possibilities, new_xyd(cx, cy, D)); while (count234(possibilities) > 0) { int i; struct xyd *xyd; int x1, y1, d1, x2, y2, d2, d; /* * Extract a randomly chosen possibility from the list. */ i = random_upto(rs, count234(possibilities)); xyd = delpos234(possibilities, i); x1 = xyd->x; y1 = xyd->y; d1 = xyd->direction; sfree(xyd); OFFSET(x2, y2, x1, y1, d1, params); d2 = F(d1); #ifdef GENERATION_DIAGNOSTICS printf("picked (%d,%d,%c) <-> (%d,%d,%c)\n", x1, y1, "0RU3L567D9abcdef"[d1], x2, y2, "0RU3L567D9abcdef"[d2]); #endif /* * Make the connection. (We should be moving to an as yet * unused tile.) */ index(params, tiles, x1, y1) |= d1; assert(index(params, tiles, x2, y2) == 0); index(params, tiles, x2, y2) |= d2; /* * If we have created a T-piece, remove its last * possibility. */ if (COUNT(index(params, tiles, x1, y1)) == 3) { struct xyd xyd1, *xydp; xyd1.x = x1; xyd1.y = y1; xyd1.direction = 0x0F ^ index(params, tiles, x1, y1); xydp = find234(possibilities, &xyd1, NULL); if (xydp) { #ifdef GENERATION_DIAGNOSTICS printf("T-piece; removing (%d,%d,%c)\n", xydp->x, xydp->y, "0RU3L567D9abcdef"[xydp->direction]); #endif del234(possibilities, xydp); sfree(xydp); } } /* * Remove all other possibilities that were pointing at the * tile we've just moved into. */ for (d = 1; d < 0x10; d <<= 1) { int x3, y3, d3; struct xyd xyd1, *xydp; OFFSET(x3, y3, x2, y2, d, params); d3 = F(d); xyd1.x = x3; xyd1.y = y3; xyd1.direction = d3; xydp = find234(possibilities, &xyd1, NULL); if (xydp) { #ifdef GENERATION_DIAGNOSTICS printf("Loop avoidance; removing (%d,%d,%c)\n", xydp->x, xydp->y, "0RU3L567D9abcdef"[xydp->direction]); #endif del234(possibilities, xydp); sfree(xydp); } } /* * Add new possibilities to the list for moving _out_ of * the tile we have just moved into. */ for (d = 1; d < 0x10; d <<= 1) { int x3, y3; if (d == d2) continue; /* we've got this one already */ if (!params->wrapping) { if (d == U && y2 == 0) continue; if (d == D && y2 == h-1) continue; if (d == L && x2 == 0) continue; if (d == R && x2 == w-1) continue; } OFFSET(x3, y3, x2, y2, d, params); if (index(params, tiles, x3, y3)) continue; /* this would create a loop */ #ifdef GENERATION_DIAGNOSTICS printf("New frontier; adding (%d,%d,%c)\n", x2, y2, "0RU3L567D9abcdef"[d]); #endif add234(possibilities, new_xyd(x2, y2, d)); } } /* Having done that, we should have no possibilities remaining. */ assert(count234(possibilities) == 0); freetree234(possibilities); /* * Now compute a list of the possible barrier locations. */ barriertree = newtree234(xyd_cmp); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { if (!(index(params, tiles, x, y) & R) && (params->wrapping || x < w-1)) add234(barriertree, new_xyd(x, y, R)); if (!(index(params, tiles, x, y) & D) && (params->wrapping || y < h-1)) add234(barriertree, new_xyd(x, y, D)); } } /* * Save the unshuffled grid in aux. */ { char *solution; int i; /* * String format is exactly the same as a solve move, so we * can just dupstr this in solve_game(). */ solution = snewn(w * h + 2, char); solution[0] = 'S'; for (i = 0; i < w * h; i++) solution[i+1] = "0123456789abcdef"[tiles[i] & 0xF]; solution[w*h+1] = '\0'; *aux = solution; } /* * Now shuffle the grid. * FIXME - this simply does a set of random moves to shuffle the pieces, * although we make a token effort to avoid boring cases by avoiding moves * that directly undo the previous one, or that repeat so often as to * turn into fewer moves. * * A better way would be to number all the pieces, generate a placement * for all the numbers as for "sixteen", observing parity constraints if * neccessary, and then place the pieces according to their numbering. * BUT - I'm not sure if this will work, since we disallow movement of * the middle row and column. */ { int i; int cols = w - 1; int rows = h - 1; int moves = params->movetarget; int prevdir = -1, prevrowcol = -1, nrepeats = 0; if (!moves) moves = cols * rows * 2; for (i = 0; i < moves; /* incremented conditionally */) { /* Choose a direction: 0,1,2,3 = up, right, down, left. */ int dir = random_upto(rs, 4); int rowcol; if (dir % 2 == 0) { int col = random_upto(rs, cols); if (col >= cx) col += 1; /* avoid centre */ if (col == prevrowcol) { if (dir == 2-prevdir) continue; /* undoes last move */ else if (dir == prevdir && (nrepeats+1)*2 > h) continue; /* makes fewer moves */ } slide_col_int(w, h, tiles, 1 - dir, col); rowcol = col; } else { int row = random_upto(rs, rows); if (row >= cy) row += 1; /* avoid centre */ if (row == prevrowcol) { if (dir == 4-prevdir) continue; /* undoes last move */ else if (dir == prevdir && (nrepeats+1)*2 > w) continue; /* makes fewer moves */ } slide_row_int(w, h, tiles, 2 - dir, row); rowcol = row; } if (dir == prevdir && rowcol == prevrowcol) nrepeats++; else nrepeats = 1; prevdir = dir; prevrowcol = rowcol; i++; /* if we got here, the move was accepted */ } } /* * And now choose barrier locations. (We carefully do this * _after_ shuffling, so that changing the barrier rate in the * params while keeping the random seed the same will give the * same shuffled grid and _only_ change the barrier locations. * Also the way we choose barrier locations, by repeatedly * choosing one possibility from the list until we have enough, * is designed to ensure that raising the barrier rate while * keeping the seed the same will provide a superset of the * previous barrier set - i.e. if you ask for 10 barriers, and * then decide that's still too hard and ask for 20, you'll get * the original 10 plus 10 more, rather than getting 20 new * ones and the chance of remembering your first 10.) */ nbarriers = (int)(params->barrier_probability * count234(barriertree)); assert(nbarriers >= 0 && nbarriers <= count234(barriertree)); while (nbarriers > 0) { int i; struct xyd *xyd; int x1, y1, d1, x2, y2, d2; /* * Extract a randomly chosen barrier from the list. */ i = random_upto(rs, count234(barriertree)); xyd = delpos234(barriertree, i); assert(xyd != NULL); x1 = xyd->x; y1 = xyd->y; d1 = xyd->direction; sfree(xyd); OFFSET(x2, y2, x1, y1, d1, params); d2 = F(d1); index(params, barriers, x1, y1) |= d1; index(params, barriers, x2, y2) |= d2; nbarriers--; } /* * Clean up the rest of the barrier list. */ { struct xyd *xyd; while ( (xyd = delpos234(barriertree, 0)) != NULL) sfree(xyd); freetree234(barriertree); } /* * Finally, encode the grid into a string game description. * * My syntax is extremely simple: each square is encoded as a * hex digit in which bit 0 means a connection on the right, * bit 1 means up, bit 2 left and bit 3 down. (i.e. the same * encoding as used internally). Each digit is followed by * optional barrier indicators: `v' means a vertical barrier to * the right of it, and `h' means a horizontal barrier below * it. */ desc = snewn(w * h * 3 + 1, char); p = desc; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { *p++ = "0123456789abcdef"[index(params, tiles, x, y)]; if ((params->wrapping || x < w-1) && (index(params, barriers, x, y) & R)) *p++ = 'v'; if ((params->wrapping || y < h-1) && (index(params, barriers, x, y) & D)) *p++ = 'h'; } } assert(p - desc <= w*h*3); *p = '\0'; sfree(tiles); sfree(barriers); return desc; } static char *validate_desc(const game_params *params, const char *desc) { int w = params->width, h = params->height; int i; for (i = 0; i < w*h; i++) { if (*desc >= '0' && *desc <= '9') /* OK */; else if (*desc >= 'a' && *desc <= 'f') /* OK */; else if (*desc >= 'A' && *desc <= 'F') /* OK */; else if (!*desc) return "Game description shorter than expected"; else return "Game description contained unexpected character"; desc++; while (*desc == 'h' || *desc == 'v') desc++; } if (*desc) return "Game description longer than expected"; return NULL; } /* ---------------------------------------------------------------------- * Construct an initial game state, given a description and parameters. */ static game_state *new_game(midend *me, const game_params *params, const char *desc) { game_state *state; int w, h, x, y; assert(params->width > 0 && params->height > 0); assert(params->width > 1 || params->height > 1); /* * Create a blank game state. */ state = snew(game_state); w = state->width = params->width; h = state->height = params->height; state->cx = state->width / 2; state->cy = state->height / 2; state->wrapping = params->wrapping; state->movetarget = params->movetarget; state->completed = 0; state->used_solve = FALSE; state->move_count = 0; state->last_move_row = -1; state->last_move_col = -1; state->last_move_dir = 0; state->tiles = snewn(state->width * state->height, unsigned char); memset(state->tiles, 0, state->width * state->height); state->barriers = snewn(state->width * state->height, unsigned char); memset(state->barriers, 0, state->width * state->height); /* * Parse the game description into the grid. */ for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { if (*desc >= '0' && *desc <= '9') tile(state, x, y) = *desc - '0'; else if (*desc >= 'a' && *desc <= 'f') tile(state, x, y) = *desc - 'a' + 10; else if (*desc >= 'A' && *desc <= 'F') tile(state, x, y) = *desc - 'A' + 10; if (*desc) desc++; while (*desc == 'h' || *desc == 'v') { int x2, y2, d1, d2; if (*desc == 'v') d1 = R; else d1 = D; OFFSET(x2, y2, x, y, d1, state); d2 = F(d1); barrier(state, x, y) |= d1; barrier(state, x2, y2) |= d2; desc++; } } } /* * Set up border barriers if this is a non-wrapping game. */ if (!state->wrapping) { for (x = 0; x < state->width; x++) { barrier(state, x, 0) |= U; barrier(state, x, state->height-1) |= D; } for (y = 0; y < state->height; y++) { barrier(state, 0, y) |= L; barrier(state, state->width-1, y) |= R; } } /* * Set up the barrier corner flags, for drawing barriers * prettily when they meet. */ for (y = 0; y < state->height; y++) { for (x = 0; x < state->width; x++) { int dir; for (dir = 1; dir < 0x10; dir <<= 1) { int dir2 = A(dir); int x1, y1, x2, y2, x3, y3; int corner = FALSE; if (!(barrier(state, x, y) & dir)) continue; if (barrier(state, x, y) & dir2) corner = TRUE; x1 = x + X(dir), y1 = y + Y(dir); if (x1 >= 0 && x1 < state->width && y1 >= 0 && y1 < state->height && (barrier(state, x1, y1) & dir2)) corner = TRUE; x2 = x + X(dir2), y2 = y + Y(dir2); if (x2 >= 0 && x2 < state->width && y2 >= 0 && y2 < state->height && (barrier(state, x2, y2) & dir)) corner = TRUE; if (corner) { barrier(state, x, y) |= (dir << 4); if (x1 >= 0 && x1 < state->width && y1 >= 0 && y1 < state->height) barrier(state, x1, y1) |= (A(dir) << 4); if (x2 >= 0 && x2 < state->width && y2 >= 0 && y2 < state->height) barrier(state, x2, y2) |= (C(dir) << 4); x3 = x + X(dir) + X(dir2), y3 = y + Y(dir) + Y(dir2); if (x3 >= 0 && x3 < state->width && y3 >= 0 && y3 < state->height) barrier(state, x3, y3) |= (F(dir) << 4); } } } } return state; } static game_state *dup_game(const game_state *state) { game_state *ret; ret = snew(game_state); ret->width = state->width; ret->height = state->height; ret->cx = state->cx; ret->cy = state->cy; ret->wrapping = state->wrapping; ret->movetarget = state->movetarget; ret->completed = state->completed; ret->used_solve = state->used_solve; ret->move_count = state->move_count; ret->last_move_row = state->last_move_row; ret->last_move_col = state->last_move_col; ret->last_move_dir = state->last_move_dir; ret->tiles = snewn(state->width * state->height, unsigned char); memcpy(ret->tiles, state->tiles, state->width * state->height); ret->barriers = snewn(state->width * state->height, unsigned char); memcpy(ret->barriers, state->barriers, state->width * state->height); return ret; } static void free_game(game_state *state) { sfree(state->tiles); sfree(state->barriers); sfree(state); } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { if (!aux) { *error = "Solution not known for this puzzle"; return NULL; } return dupstr(aux); } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { return NULL; } /* ---------------------------------------------------------------------- * Utility routine. */ /* * Compute which squares are reachable from the centre square, as a * quick visual aid to determining how close the game is to * completion. This is also a simple way to tell if the game _is_ * completed - just call this function and see whether every square * is marked active. * * squares in the moving_row and moving_col are always inactive - this * is so that "current" doesn't appear to jump across moving lines. */ static unsigned char *compute_active(const game_state *state, int moving_row, int moving_col) { unsigned char *active; tree234 *todo; struct xyd *xyd; active = snewn(state->width * state->height, unsigned char); memset(active, 0, state->width * state->height); /* * We only store (x,y) pairs in todo, but it's easier to reuse * xyd_cmp and just store direction 0 every time. */ todo = newtree234(xyd_cmp); index(state, active, state->cx, state->cy) = ACTIVE; add234(todo, new_xyd(state->cx, state->cy, 0)); while ( (xyd = delpos234(todo, 0)) != NULL) { int x1, y1, d1, x2, y2, d2; x1 = xyd->x; y1 = xyd->y; sfree(xyd); for (d1 = 1; d1 < 0x10; d1 <<= 1) { OFFSET(x2, y2, x1, y1, d1, state); d2 = F(d1); /* * If the next tile in this direction is connected to * us, and there isn't a barrier in the way, and it * isn't already marked active, then mark it active and * add it to the to-examine list. */ if ((x2 != moving_col && y2 != moving_row) && (tile(state, x1, y1) & d1) && (tile(state, x2, y2) & d2) && !(barrier(state, x1, y1) & d1) && !index(state, active, x2, y2)) { index(state, active, x2, y2) = ACTIVE; add234(todo, new_xyd(x2, y2, 0)); } } } /* Now we expect the todo list to have shrunk to zero size. */ assert(count234(todo) == 0); freetree234(todo); return active; } struct game_ui { int cur_x, cur_y; int cur_visible; }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->cur_x = 0; ui->cur_y = -1; ui->cur_visible = FALSE; 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) { } /* ---------------------------------------------------------------------- * Process a move. */ static void slide_row_int(int w, int h, unsigned char *tiles, int dir, int row) { int x = dir > 0 ? -1 : w; int tx = x + dir; int n = w - 1; unsigned char endtile = tiles[row * w + tx]; do { x = tx; tx = (x + dir + w) % w; tiles[row * w + x] = tiles[row * w + tx]; } while (--n > 0); tiles[row * w + tx] = endtile; } static void slide_col_int(int w, int h, unsigned char *tiles, int dir, int col) { int y = dir > 0 ? -1 : h; int ty = y + dir; int n = h - 1; unsigned char endtile = tiles[ty * w + col]; do { y = ty; ty = (y + dir + h) % h; tiles[y * w + col] = tiles[ty * w + col]; } while (--n > 0); tiles[ty * w + col] = endtile; } static void slide_row(game_state *state, int dir, int row) { slide_row_int(state->width, state->height, state->tiles, dir, row); } static void slide_col(game_state *state, int dir, int col) { slide_col_int(state->width, state->height, state->tiles, dir, col); } static void game_changed_state(game_ui *ui, const game_state *oldstate, const game_state *newstate) { } struct game_drawstate { int started; int width, height; int tilesize; unsigned char *visible; int cur_x, cur_y; }; static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { int cx, cy; int dx, dy; char buf[80]; button &= ~MOD_MASK; if (IS_CURSOR_MOVE(button)) { int cpos, diff = 0; cpos = c2pos(state->width, state->height, ui->cur_x, ui->cur_y); diff = c2diff(state->width, state->height, ui->cur_x, ui->cur_y, button); if (diff != 0) { do { /* we might have to do this more than once to skip missing arrows */ cpos += diff; pos2c(state->width, state->height, cpos, &ui->cur_x, &ui->cur_y); } while (ui->cur_x == state->cx || ui->cur_y == state->cy); } ui->cur_visible = 1; return ""; } if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { cx = (x - (BORDER + WINDOW_OFFSET + TILE_BORDER) + 2*TILE_SIZE) / TILE_SIZE - 2; cy = (y - (BORDER + WINDOW_OFFSET + TILE_BORDER) + 2*TILE_SIZE) / TILE_SIZE - 2; ui->cur_visible = 0; } else if (IS_CURSOR_SELECT(button)) { if (ui->cur_visible) { cx = ui->cur_x; cy = ui->cur_y; } else { /* 'click' when cursor is invisible just makes cursor visible. */ ui->cur_visible = 1; return ""; } } else return NULL; if (cy >= 0 && cy < state->height && cy != state->cy) { if (cx == -1) dx = +1; else if (cx == state->width) dx = -1; else return NULL; dy = 0; } else if (cx >= 0 && cx < state->width && cx != state->cx) { if (cy == -1) dy = +1; else if (cy == state->height) dy = -1; else return NULL; dx = 0; } else return NULL; /* reverse direction if right hand button is pressed */ if (button == RIGHT_BUTTON) { dx = -dx; dy = -dy; } if (dx == 0) sprintf(buf, "C%d,%d", cx, dy); else sprintf(buf, "R%d,%d", cy, dx); return dupstr(buf); } static game_state *execute_move(const game_state *from, const char *move) { game_state *ret; int c, d, col; if ((move[0] == 'C' || move[0] == 'R') && sscanf(move+1, "%d,%d", &c, &d) == 2 && c >= 0 && c < (move[0] == 'C' ? from->width : from->height)) { col = (move[0] == 'C'); } else if (move[0] == 'S' && strlen(move) == from->width * from->height + 1) { int i; ret = dup_game(from); ret->used_solve = TRUE; ret->completed = ret->move_count = 1; for (i = 0; i < from->width * from->height; i++) { c = move[i+1]; if (c >= '0' && c <= '9') c -= '0'; else if (c >= 'A' && c <= 'F') c -= 'A' - 10; else if (c >= 'a' && c <= 'f') c -= 'a' - 10; else { free_game(ret); return NULL; } ret->tiles[i] = c; } return ret; } else return NULL; /* can't parse move string */ ret = dup_game(from); if (col) slide_col(ret, d, c); else slide_row(ret, d, c); ret->move_count++; ret->last_move_row = col ? -1 : c; ret->last_move_col = col ? c : -1; ret->last_move_dir = d; /* * See if the game has been completed. */ if (!ret->completed) { unsigned char *active = compute_active(ret, -1, -1); int x1, y1; int complete = TRUE; for (x1 = 0; x1 < ret->width; x1++) for (y1 = 0; y1 < ret->height; y1++) if (!index(ret, active, x1, y1)) { complete = FALSE; goto break_label; /* break out of two loops at once */ } break_label: sfree(active); if (complete) ret->completed = ret->move_count; } return ret; } /* ---------------------------------------------------------------------- * Routines for drawing the game position on the screen. */ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { game_drawstate *ds = snew(game_drawstate); ds->started = FALSE; ds->width = state->width; ds->height = state->height; ds->visible = snewn(state->width * state->height, unsigned char); ds->tilesize = 0; /* not decided yet */ memset(ds->visible, 0xFF, state->width * state->height); ds->cur_x = ds->cur_y = -1; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->visible); sfree(ds); } 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 = BORDER * 2 + WINDOW_OFFSET * 2 + TILE_SIZE * params->width + TILE_BORDER; *y = BORDER * 2 + WINDOW_OFFSET * 2 + TILE_SIZE * params->height + TILE_BORDER; } 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; ret = snewn(NCOLOURS * 3, float); *ncolours = NCOLOURS; /* * Basic background colour is whatever the front end thinks is * a sensible default. */ frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); /* * Wires are black. */ ret[COL_WIRE * 3 + 0] = 0.0F; ret[COL_WIRE * 3 + 1] = 0.0F; ret[COL_WIRE * 3 + 2] = 0.0F; /* * Powered wires and powered endpoints are cyan. */ ret[COL_POWERED * 3 + 0] = 0.0F; ret[COL_POWERED * 3 + 1] = 1.0F; ret[COL_POWERED * 3 + 2] = 1.0F; /* * Barriers are red. */ ret[COL_BARRIER * 3 + 0] = 1.0F; ret[COL_BARRIER * 3 + 1] = 0.0F; ret[COL_BARRIER * 3 + 2] = 0.0F; /* * Unpowered endpoints are blue. */ ret[COL_ENDPOINT * 3 + 0] = 0.0F; ret[COL_ENDPOINT * 3 + 1] = 0.0F; ret[COL_ENDPOINT * 3 + 2] = 1.0F; /* * Tile borders are a darker grey than the background. */ ret[COL_BORDER * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0]; ret[COL_BORDER * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1]; ret[COL_BORDER * 3 + 2] = 0.5F * ret[COL_BACKGROUND * 3 + 2]; /* * Flashing tiles are a grey in between those two. */ ret[COL_FLASHING * 3 + 0] = 0.75F * ret[COL_BACKGROUND * 3 + 0]; ret[COL_FLASHING * 3 + 1] = 0.75F * ret[COL_BACKGROUND * 3 + 1]; ret[COL_FLASHING * 3 + 2] = 0.75F * ret[COL_BACKGROUND * 3 + 2]; ret[COL_LOWLIGHT * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.8F; ret[COL_LOWLIGHT * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.8F; ret[COL_LOWLIGHT * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 0.8F; ret[COL_TEXT * 3 + 0] = 0.0; ret[COL_TEXT * 3 + 1] = 0.0; ret[COL_TEXT * 3 + 2] = 0.0; return ret; } static void draw_filled_line(drawing *dr, int x1, int y1, int x2, int y2, int colour) { draw_line(dr, x1-1, y1, x2-1, y2, COL_WIRE); draw_line(dr, x1+1, y1, x2+1, y2, COL_WIRE); draw_line(dr, x1, y1-1, x2, y2-1, COL_WIRE); draw_line(dr, x1, y1+1, x2, y2+1, COL_WIRE); draw_line(dr, x1, y1, x2, y2, colour); } static void draw_rect_coords(drawing *dr, int x1, int y1, int x2, int y2, int colour) { int mx = (x1 < x2 ? x1 : x2); int my = (y1 < y2 ? y1 : y2); int dx = (x2 + x1 - 2*mx + 1); int dy = (y2 + y1 - 2*my + 1); draw_rect(dr, mx, my, dx, dy, colour); } static void draw_barrier_corner(drawing *dr, game_drawstate *ds, int x, int y, int dir, int phase) { int bx = BORDER + WINDOW_OFFSET + TILE_SIZE * x; int by = BORDER + WINDOW_OFFSET + TILE_SIZE * y; int x1, y1, dx, dy, dir2; dir >>= 4; dir2 = A(dir); dx = X(dir) + X(dir2); dy = Y(dir) + Y(dir2); x1 = (dx > 0 ? TILE_SIZE+TILE_BORDER-1 : 0); y1 = (dy > 0 ? TILE_SIZE+TILE_BORDER-1 : 0); if (phase == 0) { draw_rect_coords(dr, bx+x1, by+y1, bx+x1-TILE_BORDER*dx, by+y1-(TILE_BORDER-1)*dy, COL_WIRE); draw_rect_coords(dr, bx+x1, by+y1, bx+x1-(TILE_BORDER-1)*dx, by+y1-TILE_BORDER*dy, COL_WIRE); } else { draw_rect_coords(dr, bx+x1, by+y1, bx+x1-(TILE_BORDER-1)*dx, by+y1-(TILE_BORDER-1)*dy, COL_BARRIER); } } static void draw_barrier(drawing *dr, game_drawstate *ds, int x, int y, int dir, int phase) { int bx = BORDER + WINDOW_OFFSET + TILE_SIZE * x; int by = BORDER + WINDOW_OFFSET + TILE_SIZE * y; int x1, y1, w, h; x1 = (X(dir) > 0 ? TILE_SIZE : X(dir) == 0 ? TILE_BORDER : 0); y1 = (Y(dir) > 0 ? TILE_SIZE : Y(dir) == 0 ? TILE_BORDER : 0); w = (X(dir) ? TILE_BORDER : TILE_SIZE - TILE_BORDER); h = (Y(dir) ? TILE_BORDER : TILE_SIZE - TILE_BORDER); if (phase == 0) { draw_rect(dr, bx+x1-X(dir), by+y1-Y(dir), w, h, COL_WIRE); } else { draw_rect(dr, bx+x1, by+y1, w, h, COL_BARRIER); } } static void draw_tile(drawing *dr, game_drawstate *ds, const game_state *state, int x, int y, int tile, float xshift, float yshift) { int bx = BORDER + WINDOW_OFFSET + TILE_SIZE * x + (int)(xshift * TILE_SIZE); int by = BORDER + WINDOW_OFFSET + TILE_SIZE * y + (int)(yshift * TILE_SIZE); float cx, cy, ex, ey; int dir, col; /* * When we draw a single tile, we must draw everything up to * and including the borders around the tile. This means that * if the neighbouring tiles have connections to those borders, * we must draw those connections on the borders themselves. * * This would be terribly fiddly if we ever had to draw a tile * while its neighbour was in mid-rotate, because we'd have to * arrange to _know_ that the neighbour was being rotated and * hence had an anomalous effect on the redraw of this tile. * Fortunately, the drawing algorithm avoids ever calling us in * this circumstance: we're either drawing lots of straight * tiles at game start or after a move is complete, or we're * repeatedly drawing only the rotating tile. So no problem. */ /* * So. First blank the tile out completely: draw a big * rectangle in border colour, and a smaller rectangle in * background colour to fill it in. */ draw_rect(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER, COL_BORDER); draw_rect(dr, bx+TILE_BORDER, by+TILE_BORDER, TILE_SIZE-TILE_BORDER, TILE_SIZE-TILE_BORDER, tile & FLASHING ? COL_FLASHING : COL_BACKGROUND); /* * Draw the wires. */ cx = cy = TILE_BORDER + (TILE_SIZE-TILE_BORDER) / 2.0F - 0.5F; col = (tile & ACTIVE ? COL_POWERED : COL_WIRE); for (dir = 1; dir < 0x10; dir <<= 1) { if (tile & dir) { ex = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * X(dir); ey = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * Y(dir); draw_filled_line(dr, bx+(int)cx, by+(int)cy, bx+(int)(cx+ex), by+(int)(cy+ey), COL_WIRE); } } for (dir = 1; dir < 0x10; dir <<= 1) { if (tile & dir) { ex = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * X(dir); ey = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * Y(dir); draw_line(dr, bx+(int)cx, by+(int)cy, bx+(int)(cx+ex), by+(int)(cy+ey), col); } } /* * Draw the box in the middle. We do this in blue if the tile * is an unpowered endpoint, in cyan if the tile is a powered * endpoint, in black if the tile is the centrepiece, and * otherwise not at all. */ col = -1; if (x == state->cx && y == state->cy) col = COL_WIRE; else if (COUNT(tile) == 1) { col = (tile & ACTIVE ? COL_POWERED : COL_ENDPOINT); } if (col >= 0) { int i, points[8]; points[0] = +1; points[1] = +1; points[2] = +1; points[3] = -1; points[4] = -1; points[5] = -1; points[6] = -1; points[7] = +1; for (i = 0; i < 8; i += 2) { ex = (TILE_SIZE * 0.24F) * points[i]; ey = (TILE_SIZE * 0.24F) * points[i+1]; points[i] = bx+(int)(cx+ex); points[i+1] = by+(int)(cy+ey); } draw_polygon(dr, points, 4, col, COL_WIRE); } /* * Draw the points on the border if other tiles are connected * to us. */ for (dir = 1; dir < 0x10; dir <<= 1) { int dx, dy, px, py, lx, ly, vx, vy, ox, oy; dx = X(dir); dy = Y(dir); ox = x + dx; oy = y + dy; if (ox < 0 || ox >= state->width || oy < 0 || oy >= state->height) continue; if (!(tile(state, ox, oy) & F(dir))) continue; px = bx + (int)(dx>0 ? TILE_SIZE + TILE_BORDER - 1 : dx<0 ? 0 : cx); py = by + (int)(dy>0 ? TILE_SIZE + TILE_BORDER - 1 : dy<0 ? 0 : cy); lx = dx * (TILE_BORDER-1); ly = dy * (TILE_BORDER-1); vx = (dy ? 1 : 0); vy = (dx ? 1 : 0); if (xshift == 0.0 && yshift == 0.0 && (tile & dir)) { /* * If we are fully connected to the other tile, we must * draw right across the tile border. (We can use our * own ACTIVE state to determine what colour to do this * in: if we are fully connected to the other tile then * the two ACTIVE states will be the same.) */ draw_rect_coords(dr, px-vx, py-vy, px+lx+vx, py+ly+vy, COL_WIRE); draw_rect_coords(dr, px, py, px+lx, py+ly, (tile & ACTIVE) ? COL_POWERED : COL_WIRE); } else { /* * The other tile extends into our border, but isn't * actually connected to us. Just draw a single black * dot. */ draw_rect_coords(dr, px, py, px, py, COL_WIRE); } } draw_update(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER); } static void draw_tile_barriers(drawing *dr, game_drawstate *ds, const game_state *state, int x, int y) { int phase; int dir; int bx = BORDER + WINDOW_OFFSET + TILE_SIZE * x; int by = BORDER + WINDOW_OFFSET + TILE_SIZE * y; /* * Draw barrier corners, and then barriers. */ for (phase = 0; phase < 2; phase++) { for (dir = 1; dir < 0x10; dir <<= 1) if (barrier(state, x, y) & (dir << 4)) draw_barrier_corner(dr, ds, x, y, dir << 4, phase); for (dir = 1; dir < 0x10; dir <<= 1) if (barrier(state, x, y) & dir) draw_barrier(dr, ds, x, y, dir, phase); } draw_update(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER); } static void draw_arrow(drawing *dr, game_drawstate *ds, int x, int y, int xdx, int xdy, int cur) { int coords[14]; int ydy = -xdx, ydx = xdy; x = x * TILE_SIZE + BORDER + WINDOW_OFFSET; y = y * TILE_SIZE + BORDER + WINDOW_OFFSET; #define POINT(n, xx, yy) ( \ coords[2*(n)+0] = x + (xx)*xdx + (yy)*ydx, \ coords[2*(n)+1] = y + (xx)*xdy + (yy)*ydy) POINT(0, TILE_SIZE / 2, 3 * TILE_SIZE / 4); /* top of arrow */ POINT(1, 3 * TILE_SIZE / 4, TILE_SIZE / 2); /* right corner */ POINT(2, 5 * TILE_SIZE / 8, TILE_SIZE / 2); /* right concave */ POINT(3, 5 * TILE_SIZE / 8, TILE_SIZE / 4); /* bottom right */ POINT(4, 3 * TILE_SIZE / 8, TILE_SIZE / 4); /* bottom left */ POINT(5, 3 * TILE_SIZE / 8, TILE_SIZE / 2); /* left concave */ POINT(6, TILE_SIZE / 4, TILE_SIZE / 2); /* left corner */ draw_polygon(dr, coords, 7, cur ? COL_POWERED : COL_LOWLIGHT, COL_TEXT); } static void draw_arrow_for_cursor(drawing *dr, game_drawstate *ds, int cur_x, int cur_y, int cur) { if (cur_x == -1 && cur_y == -1) return; /* 'no cursur here */ else if (cur_x == -1) /* LH column. */ draw_arrow(dr, ds, 0, cur_y+1, 0, -1, cur); else if (cur_x == ds->width) /* RH column */ draw_arrow(dr, ds, ds->width, cur_y, 0, +1, cur); else if (cur_y == -1) /* Top row */ draw_arrow(dr, ds, cur_x, 0, +1, 0, cur); else if (cur_y == ds->height) /* Bottom row */ draw_arrow(dr, ds, cur_x+1, ds->height, -1, 0, cur); else assert(!"Invalid cursor position"); draw_update(dr, cur_x * TILE_SIZE + BORDER + WINDOW_OFFSET, cur_y * TILE_SIZE + BORDER + WINDOW_OFFSET, TILE_SIZE, TILE_SIZE); } static void game_redraw(drawing *dr, game_drawstate *ds, const game_state *oldstate, const game_state *state, int dir, const game_ui *ui, float t, float ft) { int x, y, frame; unsigned char *active; float xshift = 0.0; float yshift = 0.0; int cur_x = -1, cur_y = -1; /* * Clear the screen and draw the exterior barrier lines if this * is our first call. */ if (!ds->started) { int phase; ds->started = TRUE; draw_rect(dr, 0, 0, BORDER * 2 + WINDOW_OFFSET * 2 + TILE_SIZE * state->width + TILE_BORDER, BORDER * 2 + WINDOW_OFFSET * 2 + TILE_SIZE * state->height + TILE_BORDER, COL_BACKGROUND); draw_update(dr, 0, 0, BORDER * 2 + WINDOW_OFFSET*2 + TILE_SIZE*state->width + TILE_BORDER, BORDER * 2 + WINDOW_OFFSET*2 + TILE_SIZE*state->height + TILE_BORDER); for (phase = 0; phase < 2; phase++) { for (x = 0; x < ds->width; x++) { if (barrier(state, x, 0) & UL) draw_barrier_corner(dr, ds, x, -1, LD, phase); if (barrier(state, x, 0) & RU) draw_barrier_corner(dr, ds, x, -1, DR, phase); if (barrier(state, x, 0) & U) draw_barrier(dr, ds, x, -1, D, phase); if (barrier(state, x, ds->height-1) & DR) draw_barrier_corner(dr, ds, x, ds->height, RU, phase); if (barrier(state, x, ds->height-1) & LD) draw_barrier_corner(dr, ds, x, ds->height, UL, phase); if (barrier(state, x, ds->height-1) & D) draw_barrier(dr, ds, x, ds->height, U, phase); } for (y = 0; y < ds->height; y++) { if (barrier(state, 0, y) & UL) draw_barrier_corner(dr, ds, -1, y, RU, phase); if (barrier(state, 0, y) & LD) draw_barrier_corner(dr, ds, -1, y, DR, phase); if (barrier(state, 0, y) & L) draw_barrier(dr, ds, -1, y, R, phase); if (barrier(state, ds->width-1, y) & RU) draw_barrier_corner(dr, ds, ds->width, y, UL, phase); if (barrier(state, ds->width-1, y) & DR) draw_barrier_corner(dr, ds, ds->width, y, LD, phase); if (barrier(state, ds->width-1, y) & R) draw_barrier(dr, ds, ds->width, y, L, phase); } } /* * Arrows for making moves. */ for (x = 0; x < ds->width; x++) { if (x == state->cx) continue; draw_arrow(dr, ds, x, 0, +1, 0, 0); draw_arrow(dr, ds, x+1, ds->height, -1, 0, 0); } for (y = 0; y < ds->height; y++) { if (y == state->cy) continue; draw_arrow(dr, ds, ds->width, y, 0, +1, 0); draw_arrow(dr, ds, 0, y+1, 0, -1, 0); } } if (ui->cur_visible) { cur_x = ui->cur_x; cur_y = ui->cur_y; } if (cur_x != ds->cur_x || cur_y != ds->cur_y) { /* Cursor has changed; redraw two (prev and curr) arrows. */ assert(cur_x != state->cx && cur_y != state->cy); draw_arrow_for_cursor(dr, ds, cur_x, cur_y, 1); draw_arrow_for_cursor(dr, ds, ds->cur_x, ds->cur_y, 0); ds->cur_x = cur_x; ds->cur_y = cur_y; } /* Check if this is an undo. If so, we will need to run any animation * backwards. */ if (oldstate && oldstate->move_count > state->move_count) { const game_state * tmpstate = state; state = oldstate; oldstate = tmpstate; t = ANIM_TIME - t; } if (oldstate && (t < ANIM_TIME)) { /* * We're animating a slide, of row/column number * state->last_move_pos, in direction * state->last_move_dir */ xshift = state->last_move_row == -1 ? 0.0F : (1 - t / ANIM_TIME) * state->last_move_dir; yshift = state->last_move_col == -1 ? 0.0F : (1 - t / ANIM_TIME) * state->last_move_dir; } frame = -1; if (ft > 0) { /* * We're animating a completion flash. Find which frame * we're at. */ frame = (int)(ft / FLASH_FRAME); } /* * Draw any tile which differs from the way it was last drawn. */ if (xshift != 0.0 || yshift != 0.0) { active = compute_active(state, state->last_move_row, state->last_move_col); } else { active = compute_active(state, -1, -1); } clip(dr, BORDER + WINDOW_OFFSET, BORDER + WINDOW_OFFSET, TILE_SIZE * state->width + TILE_BORDER, TILE_SIZE * state->height + TILE_BORDER); for (x = 0; x < ds->width; x++) for (y = 0; y < ds->height; y++) { unsigned char c = tile(state, x, y) | index(state, active, x, y); /* * In a completion flash, we adjust the FLASHING bit * depending on our distance from the centre point and * the frame number. */ if (frame >= 0) { int xdist, ydist, dist; xdist = (x < state->cx ? state->cx - x : x - state->cx); ydist = (y < state->cy ? state->cy - y : y - state->cy); dist = (xdist > ydist ? xdist : ydist); if (frame >= dist && frame < dist+4) { int flash = (frame - dist) & 1; flash = flash ? FLASHING : 0; c = (c &~ FLASHING) | flash; } } if (index(state, ds->visible, x, y) != c || index(state, ds->visible, x, y) == 0xFF || (x == state->last_move_col || y == state->last_move_row)) { float xs = (y == state->last_move_row ? xshift : (float)0.0); float ys = (x == state->last_move_col ? yshift : (float)0.0); draw_tile(dr, ds, state, x, y, c, xs, ys); if (xs < 0 && x == 0) draw_tile(dr, ds, state, state->width, y, c, xs, ys); else if (xs > 0 && x == state->width - 1) draw_tile(dr, ds, state, -1, y, c, xs, ys); else if (ys < 0 && y == 0) draw_tile(dr, ds, state, x, state->height, c, xs, ys); else if (ys > 0 && y == state->height - 1) draw_tile(dr, ds, state, x, -1, c, xs, ys); if (x == state->last_move_col || y == state->last_move_row) index(state, ds->visible, x, y) = 0xFF; else index(state, ds->visible, x, y) = c; } } for (x = 0; x < ds->width; x++) for (y = 0; y < ds->height; y++) draw_tile_barriers(dr, ds, state, x, y); unclip(dr); /* * Update the status bar. */ { char statusbuf[256]; int i, n, a; n = state->width * state->height; for (i = a = 0; i < n; i++) if (active[i]) a++; if (state->used_solve) sprintf(statusbuf, "Moves since auto-solve: %d", state->move_count - state->completed); else sprintf(statusbuf, "%sMoves: %d", (state->completed ? "COMPLETED! " : ""), (state->completed ? state->completed : state->move_count)); if (state->movetarget) sprintf(statusbuf + strlen(statusbuf), " (target %d)", state->movetarget); sprintf(statusbuf + strlen(statusbuf), " Active: %d/%d", a, n); status_bar(dr, statusbuf); } sfree(active); } static float game_anim_length(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui) { return ANIM_TIME; } static float game_flash_length(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui) { /* * If the game has just been completed, we display a completion * flash. */ if (!oldstate->completed && newstate->completed && !oldstate->used_solve && !newstate->used_solve) { int size; size = 0; if (size < newstate->cx+1) size = newstate->cx+1; if (size < newstate->cy+1) size = newstate->cy+1; if (size < newstate->width - newstate->cx) size = newstate->width - newstate->cx; if (size < newstate->height - newstate->cy) size = newstate->height - newstate->cy; return FLASH_FRAME * (size+4); } return 0.0F; } static int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int game_timing_state(const game_state *state, game_ui *ui) { return FALSE; } 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 netslide #endif const struct game thegame = { "Netslide", "games.netslide", "netslide", default_params, game_fetch_preset, 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, FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, decode_ui, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, FALSE, FALSE, game_print_size, game_print, TRUE, /* wants_statusbar */ FALSE, game_timing_state, 0, /* flags */ }; /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/no-icon.c0000644000175300017530000000027711023562605014141 0ustar simonsimon /* * Dummy source file which replaces the files generated in the * `icons' subdirectory, when they're absent. */ const char *const *const xpm_icons[] = { 0 }; const int n_xpm_icons = 0; puzzles-r9872/nullfe.c0000644000175300017530000000463611400233062014056 0ustar simonsimon/* * nullfe.c: Null front-end code containing a bunch of boring stub * functions. Used to ensure successful linking when building the * various stand-alone solver binaries. */ #include #include "puzzles.h" void frontend_default_colour(frontend *fe, float *output) {} void draw_text(drawing *dr, int x, int y, int fonttype, int fontsize, int align, int colour, char *text) {} void draw_rect(drawing *dr, int x, int y, int w, int h, int colour) {} void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour) {} void draw_thick_line(drawing *dr, float thickness, float x1, float y1, float x2, float y2, int colour) {} void draw_polygon(drawing *dr, int *coords, int npoints, int fillcolour, int outlinecolour) {} void draw_circle(drawing *dr, int cx, int cy, int radius, int fillcolour, int outlinecolour) {} char *text_fallback(drawing *dr, const char *const *strings, int nstrings) { return dupstr(strings[0]); } void clip(drawing *dr, int x, int y, int w, int h) {} void unclip(drawing *dr) {} void start_draw(drawing *dr) {} void draw_update(drawing *dr, int x, int y, int w, int h) {} void end_draw(drawing *dr) {} blitter *blitter_new(drawing *dr, int w, int h) {return NULL;} void blitter_free(drawing *dr, blitter *bl) {} void blitter_save(drawing *dr, blitter *bl, int x, int y) {} void blitter_load(drawing *dr, blitter *bl, int x, int y) {} int print_mono_colour(drawing *dr, int grey) { return 0; } int print_grey_colour(drawing *dr, float grey) { return 0; } int print_hatched_colour(drawing *dr, int hatch) { return 0; } int print_rgb_mono_colour(drawing *dr, float r, float g, float b, int grey) { return 0; } int print_rgb_grey_colour(drawing *dr, float r, float g, float b, float grey) { return 0; } int print_rgb_hatched_colour(drawing *dr, float r, float g, float b, int hatch) { return 0; } void print_line_width(drawing *dr, int width) {} void print_line_dotted(drawing *dr, int dotted) {} void midend_supersede_game_desc(midend *me, char *desc, char *privdesc) {} void status_bar(drawing *dr, char *text) {} void fatal(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); } #ifdef DEBUGGING void debug_printf(char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stdout, fmt, ap); va_end(ap); } #endif puzzles-r9872/nullgame.c0000644000175300017530000001452012132232554014376 0ustar simonsimon/* * nullgame.c [FIXME]: Template defining the null game (in which no * moves are permitted and nothing is ever drawn). This file exists * solely as a basis for constructing new game definitions - it * helps to have something which will compile from the word go and * merely doesn't _do_ very much yet. * * Parts labelled FIXME actually want _removing_ (e.g. the dummy * field in each of the required data structures, and this entire * comment itself) when converting this source file into one * describing a real game. */ #include #include #include #include #include #include #include "puzzles.h" enum { COL_BACKGROUND, NCOLOURS }; struct game_params { int FIXME; }; struct game_state { int FIXME; }; static game_params *default_params(void) { game_params *ret = snew(game_params); ret->FIXME = 0; return ret; } static int 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) { } static char *encode_params(const game_params *params, int full) { return dupstr("FIXME"); } static config_item *game_configure(const game_params *params) { return NULL; } static game_params *custom_params(const config_item *cfg) { return NULL; } static char *validate_params(const game_params *params, int full) { return NULL; } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { return dupstr("FIXME"); } static 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, char **error) { return NULL; } static int 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); draw_update(dr, 0, 0, 10*ds->tilesize, 10*ds->tilesize); } 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 int game_status(const game_state *state) { return 0; } static int 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 nullgame #endif const struct game thegame = { "Null Game", NULL, NULL, default_params, game_fetch_preset, 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, 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_status, FALSE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, 0, /* flags */ }; puzzles-r9872/obfusc.c0000644000175300017530000000573111120540061014046 0ustar simonsimon/* * Stand-alone tool to access the Puzzles obfuscation algorithm. * * To deobfuscate, use "obfusc -d": * * obfusc -d reads binary data from stdin, writes to stdout * obfusc -d works on the given hex string instead of stdin * obfusc -d -h writes a hex string instead of binary to stdout * * To obfuscate, "obfusc -e": * * obfusc -e reads binary from stdin, writes hex to stdout * obfusc -e works on the given hex string instead of stdin * obfusc -e -b writes binary instead of text to stdout * * The default output format is hex for -e and binary for -d * because that's the way obfuscation is generally used in * Puzzles. Either of -b and -h can always be specified to set it * explicitly. * * Data read from standard input is assumed always to be binary; * data provided on the command line is taken to be hex. */ #include #include #include #include #include #include "puzzles.h" int main(int argc, char **argv) { enum { BINARY, DEFAULT, HEX } outputmode = DEFAULT; char *inhex = NULL; unsigned char *data; int datalen; int decode = -1; int doing_opts = TRUE; while (--argc > 0) { char *p = *++argv; if (doing_opts && *p == '-') { if (!strcmp(p, "--")) { doing_opts = 0; continue; } p++; while (*p) { switch (*p) { case 'e': decode = 0; break; case 'd': decode = 1; break; case 'b': outputmode = BINARY; break; case 'h': outputmode = HEX; break; default: fprintf(stderr, "obfusc: unrecognised option '-%c'\n", *p); return 1; } p++; } } else { if (!inhex) { inhex = p; } else { fprintf(stderr, "obfusc: expected at most one argument\n"); return 1; } } } if (decode < 0) { fprintf(stderr, "usage: obfusc < -e | -d > [ -b | -h ] [hex data]\n"); return 0; } if (outputmode == DEFAULT) outputmode = (decode ? BINARY : HEX); if (inhex) { datalen = strlen(inhex) / 2; data = hex2bin(inhex, datalen); } else { int datasize = 4096; datalen = 0; data = snewn(datasize, unsigned char); while (1) { int ret = fread(data + datalen, 1, datasize - datalen, stdin); if (ret < 0) { fprintf(stderr, "obfusc: read: %s\n", strerror(errno)); return 1; } else if (ret == 0) { break; } else { datalen += ret; if (datasize - datalen < 4096) { datasize = datalen * 5 / 4 + 4096; data = sresize(data, datasize, unsigned char); } } } } obfuscate_bitmap(data, datalen * 8, decode); if (outputmode == BINARY) { int ret = fwrite(data, 1, datalen, stdout); if (ret < 0) { fprintf(stderr, "obfusc: write: %s\n", strerror(errno)); return 1; } } else { int i; for (i = 0; i < datalen; i++) printf("%02x", data[i]); printf("\n"); } return 0; } puzzles-r9872/pattern.c0000644000175300017530000014437612132232554014264 0ustar simonsimon/* * pattern.c: the pattern-reconstruction game known as `nonograms'. */ #include #include #include #include #include #include #include "puzzles.h" enum { COL_BACKGROUND, COL_EMPTY, COL_FULL, COL_TEXT, COL_UNKNOWN, COL_GRID, COL_CURSOR, COL_ERROR, NCOLOURS }; #define PREFERRED_TILE_SIZE 24 #define TILE_SIZE (ds->tilesize) #define BORDER (3 * TILE_SIZE / 4) #define TLBORDER(d) ( (d) / 5 + 2 ) #define GUTTER (TILE_SIZE / 2) #define FROMCOORD(d, x) \ ( ((x) - (BORDER + GUTTER + TILE_SIZE * TLBORDER(d))) / TILE_SIZE ) #define SIZE(d) (2*BORDER + GUTTER + TILE_SIZE * (TLBORDER(d) + (d))) #define GETTILESIZE(d, w) ((double)w / (2.0 + (double)TLBORDER(d) + (double)(d))) #define TOCOORD(d, x) (BORDER + GUTTER + TILE_SIZE * (TLBORDER(d) + (x))) struct game_params { int w, h; }; #define GRID_UNKNOWN 2 #define GRID_FULL 1 #define GRID_EMPTY 0 struct game_state { int w, h; unsigned char *grid; int rowsize; int *rowdata, *rowlen; int completed, cheated; }; #define FLASH_TIME 0.13F static game_params *default_params(void) { game_params *ret = snew(game_params); ret->w = ret->h = 15; return ret; } static const struct game_params pattern_presets[] = { {10, 10}, {15, 15}, {20, 20}, #ifndef SLOW_SYSTEM {25, 25}, {30, 30}, #endif }; static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char str[80]; if (i < 0 || i >= lenof(pattern_presets)) return FALSE; ret = snew(game_params); *ret = pattern_presets[i]; sprintf(str, "%dx%d", ret->w, ret->h); *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 *ret, char const *string) { char const *p = string; ret->w = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; if (*p == 'x') { p++; ret->h = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; } else { ret->h = ret->w; } } static char *encode_params(const game_params *params, int full) { char ret[400]; int len; len = sprintf(ret, "%dx%d", params->w, params->h); assert(len < lenof(ret)); ret[len] = '\0'; return dupstr(ret); } 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].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = NULL; ret[2].type = C_END; ret[2].sval = NULL; ret[2].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); return ret; } static char *validate_params(const game_params *params, int full) { if (params->w <= 0 || params->h <= 0) return "Width and height must both be greater than zero"; return NULL; } /* ---------------------------------------------------------------------- * Puzzle generation code. * * For this particular puzzle, it seemed important to me to ensure * a unique solution. I do this the brute-force way, by having a * solver algorithm alongside the generator, and repeatedly * generating a random grid until I find one whose solution is * unique. It turns out that this isn't too onerous on a modern PC * provided you keep grid size below around 30. Any offers of * better algorithms, however, will be very gratefully received. * * Another annoyance of this approach is that it limits the * available puzzles to those solvable by the algorithm I've used. * My algorithm only ever considers a single row or column at any * one time, which means it's incapable of solving the following * difficult example (found by Bella Image around 1995/6, when she * and I were both doing maths degrees): * * 2 1 2 1 * * +--+--+--+--+ * 1 1 | | | | | * +--+--+--+--+ * 2 | | | | | * +--+--+--+--+ * 1 | | | | | * +--+--+--+--+ * 1 | | | | | * +--+--+--+--+ * * Obviously this cannot be solved by a one-row-or-column-at-a-time * algorithm (it would require at least one row or column reading * `2 1', `1 2', `3' or `4' to get started). However, it can be * proved to have a unique solution: if the top left square were * empty, then the only option for the top row would be to fill the * two squares in the 1 columns, which would imply the squares * below those were empty, leaving no place for the 2 in the second * row. Contradiction. Hence the top left square is full, and the * unique solution follows easily from that starting point. * * (The game ID for this puzzle is 4x4:2/1/2/1/1.1/2/1/1 , in case * it's useful to anyone.) */ static int float_compare(const void *av, const void *bv) { const float *a = (const float *)av; const float *b = (const float *)bv; if (*a < *b) return -1; else if (*a > *b) return +1; else return 0; } static void generate(random_state *rs, int w, int h, unsigned char *retgrid) { float *fgrid; float *fgrid2; int step, i, j; float threshold; fgrid = snewn(w*h, float); for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { fgrid[i*w+j] = random_upto(rs, 100000000UL) / 100000000.F; } } /* * The above gives a completely random splattering of black and * white cells. We want to gently bias this in favour of _some_ * reasonably thick areas of white and black, while retaining * some randomness and fine detail. * * So we evolve the starting grid using a cellular automaton. * Currently, I'm doing something very simple indeed, which is * to set each square to the average of the surrounding nine * cells (or the average of fewer, if we're on a corner). */ for (step = 0; step < 1; step++) { fgrid2 = snewn(w*h, float); for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { float sx, xbar; int n, p, q; /* * Compute the average of the surrounding cells. */ n = 0; sx = 0.F; for (p = -1; p <= +1; p++) { for (q = -1; q <= +1; q++) { if (i+p < 0 || i+p >= h || j+q < 0 || j+q >= w) continue; /* * An additional special case not mentioned * above: if a grid dimension is 2xn then * we do not average across that dimension * at all. Otherwise a 2x2 grid would * contain four identical squares. */ if ((h==2 && p!=0) || (w==2 && q!=0)) continue; n++; sx += fgrid[(i+p)*w+(j+q)]; } } xbar = sx / n; fgrid2[i*w+j] = xbar; } } sfree(fgrid); fgrid = fgrid2; } fgrid2 = snewn(w*h, float); memcpy(fgrid2, fgrid, w*h*sizeof(float)); qsort(fgrid2, w*h, sizeof(float), float_compare); threshold = fgrid2[w*h/2]; sfree(fgrid2); for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { retgrid[i*w+j] = (fgrid[i*w+j] >= threshold ? GRID_FULL : GRID_EMPTY); } } sfree(fgrid); } static int compute_rowdata(int *ret, unsigned char *start, int len, int step) { int i, n; n = 0; for (i = 0; i < len; i++) { if (start[i*step] == GRID_FULL) { int runlen = 1; while (i+runlen < len && start[(i+runlen)*step] == GRID_FULL) runlen++; ret[n++] = runlen; i += runlen; } if (i < len && start[i*step] == GRID_UNKNOWN) return -1; } return n; } #define UNKNOWN 0 #define BLOCK 1 #define DOT 2 #define STILL_UNKNOWN 3 #ifdef STANDALONE_SOLVER int verbose = FALSE; #endif static int do_recurse(unsigned char *known, unsigned char *deduced, unsigned char *row, unsigned char *minpos_done, unsigned char *maxpos_done, unsigned char *minpos_ok, unsigned char *maxpos_ok, int *data, int len, int freespace, int ndone, int lowest) { int i, j, k; /* This algorithm basically tries all possible ways the given rows of * black blocks can be laid out in the row/column being examined. * Special care is taken to avoid checking the tail of a row/column * if the same conditions have already been checked during this recursion * The algorithm also takes care to cut its losses as soon as an * invalid (partial) solution is detected. */ if (data[ndone]) { if (lowest >= minpos_done[ndone] && lowest <= maxpos_done[ndone]) { if (lowest >= minpos_ok[ndone] && lowest <= maxpos_ok[ndone]) { for (i=0; i= minpos_ok[ndone] && lowest <= maxpos_ok[ndone]; } else { if (lowest < minpos_done[ndone]) minpos_done[ndone] = lowest; if (lowest > maxpos_done[ndone]) maxpos_done[ndone] = lowest; } for (i=0; i<=freespace; i++) { j = lowest; for (k=0; k maxpos_ok[ndone]) maxpos_ok[ndone] = lowest + i; if (lowest + i > maxpos_done[ndone]) maxpos_done[ndone] = lowest + i; } next_iter: j++; } return lowest >= minpos_ok[ndone] && lowest <= maxpos_ok[ndone]; } else { for (i=lowest; i= 0 && known[i] == DOT; i--) freespace--; do_recurse(known, deduced, row, minpos_done, maxpos_done, minpos_ok, maxpos_ok, data, len, freespace, 0, 0); done_any = FALSE; for (i=0; i "); for (i = 0; i < len; i++) putchar(start[i*step] == BLOCK ? '#' : start[i*step] == DOT ? '.' : '?'); putchar('\n'); } #endif return done_any; } static int solve_puzzle(const game_state *state, unsigned char *grid, int w, int h, unsigned char *matrix, unsigned char *workspace, unsigned int *changed_h, unsigned int *changed_w, int *rowdata #ifdef STANDALONE_SOLVER , int cluewid #else , int dummy #endif ) { int i, j, ok, max; int max_h, max_w; assert((state!=NULL) ^ (grid!=NULL)); max = max(w, h); memset(matrix, 0, w*h); /* For each column, compute how many squares can be deduced * from just the row-data. * Later, changed_* will hold how many squares were changed * in every row/column in the previous iteration * Changed_* is used to choose the next rows / cols to re-examine */ for (i=0; irowdata + state->rowsize*(w+i), max*sizeof(int)); rowdata[state->rowlen[w+i]] = 0; } else { rowdata[compute_rowdata(rowdata, grid+i*w, w, 1)] = 0; } for (j=0, freespace=w+1; rowdata[j]; j++) freespace -= rowdata[j] + 1; for (j=0, changed_h[i]=0; rowdata[j]; j++) if (rowdata[j] > freespace) changed_h[i] += rowdata[j] - freespace; } for (i=0,max_h=0; i max_h) max_h = changed_h[i]; for (i=0; irowdata + state->rowsize*i, max*sizeof(int)); rowdata[state->rowlen[i]] = 0; } else { rowdata[compute_rowdata(rowdata, grid+i, h, w)] = 0; } for (j=0, freespace=h+1; rowdata[j]; j++) freespace -= rowdata[j] + 1; for (j=0, changed_w[i]=0; rowdata[j]; j++) if (rowdata[j] > freespace) changed_w[i] += rowdata[j] - freespace; } for (i=0,max_w=0; i max_w) max_w = changed_w[i]; /* Solve the puzzle. * Process rows/columns individually. Deductions involving more than one * row and/or column at a time are not supported. * Take care to only process rows/columns which have been changed since they * were previously processed. * Also, prioritize rows/columns which have had the most changes since their * previous processing, as they promise the greatest benefit. * Extremely rectangular grids (e.g. 10x20, 15x40, etc.) are not treated specially. */ do { for (; max_h && max_h >= max_w; max_h--) { for (i=0; i= max_h) { if (state) { memcpy(rowdata, state->rowdata + state->rowsize*(w+i), max*sizeof(int)); rowdata[state->rowlen[w+i]] = 0; } else { rowdata[compute_rowdata(rowdata, grid+i*w, w, 1)] = 0; } do_row(workspace, workspace+max, workspace+2*max, workspace+3*max, workspace+4*max, workspace+5*max, workspace+6*max, matrix+i*w, w, 1, rowdata, changed_w #ifdef STANDALONE_SOLVER , "row", i+1, cluewid #endif ); changed_h[i] = 0; } } for (i=0,max_w=0; i max_w) max_w = changed_w[i]; } for (; max_w && max_w >= max_h; max_w--) { for (i=0; i= max_w) { if (state) { memcpy(rowdata, state->rowdata + state->rowsize*i, max*sizeof(int)); rowdata[state->rowlen[i]] = 0; } else { rowdata[compute_rowdata(rowdata, grid+i, h, w)] = 0; } do_row(workspace, workspace+max, workspace+2*max, workspace+3*max, workspace+4*max, workspace+5*max, workspace+6*max, matrix+i, h, w, rowdata, changed_h #ifdef STANDALONE_SOLVER , "col", i+1, cluewid #endif ); changed_w[i] = 0; } } for (i=0,max_h=0; i max_h) max_h = changed_h[i]; } } while (max_h>0 || max_w>0); ok = TRUE; for (i=0; i 2) { for (i = 0; i < h; i++) { int colours = 0; for (j = 0; j < w; j++) colours |= (grid[i*w+j] == GRID_FULL ? 2 : 1); if (colours != 3) ok = FALSE; } } if (h > 2) { for (j = 0; j < w; j++) { int colours = 0; for (i = 0; i < h; i++) colours |= (grid[i*w+j] == GRID_FULL ? 2 : 1); if (colours != 3) ok = FALSE; } } if (!ok) continue; ok = solve_puzzle(NULL, grid, w, h, matrix, workspace, changed_h, changed_w, rowdata, 0); } while (!ok); sfree(matrix); sfree(workspace); sfree(changed_h); sfree(changed_w); sfree(rowdata); return grid; } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { unsigned char *grid; int i, j, max, rowlen, *rowdata; char intbuf[80], *desc; int desclen, descpos; grid = generate_soluble(rs, params->w, params->h); max = max(params->w, params->h); rowdata = snewn(max, int); /* * Save the solved game in aux. */ { char *ai = snewn(params->w * params->h + 2, char); /* * String format is exactly the same as a solve move, so we * can just dupstr this in solve_game(). */ ai[0] = 'S'; for (i = 0; i < params->w * params->h; i++) ai[i+1] = grid[i] ? '1' : '0'; ai[params->w * params->h + 1] = '\0'; *aux = ai; } /* * Seed is a slash-separated list of row contents; each row * contents section is a dot-separated list of integers. Row * contents are listed in the order (columns left to right, * then rows top to bottom). * * Simplest way to handle memory allocation is to make two * passes, first computing the seed size and then writing it * out. */ desclen = 0; for (i = 0; i < params->w + params->h; i++) { if (i < params->w) rowlen = compute_rowdata(rowdata, grid+i, params->h, params->w); else rowlen = compute_rowdata(rowdata, grid+(i-params->w)*params->w, params->w, 1); if (rowlen > 0) { for (j = 0; j < rowlen; j++) { desclen += 1 + sprintf(intbuf, "%d", rowdata[j]); } } else { desclen++; } } desc = snewn(desclen, char); descpos = 0; for (i = 0; i < params->w + params->h; i++) { if (i < params->w) rowlen = compute_rowdata(rowdata, grid+i, params->h, params->w); else rowlen = compute_rowdata(rowdata, grid+(i-params->w)*params->w, params->w, 1); if (rowlen > 0) { for (j = 0; j < rowlen; j++) { int len = sprintf(desc+descpos, "%d", rowdata[j]); if (j+1 < rowlen) desc[descpos + len] = '.'; else desc[descpos + len] = '/'; descpos += len+1; } } else { desc[descpos++] = '/'; } } assert(descpos == desclen); assert(desc[desclen-1] == '/'); desc[desclen-1] = '\0'; sfree(rowdata); sfree(grid); return desc; } static char *validate_desc(const game_params *params, const char *desc) { int i, n, rowspace; const char *p; for (i = 0; i < params->w + params->h; i++) { if (i < params->w) rowspace = params->h + 1; else rowspace = params->w + 1; if (*desc && isdigit((unsigned char)*desc)) { do { p = desc; while (*desc && isdigit((unsigned char)*desc)) desc++; n = atoi(p); rowspace -= n+1; if (rowspace < 0) { if (i < params->w) return "at least one column contains more numbers than will fit"; else return "at least one row contains more numbers than will fit"; } } while (*desc++ == '.'); } else { desc++; /* expect a slash immediately */ } if (desc[-1] == '/') { if (i+1 == params->w + params->h) return "too many row/column specifications"; } else if (desc[-1] == '\0') { if (i+1 < params->w + params->h) return "too few row/column specifications"; } else return "unrecognised character in game specification"; } return NULL; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { int i; const char *p; game_state *state = snew(game_state); state->w = params->w; state->h = params->h; state->grid = snewn(state->w * state->h, unsigned char); memset(state->grid, GRID_UNKNOWN, state->w * state->h); state->rowsize = max(state->w, state->h); state->rowdata = snewn(state->rowsize * (state->w + state->h), int); state->rowlen = snewn(state->w + state->h, int); state->completed = state->cheated = FALSE; for (i = 0; i < params->w + params->h; i++) { state->rowlen[i] = 0; if (*desc && isdigit((unsigned char)*desc)) { do { p = desc; while (*desc && isdigit((unsigned char)*desc)) desc++; state->rowdata[state->rowsize * i + state->rowlen[i]++] = atoi(p); } while (*desc++ == '.'); } else { desc++; /* expect a slash immediately */ } } return state; } static game_state *dup_game(const game_state *state) { game_state *ret = snew(game_state); ret->w = state->w; ret->h = state->h; ret->grid = snewn(ret->w * ret->h, unsigned char); memcpy(ret->grid, state->grid, ret->w * ret->h); ret->rowsize = state->rowsize; ret->rowdata = snewn(ret->rowsize * (ret->w + ret->h), int); ret->rowlen = snewn(ret->w + ret->h, int); memcpy(ret->rowdata, state->rowdata, ret->rowsize * (ret->w + ret->h) * sizeof(int)); memcpy(ret->rowlen, state->rowlen, (ret->w + ret->h) * sizeof(int)); ret->completed = state->completed; ret->cheated = state->cheated; return ret; } static void free_game(game_state *state) { sfree(state->rowdata); sfree(state->rowlen); sfree(state->grid); sfree(state); } static char *solve_game(const game_state *state, const game_state *currstate, const char *ai, char **error) { unsigned char *matrix; int w = state->w, h = state->h; int i; char *ret; int max, ok; unsigned char *workspace; unsigned int *changed_h, *changed_w; int *rowdata; /* * If we already have the solved state in ai, copy it out. */ if (ai) return dupstr(ai); max = max(w, h); matrix = snewn(w*h, unsigned char); workspace = snewn(max*7, unsigned char); changed_h = snewn(max+1, unsigned int); changed_w = snewn(max+1, unsigned int); rowdata = snewn(max+1, int); ok = solve_puzzle(state, NULL, w, h, matrix, workspace, changed_h, changed_w, rowdata, 0); sfree(workspace); sfree(changed_h); sfree(changed_w); sfree(rowdata); if (!ok) { sfree(matrix); *error = "Solving algorithm cannot complete this puzzle"; return NULL; } ret = snewn(w*h+2, char); ret[0] = 'S'; for (i = 0; i < w*h; i++) { assert(matrix[i] == BLOCK || matrix[i] == DOT); ret[i+1] = (matrix[i] == BLOCK ? '1' : '0'); } ret[w*h+1] = '\0'; sfree(matrix); return ret; } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { return NULL; } struct game_ui { int dragging; int drag_start_x; int drag_start_y; int drag_end_x; int drag_end_y; int drag, release, state; int cur_x, cur_y, cur_visible; }; static game_ui *new_ui(const game_state *state) { game_ui *ret; ret = snew(game_ui); ret->dragging = FALSE; ret->cur_x = ret->cur_y = ret->cur_visible = 0; return ret; } 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) { } struct game_drawstate { int started; int w, h; int tilesize; unsigned char *visible, *numcolours; int cur_x, cur_y; }; static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { button &= ~MOD_MASK; x = FROMCOORD(state->w, x); y = FROMCOORD(state->h, y); if (x >= 0 && x < state->w && y >= 0 && y < state->h && (button == LEFT_BUTTON || button == RIGHT_BUTTON || button == MIDDLE_BUTTON)) { #ifdef STYLUS_BASED int currstate = state->grid[y * state->w + x]; #endif ui->dragging = TRUE; if (button == LEFT_BUTTON) { ui->drag = LEFT_DRAG; ui->release = LEFT_RELEASE; #ifdef STYLUS_BASED ui->state = (currstate + 2) % 3; /* FULL -> EMPTY -> UNKNOWN */ #else ui->state = GRID_FULL; #endif } else if (button == RIGHT_BUTTON) { ui->drag = RIGHT_DRAG; ui->release = RIGHT_RELEASE; #ifdef STYLUS_BASED ui->state = (currstate + 1) % 3; /* EMPTY -> FULL -> UNKNOWN */ #else ui->state = GRID_EMPTY; #endif } else /* if (button == MIDDLE_BUTTON) */ { ui->drag = MIDDLE_DRAG; ui->release = MIDDLE_RELEASE; ui->state = GRID_UNKNOWN; } ui->drag_start_x = ui->drag_end_x = x; ui->drag_start_y = ui->drag_end_y = y; ui->cur_visible = 0; return ""; /* UI activity occurred */ } if (ui->dragging && button == ui->drag) { /* * There doesn't seem much point in allowing a rectangle * drag; people will generally only want to drag a single * horizontal or vertical line, so we make that easy by * snapping to it. * * Exception: if we're _middle_-button dragging to tag * things as UNKNOWN, we may well want to trash an entire * area and start over! */ if (ui->state != GRID_UNKNOWN) { if (abs(x - ui->drag_start_x) > abs(y - ui->drag_start_y)) y = ui->drag_start_y; else x = ui->drag_start_x; } if (x < 0) x = 0; if (y < 0) y = 0; if (x >= state->w) x = state->w - 1; if (y >= state->h) y = state->h - 1; ui->drag_end_x = x; ui->drag_end_y = y; return ""; /* UI activity occurred */ } if (ui->dragging && button == ui->release) { int x1, x2, y1, y2, xx, yy; int move_needed = FALSE; x1 = min(ui->drag_start_x, ui->drag_end_x); x2 = max(ui->drag_start_x, ui->drag_end_x); y1 = min(ui->drag_start_y, ui->drag_end_y); y2 = max(ui->drag_start_y, ui->drag_end_y); for (yy = y1; yy <= y2; yy++) for (xx = x1; xx <= x2; xx++) if (state->grid[yy * state->w + xx] != ui->state) move_needed = TRUE; ui->dragging = FALSE; if (move_needed) { char buf[80]; sprintf(buf, "%c%d,%d,%d,%d", (char)(ui->state == GRID_FULL ? 'F' : ui->state == GRID_EMPTY ? 'E' : 'U'), x1, y1, x2-x1+1, y2-y1+1); return dupstr(buf); } else return ""; /* UI activity occurred */ } if (IS_CURSOR_MOVE(button)) { move_cursor(button, &ui->cur_x, &ui->cur_y, state->w, state->h, 0); ui->cur_visible = 1; return ""; } if (IS_CURSOR_SELECT(button)) { int currstate = state->grid[ui->cur_y * state->w + ui->cur_x]; int newstate; char buf[80]; if (!ui->cur_visible) { ui->cur_visible = 1; return ""; } if (button == CURSOR_SELECT2) newstate = currstate == GRID_UNKNOWN ? GRID_EMPTY : currstate == GRID_EMPTY ? GRID_FULL : GRID_UNKNOWN; else newstate = currstate == GRID_UNKNOWN ? GRID_FULL : currstate == GRID_FULL ? GRID_EMPTY : GRID_UNKNOWN; sprintf(buf, "%c%d,%d,%d,%d", (char)(newstate == GRID_FULL ? 'F' : newstate == GRID_EMPTY ? 'E' : 'U'), ui->cur_x, ui->cur_y, 1, 1); return dupstr(buf); } return NULL; } static game_state *execute_move(const game_state *from, const char *move) { game_state *ret; int x1, x2, y1, y2, xx, yy; int val; if (move[0] == 'S' && strlen(move) == from->w * from->h + 1) { int i; ret = dup_game(from); for (i = 0; i < ret->w * ret->h; i++) ret->grid[i] = (move[i+1] == '1' ? GRID_FULL : GRID_EMPTY); ret->completed = ret->cheated = TRUE; return ret; } else if ((move[0] == 'F' || move[0] == 'E' || move[0] == 'U') && sscanf(move+1, "%d,%d,%d,%d", &x1, &y1, &x2, &y2) == 4 && x1 >= 0 && x2 >= 0 && x1+x2 <= from->w && y1 >= 0 && y2 >= 0 && y1+y2 <= from->h) { x2 += x1; y2 += y1; val = (move[0] == 'F' ? GRID_FULL : move[0] == 'E' ? GRID_EMPTY : GRID_UNKNOWN); ret = dup_game(from); for (yy = y1; yy < y2; yy++) for (xx = x1; xx < x2; xx++) ret->grid[yy * ret->w + xx] = val; /* * An actual change, so check to see if we've completed the * game. */ if (!ret->completed) { int *rowdata = snewn(ret->rowsize, int); int i, len; ret->completed = TRUE; for (i=0; iw; i++) { len = compute_rowdata(rowdata, ret->grid+i, ret->h, ret->w); if (len != ret->rowlen[i] || memcmp(ret->rowdata+i*ret->rowsize, rowdata, len * sizeof(int))) { ret->completed = FALSE; break; } } for (i=0; ih; i++) { len = compute_rowdata(rowdata, ret->grid+i*ret->w, ret->w, 1); if (len != ret->rowlen[i+ret->w] || memcmp(ret->rowdata+(i+ret->w)*ret->rowsize, rowdata, len * sizeof(int))) { ret->completed = FALSE; break; } } sfree(rowdata); } return ret; } else return NULL; } /* ---------------------------------------------------------------------- * Error-checking during gameplay. */ /* * The difficulty in error-checking Pattern is to make the error check * _weak_ enough. The most obvious way would be to check each row and * column by calling (a modified form of) do_row() to recursively * analyse the row contents against the clue set and see if the * GRID_UNKNOWNs could be filled in in any way that would end up * correct. However, this turns out to be such a strong error check as * to constitute a spoiler in many situations: you make a typo while * trying to fill in one row, and not only does the row light up to * indicate an error, but several columns crossed by the move also * light up and draw your attention to deductions you hadn't even * noticed you could make. * * So instead I restrict error-checking to 'complete runs' within a * row, by which I mean contiguous sequences of GRID_FULL bounded at * both ends by either GRID_EMPTY or the ends of the row. We identify * all the complete runs in a row, and verify that _those_ are * consistent with the row's clue list. Sequences of complete runs * separated by solid GRID_EMPTY are required to match contiguous * sequences in the clue list, whereas if there's at least one * GRID_UNKNOWN between any two complete runs then those two need not * be contiguous in the clue list. * * To simplify the edge cases, I pretend that the clue list for the * row is extended with a 0 at each end, and I also pretend that the * grid data for the row is extended with a GRID_EMPTY and a * zero-length run at each end. This permits the contiguity checker to * handle the fiddly end effects (e.g. if the first contiguous * sequence of complete runs in the grid matches _something_ in the * clue list but not at the beginning, this is allowable iff there's a * GRID_UNKNOWN before the first one) with minimal faff, since the end * effects just drop out as special cases of the normal inter-run * handling (in this code the above case is not 'at the end of the * clue list' at all, but between the implicit initial zero run and * the first nonzero one). * * We must also be a little careful about how we search for a * contiguous sequence of runs. In the clue list (1 1 2 1 2 3), * suppose we see a GRID_UNKNOWN and then a length-1 run. We search * for 1 in the clue list and find it at the very beginning. But now * suppose we find a length-2 run with no GRID_UNKNOWN before it. We * can't naively look at the next clue from the 1 we found, because * that'll be the second 1 and won't match. Instead, we must backtrack * by observing that the 2 we've just found must be contiguous with * the 1 we've already seen, so we search for the sequence (1 2) and * find it starting at the second 1. Now if we see a 3, we must * rethink again and search for (1 2 3). */ struct errcheck_state { /* * rowdata and rowlen point at the clue data for this row in the * game state. */ int *rowdata; int rowlen; /* * rowpos indicates the lowest position where it would be valid to * see our next run length. It might be equal to rowlen, * indicating that the next run would have to be the terminating 0. */ int rowpos; /* * ncontig indicates how many runs we've seen in a contiguous * block. This is taken into account when searching for the next * run we find, unless ncontig is zeroed out first by encountering * a GRID_UNKNOWN. */ int ncontig; }; static int errcheck_found_run(struct errcheck_state *es, int r) { /* Macro to handle the pretence that rowdata has a 0 at each end */ #define ROWDATA(k) ((k)<0 || (k)>=es->rowlen ? 0 : es->rowdata[(k)]) /* * See if we can find this new run length at a position where it * also matches the last 'ncontig' runs we've seen. */ int i, newpos; for (newpos = es->rowpos; newpos <= es->rowlen; newpos++) { if (ROWDATA(newpos) != r) goto notfound; for (i = 1; i <= es->ncontig; i++) if (ROWDATA(newpos - i) != ROWDATA(es->rowpos - i)) goto notfound; es->rowpos = newpos+1; es->ncontig++; return TRUE; notfound:; } return FALSE; #undef ROWDATA } static int check_errors(const game_state *state, int i) { int start, step, end, j; int val, runlen; struct errcheck_state aes, *es = &aes; es->rowlen = state->rowlen[i]; es->rowdata = state->rowdata + state->rowsize * i; /* Pretend that we've already encountered the initial zero run */ es->ncontig = 1; es->rowpos = 0; if (i < state->w) { start = i; step = state->w; end = start + step * state->h; } else { start = (i - state->w) * state->w; step = 1; end = start + step * state->w; } runlen = -1; for (j = start - step; j <= end; j += step) { if (j < start || j == end) val = GRID_EMPTY; else val = state->grid[j]; if (val == GRID_UNKNOWN) { runlen = -1; es->ncontig = 0; } else if (val == GRID_FULL) { if (runlen >= 0) runlen++; } else if (val == GRID_EMPTY) { if (runlen > 0) { if (!errcheck_found_run(es, runlen)) return TRUE; /* error! */ } runlen = 0; } } /* Signal end-of-row by sending errcheck_found_run the terminating * zero run, which will be marked as contiguous with the previous * run if and only if there hasn't been a GRID_UNKNOWN before. */ if (!errcheck_found_run(es, 0)) return TRUE; /* error at the last minute! */ return FALSE; /* no error */ } /* ---------------------------------------------------------------------- * 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 = SIZE(params->w); *y = SIZE(params->h); } 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; frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); for (i = 0; i < 3; i++) { ret[COL_GRID * 3 + i] = 0.3F; ret[COL_UNKNOWN * 3 + i] = 0.5F; ret[COL_TEXT * 3 + i] = 0.0F; ret[COL_FULL * 3 + i] = 0.0F; ret[COL_EMPTY * 3 + i] = 1.0F; } ret[COL_CURSOR * 3 + 0] = 1.0F; ret[COL_CURSOR * 3 + 1] = 0.25F; ret[COL_CURSOR * 3 + 2] = 0.25F; ret[COL_ERROR * 3 + 0] = 1.0F; ret[COL_ERROR * 3 + 1] = 0.0F; ret[COL_ERROR * 3 + 2] = 0.0F; *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->started = FALSE; ds->w = state->w; ds->h = state->h; ds->visible = snewn(ds->w * ds->h, unsigned char); ds->tilesize = 0; /* not decided yet */ memset(ds->visible, 255, ds->w * ds->h); ds->numcolours = snewn(ds->w + ds->h, unsigned char); memset(ds->numcolours, 255, ds->w + ds->h); ds->cur_x = ds->cur_y = 0; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->visible); sfree(ds); } static void grid_square(drawing *dr, game_drawstate *ds, int y, int x, int state, int cur) { int xl, xr, yt, yb, dx, dy, dw, dh; draw_rect(dr, TOCOORD(ds->w, x), TOCOORD(ds->h, y), TILE_SIZE, TILE_SIZE, COL_GRID); xl = (x % 5 == 0 ? 1 : 0); yt = (y % 5 == 0 ? 1 : 0); xr = (x % 5 == 4 || x == ds->w-1 ? 1 : 0); yb = (y % 5 == 4 || y == ds->h-1 ? 1 : 0); dx = TOCOORD(ds->w, x) + 1 + xl; dy = TOCOORD(ds->h, y) + 1 + yt; dw = TILE_SIZE - xl - xr - 1; dh = TILE_SIZE - yt - yb - 1; draw_rect(dr, dx, dy, dw, dh, (state == GRID_FULL ? COL_FULL : state == GRID_EMPTY ? COL_EMPTY : COL_UNKNOWN)); if (cur) { draw_rect_outline(dr, dx, dy, dw, dh, COL_CURSOR); draw_rect_outline(dr, dx+1, dy+1, dw-2, dh-2, COL_CURSOR); } draw_update(dr, TOCOORD(ds->w, x), TOCOORD(ds->h, y), TILE_SIZE, TILE_SIZE); } /* * Draw the numbers for a single row or column. */ static void draw_numbers(drawing *dr, game_drawstate *ds, const game_state *state, int i, int erase, int colour) { int rowlen = state->rowlen[i]; int *rowdata = state->rowdata + state->rowsize * i; int nfit; int j; if (erase) { if (i < state->w) { draw_rect(dr, TOCOORD(state->w, i), 0, TILE_SIZE, BORDER + TLBORDER(state->h) * TILE_SIZE, COL_BACKGROUND); } else { draw_rect(dr, 0, TOCOORD(state->h, i - state->w), BORDER + TLBORDER(state->w) * TILE_SIZE, TILE_SIZE, COL_BACKGROUND); } } /* * Normally I space the numbers out by the same distance as the * tile size. However, if there are more numbers than available * spaces, I have to squash them up a bit. */ if (i < state->w) nfit = TLBORDER(state->h); else nfit = TLBORDER(state->w); nfit = max(rowlen, nfit) - 1; assert(nfit > 0); for (j = 0; j < rowlen; j++) { int x, y; char str[80]; if (i < state->w) { x = TOCOORD(state->w, i); y = BORDER + TILE_SIZE * (TLBORDER(state->h)-1); y -= ((rowlen-j-1)*TILE_SIZE) * (TLBORDER(state->h)-1) / nfit; } else { y = TOCOORD(state->h, i - state->w); x = BORDER + TILE_SIZE * (TLBORDER(state->w)-1); x -= ((rowlen-j-1)*TILE_SIZE) * (TLBORDER(state->w)-1) / nfit; } sprintf(str, "%d", rowdata[j]); draw_text(dr, x+TILE_SIZE/2, y+TILE_SIZE/2, FONT_VARIABLE, TILE_SIZE/2, ALIGN_HCENTRE | ALIGN_VCENTRE, colour, str); } if (i < state->w) { draw_update(dr, TOCOORD(state->w, i), 0, TILE_SIZE, BORDER + TLBORDER(state->h) * TILE_SIZE); } else { draw_update(dr, 0, TOCOORD(state->h, i - state->w), BORDER + TLBORDER(state->w) * TILE_SIZE, TILE_SIZE); } } 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 i, j; int x1, x2, y1, y2; int cx, cy, cmoved; 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(ds->w), SIZE(ds->h), COL_BACKGROUND); /* * Draw the grid outline. */ draw_rect(dr, TOCOORD(ds->w, 0) - 1, TOCOORD(ds->h, 0) - 1, ds->w * TILE_SIZE + 3, ds->h * TILE_SIZE + 3, COL_GRID); ds->started = TRUE; draw_update(dr, 0, 0, SIZE(ds->w), SIZE(ds->h)); } if (ui->dragging) { x1 = min(ui->drag_start_x, ui->drag_end_x); x2 = max(ui->drag_start_x, ui->drag_end_x); y1 = min(ui->drag_start_y, ui->drag_end_y); y2 = max(ui->drag_start_y, ui->drag_end_y); } else { x1 = x2 = y1 = y2 = -1; /* placate gcc warnings */ } if (ui->cur_visible) { cx = ui->cur_x; cy = ui->cur_y; } else { cx = cy = -1; } cmoved = (cx != ds->cur_x || cy != ds->cur_y); /* * Now draw any grid squares which have changed since last * redraw. */ for (i = 0; i < ds->h; i++) { for (j = 0; j < ds->w; j++) { int val, cc = 0; /* * Work out what state this square should be drawn in, * taking any current drag operation into account. */ if (ui->dragging && x1 <= j && j <= x2 && y1 <= i && i <= y2) val = ui->state; else val = state->grid[i * state->w + j]; if (cmoved) { /* the cursor has moved; if we were the old or * the new cursor position we need to redraw. */ if (j == cx && i == cy) cc = 1; if (j == ds->cur_x && i == ds->cur_y) cc = 1; } /* * Briefly invert everything twice during a completion * flash. */ if (flashtime > 0 && (flashtime <= FLASH_TIME/3 || flashtime >= FLASH_TIME*2/3) && val != GRID_UNKNOWN) val = (GRID_FULL ^ GRID_EMPTY) ^ val; if (ds->visible[i * ds->w + j] != val || cc) { grid_square(dr, ds, i, j, val, (j == cx && i == cy)); ds->visible[i * ds->w + j] = val; } } } ds->cur_x = cx; ds->cur_y = cy; /* * Redraw any numbers which have changed their colour due to error * indication. */ for (i = 0; i < state->w + state->h; i++) { int colour = check_errors(state, i) ? COL_ERROR : COL_TEXT; if (ds->numcolours[i] != colour) { draw_numbers(dr, ds, state, i, TRUE, colour); ds->numcolours[i] = colour; } } } 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 int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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) { int pw, ph; /* * I'll use 5mm squares by default. */ game_compute_size(params, 500, &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->w, h = state->h; int ink = print_mono_colour(dr, 0); int x, y, i; /* 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, TILE_SIZE / 16); draw_rect_outline(dr, TOCOORD(w, 0), TOCOORD(h, 0), w*TILE_SIZE, h*TILE_SIZE, ink); /* * Grid. */ for (x = 1; x < w; x++) { print_line_width(dr, TILE_SIZE / (x % 5 ? 128 : 24)); draw_line(dr, TOCOORD(w, x), TOCOORD(h, 0), TOCOORD(w, x), TOCOORD(h, h), ink); } for (y = 1; y < h; y++) { print_line_width(dr, TILE_SIZE / (y % 5 ? 128 : 24)); draw_line(dr, TOCOORD(w, 0), TOCOORD(h, y), TOCOORD(w, w), TOCOORD(h, y), ink); } /* * Clues. */ for (i = 0; i < state->w + state->h; i++) draw_numbers(dr, ds, state, i, FALSE, ink); /* * Solution. */ print_line_width(dr, TILE_SIZE / 128); for (y = 0; y < h; y++) for (x = 0; x < w; x++) { if (state->grid[y*w+x] == GRID_FULL) draw_rect(dr, TOCOORD(w, x), TOCOORD(h, y), TILE_SIZE, TILE_SIZE, ink); else if (state->grid[y*w+x] == GRID_EMPTY) draw_circle(dr, TOCOORD(w, x) + TILE_SIZE/2, TOCOORD(h, y) + TILE_SIZE/2, TILE_SIZE/12, ink, ink); } } #ifdef COMBINED #define thegame pattern #endif const struct game thegame = { "Pattern", "games.pattern", "pattern", default_params, game_fetch_preset, 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, FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, decode_ui, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, REQUIRE_RBUTTON, /* flags */ }; #ifdef STANDALONE_SOLVER int main(int argc, char **argv) { game_params *p; game_state *s; char *id = NULL, *desc, *err; while (--argc > 0) { char *p = *++argv; if (*p == '-') { if (!strcmp(p, "-v")) { verbose = TRUE; } else { fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p); return 1; } } else { id = p; } } if (!id) { fprintf(stderr, "usage: %s \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); { int w = p->w, h = p->h, i, j, max, cluewid = 0; unsigned char *matrix, *workspace; unsigned int *changed_h, *changed_w; int *rowdata; matrix = snewn(w*h, unsigned char); max = max(w, h); workspace = snewn(max*7, unsigned char); changed_h = snewn(max+1, unsigned int); changed_w = snewn(max+1, unsigned int); rowdata = snewn(max+1, int); if (verbose) { int thiswid; /* * Work out the maximum text width of the clue numbers * in a row or column, so we can print the solver's * working in a nicely lined up way. */ for (i = 0; i < (w+h); i++) { char buf[80]; for (thiswid = -1, j = 0; j < s->rowlen[i]; j++) thiswid += sprintf(buf, " %d", s->rowdata[s->rowsize*i+j]); if (cluewid < thiswid) cluewid = thiswid; } } solve_puzzle(s, NULL, w, h, matrix, workspace, changed_h, changed_w, rowdata, cluewid); for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { int c = (matrix[i*w+j] == UNKNOWN ? '?' : matrix[i*w+j] == BLOCK ? '#' : matrix[i*w+j] == DOT ? '.' : '!'); putchar(c); } printf("\n"); } } return 0; } #endif /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/pearl.c0000644000175300017530000023502012132232554013675 0ustar simonsimon/* * pearl.c: Nikoli's `Masyu' puzzle. */ /* * TODO: * * - The current keyboard cursor mechanism works well on ordinary PC * keyboards, but for platforms with only arrow keys and a select * button or two, we may at some point need a simpler one which can * handle 'x' markings without needing shift keys. For instance, a * cursor with twice the grid resolution, so that it can range * across face centres, edge centres and vertices; 'clicks' on face * centres begin a drag as currently, clicks on edges toggle * markings, and clicks on vertices are ignored (but it would be * too confusing not to let the cursor rest on them). But I'm * pretty sure that would be less pleasant to play on a full * keyboard, so probably a #ifdef would be the thing. * * - Generation is still pretty slow, due to difficulty coming up in * the first place with a loop that makes a soluble puzzle even * with all possible clues filled in. * + A possible alternative strategy to further tuning of the * existing loop generator would be to throw the entire * mechanism out and instead write a different generator from * scratch which evolves the solution along with the puzzle: * place a few clues, nail down a bit of the loop, place another * clue, nail down some more, etc. However, I don't have a * detailed plan for any such mechanism, so it may be a pipe * dream. */ #include #include #include #include #include #include #include "puzzles.h" #include "grid.h" #include "loopgen.h" #define SWAP(i,j) do { int swaptmp = (i); (i) = (j); (j) = swaptmp; } while (0) #define NOCLUE 0 #define CORNER 1 #define STRAIGHT 2 #define R 1 #define U 2 #define L 4 #define D 8 #define DX(d) ( ((d)==R) - ((d)==L) ) #define DY(d) ( ((d)==D) - ((d)==U) ) #define F(d) (((d << 2) | (d >> 2)) & 0xF) #define C(d) (((d << 3) | (d >> 1)) & 0xF) #define A(d) (((d << 1) | (d >> 3)) & 0xF) #define LR (L | R) #define RL (R | L) #define UD (U | D) #define DU (D | U) #define LU (L | U) #define UL (U | L) #define LD (L | D) #define DL (D | L) #define RU (R | U) #define UR (U | R) #define RD (R | D) #define DR (D | R) #define BLANK 0 #define UNKNOWN 15 #define bLR (1 << LR) #define bRL (1 << RL) #define bUD (1 << UD) #define bDU (1 << DU) #define bLU (1 << LU) #define bUL (1 << UL) #define bLD (1 << LD) #define bDL (1 << DL) #define bRU (1 << RU) #define bUR (1 << UR) #define bRD (1 << RD) #define bDR (1 << DR) #define bBLANK (1 << BLANK) enum { COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT, COL_CURSOR_BACKGROUND = COL_LOWLIGHT, COL_BLACK, COL_WHITE, COL_ERROR, COL_GRID, COL_FLASH, COL_DRAGON, COL_DRAGOFF, NCOLOURS }; /* Macro ickery copied from slant.c */ #define DIFFLIST(A) \ A(EASY,Easy,e) \ A(TRICKY,Tricky,t) #define ENUM(upper,title,lower) DIFF_ ## upper, #define TITLE(upper,title,lower) #title, #define ENCODE(upper,title,lower) #lower #define CONFIG(upper,title,lower) ":" #title enum { DIFFLIST(ENUM) DIFFCOUNT }; static char const *const pearl_diffnames[] = { DIFFLIST(TITLE) "(count)" }; static char const pearl_diffchars[] = DIFFLIST(ENCODE); #define DIFFCONFIG DIFFLIST(CONFIG) struct game_params { int w, h; int difficulty; int nosolve; /* XXX remove me! */ }; struct shared_state { int w, h, sz; char *clues; /* size w*h */ int refcnt; }; #define INGRID(state, gx, gy) ((gx) >= 0 && (gx) < (state)->shared->w && \ (gy) >= 0 && (gy) < (state)->shared->h) struct game_state { struct shared_state *shared; char *lines; /* size w*h: lines placed */ char *errors; /* size w*h: errors detected */ char *marks; /* size w*h: 'no line here' marks placed. */ int completed, used_solve; int loop_length; /* filled in by check_completion when complete. */ }; #define DEFAULT_PRESET 3 static const struct game_params pearl_presets[] = { {6, 6, DIFF_EASY}, {6, 6, DIFF_TRICKY}, {8, 8, DIFF_EASY}, {8, 8, DIFF_TRICKY}, {10, 10, DIFF_EASY}, {10, 10, DIFF_TRICKY}, {12, 8, DIFF_EASY}, {12, 8, DIFF_TRICKY}, }; static game_params *default_params(void) { game_params *ret = snew(game_params); *ret = pearl_presets[DEFAULT_PRESET]; ret->nosolve = FALSE; return ret; } static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char buf[64]; if (i < 0 || i >= lenof(pearl_presets)) return FALSE; ret = default_params(); *ret = pearl_presets[i]; /* struct copy */ *params = ret; sprintf(buf, "%dx%d %s", pearl_presets[i].w, pearl_presets[i].h, pearl_diffnames[pearl_presets[i].difficulty]); *name = dupstr(buf); 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 *ret, char const *string) { ret->w = ret->h = atoi(string); while (*string && isdigit((unsigned char) *string)) ++string; if (*string == 'x') { string++; ret->h = atoi(string); while (*string && isdigit((unsigned char)*string)) string++; } ret->difficulty = DIFF_EASY; if (*string == 'd') { int i; string++; for (i = 0; i < DIFFCOUNT; i++) if (*string == pearl_diffchars[i]) ret->difficulty = i; if (*string) string++; } ret->nosolve = FALSE; if (*string == 'n') { ret->nosolve = TRUE; string++; } } static char *encode_params(const game_params *params, int full) { char buf[256]; sprintf(buf, "%dx%d", params->w, params->h); if (full) sprintf(buf + strlen(buf), "d%c%s", pearl_diffchars[params->difficulty], params->nosolve ? "n" : ""); return dupstr(buf); } static config_item *game_configure(const game_params *params) { config_item *ret; char buf[64]; ret = snewn(5, config_item); ret[0].name = "Width"; ret[0].type = C_STRING; sprintf(buf, "%d", params->w); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Difficulty"; ret[2].type = C_CHOICES; ret[2].sval = DIFFCONFIG; ret[2].ival = params->difficulty; ret[3].name = "Allow unsoluble"; ret[3].type = C_BOOLEAN; ret[3].sval = NULL; ret[3].ival = params->nosolve; ret[4].name = NULL; ret[4].type = C_END; ret[4].sval = NULL; ret[4].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); ret->difficulty = cfg[2].ival; ret->nosolve = cfg[3].ival; return ret; } static char *validate_params(const game_params *params, int full) { if (params->w < 5) return "Width must be at least five"; if (params->h < 5) return "Height must be at least five"; if (params->difficulty < 0 || params->difficulty >= DIFFCOUNT) return "Unknown difficulty level"; return NULL; } /* ---------------------------------------------------------------------- * Solver. */ int pearl_solve(int w, int h, char *clues, char *result, int difficulty, int partial) { int W = 2*w+1, H = 2*h+1; short *workspace; int *dsf, *dsfsize; int x, y, b, d; int ret = -1; /* * workspace[(2*y+1)*W+(2*x+1)] indicates the possible nature * of the square (x,y), as a logical OR of bitfields. * * workspace[(2*y)*W+(2*x+1)], for x odd and y even, indicates * whether the horizontal edge between (x,y) and (x+1,y) is * connected (1), disconnected (2) or unknown (3). * * workspace[(2*y+1)*W+(2*x)], indicates the same about the * vertical edge between (x,y) and (x,y+1). * * Initially, every square is considered capable of being in * any of the seven possible states (two straights, four * corners and empty), except those corresponding to clue * squares which are more restricted. * * Initially, all edges are unknown, except the ones around the * grid border which are known to be disconnected. */ workspace = snewn(W*H, short); for (x = 0; x < W*H; x++) workspace[x] = 0; /* Square states */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) switch (clues[y*w+x]) { case CORNER: workspace[(2*y+1)*W+(2*x+1)] = bLU|bLD|bRU|bRD; break; case STRAIGHT: workspace[(2*y+1)*W+(2*x+1)] = bLR|bUD; break; default: workspace[(2*y+1)*W+(2*x+1)] = bLR|bUD|bLU|bLD|bRU|bRD|bBLANK; break; } /* Horizontal edges */ for (y = 0; y <= h; y++) for (x = 0; x < w; x++) workspace[(2*y)*W+(2*x+1)] = (y==0 || y==h ? 2 : 3); /* Vertical edges */ for (y = 0; y < h; y++) for (x = 0; x <= w; x++) workspace[(2*y+1)*W+(2*x)] = (x==0 || x==w ? 2 : 3); /* * We maintain a dsf of connected squares, together with a * count of the size of each equivalence class. */ dsf = snewn(w*h, int); dsfsize = snewn(w*h, int); /* * Now repeatedly try to find something we can do. */ while (1) { int done_something = FALSE; #ifdef SOLVER_DIAGNOSTICS for (y = 0; y < H; y++) { for (x = 0; x < W; x++) printf("%*x", (x&1) ? 5 : 2, workspace[y*W+x]); printf("\n"); } #endif /* * Go through the square state words, and discard any * square state which is inconsistent with known facts * about the edges around the square. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) { for (b = 0; b < 0xD; b++) if (workspace[(2*y+1)*W+(2*x+1)] & (1<= 0) { /* * This square state would form * a loop on equivalence class * e. Measure the size of that * loop, and see if it's a * shortcut. */ int loopsize = dsfsize[e]; if (e != ae) loopsize++;/* add the square itself */ if (loopsize < nonblanks) { /* * It is! Mark this square * state invalid. */ workspace[y*W+x] &= ~(1<= 0); return ret; } /* ---------------------------------------------------------------------- * Loop generator. */ /* * We use the loop generator code from loopy, hard-coding to a square * grid of the appropriate size. Knowing the grid layout and the tile * size we can shrink that to our small grid and then make our line * layout from the face colour info. * * We provide a bias function to the loop generator which tries to * bias in favour of loops with more scope for Pearl black clues. This * seems to improve the success rate of the puzzle generator, in that * such loops have a better chance of being soluble with all valid * clues put in. */ struct pearl_loopgen_bias_ctx { /* * Our bias function counts the number of 'black clue' corners * (i.e. corners adjacent to two straights) in both the * BLACK/nonBLACK and WHITE/nonWHITE boundaries. In order to do * this, we must: * * - track the edges that are part of each of those loops * - track the types of vertex in each loop (corner, straight, * none) * - track the current black-clue status of each vertex in each * loop. * * Each of these chunks of data is updated incrementally from the * previous one, to avoid slowdown due to the bias function * rescanning the whole grid every time it's called. * * So we need a lot of separate arrays, plus a tdq for each one, * and we must repeat it all twice for the BLACK and WHITE * boundaries. */ struct pearl_loopgen_bias_ctx_boundary { int colour; /* FACE_WHITE or FACE_BLACK */ char *edges; /* is each edge part of the loop? */ tdq *edges_todo; char *vertextypes; /* bits 0-3 == outgoing edge bitmap; * bit 4 set iff corner clue. * Hence, 0 means non-vertex; * nonzero but bit 4 zero = straight. */ int *neighbour[2]; /* indices of neighbour vertices in loop */ tdq *vertextypes_todo; char *blackclues; /* is each vertex a black clue site? */ tdq *blackclues_todo; } boundaries[2]; /* boundaries[0]=WHITE, [1]=BLACK */ char *faces; /* remember last-seen colour of each face */ tdq *faces_todo; int score; grid *g; }; int pearl_loopgen_bias(void *vctx, char *board, int face) { struct pearl_loopgen_bias_ctx *ctx = (struct pearl_loopgen_bias_ctx *)vctx; grid *g = ctx->g; int oldface, newface; int i, j, k; tdq_add(ctx->faces_todo, face); while ((j = tdq_remove(ctx->faces_todo)) >= 0) { oldface = ctx->faces[j]; ctx->faces[j] = newface = board[j]; for (i = 0; i < 2; i++) { struct pearl_loopgen_bias_ctx_boundary *b = &ctx->boundaries[i]; int c = b->colour; /* * If the face has changed either from or to colour c, we need * to reprocess the edges for this boundary. */ if (oldface == c || newface == c) { grid_face *f = &g->faces[face]; for (k = 0; k < f->order; k++) tdq_add(b->edges_todo, f->edges[k] - g->edges); } } } for (i = 0; i < 2; i++) { struct pearl_loopgen_bias_ctx_boundary *b = &ctx->boundaries[i]; int c = b->colour; /* * Go through the to-do list of edges. For each edge, decide * anew whether it's part of this boundary or not. Any edge * that changes state has to have both its endpoints put on * the vertextypes_todo list. */ while ((j = tdq_remove(b->edges_todo)) >= 0) { grid_edge *e = &g->edges[j]; int fc1 = e->face1 ? board[e->face1 - g->faces] : FACE_BLACK; int fc2 = e->face2 ? board[e->face2 - g->faces] : FACE_BLACK; int oldedge = b->edges[j]; int newedge = (fc1==c) ^ (fc2==c); if (oldedge != newedge) { b->edges[j] = newedge; tdq_add(b->vertextypes_todo, e->dot1 - g->dots); tdq_add(b->vertextypes_todo, e->dot2 - g->dots); } } /* * Go through the to-do list of vertices whose types need * refreshing. For each one, decide whether it's a corner, a * straight, or a vertex not in the loop, and in the former * two cases also work out the indices of its neighbour * vertices along the loop. Any vertex that changes state must * be put back on the to-do list for deciding if it's a black * clue site, and so must its two new neighbours _and_ its two * old neighbours. */ while ((j = tdq_remove(b->vertextypes_todo)) >= 0) { grid_dot *d = &g->dots[j]; int neighbours[2], type = 0, n = 0; for (k = 0; k < d->order; k++) { grid_edge *e = d->edges[k]; grid_dot *d2 = (e->dot1 == d ? e->dot2 : e->dot1); /* dir == 0,1,2,3 for an edge going L,U,R,D */ int dir = (d->y == d2->y) + 2*(d->x+d->y > d2->x+d2->y); int ei = e - g->edges; if (b->edges[ei]) { type |= 1 << dir; neighbours[n] = d2 - g->dots; n++; } } /* * Decide if it's a corner, and set the corner flag if so. */ if (type != 0 && type != 0x5 && type != 0xA) type |= 0x10; if (type != b->vertextypes[j]) { /* * Recompute old neighbours, if any. */ if (b->vertextypes[j]) { tdq_add(b->blackclues_todo, b->neighbour[0][j]); tdq_add(b->blackclues_todo, b->neighbour[1][j]); } /* * Recompute this vertex. */ tdq_add(b->blackclues_todo, j); b->vertextypes[j] = type; /* * Recompute new neighbours, if any. */ if (b->vertextypes[j]) { b->neighbour[0][j] = neighbours[0]; b->neighbour[1][j] = neighbours[1]; tdq_add(b->blackclues_todo, b->neighbour[0][j]); tdq_add(b->blackclues_todo, b->neighbour[1][j]); } } } /* * Go through the list of vertices which we must check to see * if they're black clue sites. Each one is a black clue site * iff it is a corner and its loop neighbours are non-corners. * Adjust the running total of black clues we've counted. */ while ((j = tdq_remove(b->blackclues_todo)) >= 0) { ctx->score -= b->blackclues[j]; b->blackclues[j] = ((b->vertextypes[j] & 0x10) && !((b->vertextypes[b->neighbour[0][j]] | b->vertextypes[b->neighbour[1][j]]) & 0x10)); ctx->score += b->blackclues[j]; } } return ctx->score; } void pearl_loopgen(int w, int h, char *lines, random_state *rs) { grid *g = grid_new(GRID_SQUARE, w-1, h-1, NULL); char *board = snewn(g->num_faces, char); int i, s = g->tilesize; struct pearl_loopgen_bias_ctx biasctx; memset(lines, 0, w*h); /* * Initialise the context for the bias function. Initially we fill * all the to-do lists, so that the first call will scan * everything; thereafter the lists stay empty so we make * incremental changes. */ biasctx.g = g; biasctx.faces = snewn(g->num_faces, char); biasctx.faces_todo = tdq_new(g->num_faces); tdq_fill(biasctx.faces_todo); biasctx.score = 0; memset(biasctx.faces, FACE_GREY, g->num_faces); for (i = 0; i < 2; i++) { biasctx.boundaries[i].edges = snewn(g->num_edges, char); memset(biasctx.boundaries[i].edges, 0, g->num_edges); biasctx.boundaries[i].edges_todo = tdq_new(g->num_edges); tdq_fill(biasctx.boundaries[i].edges_todo); biasctx.boundaries[i].vertextypes = snewn(g->num_dots, char); memset(biasctx.boundaries[i].vertextypes, 0, g->num_dots); biasctx.boundaries[i].neighbour[0] = snewn(g->num_dots, int); biasctx.boundaries[i].neighbour[1] = snewn(g->num_dots, int); biasctx.boundaries[i].vertextypes_todo = tdq_new(g->num_dots); tdq_fill(biasctx.boundaries[i].vertextypes_todo); biasctx.boundaries[i].blackclues = snewn(g->num_dots, char); memset(biasctx.boundaries[i].blackclues, 0, g->num_dots); biasctx.boundaries[i].blackclues_todo = tdq_new(g->num_dots); tdq_fill(biasctx.boundaries[i].blackclues_todo); } biasctx.boundaries[0].colour = FACE_WHITE; biasctx.boundaries[1].colour = FACE_BLACK; generate_loop(g, board, rs, pearl_loopgen_bias, &biasctx); sfree(biasctx.faces); tdq_free(biasctx.faces_todo); for (i = 0; i < 2; i++) { sfree(biasctx.boundaries[i].edges); tdq_free(biasctx.boundaries[i].edges_todo); sfree(biasctx.boundaries[i].vertextypes); sfree(biasctx.boundaries[i].neighbour[0]); sfree(biasctx.boundaries[i].neighbour[1]); tdq_free(biasctx.boundaries[i].vertextypes_todo); sfree(biasctx.boundaries[i].blackclues); tdq_free(biasctx.boundaries[i].blackclues_todo); } for (i = 0; i < g->num_edges; i++) { grid_edge *e = g->edges + i; enum face_colour c1 = FACE_COLOUR(e->face1); enum face_colour c2 = FACE_COLOUR(e->face2); assert(c1 != FACE_GREY); assert(c2 != FACE_GREY); if (c1 != c2) { /* This grid edge is on the loop: lay line along it */ int x1 = e->dot1->x/s, y1 = e->dot1->y/s; int x2 = e->dot2->x/s, y2 = e->dot2->y/s; /* (x1,y1) and (x2,y2) are now in our grid coords (0-w,0-h). */ if (x1 == x2) { if (y1 > y2) SWAP(y1,y2); assert(y1+1 == y2); lines[y1*w+x1] |= D; lines[y2*w+x1] |= U; } else if (y1 == y2) { if (x1 > x2) SWAP(x1,x2); assert(x1+1 == x2); lines[y1*w+x1] |= R; lines[y1*w+x2] |= L; } else assert(!"grid with diagonal coords?!"); } } grid_free(g); sfree(board); #if defined LOOPGEN_DIAGNOSTICS && !defined GENERATION_DIAGNOSTICS printf("as returned:\n"); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { int type = lines[y*w+x]; char s[5], *p = s; if (type & L) *p++ = 'L'; if (type & R) *p++ = 'R'; if (type & U) *p++ = 'U'; if (type & D) *p++ = 'D'; *p = '\0'; printf("%3s", s); } printf("\n"); } printf("\n"); #endif } static int new_clues(const game_params *params, random_state *rs, char *clues, char *grid) { int w = params->w, h = params->h, diff = params->difficulty; int ngen = 0, x, y, d, ret, i; /* * Difficulty exception: 5x5 Tricky is not generable (the * generator will spin forever trying) and so we fudge it to Easy. */ if (w == 5 && h == 5 && diff > DIFF_EASY) diff = DIFF_EASY; while (1) { ngen++; pearl_loopgen(w, h, grid, rs); #ifdef GENERATION_DIAGNOSTICS printf("grid array:\n"); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { int type = grid[y*w+x]; char s[5], *p = s; if (type & L) *p++ = 'L'; if (type & R) *p++ = 'R'; if (type & U) *p++ = 'U'; if (type & D) *p++ = 'D'; *p = '\0'; printf("%2s ", s); } printf("\n"); } printf("\n"); #endif /* * Set up the maximal clue array. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) { int type = grid[y*w+x]; clues[y*w+x] = NOCLUE; if ((bLR|bUD) & (1 << type)) { /* * This is a straight; see if it's a viable * candidate for a straight clue. It qualifies if * at least one of the squares it connects to is a * corner. */ for (d = 1; d <= 8; d += d) if (type & d) { int xx = x + DX(d), yy = y + DY(d); assert(xx >= 0 && xx < w && yy >= 0 && yy < h); if ((bLU|bLD|bRU|bRD) & (1 << grid[yy*w+xx])) break; } if (d <= 8) /* we found one */ clues[y*w+x] = STRAIGHT; } else if ((bLU|bLD|bRU|bRD) & (1 << type)) { /* * This is a corner; see if it's a viable candidate * for a corner clue. It qualifies if all the * squares it connects to are straights. */ for (d = 1; d <= 8; d += d) if (type & d) { int xx = x + DX(d), yy = y + DY(d); assert(xx >= 0 && xx < w && yy >= 0 && yy < h); if (!((bLR|bUD) & (1 << grid[yy*w+xx]))) break; } if (d > 8) /* we didn't find a counterexample */ clues[y*w+x] = CORNER; } } #ifdef GENERATION_DIAGNOSTICS printf("clue array:\n"); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { printf("%c", " *O"[(unsigned char)clues[y*w+x]]); } printf("\n"); } printf("\n"); #endif if (!params->nosolve) { int *cluespace, *straights, *corners; int nstraights, ncorners, nstraightpos, ncornerpos; /* * See if we can solve the puzzle just like this. */ ret = pearl_solve(w, h, clues, grid, diff, FALSE); assert(ret > 0); /* shouldn't be inconsistent! */ if (ret != 1) continue; /* go round and try again */ /* * Check this puzzle isn't too easy. */ if (diff > DIFF_EASY) { ret = pearl_solve(w, h, clues, grid, diff-1, FALSE); assert(ret > 0); if (ret == 1) continue; /* too easy: try again */ } /* * Now shuffle the grid points and gradually remove the * clues to find a minimal set which still leaves the * puzzle soluble. * * We preferentially attempt to remove whichever type of * clue is currently most numerous, to combat a general * tendency of plain random generation to bias in favour * of many white clues and few black. * * 'nstraights' and 'ncorners' count the number of clues * of each type currently remaining in the grid; * 'nstraightpos' and 'ncornerpos' count the clues of each * type we have left to try to remove. (Clues which we * have tried and failed to remove are counted by the * former but not the latter.) */ cluespace = snewn(w*h, int); straights = cluespace; nstraightpos = 0; for (i = 0; i < w*h; i++) if (clues[i] == STRAIGHT) straights[nstraightpos++] = i; corners = straights + nstraightpos; ncornerpos = 0; for (i = 0; i < w*h; i++) if (clues[i] == STRAIGHT) corners[ncornerpos++] = i; nstraights = nstraightpos; ncorners = ncornerpos; shuffle(straights, nstraightpos, sizeof(*straights), rs); shuffle(corners, ncornerpos, sizeof(*corners), rs); while (nstraightpos > 0 || ncornerpos > 0) { int cluepos; int clue; /* * Decide which clue to try to remove next. If both * types are available, we choose whichever kind is * currently overrepresented; otherwise we take * whatever we can get. */ if (nstraightpos > 0 && ncornerpos > 0) { if (nstraights >= ncorners) cluepos = straights[--nstraightpos]; else cluepos = straights[--ncornerpos]; } else { if (nstraightpos > 0) cluepos = straights[--nstraightpos]; else cluepos = straights[--ncornerpos]; } y = cluepos / w; x = cluepos % w; clue = clues[y*w+x]; clues[y*w+x] = 0; /* try removing this clue */ ret = pearl_solve(w, h, clues, grid, diff, FALSE); assert(ret > 0); if (ret != 1) clues[y*w+x] = clue; /* oops, put it back again */ } sfree(cluespace); } #ifdef FINISHED_PUZZLE printf("clue array:\n"); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { printf("%c", " *O"[(unsigned char)clues[y*w+x]]); } printf("\n"); } printf("\n"); #endif break; /* got it */ } debug(("%d %dx%d loops before finished puzzle.\n", ngen, w, h)); return ngen; } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { char *grid, *clues; char *desc; int w = params->w, h = params->h, i, j; grid = snewn(w*h, char); clues = snewn(w*h, char); new_clues(params, rs, clues, grid); desc = snewn(w * h + 1, char); for (i = j = 0; i < w*h; i++) { if (clues[i] == NOCLUE && j > 0 && desc[j-1] >= 'a' && desc[j-1] < 'z') desc[j-1]++; else if (clues[i] == NOCLUE) desc[j++] = 'a'; else if (clues[i] == CORNER) desc[j++] = 'B'; else if (clues[i] == STRAIGHT) desc[j++] = 'W'; } desc[j] = '\0'; *aux = snewn(w*h+1, char); for (i = 0; i < w*h; i++) (*aux)[i] = (grid[i] < 10) ? (grid[i] + '0') : (grid[i] + 'A' - 10); (*aux)[w*h] = '\0'; sfree(grid); sfree(clues); return desc; } static char *validate_desc(const game_params *params, const char *desc) { int i, sizesofar; const int totalsize = params->w * params->h; sizesofar = 0; for (i = 0; desc[i]; i++) { if (desc[i] >= 'a' && desc[i] <= 'z') sizesofar += desc[i] - 'a' + 1; else if (desc[i] == 'B' || desc[i] == 'W') sizesofar++; else return "unrecognised character in string"; } if (sizesofar > totalsize) return "string too long"; else if (sizesofar < totalsize) return "string too short"; return NULL; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { game_state *state = snew(game_state); int i, j, sz = params->w*params->h; state->completed = state->used_solve = FALSE; state->shared = snew(struct shared_state); state->shared->w = params->w; state->shared->h = params->h; state->shared->sz = sz; state->shared->refcnt = 1; state->shared->clues = snewn(sz, char); for (i = j = 0; desc[i]; i++) { assert(j < sz); if (desc[i] >= 'a' && desc[i] <= 'z') { int n = desc[i] - 'a' + 1; assert(j + n <= sz); while (n-- > 0) state->shared->clues[j++] = NOCLUE; } else if (desc[i] == 'B') { state->shared->clues[j++] = CORNER; } else if (desc[i] == 'W') { state->shared->clues[j++] = STRAIGHT; } } state->lines = snewn(sz, char); state->errors = snewn(sz, char); state->marks = snewn(sz, char); for (i = 0; i < sz; i++) state->lines[i] = state->errors[i] = state->marks[i] = BLANK; return state; } static game_state *dup_game(const game_state *state) { game_state *ret = snew(game_state); int sz = state->shared->sz, i; ret->shared = state->shared; ret->completed = state->completed; ret->used_solve = state->used_solve; ++ret->shared->refcnt; ret->lines = snewn(sz, char); ret->errors = snewn(sz, char); ret->marks = snewn(sz, char); for (i = 0; i < sz; i++) { ret->lines[i] = state->lines[i]; ret->errors[i] = state->errors[i]; ret->marks[i] = state->marks[i]; } return ret; } static void free_game(game_state *state) { assert(state); if (--state->shared->refcnt == 0) { sfree(state->shared->clues); sfree(state->shared); } sfree(state->lines); sfree(state->errors); sfree(state->marks); sfree(state); } static char nbits[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 }; #define NBITS(l) ( ((l) < 0 || (l) > 15) ? 4 : nbits[l] ) #define ERROR_CLUE 16 static void dsf_update_completion(game_state *state, int *loopclass, int ax, int ay, char dir, int *dsf, int *dsfsize) { int w = state->shared->w /*, h = state->shared->h */; int ac = ay*w+ax, ae, bx, by, bc, be; if (!(state->lines[ac] & dir)) return; /* no link */ bx = ax + DX(dir); by = ay + DY(dir); assert(INGRID(state, bx, by)); /* should not have a link off grid */ bc = by*w+bx; #if 0 assert(state->lines[bc] & F(dir)); /* should have reciprocal link */ #endif /* TODO put above assertion back in once we stop generating partially * soluble puzzles. */ if (!(state->lines[bc] & F(dir))) return; ae = dsf_canonify(dsf, ac); be = dsf_canonify(dsf, bc); if (ae == be) { /* detected a loop! */ if (*loopclass != -1) /* this is the second loop, doom. */ return; *loopclass = ae; } else { int size = dsfsize[ae] + dsfsize[be]; dsf_merge(dsf, ac, bc); ae = dsf_canonify(dsf, ac); dsfsize[ae] = size; } return; } static void check_completion(game_state *state, int mark) { int w = state->shared->w, h = state->shared->h, x, y, i, d; int had_error = FALSE /*, is_complete = FALSE */, loopclass; int *dsf, *dsfsize; if (mark) { for (i = 0; i < w*h; i++) { state->errors[i] = 0; } } #define ERROR(x,y,e) do { had_error = TRUE; if (mark) state->errors[(y)*w+(x)] |= (e); } while(0) /* * First of all: we should have one single closed loop, passing through all clues. */ dsf = snewn(w*h, int); dsfsize = snewn(w*h, int); dsf_init(dsf, w*h); for (i = 0; i < w*h; i++) dsfsize[i] = 1; loopclass = -1; for (x = 0; x < w; x++) { for (y = 0; y < h; y++) { dsf_update_completion(state, &loopclass, x, y, R, dsf, dsfsize); dsf_update_completion(state, &loopclass, x, y, D, dsf, dsfsize); } } if (loopclass != -1) { /* We have a loop. Check all squares with lines on. */ for (x = 0; x < w; x++) { for (y = 0; y < h; y++) { if (state->lines[y*w+x] == BLANK) { if (state->shared->clues[y*w+x] != NOCLUE) { /* the loop doesn't include this clue square! */ ERROR(x, y, ERROR_CLUE); } } else { if (dsf_canonify(dsf, y*w+x) != loopclass) { /* these lines are not on the loop: mark them as error. */ ERROR(x, y, state->lines[y*w+x]); } } } } } /* * Second: check no clues are contradicted. */ for (x = 0; x < w; x++) { for (y = 0; y < h; y++) { int type = state->lines[y*w+x]; /* * Check that no square has more than two line segments. */ if (NBITS(type) > 2) { ERROR(x,y,type); } /* * Check that no clues are contradicted. This code is similar to * the code that sets up the maximal clue array for any given * loop. */ if (state->shared->clues[y*w+x] == CORNER) { /* Supposed to be a corner: will find a contradiction if * it actually contains a straight line, or if it touches any * corners. */ if ((bLR|bUD) & (1 << type)) { ERROR(x,y,ERROR_CLUE); /* actually straight */ } for (d = 1; d <= 8; d += d) if (type & d) { int xx = x + DX(d), yy = y + DY(d); if (!INGRID(state, xx, yy)) { ERROR(x,y,d); /* leads off grid */ } else { if ((bLU|bLD|bRU|bRD) & (1 << state->lines[yy*w+xx])) { ERROR(x,y,ERROR_CLUE); /* touches corner */ } } } } else if (state->shared->clues[y*w+x] == STRAIGHT) { /* Supposed to be straight: will find a contradiction if * it actually contains a corner, or if it only touches * straight lines. */ if ((bLU|bLD|bRU|bRD) & (1 << type)) { ERROR(x,y,ERROR_CLUE); /* actually a corner */ } i = 0; for (d = 1; d <= 8; d += d) if (type & d) { int xx = x + DX(d), yy = y + DY(d); if (!INGRID(state, xx, yy)) { ERROR(x,y,d); /* leads off grid */ } else { if ((bLR|bUD) & (1 << state->lines[yy*w+xx])) i++; /* a straight */ } } if (i >= 2 && NBITS(type) >= 2) { ERROR(x,y,ERROR_CLUE); /* everything touched is straight */ } } } } if (!had_error && loopclass != -1) { state->completed = TRUE; state->loop_length = dsfsize[loopclass]; } sfree(dsf); sfree(dsfsize); return; } /* completion check: * * - no clues must be contradicted (highlight clue itself in error if so) * - if there is a closed loop it must include every line segment laid * - if there's a smaller closed loop then highlight whole loop as error * - no square must have more than 3 lines radiating from centre point * (highlight all lines in that square as error if so) */ static char *solve_for_diff(game_state *state, char *old_lines, char *new_lines) { int w = state->shared->w, h = state->shared->h, i; char *move = snewn(w*h*40, char), *p = move; *p++ = 'S'; for (i = 0; i < w*h; i++) { if (old_lines[i] != new_lines[i]) { p += sprintf(p, ";R%d,%d,%d", new_lines[i], i%w, i/w); } } *p++ = '\0'; move = sresize(move, p - move, char); return move; } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { game_state *solved = dup_game(state); int i, ret, sz = state->shared->sz; char *move; if (aux) { for (i = 0; i < sz; i++) { if (aux[i] >= '0' && aux[i] <= '9') solved->lines[i] = aux[i] - '0'; else if (aux[i] >= 'A' && aux[i] <= 'F') solved->lines[i] = aux[i] - 'A' + 10; else { *error = "invalid char in aux"; move = NULL; goto done; } } ret = 1; } else { /* Try to solve with present (half-solved) state first: if there's no * solution from there go back to original state. */ ret = pearl_solve(currstate->shared->w, currstate->shared->h, currstate->shared->clues, solved->lines, DIFFCOUNT, FALSE); if (ret < 1) ret = pearl_solve(state->shared->w, state->shared->h, state->shared->clues, solved->lines, DIFFCOUNT, FALSE); } if (ret < 1) { *error = "Unable to find solution"; move = NULL; } else { move = solve_for_diff(solved, currstate->lines, solved->lines); } done: free_game(solved); return move; } static int game_can_format_as_text_now(const game_params *params) { return FALSE; } static char *game_text_format(const game_state *state) { return NULL; } struct game_ui { int *dragcoords; /* list of (y*w+x) coords in drag so far */ int ndragcoords; /* number of entries in dragcoords. * 0 = click but no drag yet. -1 = no drag at all */ int clickx, clicky; /* pixel position of initial click */ int curx, cury; /* grid position of keyboard cursor */ int cursor_active; /* TRUE iff cursor is shown */ }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); int sz = state->shared->sz; ui->ndragcoords = -1; ui->dragcoords = snewn(sz, int); ui->cursor_active = FALSE; ui->curx = ui->cury = 0; return ui; } static void free_ui(game_ui *ui) { sfree(ui->dragcoords); 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_TILE_SIZE 31 #define HALFSZ (ds->halfsz) #define TILE_SIZE (ds->halfsz*2 + 1) #define BORDER ((get_gui_style() == GUI_LOOPY) ? (TILE_SIZE/8) : (TILE_SIZE/2)) #define BORDER_WIDTH (max(TILE_SIZE / 32, 1)) #define COORD(x) ( (x) * TILE_SIZE + BORDER ) #define CENTERED_COORD(x) ( COORD(x) + TILE_SIZE/2 ) #define FROMCOORD(x) ( ((x) < BORDER) ? -1 : ( ((x) - BORDER) / TILE_SIZE) ) #define DS_ESHIFT 4 /* R/U/L/D shift, for error flags */ #define DS_DSHIFT 8 /* R/U/L/D shift, for drag-in-progress flags */ #define DS_MSHIFT 12 /* shift for no-line mark */ #define DS_ERROR_CLUE (1 << 20) #define DS_FLASH (1 << 21) #define DS_CURSOR (1 << 22) enum { GUI_MASYU, GUI_LOOPY }; static int get_gui_style(void) { static int gui_style = -1; if (gui_style == -1) { char *env = getenv("PEARL_GUI_LOOPY"); if (env && (env[0] == 'y' || env[0] == 'Y')) gui_style = GUI_LOOPY; else gui_style = GUI_MASYU; } return gui_style; } struct game_drawstate { int halfsz; int started; int w, h, sz; unsigned int *lflags; /* size w*h */ char *draglines; /* size w*h; lines flipped by current drag */ }; static void update_ui_drag(const game_state *state, game_ui *ui, int gx, int gy) { int /* sz = state->shared->sz, */ w = state->shared->w; int i, ox, oy, pos; int lastpos; if (!INGRID(state, gx, gy)) return; /* square is outside grid */ if (ui->ndragcoords < 0) return; /* drag not in progress anyway */ pos = gy * w + gx; lastpos = ui->dragcoords[ui->ndragcoords > 0 ? ui->ndragcoords-1 : 0]; if (pos == lastpos) return; /* same square as last visited one */ /* Drag confirmed, if it wasn't already. */ if (ui->ndragcoords == 0) ui->ndragcoords = 1; /* * Dragging the mouse into a square that's already been visited by * the drag path so far has the effect of truncating the path back * to that square, so a player can back out part of an uncommitted * drag without having to let go of the mouse. */ for (i = 0; i < ui->ndragcoords; i++) if (pos == ui->dragcoords[i]) { ui->ndragcoords = i+1; return; } /* * Otherwise, dragging the mouse into a square that's a rook-move * away from the last one on the path extends the path. */ oy = ui->dragcoords[ui->ndragcoords-1] / w; ox = ui->dragcoords[ui->ndragcoords-1] % w; if (ox == gx || oy == gy) { int dx = (gx < ox ? -1 : gx > ox ? +1 : 0); int dy = (gy < oy ? -1 : gy > oy ? +1 : 0); int dir = (dy>0 ? D : dy<0 ? U : dx>0 ? R : L); while (ox != gx || oy != gy) { /* * If the drag attempts to cross a 'no line here' mark, * stop there. We physically don't allow the user to drag * over those marks. */ if (state->marks[oy*w+ox] & dir) break; ox += dx; oy += dy; ui->dragcoords[ui->ndragcoords++] = oy * w + ox; } } /* * Failing that, we do nothing at all: if the user has dragged * diagonally across the board, they'll just have to return the * mouse to the last known position and do whatever they meant to * do again, more slowly and clearly. */ } /* * Routine shared between interpret_move and game_redraw to work out * the intended effect of a drag path on the grid. * * Call it in a loop, like this: * * int clearing = TRUE; * for (i = 0; i < ui->ndragcoords - 1; i++) { * int sx, sy, dx, dy, dir, oldstate, newstate; * interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy, * &dir, &oldstate, &newstate); * * [do whatever is needed to handle the fact that the drag * wants the edge from sx,sy to dx,dy (heading in direction * 'dir' at the sx,sy end) to be changed from state oldstate * to state newstate, each of which equals either 0 or dir] * } */ static void interpret_ui_drag(const game_state *state, const game_ui *ui, int *clearing, int i, int *sx, int *sy, int *dx, int *dy, int *dir, int *oldstate, int *newstate) { int w = state->shared->w; int sp = ui->dragcoords[i], dp = ui->dragcoords[i+1]; *sy = sp/w; *sx = sp%w; *dy = dp/w; *dx = dp%w; *dir = (*dy>*sy ? D : *dy<*sy ? U : *dx>*sx ? R : L); *oldstate = state->lines[sp] & *dir; if (*oldstate) { /* * The edge we've dragged over was previously * present. Set it to absent, unless we've already * stopped doing that. */ *newstate = *clearing ? 0 : *dir; } else { /* * The edge we've dragged over was previously * absent. Set it to present, and cancel the * 'clearing' flag so that all subsequent edges in * the drag are set rather than cleared. */ *newstate = *dir; *clearing = FALSE; } } static char *mark_in_direction(const game_state *state, int x, int y, int dir, int ismark, char *buf) { int w = state->shared->w /*, h = state->shared->h, sz = state->shared->sz */; int x2 = x + DX(dir); int y2 = y + DY(dir); int dir2 = F(dir); char ch = ismark ? 'M' : 'F'; if (!INGRID(state, x, y) || !INGRID(state, x2, y2)) return ""; /* disallow laying a mark over a line, or vice versa. */ if (ismark) { if ((state->lines[y*w+x] & dir) || (state->lines[y2*w+x2] & dir2)) return ""; } else { if ((state->marks[y*w+x] & dir) || (state->marks[y2*w+x2] & dir2)) return ""; } sprintf(buf, "%c%d,%d,%d;%c%d,%d,%d", ch, dir, x, y, ch, dir2, x2, y2); return dupstr(buf); } #define KEY_DIRECTION(btn) (\ (btn) == CURSOR_DOWN ? D : (btn) == CURSOR_UP ? U :\ (btn) == CURSOR_LEFT ? L : R) static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { int w = state->shared->w, h = state->shared->h /*, sz = state->shared->sz */; int gx = FROMCOORD(x), gy = FROMCOORD(y), i; int release = FALSE; char tmpbuf[80]; if (IS_MOUSE_DOWN(button)) { ui->cursor_active = FALSE; if (!INGRID(state, gx, gy)) { ui->ndragcoords = -1; return NULL; } ui->clickx = x; ui->clicky = y; ui->dragcoords[0] = gy * w + gx; ui->ndragcoords = 0; /* will be 1 once drag is confirmed */ return ""; } if (button == LEFT_DRAG && ui->ndragcoords >= 0) { update_ui_drag(state, ui, gx, gy); return ""; } if (IS_MOUSE_RELEASE(button)) release = TRUE; if (IS_CURSOR_MOVE(button & ~MOD_MASK)) { if (!ui->cursor_active) { ui->cursor_active = TRUE; } else if (button & (MOD_SHFT | MOD_CTRL)) { if (ui->ndragcoords > 0) return NULL; ui->ndragcoords = -1; return mark_in_direction(state, ui->curx, ui->cury, KEY_DIRECTION(button & ~MOD_MASK), (button & MOD_SHFT), tmpbuf); } else { move_cursor(button, &ui->curx, &ui->cury, w, h, FALSE); if (ui->ndragcoords >= 0) update_ui_drag(state, ui, ui->curx, ui->cury); } return ""; } if (IS_CURSOR_SELECT(button & ~MOD_MASK)) { if (!ui->cursor_active) { ui->cursor_active = TRUE; return ""; } else if (button == CURSOR_SELECT) { if (ui->ndragcoords == -1) { ui->ndragcoords = 0; ui->dragcoords[0] = ui->cury * w + ui->curx; ui->clickx = CENTERED_COORD(ui->curx); ui->clicky = CENTERED_COORD(ui->cury); return ""; } else release = TRUE; } else if (button == CURSOR_SELECT2 && ui->ndragcoords >= 0) { ui->ndragcoords = -1; return ""; } } if (release) { if (ui->ndragcoords > 0) { /* End of a drag: process the cached line data. */ int buflen = 0, bufsize = 256, tmplen; char *buf = NULL; const char *sep = ""; int clearing = TRUE; for (i = 0; i < ui->ndragcoords - 1; i++) { int sx, sy, dx, dy, dir, oldstate, newstate; interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy, &dir, &oldstate, &newstate); if (oldstate != newstate) { if (!buf) buf = snewn(bufsize, char); tmplen = sprintf(tmpbuf, "%sF%d,%d,%d;F%d,%d,%d", sep, dir, sx, sy, F(dir), dx, dy); if (buflen + tmplen >= bufsize) { bufsize = (buflen + tmplen) * 5 / 4 + 256; buf = sresize(buf, bufsize, char); } strcpy(buf + buflen, tmpbuf); buflen += tmplen; sep = ";"; } } ui->ndragcoords = -1; return buf ? buf : ""; } else if (ui->ndragcoords == 0) { /* Click (or tiny drag). Work out which edge we were * closest to. */ int cx, cy; ui->ndragcoords = -1; /* * We process clicks based on the mouse-down location, * because that's more natural for a user to carefully * control than the mouse-up. */ x = ui->clickx; y = ui->clicky; gx = FROMCOORD(x); gy = FROMCOORD(y); cx = CENTERED_COORD(gx); cy = CENTERED_COORD(gy); if (!INGRID(state, gx, gy)) return ""; if (max(abs(x-cx),abs(y-cy)) < TILE_SIZE/4) { /* TODO closer to centre of grid: process as a cell click not an edge click. */ return ""; } else { int direction; if (abs(x-cx) < abs(y-cy)) { /* Closest to top/bottom edge. */ direction = (y < cy) ? U : D; } else { /* Closest to left/right edge. */ direction = (x < cx) ? L : R; } return mark_in_direction(state, gx, gy, direction, (button == RIGHT_RELEASE), tmpbuf); } } } if (button == 'H' || button == 'h') return dupstr("H"); return NULL; } static game_state *execute_move(const game_state *state, const char *move) { int w = state->shared->w, h = state->shared->h; char c; int x, y, l, n; game_state *ret = dup_game(state); debug(("move: %s\n", move)); while (*move) { c = *move; if (c == 'S') { ret->used_solve = TRUE; move++; } else if (c == 'L' || c == 'N' || c == 'R' || c == 'F' || c == 'M') { /* 'line' or 'noline' or 'replace' or 'flip' or 'mark' */ move++; if (sscanf(move, "%d,%d,%d%n", &l, &x, &y, &n) != 3) goto badmove; if (!INGRID(state, x, y)) goto badmove; if (l < 0 || l > 15) goto badmove; if (c == 'L') ret->lines[y*w + x] |= (char)l; else if (c == 'N') ret->lines[y*w + x] &= ~((char)l); else if (c == 'R') { ret->lines[y*w + x] = (char)l; ret->marks[y*w + x] &= ~((char)l); /* erase marks too */ } else if (c == 'F') ret->lines[y*w + x] ^= (char)l; else if (c == 'M') ret->marks[y*w + x] ^= (char)l; /* * If we ended up trying to lay a line _over_ a mark, * that's a failed move: interpret_move() should have * ensured we never received a move string like that in * the first place. */ if ((ret->lines[y*w + x] & (char)l) && (ret->marks[y*w + x] & (char)l)) goto badmove; move += n; } else if (strcmp(move, "H") == 0) { pearl_solve(ret->shared->w, ret->shared->h, ret->shared->clues, ret->lines, DIFFCOUNT, TRUE); for (n = 0; n < w*h; n++) ret->marks[n] &= ~ret->lines[n]; /* erase marks too */ move++; } else { goto badmove; } if (*move == ';') move++; else if (*move) goto badmove; } check_completion(ret, TRUE); return ret; badmove: free_game(ret); return NULL; } /* ---------------------------------------------------------------------- * Drawing routines. */ #define FLASH_TIME 0.5F 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 halfsz; } ads, *ds = &ads; ads.halfsz = (tilesize-1)/2; *x = (params->w) * TILE_SIZE + 2 * BORDER; *y = (params->h) * TILE_SIZE + 2 * BORDER; } static void game_set_size(drawing *dr, game_drawstate *ds, const game_params *params, int tilesize) { ds->halfsz = (tilesize-1)/2; } 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); for (i = 0; i < 3; i++) { ret[COL_BLACK * 3 + i] = 0.0F; ret[COL_WHITE * 3 + i] = 1.0F; ret[COL_GRID * 3 + i] = 0.4F; } ret[COL_ERROR * 3 + 0] = 1.0F; ret[COL_ERROR * 3 + 1] = 0.0F; ret[COL_ERROR * 3 + 2] = 0.0F; ret[COL_DRAGON * 3 + 0] = 0.0F; ret[COL_DRAGON * 3 + 1] = 0.0F; ret[COL_DRAGON * 3 + 2] = 1.0F; ret[COL_DRAGOFF * 3 + 0] = 0.8F; ret[COL_DRAGOFF * 3 + 1] = 0.8F; ret[COL_DRAGOFF * 3 + 2] = 1.0F; ret[COL_FLASH * 3 + 0] = 1.0F; ret[COL_FLASH * 3 + 1] = 1.0F; ret[COL_FLASH * 3 + 2] = 1.0F; *ncolours = NCOLOURS; return ret; } static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { struct game_drawstate *ds = snew(struct game_drawstate); int i; ds->halfsz = 0; ds->started = FALSE; ds->w = state->shared->w; ds->h = state->shared->h; ds->sz = state->shared->sz; ds->lflags = snewn(ds->sz, unsigned int); for (i = 0; i < ds->sz; i++) ds->lflags[i] = 0; ds->draglines = snewn(ds->sz, char); return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->draglines); sfree(ds->lflags); sfree(ds); } static void draw_lines_specific(drawing *dr, game_drawstate *ds, int x, int y, unsigned int lflags, unsigned int shift, int c) { int ox = COORD(x), oy = COORD(y); int t2 = HALFSZ, t16 = HALFSZ/4; int cx = ox + t2, cy = oy + t2; int d; /* Draw each of the four directions, where laid (or error, or drag, etc.) */ for (d = 1; d < 16; d *= 2) { int xoff = t2 * DX(d), yoff = t2 * DY(d); int xnudge = abs(t16 * DX(C(d))), ynudge = abs(t16 * DY(C(d))); if ((lflags >> shift) & d) { int lx = cx + ((xoff < 0) ? xoff : 0) - xnudge; int ly = cy + ((yoff < 0) ? yoff : 0) - ynudge; if (c == COL_DRAGOFF && !(lflags & d)) continue; if (c == COL_DRAGON && (lflags & d)) continue; draw_rect(dr, lx, ly, abs(xoff)+2*xnudge+1, abs(yoff)+2*ynudge+1, c); /* end cap */ draw_rect(dr, cx - t16, cy - t16, 2*t16+1, 2*t16+1, c); } } } static void draw_square(drawing *dr, game_drawstate *ds, const game_ui *ui, int x, int y, unsigned int lflags, char clue) { int ox = COORD(x), oy = COORD(y); int t2 = HALFSZ, t16 = HALFSZ/4; int cx = ox + t2, cy = oy + t2; int d; assert(dr); /* Clip to the grid square. */ clip(dr, ox, oy, TILE_SIZE, TILE_SIZE); /* Clear the square. */ draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE, (lflags & DS_CURSOR) ? COL_CURSOR_BACKGROUND : COL_BACKGROUND); if (get_gui_style() == GUI_LOOPY) { /* Draw small dot, underneath any lines. */ draw_circle(dr, cx, cy, t16, COL_GRID, COL_GRID); } else { /* Draw outline of grid square */ draw_line(dr, ox, oy, COORD(x+1), oy, COL_GRID); draw_line(dr, ox, oy, ox, COORD(y+1), COL_GRID); } /* Draw grid: either thin gridlines, or no-line marks. * We draw these first because the thick laid lines should be on top. */ for (d = 1; d < 16; d *= 2) { int xoff = t2 * DX(d), yoff = t2 * DY(d); if ((x == 0 && d == L) || (y == 0 && d == U) || (x == ds->w-1 && d == R) || (y == ds->h-1 && d == D)) continue; /* no gridlines out to the border. */ if ((lflags >> DS_MSHIFT) & d) { /* either a no-line mark ... */ int mx = cx + xoff, my = cy + yoff, msz = t16; draw_line(dr, mx-msz, my-msz, mx+msz, my+msz, COL_BLACK); draw_line(dr, mx-msz, my+msz, mx+msz, my-msz, COL_BLACK); } else { if (get_gui_style() == GUI_LOOPY) { /* draw grid lines connecting centre of cells */ draw_line(dr, cx, cy, cx+xoff, cy+yoff, COL_GRID); } } } /* Draw each of the four directions, where laid (or error, or drag, etc.) * Order is important here, specifically for the eventual colours of the * exposed end caps. */ draw_lines_specific(dr, ds, x, y, lflags, 0, (lflags & DS_FLASH ? COL_FLASH : COL_BLACK)); draw_lines_specific(dr, ds, x, y, lflags, DS_ESHIFT, COL_ERROR); draw_lines_specific(dr, ds, x, y, lflags, DS_DSHIFT, COL_DRAGOFF); draw_lines_specific(dr, ds, x, y, lflags, DS_DSHIFT, COL_DRAGON); /* Draw a clue, if present */ if (clue != NOCLUE) { int c = (lflags & DS_FLASH) ? COL_FLASH : (clue == STRAIGHT) ? COL_WHITE : COL_BLACK; if (lflags & DS_ERROR_CLUE) /* draw a bigger 'error' clue circle. */ draw_circle(dr, cx, cy, TILE_SIZE*3/8, COL_ERROR, COL_ERROR); draw_circle(dr, cx, cy, TILE_SIZE/4, c, COL_BLACK); } unclip(dr); draw_update(dr, ox, oy, TILE_SIZE, TILE_SIZE); } 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->shared->w, h = state->shared->h, sz = state->shared->sz; int x, y, force = 0, flashing = 0; 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, w*TILE_SIZE + 2*BORDER, h*TILE_SIZE + 2*BORDER, COL_BACKGROUND); if (get_gui_style() == GUI_MASYU) { /* * Smaller black rectangle which is the main grid. */ draw_rect(dr, BORDER - BORDER_WIDTH, BORDER - BORDER_WIDTH, w*TILE_SIZE + 2*BORDER_WIDTH + 1, h*TILE_SIZE + 2*BORDER_WIDTH + 1, COL_GRID); } draw_update(dr, 0, 0, w*TILE_SIZE + 2*BORDER, h*TILE_SIZE + 2*BORDER); ds->started = TRUE; force = 1; } if (flashtime > 0 && (flashtime <= FLASH_TIME/3 || flashtime >= FLASH_TIME*2/3)) flashing = DS_FLASH; memset(ds->draglines, 0, sz); if (ui->ndragcoords > 0) { int i, clearing = TRUE; for (i = 0; i < ui->ndragcoords - 1; i++) { int sx, sy, dx, dy, dir, oldstate, newstate; interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy, &dir, &oldstate, &newstate); ds->draglines[sy*w+sx] ^= (oldstate ^ newstate); ds->draglines[dy*w+dx] ^= (F(oldstate) ^ F(newstate)); } } for (x = 0; x < w; x++) { for (y = 0; y < h; y++) { unsigned int f = (unsigned int)state->lines[y*w+x]; unsigned int eline = (unsigned int)(state->errors[y*w+x] & (R|U|L|D)); f |= eline << DS_ESHIFT; f |= ((unsigned int)ds->draglines[y*w+x]) << DS_DSHIFT; f |= ((unsigned int)state->marks[y*w+x]) << DS_MSHIFT; if (state->errors[y*w+x] & ERROR_CLUE) f |= DS_ERROR_CLUE; f |= flashing; if (ui->cursor_active && x == ui->curx && y == ui->cury) f |= DS_CURSOR; if (f != ds->lflags[y*w+x] || force) { ds->lflags[y*w+x] = f; draw_square(dr, ds, ui, x, y, f, state->shared->clues[y*w+x]); } } } } 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->used_solve && !newstate->used_solve) return FLASH_TIME; else return 0.0F; } static int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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) { int pw, ph; /* * I'll use 6mm squares by default. */ game_compute_size(params, 600, &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->shared->w, h = state->shared->h, x, y; int black = print_mono_colour(dr, 0); int white = print_mono_colour(dr, 1); /* No GUI_LOOPY here: only use the familiar masyu style. */ /* Ick: fake up `ds->tilesize' for macro expansion purposes */ game_drawstate *ds = game_new_drawstate(dr, state); game_set_size(dr, ds, NULL, tilesize); /* Draw grid outlines (black). */ for (x = 0; x <= w; x++) draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), black); for (y = 0; y <= h; y++) draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), black); for (x = 0; x < w; x++) { for (y = 0; y < h; y++) { int cx = COORD(x) + HALFSZ, cy = COORD(y) + HALFSZ; int clue = state->shared->clues[y*w+x]; draw_lines_specific(dr, ds, x, y, state->lines[y*w+x], 0, black); if (clue != NOCLUE) { int c = (clue == CORNER) ? black : white; draw_circle(dr, cx, cy, TILE_SIZE/4, c, black); } } } game_free_drawstate(dr, ds); } #ifdef COMBINED #define thegame pearl #endif const struct game thegame = { "Pearl", "games.pearl", "pearl", default_params, game_fetch_preset, 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, FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, decode_ui, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, 0, /* flags */ }; #ifdef STANDALONE_SOLVER #include #include const char *quis = NULL; static void usage(FILE *out) { fprintf(out, "usage: %s \n", quis); } static void pnum(int n, int ntot, const char *desc) { printf("%2.1f%% (%d) %s", (double)n*100.0 / (double)ntot, n, desc); } static void start_soak(game_params *p, random_state *rs, int nsecs) { time_t tt_start, tt_now, tt_last; int n = 0, nsolved = 0, nimpossible = 0, ret; char *grid, *clues; tt_start = tt_last = time(NULL); /* Currently this generates puzzles of any difficulty (trying to solve it * on the maximum difficulty level and not checking it's not too easy). */ printf("Soak-testing a %dx%d grid (any difficulty)", p->w, p->h); if (nsecs > 0) printf(" for %d seconds", nsecs); printf(".\n"); p->nosolve = TRUE; grid = snewn(p->w*p->h, char); clues = snewn(p->w*p->h, char); while (1) { n += new_clues(p, rs, clues, grid); /* should be 1, with nosolve */ ret = pearl_solve(p->w, p->h, clues, grid, DIFF_TRICKY, FALSE); if (ret <= 0) nimpossible++; if (ret == 1) nsolved++; tt_now = time(NULL); if (tt_now > tt_last) { tt_last = tt_now; printf("%d total, %3.1f/s, ", n, (double)n / ((double)tt_now - tt_start)); pnum(nsolved, n, "solved"); printf(", "); printf("%3.1f/s", (double)nsolved / ((double)tt_now - tt_start)); if (nimpossible > 0) pnum(nimpossible, n, "impossible"); printf("\n"); } if (nsecs > 0 && (tt_now - tt_start) > nsecs) { printf("\n"); break; } } sfree(grid); sfree(clues); } int main(int argc, const char *argv[]) { game_params *p = NULL; random_state *rs = NULL; time_t seed = time(NULL); char *id = NULL, *err; setvbuf(stdout, NULL, _IONBF, 0); quis = argv[0]; while (--argc > 0) { char *p = (char*)(*++argv); if (!strcmp(p, "-e") || !strcmp(p, "--seed")) { seed = atoi(*++argv); argc--; } else if (*p == '-') { fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p); usage(stderr); exit(1); } else { id = p; } } rs = random_new((void*)&seed, sizeof(time_t)); p = default_params(); if (id) { if (strchr(id, ':')) { fprintf(stderr, "soak takes params only.\n"); goto done; } decode_params(p, id); err = validate_params(p, 1); if (err) { fprintf(stderr, "%s: %s", argv[0], err); goto done; } start_soak(p, rs, 0); /* run forever */ } else { int i; for (i = 5; i <= 12; i++) { p->w = p->h = i; start_soak(p, rs, 5); } } done: free_params(p); random_free(rs); return 0; } #endif /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/pegs.c0000644000175300017530000010475212132232554013537 0ustar simonsimon/* * pegs.c: the classic Peg Solitaire game. */ #include #include #include #include #include #include #include "puzzles.h" #include "tree234.h" #define GRID_HOLE 0 #define GRID_PEG 1 #define GRID_OBST 2 #define GRID_CURSOR 10 #define GRID_JUMPING 20 enum { COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT, COL_PEG, COL_CURSOR, NCOLOURS }; /* * Grid shapes. I do some macro ickery here to ensure that my enum * and the various forms of my name list always match up. */ #define TYPELIST(A) \ A(CROSS,Cross,cross) \ A(OCTAGON,Octagon,octagon) \ A(RANDOM,Random,random) #define ENUM(upper,title,lower) TYPE_ ## upper, #define TITLE(upper,title,lower) #title, #define LOWER(upper,title,lower) #lower, #define CONFIG(upper,title,lower) ":" #title enum { TYPELIST(ENUM) TYPECOUNT }; static char const *const pegs_titletypes[] = { TYPELIST(TITLE) }; static char const *const pegs_lowertypes[] = { TYPELIST(LOWER) }; #define TYPECONFIG TYPELIST(CONFIG) #define FLASH_FRAME 0.13F struct game_params { int w, h; int type; }; struct game_state { int w, h; int completed; unsigned char *grid; }; static game_params *default_params(void) { game_params *ret = snew(game_params); ret->w = ret->h = 7; ret->type = TYPE_CROSS; return ret; } static const struct game_params pegs_presets[] = { {7, 7, TYPE_CROSS}, {7, 7, TYPE_OCTAGON}, {5, 5, TYPE_RANDOM}, {7, 7, TYPE_RANDOM}, {9, 9, TYPE_RANDOM}, }; static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char str[80]; if (i < 0 || i >= lenof(pegs_presets)) return FALSE; ret = snew(game_params); *ret = pegs_presets[i]; strcpy(str, pegs_titletypes[ret->type]); if (ret->type == TYPE_RANDOM) sprintf(str + strlen(str), " %dx%d", ret->w, ret->h); *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) { char const *p = string; int i; params->w = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; if (*p == 'x') { p++; params->h = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; } else { params->h = params->w; } for (i = 0; i < lenof(pegs_lowertypes); i++) if (!strcmp(p, pegs_lowertypes[i])) params->type = i; } static char *encode_params(const game_params *params, int full) { char str[80]; sprintf(str, "%dx%d", params->w, params->h); if (full) { assert(params->type >= 0 && params->type < lenof(pegs_lowertypes)); strcat(str, pegs_lowertypes[params->type]); } return dupstr(str); } static config_item *game_configure(const game_params *params) { config_item *ret = snewn(4, config_item); char buf[80]; ret[0].name = "Width"; ret[0].type = C_STRING; sprintf(buf, "%d", params->w); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Board type"; ret[2].type = C_CHOICES; ret[2].sval = TYPECONFIG; ret[2].ival = params->type; ret[3].name = NULL; ret[3].type = C_END; ret[3].sval = NULL; ret[3].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); ret->type = cfg[2].ival; return ret; } static char *validate_params(const game_params *params, int full) { if (full && (params->w <= 3 || params->h <= 3)) return "Width and height must both be greater than three"; /* * It might be possible to implement generalisations of Cross * and Octagon, but only if I can find a proof that they're all * soluble. For the moment, therefore, I'm going to disallow * them at any size other than the standard one. */ if (full && (params->type == TYPE_CROSS || params->type == TYPE_OCTAGON)) { if (params->w != 7 || params->h != 7) return "This board type is only supported at 7x7"; } return NULL; } /* ---------------------------------------------------------------------- * Beginning of code to generate random Peg Solitaire boards. * * This procedure is done with no aesthetic judgment, no effort at * symmetry, no difficulty grading and generally no finesse * whatsoever. We simply begin with an empty board containing a * single peg, and repeatedly make random reverse moves until it's * plausibly full. This typically yields a scrappy haphazard mess * with several holes, an uneven shape, and no redeeming features * except guaranteed solubility. * * My only concessions to sophistication are (a) to repeat the * generation process until I at least get a grid that touches * every edge of the specified board size, and (b) to try when * selecting moves to reuse existing space rather than expanding * into new space (so that non-rectangular board shape becomes a * factor during play). */ struct move { /* * x,y are the start point of the move during generation (hence * its endpoint during normal play). * * dx,dy are the direction of the move during generation. * Absolute value 1. Hence, for example, x=3,y=5,dx=1,dy=0 * means that the move during generation starts at (3,5) and * ends at (5,5), and vice versa during normal play. */ int x, y, dx, dy; /* * cost is 0, 1 or 2, depending on how many GRID_OBSTs we must * turn into GRID_HOLEs to play this move. */ int cost; }; static int movecmp(void *av, void *bv) { struct move *a = (struct move *)av; struct move *b = (struct move *)bv; if (a->y < b->y) return -1; else if (a->y > b->y) return +1; if (a->x < b->x) return -1; else if (a->x > b->x) return +1; if (a->dy < b->dy) return -1; else if (a->dy > b->dy) return +1; if (a->dx < b->dx) return -1; else if (a->dx > b->dx) return +1; return 0; } static int movecmpcost(void *av, void *bv) { struct move *a = (struct move *)av; struct move *b = (struct move *)bv; if (a->cost < b->cost) return -1; else if (a->cost > b->cost) return +1; return movecmp(av, bv); } struct movetrees { tree234 *bymove, *bycost; }; static void update_moves(unsigned char *grid, int w, int h, int x, int y, struct movetrees *trees) { struct move move; int dir, pos; /* * There are twelve moves that can include (x,y): three in each * of four directions. Check each one to see if it's possible. */ for (dir = 0; dir < 4; dir++) { int dx, dy; if (dir & 1) dx = 0, dy = dir - 2; else dy = 0, dx = dir - 1; assert(abs(dx) + abs(dy) == 1); for (pos = 0; pos < 3; pos++) { int v1, v2, v3; move.dx = dx; move.dy = dy; move.x = x - pos*dx; move.y = y - pos*dy; if (move.x < 0 || move.x >= w || move.y < 0 || move.y >= h) continue; /* completely invalid move */ if (move.x+2*move.dx < 0 || move.x+2*move.dx >= w || move.y+2*move.dy < 0 || move.y+2*move.dy >= h) continue; /* completely invalid move */ v1 = grid[move.y * w + move.x]; v2 = grid[(move.y+move.dy) * w + (move.x+move.dx)]; v3 = grid[(move.y+2*move.dy)*w + (move.x+2*move.dx)]; if (v1 == GRID_PEG && v2 != GRID_PEG && v3 != GRID_PEG) { struct move *m; move.cost = (v2 == GRID_OBST) + (v3 == GRID_OBST); /* * This move is possible. See if it's already in * the tree. */ m = find234(trees->bymove, &move, NULL); if (m && m->cost != move.cost) { /* * It's in the tree but listed with the wrong * cost. Remove the old version. */ #ifdef GENERATION_DIAGNOSTICS printf("correcting %d%+d,%d%+d at cost %d\n", m->x, m->dx, m->y, m->dy, m->cost); #endif del234(trees->bymove, m); del234(trees->bycost, m); sfree(m); m = NULL; } if (!m) { struct move *m, *m2; m = snew(struct move); *m = move; m2 = add234(trees->bymove, m); m2 = add234(trees->bycost, m); assert(m2 == m); #ifdef GENERATION_DIAGNOSTICS printf("adding %d%+d,%d%+d at cost %d\n", move.x, move.dx, move.y, move.dy, move.cost); #endif } else { #ifdef GENERATION_DIAGNOSTICS printf("not adding %d%+d,%d%+d at cost %d\n", move.x, move.dx, move.y, move.dy, move.cost); #endif } } else { /* * This move is impossible. If it is already in the * tree, delete it. * * (We make use here of the fact that del234 * doesn't have to be passed a pointer to the * _actual_ element it's deleting: it merely needs * one that compares equal to it, and it will * return the one it deletes.) */ struct move *m = del234(trees->bymove, &move); #ifdef GENERATION_DIAGNOSTICS printf("%sdeleting %d%+d,%d%+d\n", m ? "" : "not ", move.x, move.dx, move.y, move.dy); #endif if (m) { del234(trees->bycost, m); sfree(m); } } } } } static void pegs_genmoves(unsigned char *grid, int w, int h, random_state *rs) { struct movetrees atrees, *trees = &atrees; struct move *m; int x, y, i, nmoves; trees->bymove = newtree234(movecmp); trees->bycost = newtree234(movecmpcost); for (y = 0; y < h; y++) for (x = 0; x < w; x++) if (grid[y*w+x] == GRID_PEG) update_moves(grid, w, h, x, y, trees); nmoves = 0; while (1) { int limit, maxcost, index; struct move mtmp, move, *m; /* * See how many moves we can make at zero cost. Make one, * if possible. Failing that, make a one-cost move, and * then a two-cost one. * * After filling at least half the input grid, we no longer * accept cost-2 moves: if that's our only option, we give * up and finish. */ mtmp.y = h+1; maxcost = (nmoves < w*h/2 ? 2 : 1); m = NULL; /* placate optimiser */ for (mtmp.cost = 0; mtmp.cost <= maxcost; mtmp.cost++) { limit = -1; m = findrelpos234(trees->bycost, &mtmp, NULL, REL234_LT, &limit); #ifdef GENERATION_DIAGNOSTICS printf("%d moves available with cost %d\n", limit+1, mtmp.cost); #endif if (m) break; } if (!m) break; index = random_upto(rs, limit+1); move = *(struct move *)index234(trees->bycost, index); #ifdef GENERATION_DIAGNOSTICS printf("selecting move %d%+d,%d%+d at cost %d\n", move.x, move.dx, move.y, move.dy, move.cost); #endif grid[move.y * w + move.x] = GRID_HOLE; grid[(move.y+move.dy) * w + (move.x+move.dx)] = GRID_PEG; grid[(move.y+2*move.dy)*w + (move.x+2*move.dx)] = GRID_PEG; for (i = 0; i <= 2; i++) { int tx = move.x + i*move.dx; int ty = move.y + i*move.dy; update_moves(grid, w, h, tx, ty, trees); } nmoves++; } while ((m = delpos234(trees->bymove, 0)) != NULL) { del234(trees->bycost, m); sfree(m); } freetree234(trees->bymove); freetree234(trees->bycost); } static void pegs_generate(unsigned char *grid, int w, int h, random_state *rs) { while (1) { int x, y, extremes; memset(grid, GRID_OBST, w*h); grid[(h/2) * w + (w/2)] = GRID_PEG; #ifdef GENERATION_DIAGNOSTICS printf("beginning move selection\n"); #endif pegs_genmoves(grid, w, h, rs); #ifdef GENERATION_DIAGNOSTICS printf("finished move selection\n"); #endif extremes = 0; for (y = 0; y < h; y++) { if (grid[y*w+0] != GRID_OBST) extremes |= 1; if (grid[y*w+w-1] != GRID_OBST) extremes |= 2; } for (x = 0; x < w; x++) { if (grid[0*w+x] != GRID_OBST) extremes |= 4; if (grid[(h-1)*w+x] != GRID_OBST) extremes |= 8; } if (extremes == 15) break; #ifdef GENERATION_DIAGNOSTICS printf("insufficient extent; trying again\n"); #endif } #ifdef GENERATION_DIAGNOSTICS fflush(stdout); #endif } /* ---------------------------------------------------------------------- * End of board generation code. Now for the client code which uses * it as part of the puzzle. */ static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { int w = params->w, h = params->h; unsigned char *grid; char *ret; int i; grid = snewn(w*h, unsigned char); if (params->type == TYPE_RANDOM) { pegs_generate(grid, w, h, rs); } else { int x, y, cx, cy, v; for (y = 0; y < h; y++) for (x = 0; x < w; x++) { v = GRID_OBST; /* placate optimiser */ switch (params->type) { case TYPE_CROSS: cx = abs(x - w/2); cy = abs(y - h/2); if (cx == 0 && cy == 0) v = GRID_HOLE; else if (cx > 1 && cy > 1) v = GRID_OBST; else v = GRID_PEG; break; case TYPE_OCTAGON: cx = abs(x - w/2); cy = abs(y - h/2); if (cx + cy > 1 + max(w,h)/2) v = GRID_OBST; else v = GRID_PEG; break; } grid[y*w+x] = v; } if (params->type == TYPE_OCTAGON) { /* * The octagonal (European) solitaire layout is * actually _insoluble_ with the starting hole at the * centre. Here's a proof: * * Colour the squares of the board diagonally in * stripes of three different colours, which I'll call * A, B and C. So the board looks like this: * * A B C * A B C A B * A B C A B C A * B C A B C A B * C A B C A B C * B C A B C * A B C * * Suppose we keep running track of the number of pegs * occuping each colour of square. This colouring has * the property that any valid move whatsoever changes * all three of those counts by one (two of them go * down and one goes up), which means that the _parity_ * of every count flips on every move. * * If the centre square starts off unoccupied, then * there are twelve pegs on each colour and all three * counts start off even; therefore, after 35 moves all * three counts would have to be odd, which isn't * possible if there's only one peg left. [] * * This proof works just as well if the starting hole * is _any_ of the thirteen positions labelled B. Also, * we can stripe the board in the opposite direction * and rule out any square labelled B in that colouring * as well. This leaves: * * Y n Y * n n Y n n * Y n n Y n n Y * n Y Y n Y Y n * Y n n Y n n Y * n n Y n n * Y n Y * * where the ns are squares we've proved insoluble, and * the Ys are the ones remaining. * * That doesn't prove all those starting positions to * be soluble, of course; they're merely the ones we * _haven't_ proved to be impossible. Nevertheless, it * turns out that they are all soluble, so when the * user requests an Octagon board the simplest thing is * to pick one of these at random. * * Rather than picking equiprobably from those twelve * positions, we'll pick equiprobably from the three * equivalence classes */ switch (random_upto(rs, 3)) { case 0: /* Remove a random corner piece. */ { int dx, dy; dx = random_upto(rs, 2) * 2 - 1; /* +1 or -1 */ dy = random_upto(rs, 2) * 2 - 1; /* +1 or -1 */ if (random_upto(rs, 2)) dy *= 3; else dx *= 3; grid[(3+dy)*w+(3+dx)] = GRID_HOLE; } break; case 1: /* Remove a random piece two from the centre. */ { int dx, dy; dx = 2 * (random_upto(rs, 2) * 2 - 1); if (random_upto(rs, 2)) dy = 0; else dy = dx, dx = 0; grid[(3+dy)*w+(3+dx)] = GRID_HOLE; } break; default /* case 2 */: /* Remove a random piece one from the centre. */ { int dx, dy; dx = random_upto(rs, 2) * 2 - 1; if (random_upto(rs, 2)) dy = 0; else dy = dx, dx = 0; grid[(3+dy)*w+(3+dx)] = GRID_HOLE; } break; } } } /* * Encode a game description which is simply a long list of P * for peg, H for hole or O for obstacle. */ ret = snewn(w*h+1, char); for (i = 0; i < w*h; i++) ret[i] = (grid[i] == GRID_PEG ? 'P' : grid[i] == GRID_HOLE ? 'H' : 'O'); ret[w*h] = '\0'; sfree(grid); return ret; } static char *validate_desc(const game_params *params, const char *desc) { int len = params->w * params->h; if (len != strlen(desc)) return "Game description is wrong length"; if (len != strspn(desc, "PHO")) return "Invalid character in game description"; 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->w = w; state->h = h; state->completed = 0; state->grid = snewn(w*h, unsigned char); for (i = 0; i < w*h; i++) state->grid[i] = (desc[i] == 'P' ? GRID_PEG : desc[i] == 'H' ? GRID_HOLE : GRID_OBST); return state; } static game_state *dup_game(const game_state *state) { int w = state->w, h = state->h; game_state *ret = snew(game_state); ret->w = state->w; ret->h = state->h; ret->completed = state->completed; ret->grid = snewn(w*h, unsigned char); memcpy(ret->grid, state->grid, w*h); 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, char **error) { return NULL; } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { int w = state->w, h = state->h; int x, y; char *ret; ret = snewn((w+1)*h + 1, char); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) ret[y*(w+1)+x] = (state->grid[y*w+x] == GRID_HOLE ? '-' : state->grid[y*w+x] == GRID_PEG ? '*' : ' '); ret[y*(w+1)+w] = '\n'; } ret[h*(w+1)] = '\0'; return ret; } struct game_ui { int dragging; /* boolean: is a drag in progress? */ int sx, sy; /* grid coords of drag start cell */ int dx, dy; /* pixel coords of current drag posn */ int cur_x, cur_y, cur_visible, cur_jumping; }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); int x, y, v; ui->sx = ui->sy = ui->dx = ui->dy = 0; ui->dragging = FALSE; ui->cur_visible = ui->cur_jumping = 0; /* make sure we start the cursor somewhere on the grid. */ for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { v = state->grid[y*state->w+x]; if (v == GRID_PEG || v == GRID_HOLE) { ui->cur_x = x; ui->cur_y = y; goto found; } } } assert(!"new_ui found nowhere for cursor"); found: 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) { /* * Cancel a drag, in case the source square has become * unoccupied. */ ui->dragging = FALSE; } #define PREFERRED_TILE_SIZE 33 #define TILESIZE (ds->tilesize) #define BORDER (TILESIZE / 2) #define HIGHLIGHT_WIDTH (TILESIZE / 16) #define COORD(x) ( BORDER + (x) * TILESIZE ) #define FROMCOORD(x) ( ((x) + TILESIZE - BORDER) / TILESIZE - 1 ) struct game_drawstate { int tilesize; blitter *drag_background; int dragging, dragx, dragy; int w, h; unsigned char *grid; int started; int bgcolour; }; 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; char buf[80]; if (button == LEFT_BUTTON) { int tx, ty; /* * Left button down: we attempt to start a drag. */ /* * There certainly shouldn't be a current drag in progress, * unless the midend failed to send us button events in * order; it has a responsibility to always get that right, * so we can legitimately punish it by failing an * assertion. */ assert(!ui->dragging); tx = FROMCOORD(x); ty = FROMCOORD(y); if (tx >= 0 && tx < w && ty >= 0 && ty < h && state->grid[ty*w+tx] == GRID_PEG) { ui->dragging = TRUE; ui->sx = tx; ui->sy = ty; ui->dx = x; ui->dy = y; ui->cur_visible = ui->cur_jumping = 0; return ""; /* ui modified */ } } else if (button == LEFT_DRAG && ui->dragging) { /* * Mouse moved; just move the peg being dragged. */ ui->dx = x; ui->dy = y; return ""; /* ui modified */ } else if (button == LEFT_RELEASE && ui->dragging) { int tx, ty, dx, dy; /* * Button released. Identify the target square of the drag, * see if it represents a valid move, and if so make it. */ ui->dragging = FALSE; /* cancel the drag no matter what */ tx = FROMCOORD(x); ty = FROMCOORD(y); if (tx < 0 || tx >= w || ty < 0 || ty >= h) return ""; /* target out of range */ dx = tx - ui->sx; dy = ty - ui->sy; if (max(abs(dx),abs(dy)) != 2 || min(abs(dx),abs(dy)) != 0) return ""; /* move length was wrong */ dx /= 2; dy /= 2; if (state->grid[ty*w+tx] != GRID_HOLE || state->grid[(ty-dy)*w+(tx-dx)] != GRID_PEG || state->grid[ui->sy*w+ui->sx] != GRID_PEG) return ""; /* grid contents were invalid */ /* * We have a valid move. Encode it simply as source and * destination coordinate pairs. */ sprintf(buf, "%d,%d-%d,%d", ui->sx, ui->sy, tx, ty); return dupstr(buf); } else if (IS_CURSOR_MOVE(button)) { if (!ui->cur_jumping) { /* Not jumping; move cursor as usual, making sure we don't * leave the gameboard (which may be an irregular shape) */ int cx = ui->cur_x, cy = ui->cur_y; move_cursor(button, &cx, &cy, w, h, 0); ui->cur_visible = 1; if (state->grid[cy*w+cx] == GRID_HOLE || state->grid[cy*w+cx] == GRID_PEG) { ui->cur_x = cx; ui->cur_y = cy; } return ""; } else { int dx, dy, mx, my, jx, jy; /* We're jumping; if the requested direction has a hole, and * there's a peg in the way, */ assert(state->grid[ui->cur_y*w+ui->cur_x] == GRID_PEG); dx = (button == CURSOR_RIGHT) ? 1 : (button == CURSOR_LEFT) ? -1 : 0; dy = (button == CURSOR_DOWN) ? 1 : (button == CURSOR_UP) ? -1 : 0; mx = ui->cur_x+dx; my = ui->cur_y+dy; jx = mx+dx; jy = my+dy; ui->cur_jumping = 0; /* reset, whatever. */ if (jx >= 0 && jy >= 0 && jx < w && jy < h && state->grid[my*w+mx] == GRID_PEG && state->grid[jy*w+jx] == GRID_HOLE) { /* Move cursor to the jumped-to location (this felt more * natural while playtesting) */ sprintf(buf, "%d,%d-%d,%d", ui->cur_x, ui->cur_y, jx, jy); ui->cur_x = jx; ui->cur_y = jy; return dupstr(buf); } return ""; } } else if (IS_CURSOR_SELECT(button)) { if (!ui->cur_visible) { ui->cur_visible = 1; return ""; } if (ui->cur_jumping) { ui->cur_jumping = 0; return ""; } if (state->grid[ui->cur_y*w+ui->cur_x] == GRID_PEG) { /* cursor is on peg: next arrow-move wil jump. */ ui->cur_jumping = 1; return ""; } return NULL; } return NULL; } static game_state *execute_move(const game_state *state, const char *move) { int w = state->w, h = state->h; int sx, sy, tx, ty; game_state *ret; if (sscanf(move, "%d,%d-%d,%d", &sx, &sy, &tx, &ty) == 4) { int mx, my, dx, dy; if (sx < 0 || sx >= w || sy < 0 || sy >= h) return NULL; /* source out of range */ if (tx < 0 || tx >= w || ty < 0 || ty >= h) return NULL; /* target out of range */ dx = tx - sx; dy = ty - sy; if (max(abs(dx),abs(dy)) != 2 || min(abs(dx),abs(dy)) != 0) return NULL; /* move length was wrong */ mx = sx + dx/2; my = sy + dy/2; if (state->grid[sy*w+sx] != GRID_PEG || state->grid[my*w+mx] != GRID_PEG || state->grid[ty*w+tx] != GRID_HOLE) return NULL; /* grid contents were invalid */ ret = dup_game(state); ret->grid[sy*w+sx] = GRID_HOLE; ret->grid[my*w+mx] = GRID_HOLE; ret->grid[ty*w+tx] = GRID_PEG; /* * Opinion varies on whether getting to a single peg counts as * completing the game, or whether that peg has to be at a * specific location (central in the classic cross game, for * instance). For now we take the former, rather lax position. */ if (!ret->completed) { int count = 0, i; for (i = 0; i < w*h; i++) if (ret->grid[i] == GRID_PEG) count++; if (count == 1) ret->completed = 1; } return ret; } return NULL; } /* ---------------------------------------------------------------------- * 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 = TILESIZE * params->w + 2 * BORDER; *y = TILESIZE * params->h + 2 * BORDER; } static void game_set_size(drawing *dr, game_drawstate *ds, const game_params *params, int tilesize) { ds->tilesize = tilesize; assert(TILESIZE > 0); assert(!ds->drag_background); /* set_size is never called twice */ ds->drag_background = blitter_new(dr, TILESIZE, TILESIZE); } static float *game_colours(frontend *fe, int *ncolours) { float *ret = snewn(3 * NCOLOURS, float); game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT); ret[COL_PEG * 3 + 0] = 0.0F; ret[COL_PEG * 3 + 1] = 0.0F; ret[COL_PEG * 3 + 2] = 1.0F; ret[COL_CURSOR * 3 + 0] = 0.5F; ret[COL_CURSOR * 3 + 1] = 0.5F; ret[COL_CURSOR * 3 + 2] = 1.0F; *ncolours = NCOLOURS; return ret; } static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { int w = state->w, h = state->h; struct game_drawstate *ds = snew(struct game_drawstate); ds->tilesize = 0; /* not decided yet */ /* We can't allocate the blitter rectangle for the drag background * until we know what size to make it. */ ds->drag_background = NULL; ds->dragging = FALSE; ds->w = w; ds->h = h; ds->grid = snewn(w*h, unsigned char); memset(ds->grid, 255, w*h); ds->started = FALSE; ds->bgcolour = -1; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { if (ds->drag_background) blitter_free(dr, ds->drag_background); sfree(ds->grid); sfree(ds); } static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, int v, int bgcolour) { int cursor = 0, jumping = 0, bg; if (bgcolour >= 0) { draw_rect(dr, x, y, TILESIZE, TILESIZE, bgcolour); } if (v >= GRID_JUMPING) { jumping = 1; v -= GRID_JUMPING; } if (v >= GRID_CURSOR) { cursor = 1; v -= GRID_CURSOR; } if (v == GRID_HOLE) { bg = cursor ? COL_HIGHLIGHT : COL_LOWLIGHT; assert(!jumping); /* can't jump from a hole! */ draw_circle(dr, x+TILESIZE/2, y+TILESIZE/2, TILESIZE/4, bg, bg); } else if (v == GRID_PEG) { bg = (cursor || jumping) ? COL_CURSOR : COL_PEG; draw_circle(dr, x+TILESIZE/2, y+TILESIZE/2, TILESIZE/3, bg, bg); bg = (!cursor || jumping) ? COL_PEG : COL_CURSOR; draw_circle(dr, x+TILESIZE/2, y+TILESIZE/2, TILESIZE/4, bg, bg); } draw_update(dr, x, y, 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->w, h = state->h; int x, y; int bgcolour; if (flashtime > 0) { int frame = (int)(flashtime / FLASH_FRAME); bgcolour = (frame % 2 ? COL_LOWLIGHT : COL_HIGHLIGHT); } else bgcolour = COL_BACKGROUND; /* * Erase the sprite currently being dragged, if any. */ if (ds->dragging) { assert(ds->drag_background); blitter_load(dr, ds->drag_background, ds->dragx, ds->dragy); draw_update(dr, ds->dragx, ds->dragy, TILESIZE, TILESIZE); ds->dragging = FALSE; } if (!ds->started) { draw_rect(dr, 0, 0, TILESIZE * state->w + 2 * BORDER, TILESIZE * state->h + 2 * BORDER, COL_BACKGROUND); /* * Draw relief marks around all the squares that aren't * GRID_OBST. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) if (state->grid[y*w+x] != GRID_OBST) { /* * First pass: draw the full relief square. */ int coords[6]; coords[0] = COORD(x+1) + HIGHLIGHT_WIDTH - 1; coords[1] = COORD(y) - HIGHLIGHT_WIDTH; coords[2] = COORD(x) - HIGHLIGHT_WIDTH; coords[3] = COORD(y+1) + HIGHLIGHT_WIDTH - 1; coords[4] = COORD(x) - HIGHLIGHT_WIDTH; coords[5] = COORD(y) - HIGHLIGHT_WIDTH; draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT); coords[4] = COORD(x+1) + HIGHLIGHT_WIDTH - 1; coords[5] = COORD(y+1) + HIGHLIGHT_WIDTH - 1; draw_polygon(dr, coords, 3, COL_LOWLIGHT, COL_LOWLIGHT); } for (y = 0; y < h; y++) for (x = 0; x < w; x++) if (state->grid[y*w+x] != GRID_OBST) { /* * Second pass: draw everything but the two * diagonal corners. */ draw_rect(dr, COORD(x) - HIGHLIGHT_WIDTH, COORD(y) - HIGHLIGHT_WIDTH, TILESIZE + HIGHLIGHT_WIDTH, TILESIZE + HIGHLIGHT_WIDTH, COL_HIGHLIGHT); draw_rect(dr, COORD(x), COORD(y), TILESIZE + HIGHLIGHT_WIDTH, TILESIZE + HIGHLIGHT_WIDTH, COL_LOWLIGHT); } for (y = 0; y < h; y++) for (x = 0; x < w; x++) if (state->grid[y*w+x] != GRID_OBST) { /* * Third pass: draw a trapezium on each edge. */ int coords[8]; int dx, dy, s, sn, c; for (dx = 0; dx < 2; dx++) { dy = 1 - dx; for (s = 0; s < 2; s++) { sn = 2*s - 1; c = s ? COL_LOWLIGHT : COL_HIGHLIGHT; coords[0] = COORD(x) + (s*dx)*(TILESIZE-1); coords[1] = COORD(y) + (s*dy)*(TILESIZE-1); coords[2] = COORD(x) + (s*dx+dy)*(TILESIZE-1); coords[3] = COORD(y) + (s*dy+dx)*(TILESIZE-1); coords[4] = coords[2] - HIGHLIGHT_WIDTH * (dy-sn*dx); coords[5] = coords[3] - HIGHLIGHT_WIDTH * (dx-sn*dy); coords[6] = coords[0] + HIGHLIGHT_WIDTH * (dy+sn*dx); coords[7] = coords[1] + HIGHLIGHT_WIDTH * (dx+sn*dy); draw_polygon(dr, coords, 4, c, c); } } } for (y = 0; y < h; y++) for (x = 0; x < w; x++) if (state->grid[y*w+x] != GRID_OBST) { /* * Second pass: draw everything but the two * diagonal corners. */ draw_rect(dr, COORD(x), COORD(y), TILESIZE, TILESIZE, COL_BACKGROUND); } ds->started = TRUE; draw_update(dr, 0, 0, TILESIZE * state->w + 2 * BORDER, TILESIZE * state->h + 2 * BORDER); } /* * Loop over the grid redrawing anything that looks as if it * needs it. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) { int v; v = state->grid[y*w+x]; /* * Blank the source of a drag so it looks as if the * user picked the peg up physically. */ if (ui->dragging && ui->sx == x && ui->sy == y && v == GRID_PEG) v = GRID_HOLE; if (ui->cur_visible && ui->cur_x == x && ui->cur_y == y) v += ui->cur_jumping ? GRID_JUMPING : GRID_CURSOR; if (v != GRID_OBST && (bgcolour != ds->bgcolour || /* always redraw when flashing */ v != ds->grid[y*w+x])) { draw_tile(dr, ds, COORD(x), COORD(y), v, bgcolour); ds->grid[y*w+x] = v; } } /* * Draw the dragging sprite if any. */ if (ui->dragging) { ds->dragging = TRUE; ds->dragx = ui->dx - TILESIZE/2; ds->dragy = ui->dy - TILESIZE/2; blitter_save(dr, ds->drag_background, ds->dragx, ds->dragy); draw_tile(dr, ds, ds->dragx, ds->dragy, GRID_PEG, -1); } ds->bgcolour = bgcolour; } 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 2 * FLASH_FRAME; else return 0.0F; } static int game_status(const game_state *state) { /* * Dead-end situations are assumed to be rescuable by Undo, so we * don't bother to identify them and return -1. */ return state->completed ? +1 : 0; } static int 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 pegs #endif const struct game thegame = { "Pegs", "games.pegs", "pegs", default_params, game_fetch_preset, 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, TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, decode_ui, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, FALSE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, 0, /* flags */ }; /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/penrose.c0000644000175300017530000003755611561025457014272 0ustar simonsimon/* penrose.c * * Penrose tile generator. * * Uses half-tile technique outlined on: * * http://tartarus.org/simon/20110412-penrose/penrose.xhtml */ #include #include #include #include #include "puzzles.h" /* for malloc routines, and PI */ #include "penrose.h" /* ------------------------------------------------------- * 36-degree basis vector arithmetic routines. */ /* Imagine drawing a * ten-point 'clock face' like this: * * -E * -D | A * \ | / * -C. \ | / ,B * `-._\|/_,-' * ,-' /|\ `-. * -B' / | \ `C * / | \ * -A | D * E * * In case the ASCII art isn't clear, those are supposed to be ten * vectors of length 1, all sticking out from the origin at equal * angular spacing (hence 36 degrees). Our basis vectors are A,B,C,D (I * choose them to be symmetric about the x-axis so that the final * translation into 2d coordinates will also be symmetric, which I * think will avoid minor rounding uglinesses), so our vector * representation sets * * A = (1,0,0,0) * B = (0,1,0,0) * C = (0,0,1,0) * D = (0,0,0,1) * * The fifth vector E looks at first glance as if it needs to be * another basis vector, but in fact it doesn't, because it can be * represented in terms of the other four. Imagine starting from the * origin and following the path -A, +B, -C, +D: you'll find you've * traced four sides of a pentagram, and ended up one E-vector away * from the origin. So we have * * E = (-1,1,-1,1) * * This tells us that we can rotate any vector in this system by 36 * degrees: if we start with a*A + b*B + c*C + d*D, we want to end up * with a*B + b*C + c*D + d*E, and we substitute our identity for E to * turn that into a*B + b*C + c*D + d*(-A+B-C+D). In other words, * * rotate_one_notch_clockwise(a,b,c,d) = (-d, d+a, -d+b, d+c) * * and you can verify for yourself that applying that operation * repeatedly starting with (1,0,0,0) cycles round ten vectors and * comes back to where it started. * * The other operation that may be required is to construct vectors * with lengths that are multiples of phi. That can be done by * observing that the vector C-B is parallel to E and has length 1/phi, * and the vector D-A is parallel to E and has length phi. So this * tells us that given any vector, we can construct one which points in * the same direction and is 1/phi or phi times its length, like this: * * divide_by_phi(vector) = rotate(vector, 2) - rotate(vector, 3) * multiply_by_phi(vector) = rotate(vector, 1) - rotate(vector, 4) * * where rotate(vector, n) means applying the above * rotate_one_notch_clockwise primitive n times. Expanding out the * applications of rotate gives the following direct representation in * terms of the vector coordinates: * * divide_by_phi(a,b,c,d) = (b-d, c+d-b, a+b-c, c-a) * multiply_by_phi(a,b,c,d) = (a+b-d, c+d, a+b, c+d-a) * * and you can verify for yourself that those two operations are * inverses of each other (as you'd hope!). * * Having done all of this, testing for equality between two vectors is * a trivial matter of comparing the four integer coordinates. (Which * it _wouldn't_ have been if we'd kept E as a fifth basis vector, * because then (-1,1,-1,1,0) and (0,0,0,0,1) would have had to be * considered identical. So leaving E out is vital.) */ struct vector { int a, b, c, d; }; static vector v_origin(void) { vector v; v.a = v.b = v.c = v.d = 0; return v; } /* We start with a unit vector of B: this means we can easily * draw an isoceles triangle centred on the X axis. */ #ifdef TEST_VECTORS static vector v_unit(void) { vector v; v.b = 1; v.a = v.c = v.d = 0; return v; } #endif #define COS54 0.5877852 #define SIN54 0.8090169 #define COS18 0.9510565 #define SIN18 0.3090169 /* These two are a bit rough-and-ready for now. Note that B/C are * 18 degrees from the x-axis, and A/D are 54 degrees. */ double v_x(vector *vs, int i) { return (vs[i].a + vs[i].d) * COS54 + (vs[i].b + vs[i].c) * COS18; } double v_y(vector *vs, int i) { return (vs[i].a - vs[i].d) * SIN54 + (vs[i].b - vs[i].c) * SIN18; } static vector v_trans(vector v, vector trans) { v.a += trans.a; v.b += trans.b; v.c += trans.c; v.d += trans.d; return v; } static vector v_rotate_36(vector v) { vector vv; vv.a = -v.d; vv.b = v.d + v.a; vv.c = -v.d + v.b; vv.d = v.d + v.c; return vv; } static vector v_rotate(vector v, int ang) { int i; assert((ang % 36) == 0); while (ang < 0) ang += 360; ang = 360-ang; for (i = 0; i < (ang/36); i++) v = v_rotate_36(v); return v; } #ifdef TEST_VECTORS static vector v_scale(vector v, int sc) { v.a *= sc; v.b *= sc; v.c *= sc; v.d *= sc; return v; } #endif static vector v_growphi(vector v) { vector vv; vv.a = v.a + v.b - v.d; vv.b = v.c + v.d; vv.c = v.a + v.b; vv.d = v.c + v.d - v.a; return vv; } static vector v_shrinkphi(vector v) { vector vv; vv.a = v.b - v.d; vv.b = v.c + v.d - v.b; vv.c = v.a + v.b - v.c; vv.d = v.c - v.a; return vv; } #ifdef TEST_VECTORS static const char *v_debug(vector v) { static char buf[255]; sprintf(buf, "(%d,%d,%d,%d)[%2.2f,%2.2f]", v.a, v.b, v.c, v.d, v_x(&v,0), v_y(&v,0)); return buf; } #endif /* ------------------------------------------------------- * Tiling routines. */ static vector xform_coord(vector vo, int shrink, vector vtrans, int ang) { if (shrink < 0) vo = v_shrinkphi(vo); else if (shrink > 0) vo = v_growphi(vo); vo = v_rotate(vo, ang); vo = v_trans(vo, vtrans); return vo; } #define XFORM(n,o,s,a) vs[(n)] = xform_coord(v_edge, (s), vs[(o)], (a)) static int penrose_p2_small(penrose_state *state, int depth, int flip, vector v_orig, vector v_edge); static int penrose_p2_large(penrose_state *state, int depth, int flip, vector v_orig, vector v_edge) { vector vv_orig, vv_edge; #ifdef DEBUG_PENROSE { vector vs[3]; vs[0] = v_orig; XFORM(1, 0, 0, 0); XFORM(2, 0, 0, -36*flip); state->new_tile(state, vs, 3, depth); } #endif if (flip > 0) { vector vs[4]; vs[0] = v_orig; XFORM(1, 0, 0, -36); XFORM(2, 0, 0, 0); XFORM(3, 0, 0, 36); state->new_tile(state, vs, 4, depth); } if (depth >= state->max_depth) return 0; vv_orig = v_trans(v_orig, v_rotate(v_edge, -36*flip)); vv_edge = v_rotate(v_edge, 108*flip); penrose_p2_small(state, depth+1, flip, v_orig, v_shrinkphi(v_edge)); penrose_p2_large(state, depth+1, flip, vv_orig, v_shrinkphi(vv_edge)); penrose_p2_large(state, depth+1, -flip, vv_orig, v_shrinkphi(vv_edge)); return 0; } static int penrose_p2_small(penrose_state *state, int depth, int flip, vector v_orig, vector v_edge) { vector vv_orig; #ifdef DEBUG_PENROSE { vector vs[3]; vs[0] = v_orig; XFORM(1, 0, 0, 0); XFORM(2, 0, -1, -36*flip); state->new_tile(state, vs, 3, depth); } #endif if (flip > 0) { vector vs[4]; vs[0] = v_orig; XFORM(1, 0, 0, -72); XFORM(2, 0, -1, -36); XFORM(3, 0, 0, 0); state->new_tile(state, vs, 4, depth); } if (depth >= state->max_depth) return 0; vv_orig = v_trans(v_orig, v_edge); penrose_p2_large(state, depth+1, -flip, v_orig, v_shrinkphi(v_rotate(v_edge, -36*flip))); penrose_p2_small(state, depth+1, flip, vv_orig, v_shrinkphi(v_rotate(v_edge, -144*flip))); return 0; } static int penrose_p3_small(penrose_state *state, int depth, int flip, vector v_orig, vector v_edge); static int penrose_p3_large(penrose_state *state, int depth, int flip, vector v_orig, vector v_edge) { vector vv_orig; #ifdef DEBUG_PENROSE { vector vs[3]; vs[0] = v_orig; XFORM(1, 0, 1, 0); XFORM(2, 0, 0, -36*flip); state->new_tile(state, vs, 3, depth); } #endif if (flip > 0) { vector vs[4]; vs[0] = v_orig; XFORM(1, 0, 0, -36); XFORM(2, 0, 1, 0); XFORM(3, 0, 0, 36); state->new_tile(state, vs, 4, depth); } if (depth >= state->max_depth) return 0; vv_orig = v_trans(v_orig, v_edge); penrose_p3_large(state, depth+1, -flip, vv_orig, v_shrinkphi(v_rotate(v_edge, 180))); penrose_p3_small(state, depth+1, flip, vv_orig, v_shrinkphi(v_rotate(v_edge, -108*flip))); vv_orig = v_trans(v_orig, v_growphi(v_edge)); penrose_p3_large(state, depth+1, flip, vv_orig, v_shrinkphi(v_rotate(v_edge, -144*flip))); return 0; } static int penrose_p3_small(penrose_state *state, int depth, int flip, vector v_orig, vector v_edge) { vector vv_orig; #ifdef DEBUG_PENROSE { vector vs[3]; vs[0] = v_orig; XFORM(1, 0, 0, 0); XFORM(2, 0, 0, -36*flip); state->new_tile(state, vs, 3, depth); } #endif if (flip > 0) { vector vs[4]; vs[0] = v_orig; XFORM(1, 0, 0, -36); XFORM(3, 0, 0, 0); XFORM(2, 3, 0, -36); state->new_tile(state, vs, 4, depth); } if (depth >= state->max_depth) return 0; /* NB these two are identical to the first two of p3_large. */ vv_orig = v_trans(v_orig, v_edge); penrose_p3_large(state, depth+1, -flip, vv_orig, v_shrinkphi(v_rotate(v_edge, 180))); penrose_p3_small(state, depth+1, flip, vv_orig, v_shrinkphi(v_rotate(v_edge, -108*flip))); return 0; } /* ------------------------------------------------------- * Utility routines. */ double penrose_side_length(double start_size, int depth) { return start_size / pow(PHI, depth); } void penrose_count_tiles(int depth, int *nlarge, int *nsmall) { /* Steal sgt's fibonacci thingummy. */ } /* * It turns out that an acute isosceles triangle with sides in ratio 1:phi:phi * has an incentre which is conveniently 2*phi^-2 of the way from the apex to * the base. Why's that convenient? Because: if we situate the incentre of the * triangle at the origin, then we can place the apex at phi^-2 * (B+C), and * the other two vertices at apex-B and apex-C respectively. So that's an acute * triangle with its long sides of unit length, covering a circle about the * origin of radius 1-(2*phi^-2), which is conveniently enough phi^-3. * * (later mail: this is an overestimate by about 5%) */ int penrose(penrose_state *state, int which, int angle) { vector vo = v_origin(); vector vb = v_origin(); vo.b = vo.c = -state->start_size; vo = v_shrinkphi(v_shrinkphi(vo)); vb.b = state->start_size; vo = v_rotate(vo, angle); vb = v_rotate(vb, angle); if (which == PENROSE_P2) return penrose_p2_large(state, 0, 1, vo, vb); else return penrose_p3_small(state, 0, 1, vo, vb); } /* * We're asked for a MxN grid, which just means a tiling fitting into roughly * an MxN space in some kind of reasonable unit - say, the side length of the * two-arrow edges of the tiles. By some reasoning in a previous email, that * means we want to pick some subarea of a circle of radius 3.11*sqrt(M^2+N^2). * To cover that circle, we need to subdivide a triangle large enough that it * contains a circle of that radius. * * Hence: start with those three vectors marking triangle vertices, scale them * all up by phi repeatedly until the radius of the inscribed circle gets * bigger than the target, and then recurse into that triangle with the same * recursion depth as the number of times you scaled up. That will give you * tiles of unit side length, covering a circle big enough that if you randomly * choose an orientation and coordinates within the circle, you'll be able to * get any valid piece of Penrose tiling of size MxN. */ #define INCIRCLE_RADIUS 0.22426 /* phi^-3 less 5%: see above */ void penrose_calculate_size(int which, int tilesize, int w, int h, double *required_radius, int *start_size, int *depth) { double rradius, size; int n = 0; /* * Fudge factor to scale P2 and P3 tilings differently. This * doesn't seem to have much relevance to questions like the * average number of tiles per unit area; it's just aesthetic. */ if (which == PENROSE_P2) tilesize = tilesize * 3 / 2; else tilesize = tilesize * 5 / 4; rradius = tilesize * 3.11 * sqrt((double)(w*w + h*h)); size = tilesize; while ((size * INCIRCLE_RADIUS) < rradius) { n++; size = size * PHI; } *start_size = (int)size; *depth = n; *required_radius = rradius; } /* ------------------------------------------------------- * Test code. */ #ifdef TEST_PENROSE #include #include int show_recursion = 0; int ntiles, nfinal; int test_cb(penrose_state *state, vector *vs, int n, int depth) { int i, xoff = 0, yoff = 0; double l = penrose_side_length(state->start_size, depth); double rball = l / 10.0; const char *col; ntiles++; if (state->max_depth == depth) { col = n == 4 ? "black" : "green"; nfinal++; } else { if (!show_recursion) return 0; col = n == 4 ? "red" : "blue"; } if (n != 4) yoff = state->start_size; printf("\n", col, col); printf("", v_x(vs, 0) + xoff, v_y(vs, 0) + yoff, rball, rball, col); return 0; } void usage_exit(void) { fprintf(stderr, "Usage: penrose-test [--recursion] P2|P3 SIZE DEPTH\n"); exit(1); } int main(int argc, char *argv[]) { penrose_state ps; int which = 0; while (--argc > 0) { char *p = *++argv; if (!strcmp(p, "-h") || !strcmp(p, "--help")) { usage_exit(); } else if (!strcmp(p, "--recursion")) { show_recursion = 1; } else if (*p == '-') { fprintf(stderr, "Unrecognised option '%s'\n", p); exit(1); } else { break; } } if (argc < 3) usage_exit(); if (strcmp(argv[0], "P2") == 0) which = PENROSE_P2; else if (strcmp(argv[0], "P3") == 0) which = PENROSE_P3; else usage_exit(); ps.start_size = atoi(argv[1]); ps.max_depth = atoi(argv[2]); ps.new_tile = test_cb; ntiles = nfinal = 0; printf("\ \n\ \n\ \n\ \n\n"); printf("\n"); penrose(&ps, which); printf("\n"); printf("\n", ntiles, nfinal); printf(""); return 0; } #endif #ifdef TEST_VECTORS static void dbgv(const char *msg, vector v) { printf("%s: %s\n", msg, v_debug(v)); } int main(int argc, const char *argv[]) { vector v = v_unit(); dbgv("unit vector", v); v = v_rotate(v, 36); dbgv("rotated 36", v); v = v_scale(v, 2); dbgv("scaled x2", v); v = v_shrinkphi(v); dbgv("shrunk phi", v); v = v_rotate(v, -36); dbgv("rotated -36", v); return 0; } #endif /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/printing.c0000644000175300017530000001441610301145126014422 0ustar simonsimon/* * printing.c: Cross-platform printing manager. Handles document * setup and layout. */ #include "puzzles.h" struct puzzle { const game *game; game_params *par; game_state *st; game_state *st2; }; struct document { int pw, ph; int npuzzles; struct puzzle *puzzles; int puzzlesize; int got_solns; float *colwid, *rowht; float userscale; }; /* * Create a new print document. pw and ph are the layout * parameters: they state how many puzzles will be printed across * the page, and down the page. */ document *document_new(int pw, int ph, float userscale) { document *doc = snew(document); doc->pw = pw; doc->ph = ph; doc->puzzles = NULL; doc->puzzlesize = doc->npuzzles = 0; doc->got_solns = FALSE; doc->colwid = snewn(pw, float); doc->rowht = snewn(ph, float); doc->userscale = userscale; return doc; } /* * Free a document structure, whether it's been printed or not. */ void document_free(document *doc) { int i; for (i = 0; i < doc->npuzzles; i++) { doc->puzzles[i].game->free_params(doc->puzzles[i].par); doc->puzzles[i].game->free_game(doc->puzzles[i].st); if (doc->puzzles[i].st2) doc->puzzles[i].game->free_game(doc->puzzles[i].st2); } sfree(doc->colwid); sfree(doc->rowht); sfree(doc->puzzles); sfree(doc); } /* * Called from midend.c to add a puzzle to be printed. Provides a * game_params (for initial layout computation), a game_state, and * optionally a second game_state to be printed in parallel on * another sheet (typically the solution to the first game_state). */ void document_add_puzzle(document *doc, const game *game, game_params *par, game_state *st, game_state *st2) { if (doc->npuzzles >= doc->puzzlesize) { doc->puzzlesize += 32; doc->puzzles = sresize(doc->puzzles, doc->puzzlesize, struct puzzle); } doc->puzzles[doc->npuzzles].game = game; doc->puzzles[doc->npuzzles].par = par; doc->puzzles[doc->npuzzles].st = st; doc->puzzles[doc->npuzzles].st2 = st2; doc->npuzzles++; if (st2) doc->got_solns = TRUE; } static void get_puzzle_size(document *doc, struct puzzle *pz, float *w, float *h, float *scale) { float ww, hh, ourscale; /* Get the preferred size of the game, in mm. */ pz->game->print_size(pz->par, &ww, &hh); /* Adjust for user-supplied scale factor. */ ourscale = doc->userscale; /* * FIXME: scale it down here if it's too big for the page size. * Rather than do complicated things involving scaling all * columns down in proportion, the simplest approach seems to * me to be to scale down until the game fits within one evenly * divided cell of the page (i.e. width/pw by height/ph). * * In order to do this step we need the page size available. */ *scale = ourscale; *w = ww * ourscale; *h = hh * ourscale; } /* * Having accumulated a load of puzzles, actually do the printing. */ void document_print(document *doc, drawing *dr) { int ppp; /* puzzles per page */ int pages, passes; int page, pass; int pageno; ppp = doc->pw * doc->ph; pages = (doc->npuzzles + ppp - 1) / ppp; passes = (doc->got_solns ? 2 : 1); print_begin_doc(dr, pages * passes); pageno = 1; for (pass = 0; pass < passes; pass++) { for (page = 0; page < pages; page++) { int i, n, offset; float colsum, rowsum; print_begin_page(dr, pageno); offset = page * ppp; n = min(ppp, doc->npuzzles - offset); for (i = 0; i < doc->pw; i++) doc->colwid[i] = 0; for (i = 0; i < doc->ph; i++) doc->rowht[i] = 0; /* * Lay the page out by computing all the puzzle sizes. */ for (i = 0; i < n; i++) { struct puzzle *pz = doc->puzzles + offset + i; int x = i % doc->pw, y = i / doc->pw; float w, h, scale; get_puzzle_size(doc, pz, &w, &h, &scale); /* Update the maximum width/height of this column. */ doc->colwid[x] = max(doc->colwid[x], w); doc->rowht[y] = max(doc->rowht[y], h); } /* * Add up the maximum column/row widths to get the * total amount of space used up by puzzles on the * page. We will use this to compute gutter widths. */ colsum = 0.0; for (i = 0; i < doc->pw; i++) colsum += doc->colwid[i]; rowsum = 0.0; for (i = 0; i < doc->ph; i++) rowsum += doc->rowht[i]; /* * Now do the printing. */ for (i = 0; i < n; i++) { struct puzzle *pz = doc->puzzles + offset + i; int x = i % doc->pw, y = i / doc->pw, j; float w, h, scale, xm, xc, ym, yc; int pixw, pixh, tilesize; if (pass == 1 && !pz->st2) continue; /* nothing to do */ /* * The total amount of gutter space is the page * width minus colsum. This is divided into pw+1 * gutters, so the amount of horizontal gutter * space appearing to the left of this puzzle * column is * * (width-colsum) * (x+1)/(pw+1) * = width * (x+1)/(pw+1) - (colsum * (x+1)/(pw+1)) */ xm = (float)(x+1) / (doc->pw + 1); xc = -xm * colsum; /* And similarly for y. */ ym = (float)(y+1) / (doc->ph + 1); yc = -ym * rowsum; /* * However, the amount of space to the left of this * puzzle isn't just gutter space: we must also * count the widths of all the previous columns. */ for (j = 0; j < x; j++) xc += doc->colwid[j]; /* And similarly for rows. */ for (j = 0; j < y; j++) yc += doc->rowht[j]; /* * Now we adjust for this _specific_ puzzle, which * means centring it within the cell we've just * computed. */ get_puzzle_size(doc, pz, &w, &h, &scale); xc += (doc->colwid[x] - w) / 2; yc += (doc->rowht[y] - h) / 2; /* * And now we know where and how big we want to * print the puzzle, just go ahead and do so. For * the moment I'll pick a standard pixel tile size * of 512. * * (FIXME: would it be better to pick this value * with reference to the printer resolution? Or * permit each game to choose its own?) */ tilesize = 512; pz->game->compute_size(pz->par, tilesize, &pixw, &pixh); print_begin_puzzle(dr, xm, xc, ym, yc, pixw, pixh, w, scale); pz->game->print(dr, pass == 0 ? pz->st : pz->st2, tilesize); print_end_puzzle(dr); } print_end_page(dr, pageno); pageno++; } } print_end_doc(dr); } puzzles-r9872/ps.c0000644000175300017530000002576011315630154013224 0ustar simonsimon/* * ps.c: PostScript printing functions. */ #include #include #include #include #include "puzzles.h" #define ROOT2 1.414213562 struct psdata { FILE *fp; int colour; int ytop; int clipped; float hatchthick, hatchspace; int gamewidth, gameheight; drawing *drawing; }; static void ps_printf(psdata *ps, char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(ps->fp, fmt, ap); va_end(ap); } static void ps_fill(psdata *ps, int colour) { int hatch; float r, g, b; print_get_colour(ps->drawing, colour, ps->colour, &hatch, &r, &g, &b); if (hatch < 0) { if (ps->colour) ps_printf(ps, "%g %g %g setrgbcolor fill\n", r, g, b); else ps_printf(ps, "%g setgray fill\n", r); } else { /* Clip to the region. */ ps_printf(ps, "gsave clip\n"); /* Hatch the entire game printing area. */ ps_printf(ps, "newpath\n"); if (hatch == HATCH_VERT || hatch == HATCH_PLUS) ps_printf(ps, "0 %g %d {\n" " 0 moveto 0 %d rlineto\n" "} for\n", ps->hatchspace, ps->gamewidth, ps->gameheight); if (hatch == HATCH_HORIZ || hatch == HATCH_PLUS) ps_printf(ps, "0 %g %d {\n" " 0 exch moveto %d 0 rlineto\n" "} for\n", ps->hatchspace, ps->gameheight, ps->gamewidth); if (hatch == HATCH_SLASH || hatch == HATCH_X) ps_printf(ps, "%d %g %d {\n" " 0 moveto %d dup rlineto\n" "} for\n", -ps->gameheight, ps->hatchspace * ROOT2, ps->gamewidth, max(ps->gamewidth, ps->gameheight)); if (hatch == HATCH_BACKSLASH || hatch == HATCH_X) ps_printf(ps, "0 %g %d {\n" " 0 moveto %d neg dup neg rlineto\n" "} for\n", ps->hatchspace * ROOT2, ps->gamewidth+ps->gameheight, max(ps->gamewidth, ps->gameheight)); ps_printf(ps, "0 setgray %g setlinewidth stroke grestore\n", ps->hatchthick); } } static void ps_setcolour_internal(psdata *ps, int colour, char *suffix) { int hatch; float r, g, b; print_get_colour(ps->drawing, colour, ps->colour, &hatch, &r, &g, &b); /* * Stroking in hatched colours is not permitted. */ assert(hatch < 0); if (ps->colour) ps_printf(ps, "%g %g %g setrgbcolor%s\n", r, g, b, suffix); else ps_printf(ps, "%g setgray%s\n", r, suffix); } static void ps_setcolour(psdata *ps, int colour) { ps_setcolour_internal(ps, colour, ""); } static void ps_stroke(psdata *ps, int colour) { ps_setcolour_internal(ps, colour, " stroke"); } static void ps_draw_text(void *handle, int x, int y, int fonttype, int fontsize, int align, int colour, char *text) { psdata *ps = (psdata *)handle; y = ps->ytop - y; ps_setcolour(ps, colour); ps_printf(ps, "/%s findfont %d scalefont setfont\n", fonttype == FONT_FIXED ? "Courier-L1" : "Helvetica-L1", fontsize); if (align & ALIGN_VCENTRE) { ps_printf(ps, "newpath 0 0 moveto (X) true charpath flattenpath" " pathbbox\n" "3 -1 roll add 2 div %d exch sub %d exch moveto pop pop\n", y, x); } else { ps_printf(ps, "%d %d moveto\n", x, y); } ps_printf(ps, "("); while (*text) { if (*text == '\\' || *text == '(' || *text == ')') ps_printf(ps, "\\"); ps_printf(ps, "%c", *text); text++; } ps_printf(ps, ") "); if (align & (ALIGN_HCENTRE | ALIGN_HRIGHT)) ps_printf(ps, "dup stringwidth pop %sneg 0 rmoveto show\n", (align & ALIGN_HCENTRE) ? "2 div " : ""); else ps_printf(ps, "show\n"); } static void ps_draw_rect(void *handle, int x, int y, int w, int h, int colour) { psdata *ps = (psdata *)handle; y = ps->ytop - y; /* * Offset by half a pixel for the exactness requirement. */ ps_printf(ps, "newpath %g %g moveto %d 0 rlineto 0 %d rlineto" " %d 0 rlineto closepath\n", x - 0.5, y + 0.5, w, -h, -w); ps_fill(ps, colour); } static void ps_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour) { psdata *ps = (psdata *)handle; y1 = ps->ytop - y1; y2 = ps->ytop - y2; ps_printf(ps, "newpath %d %d moveto %d %d lineto\n", x1, y1, x2, y2); ps_stroke(ps, colour); } static void ps_draw_polygon(void *handle, int *coords, int npoints, int fillcolour, int outlinecolour) { psdata *ps = (psdata *)handle; int i; ps_printf(ps, "newpath %d %d moveto\n", coords[0], ps->ytop - coords[1]); for (i = 1; i < npoints; i++) ps_printf(ps, "%d %d lineto\n", coords[i*2], ps->ytop - coords[i*2+1]); ps_printf(ps, "closepath\n"); if (fillcolour >= 0) { ps_printf(ps, "gsave\n"); ps_fill(ps, fillcolour); ps_printf(ps, "grestore\n"); } ps_stroke(ps, outlinecolour); } static void ps_draw_circle(void *handle, int cx, int cy, int radius, int fillcolour, int outlinecolour) { psdata *ps = (psdata *)handle; cy = ps->ytop - cy; ps_printf(ps, "newpath %d %d %d 0 360 arc closepath\n", cx, cy, radius); if (fillcolour >= 0) { ps_printf(ps, "gsave\n"); ps_fill(ps, fillcolour); ps_printf(ps, "grestore\n"); } ps_stroke(ps, outlinecolour); } static void ps_unclip(void *handle) { psdata *ps = (psdata *)handle; assert(ps->clipped); ps_printf(ps, "grestore\n"); ps->clipped = FALSE; } static void ps_clip(void *handle, int x, int y, int w, int h) { psdata *ps = (psdata *)handle; if (ps->clipped) ps_unclip(ps); y = ps->ytop - y; /* * Offset by half a pixel for the exactness requirement. */ ps_printf(ps, "gsave\n"); ps_printf(ps, "newpath %g %g moveto %d 0 rlineto 0 %d rlineto" " %d 0 rlineto closepath\n", x - 0.5, y + 0.5, w, -h, -w); ps_printf(ps, "clip\n"); ps->clipped = TRUE; } static void ps_line_width(void *handle, float width) { psdata *ps = (psdata *)handle; ps_printf(ps, "%g setlinewidth\n", width); } static void ps_line_dotted(void *handle, int dotted) { psdata *ps = (psdata *)handle; if (dotted) { ps_printf(ps, "[ currentlinewidth 3 mul ] 0 setdash\n"); } else { ps_printf(ps, "[ ] 0 setdash\n"); } } static char *ps_text_fallback(void *handle, const char *const *strings, int nstrings) { /* * We can handle anything in ISO 8859-1, and we'll manually * translate it out of UTF-8 for the purpose. */ int i, maxlen; char *ret; maxlen = 0; for (i = 0; i < nstrings; i++) { int len = strlen(strings[i]); if (maxlen < len) maxlen = len; } ret = snewn(maxlen + 1, char); for (i = 0; i < nstrings; i++) { const char *p = strings[i]; char *q = ret; while (*p) { int c = (unsigned char)*p++; if (c < 0x80) { *q++ = c; /* ASCII */ } else if ((c == 0xC2 || c == 0xC3) && (*p & 0xC0) == 0x80) { *q++ = (c << 6) | (*p++ & 0x3F); /* top half of 8859-1 */ } else { break; } } if (!*p) { *q = '\0'; return ret; } } assert(!"Should never reach here"); return NULL; } static void ps_begin_doc(void *handle, int pages) { psdata *ps = (psdata *)handle; fputs("%!PS-Adobe-3.0\n", ps->fp); fputs("%%Creator: Simon Tatham's Portable Puzzle Collection\n", ps->fp); fputs("%%DocumentData: Clean7Bit\n", ps->fp); fputs("%%LanguageLevel: 1\n", ps->fp); fprintf(ps->fp, "%%%%Pages: %d\n", pages); fputs("%%DocumentNeededResources:\n", ps->fp); fputs("%%+ font Helvetica\n", ps->fp); fputs("%%+ font Courier\n", ps->fp); fputs("%%EndComments\n", ps->fp); fputs("%%BeginSetup\n", ps->fp); fputs("%%IncludeResource: font Helvetica\n", ps->fp); fputs("%%IncludeResource: font Courier\n", ps->fp); fputs("%%EndSetup\n", ps->fp); fputs("%%BeginProlog\n", ps->fp); /* * Re-encode Helvetica and Courier into ISO-8859-1, which gives * us times and divide signs - and also (according to the * Language Reference Manual) a bonus in that the ASCII '-' code * point now points to a minus sign instead of a hyphen. */ fputs("/Helvetica findfont " /* get the font dictionary */ "dup maxlength dict dup begin " /* create and open a new dict */ "exch " /* move the original font to top of stack */ "{1 index /FID ne {def} {pop pop} ifelse} forall " /* copy everything except FID */ "/Encoding ISOLatin1Encoding def " /* set the thing we actually wanted to change */ "/FontName /Helvetica-L1 def " /* set a new font name */ "FontName end exch definefont" /* and define the font */ "\n", ps->fp); fputs("/Courier findfont " /* get the font dictionary */ "dup maxlength dict dup begin " /* create and open a new dict */ "exch " /* move the original font to top of stack */ "{1 index /FID ne {def} {pop pop} ifelse} forall " /* copy everything except FID */ "/Encoding ISOLatin1Encoding def " /* set the thing we actually wanted to change */ "/FontName /Courier-L1 def " /* set a new font name */ "FontName end exch definefont" /* and define the font */ "\n", ps->fp); fputs("%%EndProlog\n", ps->fp); } static void ps_begin_page(void *handle, int number) { psdata *ps = (psdata *)handle; fprintf(ps->fp, "%%%%Page: %d %d\ngsave save\n%g dup scale\n", number, number, 72.0 / 25.4); } static void ps_begin_puzzle(void *handle, float xm, float xc, float ym, float yc, int pw, int ph, float wmm) { psdata *ps = (psdata *)handle; fprintf(ps->fp, "gsave\n" "clippath flattenpath pathbbox pop pop translate\n" "clippath flattenpath pathbbox 4 2 roll pop pop\n" "exch %g mul %g add exch dup %g mul %g add sub translate\n" "%g dup scale\n" "0 -%d translate\n", xm, xc, ym, yc, wmm/pw, ph); ps->ytop = ph; ps->clipped = FALSE; ps->gamewidth = pw; ps->gameheight = ph; ps->hatchthick = 0.2 * pw / wmm; ps->hatchspace = 1.0 * pw / wmm; } static void ps_end_puzzle(void *handle) { psdata *ps = (psdata *)handle; fputs("grestore\n", ps->fp); } static void ps_end_page(void *handle, int number) { psdata *ps = (psdata *)handle; fputs("restore grestore showpage\n", ps->fp); } static void ps_end_doc(void *handle) { psdata *ps = (psdata *)handle; fputs("%%EOF\n", ps->fp); } static const struct drawing_api ps_drawing = { ps_draw_text, ps_draw_rect, ps_draw_line, ps_draw_polygon, ps_draw_circle, NULL /* draw_update */, ps_clip, ps_unclip, NULL /* start_draw */, NULL /* end_draw */, NULL /* status_bar */, NULL /* blitter_new */, NULL /* blitter_free */, NULL /* blitter_save */, NULL /* blitter_load */, ps_begin_doc, ps_begin_page, ps_begin_puzzle, ps_end_puzzle, ps_end_page, ps_end_doc, ps_line_width, ps_line_dotted, ps_text_fallback, }; psdata *ps_init(FILE *outfile, int colour) { psdata *ps = snew(psdata); ps->fp = outfile; ps->colour = colour; ps->ytop = 0; ps->clipped = FALSE; ps->hatchthick = ps->hatchspace = ps->gamewidth = ps->gameheight = 0; ps->drawing = drawing_new(&ps_drawing, NULL, ps); return ps; } void ps_free(psdata *ps) { drawing_free(ps->drawing); sfree(ps); } drawing *ps_drawing_api(psdata *ps) { return ps->drawing; } puzzles-r9872/random.c0000644000175300017530000001775712132232554014071 0ustar simonsimon/* * random.c: Internal random number generator, guaranteed to work * the same way on all platforms. Used when generating an initial * game state from a random game seed; required to ensure that game * seeds can be exchanged between versions of a puzzle compiled for * different platforms. * * The generator is based on SHA-1. This is almost certainly * overkill, but I had the SHA-1 code kicking around and it was * easier to reuse it than to do anything else! */ #include #include #include #include "puzzles.h" /* ---------------------------------------------------------------------- * Core SHA algorithm: processes 16-word blocks into a message digest. */ #define rol(x,y) ( ((x) << (y)) | (((uint32)x) >> (32-y)) ) static void SHA_Core_Init(uint32 h[5]) { h[0] = 0x67452301; h[1] = 0xefcdab89; h[2] = 0x98badcfe; h[3] = 0x10325476; h[4] = 0xc3d2e1f0; } static void SHATransform(uint32 * digest, uint32 * block) { uint32 w[80]; uint32 a, b, c, d, e; int t; for (t = 0; t < 16; t++) w[t] = block[t]; for (t = 16; t < 80; t++) { uint32 tmp = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16]; w[t] = rol(tmp, 1); } a = digest[0]; b = digest[1]; c = digest[2]; d = digest[3]; e = digest[4]; for (t = 0; t < 20; t++) { uint32 tmp = rol(a, 5) + ((b & c) | (d & ~b)) + e + w[t] + 0x5a827999; e = d; d = c; c = rol(b, 30); b = a; a = tmp; } for (t = 20; t < 40; t++) { uint32 tmp = rol(a, 5) + (b ^ c ^ d) + e + w[t] + 0x6ed9eba1; e = d; d = c; c = rol(b, 30); b = a; a = tmp; } for (t = 40; t < 60; t++) { uint32 tmp = rol(a, 5) + ((b & c) | (b & d) | (c & d)) + e + w[t] + 0x8f1bbcdc; e = d; d = c; c = rol(b, 30); b = a; a = tmp; } for (t = 60; t < 80; t++) { uint32 tmp = rol(a, 5) + (b ^ c ^ d) + e + w[t] + 0xca62c1d6; e = d; d = c; c = rol(b, 30); b = a; a = tmp; } digest[0] += a; digest[1] += b; digest[2] += c; digest[3] += d; digest[4] += e; } /* ---------------------------------------------------------------------- * Outer SHA algorithm: take an arbitrary length byte string, * convert it into 16-word blocks with the prescribed padding at * the end, and pass those blocks to the core SHA algorithm. */ void SHA_Init(SHA_State * s) { SHA_Core_Init(s->h); s->blkused = 0; s->lenhi = s->lenlo = 0; } void SHA_Bytes(SHA_State * s, const void *p, int len) { unsigned char *q = (unsigned char *) p; uint32 wordblock[16]; uint32 lenw = len; int i; /* * Update the length field. */ s->lenlo += lenw; s->lenhi += (s->lenlo < lenw); if (s->blkused && s->blkused + len < 64) { /* * Trivial case: just add to the block. */ memcpy(s->block + s->blkused, q, len); s->blkused += len; } else { /* * We must complete and process at least one block. */ while (s->blkused + len >= 64) { memcpy(s->block + s->blkused, q, 64 - s->blkused); q += 64 - s->blkused; len -= 64 - s->blkused; /* Now process the block. Gather bytes big-endian into words */ for (i = 0; i < 16; i++) { wordblock[i] = (((uint32) s->block[i * 4 + 0]) << 24) | (((uint32) s->block[i * 4 + 1]) << 16) | (((uint32) s->block[i * 4 + 2]) << 8) | (((uint32) s->block[i * 4 + 3]) << 0); } SHATransform(s->h, wordblock); s->blkused = 0; } memcpy(s->block, q, len); s->blkused = len; } } void SHA_Final(SHA_State * s, unsigned char *output) { int i; int pad; unsigned char c[64]; uint32 lenhi, lenlo; if (s->blkused >= 56) pad = 56 + 64 - s->blkused; else pad = 56 - s->blkused; lenhi = (s->lenhi << 3) | (s->lenlo >> (32 - 3)); lenlo = (s->lenlo << 3); memset(c, 0, pad); c[0] = 0x80; SHA_Bytes(s, &c, pad); c[0] = (unsigned char)((lenhi >> 24) & 0xFF); c[1] = (unsigned char)((lenhi >> 16) & 0xFF); c[2] = (unsigned char)((lenhi >> 8) & 0xFF); c[3] = (unsigned char)((lenhi >> 0) & 0xFF); c[4] = (unsigned char)((lenlo >> 24) & 0xFF); c[5] = (unsigned char)((lenlo >> 16) & 0xFF); c[6] = (unsigned char)((lenlo >> 8) & 0xFF); c[7] = (unsigned char)((lenlo >> 0) & 0xFF); SHA_Bytes(s, &c, 8); for (i = 0; i < 5; i++) { output[i * 4] = (unsigned char)((s->h[i] >> 24) & 0xFF); output[i * 4 + 1] = (unsigned char)((s->h[i] >> 16) & 0xFF); output[i * 4 + 2] = (unsigned char)((s->h[i] >> 8) & 0xFF); output[i * 4 + 3] = (unsigned char)((s->h[i]) & 0xFF); } } void SHA_Simple(const void *p, int len, unsigned char *output) { SHA_State s; SHA_Init(&s); SHA_Bytes(&s, p, len); SHA_Final(&s, output); } /* ---------------------------------------------------------------------- * The random number generator. */ struct random_state { unsigned char seedbuf[40]; unsigned char databuf[20]; int pos; }; random_state *random_new(const char *seed, int len) { random_state *state; state = snew(random_state); SHA_Simple(seed, len, state->seedbuf); SHA_Simple(state->seedbuf, 20, state->seedbuf + 20); SHA_Simple(state->seedbuf, 40, state->databuf); state->pos = 0; return state; } random_state *random_copy(random_state *tocopy) { random_state *result; result = snew(random_state); memcpy(result->seedbuf, tocopy->seedbuf, sizeof(result->seedbuf)); memcpy(result->databuf, tocopy->databuf, sizeof(result->databuf)); result->pos = tocopy->pos; return result; } unsigned long random_bits(random_state *state, int bits) { unsigned long ret = 0; int n; for (n = 0; n < bits; n += 8) { if (state->pos >= 20) { int i; for (i = 0; i < 20; i++) { if (state->seedbuf[i] != 0xFF) { state->seedbuf[i]++; break; } else state->seedbuf[i] = 0; } SHA_Simple(state->seedbuf, 40, state->databuf); state->pos = 0; } ret = (ret << 8) | state->databuf[state->pos++]; } /* * `(1 << bits) - 1' is not good enough, since if bits==32 on a * 32-bit machine, behaviour is undefined and Intel has a nasty * habit of shifting left by zero instead. We'll shift by * bits-1 and then separately shift by one. */ ret &= (1 << (bits-1)) * 2 - 1; return ret; } unsigned long random_upto(random_state *state, unsigned long limit) { int bits = 0; unsigned long max, divisor, data; while ((limit >> bits) != 0) bits++; bits += 3; assert(bits < 32); max = 1L << bits; divisor = max / limit; max = limit * divisor; do { data = random_bits(state, bits); } while (data >= max); return data / divisor; } void random_free(random_state *state) { sfree(state); } char *random_state_encode(random_state *state) { char retbuf[256]; int len = 0, i; for (i = 0; i < lenof(state->seedbuf); i++) len += sprintf(retbuf+len, "%02x", state->seedbuf[i]); for (i = 0; i < lenof(state->databuf); i++) len += sprintf(retbuf+len, "%02x", state->databuf[i]); len += sprintf(retbuf+len, "%02x", state->pos); return dupstr(retbuf); } random_state *random_state_decode(const char *input) { random_state *state; int pos, byte, digits; state = snew(random_state); memset(state->seedbuf, 0, sizeof(state->seedbuf)); memset(state->databuf, 0, sizeof(state->databuf)); state->pos = 0; byte = digits = 0; pos = 0; while (*input) { int v = *input++; if (v >= '0' && v <= '9') v = v - '0'; else if (v >= 'A' && v <= 'F') v = v - 'A' + 10; else if (v >= 'a' && v <= 'f') v = v - 'a' + 10; else v = 0; byte = (byte << 4) | v; digits++; if (digits == 2) { /* * We have a byte. Put it somewhere. */ if (pos < lenof(state->seedbuf)) state->seedbuf[pos++] = byte; else if (pos < lenof(state->seedbuf) + lenof(state->databuf)) state->databuf[pos++ - lenof(state->seedbuf)] = byte; else if (pos == lenof(state->seedbuf) + lenof(state->databuf) && byte <= lenof(state->databuf)) state->pos = byte; byte = digits = 0; } } return state; } puzzles-r9872/range.c0000644000175300017530000015140212132232554013667 0ustar simonsimon/* * range.c: implementation of the Nikoli game 'Kurodoko' / 'Kuromasu'. */ /* * Puzzle rules: the player is given a WxH grid of white squares, some * of which contain numbers. The goal is to paint some of the squares * black, such that: * * - no cell (err, cell = square) with a number is painted black * - no black cells have an adjacent (horz/vert) black cell * - the white cells are all connected (through other white cells) * - if a cell contains a number n, let h and v be the lengths of the * maximal horizontal and vertical white sequences containing that * cell. Then n must equal h + v - 1. */ /* example instance with its encoding: * * +--+--+--+--+--+--+--+ * | | | | | 7| | | * +--+--+--+--+--+--+--+ * | 3| | | | | | 8| * +--+--+--+--+--+--+--+ * | | | | | | 5| | * +--+--+--+--+--+--+--+ * | | | 7| | 7| | | * +--+--+--+--+--+--+--+ * | |13| | | | | | * +--+--+--+--+--+--+--+ * | 4| | | | | | 8| * +--+--+--+--+--+--+--+ * | | | 4| | | | | * +--+--+--+--+--+--+--+ * * 7x7:d7b3e8e5c7a7c13e4d8b4d */ #include #include #include #include #include #include #include "puzzles.h" #include #define setmember(obj, field) ( (obj) . field = field ) static char *nfmtstr(int n, char *fmt, ...) { va_list va; char *ret = snewn(n+1, char); va_start(va, fmt); vsprintf(ret, fmt, va); va_end(va); return ret; } #define SWAP(type, lvar1, lvar2) do { \ type tmp = (lvar1); \ (lvar1) = (lvar2); \ (lvar2) = tmp; \ } while (0) /* ---------------------------------------------------------------------- * Game parameters, presets, states */ typedef signed char puzzle_size; struct game_params { puzzle_size w; puzzle_size h; }; struct game_state { struct game_params params; unsigned int has_cheated: 1; unsigned int was_solved: 1; puzzle_size *grid; }; #define DEFAULT_PRESET 0 static struct game_params range_presets[] = {{9, 6}, {12, 8}, {13, 9}, {16, 11}}; /* rationale: I want all four combinations of {odd/even, odd/even}, as * they play out differently with respect to two-way symmetry. I also * want them to be generated relatively fast yet still be large enough * to be entertaining for a decent amount of time, and I want them to * make good use of monitor real estate (the typical screen resolution * is why I do 13x9 and not 9x13). */ static game_params *default_params(void) { game_params *ret = snew(game_params); *ret = range_presets[DEFAULT_PRESET]; /* structure copy */ return ret; } static game_params *dup_params(const game_params *params) { game_params *ret = snew(game_params); *ret = *params; /* structure copy */ return ret; } static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; if (i < 0 || i >= lenof(range_presets)) return FALSE; ret = default_params(); *ret = range_presets[i]; /* struct copy */ *params = ret; *name = nfmtstr(40, "%d x %d", range_presets[i].w, range_presets[i].h); return TRUE; } static void free_params(game_params *params) { sfree(params); } static void decode_params(game_params *params, char const *string) { /* FIXME check for puzzle_size overflow and decoding issues */ 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++; } } static char *encode_params(const game_params *params, int full) { char str[80]; sprintf(str, "%dx%d", params->w, params->h); return dupstr(str); } static config_item *game_configure(const game_params *params) { config_item *ret; ret = snewn(3, config_item); ret[0].name = "Width"; ret[0].type = C_STRING; ret[0].sval = nfmtstr(10, "%d", params->w); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; ret[1].sval = nfmtstr(10, "%d", params->h); ret[1].ival = 0; ret[2].name = NULL; ret[2].type = C_END; ret[2].sval = NULL; ret[2].ival = 0; return ret; } static game_params *custom_params(const config_item *configuration) { game_params *ret = snew(game_params); ret->w = atoi(configuration[0].sval); ret->h = atoi(configuration[1].sval); return ret; } #define memdup(dst, src, n, type) do { \ dst = snewn(n, type); \ memcpy(dst, src, n * sizeof (type)); \ } while (0) static game_state *dup_game(const game_state *state) { game_state *ret = snew(game_state); int const n = state->params.w * state->params.h; *ret = *state; /* structure copy */ /* copy the poin_tee_, set a new value of the poin_ter_ */ memdup(ret->grid, state->grid, n, puzzle_size); return ret; } static void free_game(game_state *state) { sfree(state->grid); sfree(state); } /* ---------------------------------------------------------------------- * The solver subsystem. * * The solver is used for two purposes: * - To solve puzzles when the user selects `Solve'. * - To test solubility of a grid as clues are being removed from it * during the puzzle generation. * * It supports the following ways of reasoning: * * - A cell adjacent to a black cell must be white. * * - If painting a square black would bisect the white regions, that * square is white (by finding biconnected components' cut points) * * - A cell with number n, covering at most k white squares in three * directions must white-cover n-k squares in the last direction. * * - A cell with number n known to cover k squares, if extending the * cover by one square in a given direction causes the cell to * cover _more_ than n squares, that extension cell must be black. * * (either if the square already covers n, or if it extends into a * chunk of size > n - k) * * - Recursion. Pick any cell and see if this leads to either a * contradiction or a solution (and then act appropriately). * * * TODO: * * (propagation upper limit) * - If one has two numbers on the same line, the smaller limits the * larger. Example: in |b|_|_|8|4|_|_|b|, only two _'s can be both * white and connected to the "8" cell; so that cell will propagate * at least four cells orthogonally to the displayed line (which is * better than the current "at least 2"). * * (propagation upper limit) * - cells can't propagate into other cells if doing so exceeds that * number. Example: in |b|4|.|.|2|b|, at most one _ can be white; * otherwise, the |2| would have too many reaching white cells. * * (propagation lower and upper limit) * - `Full Combo': in each four directions d_1 ... d_4, find a set of * possible propagation distances S_1 ... S_4. For each i=1..4, * for each x in S_i: if not exists (y, z, w) in the other sets * such that (x+y+z+w+1 == clue value): then remove x from S_i. * Repeat until this stabilizes. If any cell would contradict */ #define idx(i, j, w) ((i)*(w) + (j)) #define out_of_bounds(r, c, w, h) \ ((r) < 0 || (r) >= h || (c) < 0 || (c) >= w) typedef struct square { puzzle_size r, c; } square; enum {BLACK = -2, WHITE, EMPTY}; /* white is for pencil marks, empty is undecided */ static int const dr[4] = {+1, 0, -1, 0}; static int const dc[4] = { 0, +1, 0, -1}; static int const cursors[4] = /* must match dr and dc */ {CURSOR_DOWN, CURSOR_RIGHT, CURSOR_UP, CURSOR_LEFT}; typedef struct move { square square; unsigned int colour: 1; } move; enum {M_BLACK = 0, M_WHITE = 1}; typedef move *(reasoning)(game_state *state, int nclues, const square *clues, move *buf); static reasoning solver_reasoning_not_too_big; static reasoning solver_reasoning_adjacency; static reasoning solver_reasoning_connectedness; static reasoning solver_reasoning_recursion; enum { DIFF_NOT_TOO_BIG, DIFF_ADJACENCY, DIFF_CONNECTEDNESS, DIFF_RECURSION }; static move *solve_internal(const game_state *state, move *base, int diff); static char *solve_game(const game_state *orig, const game_state *curpos, const char *aux, char **error) { int const n = orig->params.w * orig->params.h; move *const base = snewn(n, move); move *moves = solve_internal(orig, base, DIFF_RECURSION); char *ret = NULL; if (moves != NULL) { int const k = moves - base; char *str = ret = snewn(15*k + 2, char); char colour[2] = "BW"; move *it; *str++ = 'S'; *str = '\0'; for (it = base; it < moves; ++it) str += sprintf(str, "%c,%d,%d", colour[it->colour], it->square.r, it->square.c); } else *error = "This puzzle instance contains a contradiction"; sfree(base); return ret; } static square *find_clues(const game_state *state, int *ret_nclues); static move *do_solve(game_state *state, int nclues, const square *clues, move *move_buffer, int difficulty); /* new_game_desc entry point in the solver subsystem */ static move *solve_internal(const game_state *state, move *base, int diff) { int nclues; square *const clues = find_clues(state, &nclues); game_state *dup = dup_game(state); move *const moves = do_solve(dup, nclues, clues, base, diff); free_game(dup); sfree(clues); return moves; } static reasoning *const reasonings[] = { solver_reasoning_not_too_big, solver_reasoning_adjacency, solver_reasoning_connectedness, solver_reasoning_recursion }; static move *do_solve(game_state *state, int nclues, const square *clues, move *move_buffer, int difficulty) { struct move *buf = move_buffer, *oldbuf; int i; do { oldbuf = buf; for (i = 0; i < lenof(reasonings) && i <= difficulty; ++i) { /* only recurse if all else fails */ if (i == DIFF_RECURSION && buf > oldbuf) continue; buf = (*reasonings[i])(state, nclues, clues, buf); if (buf == NULL) return NULL; } } while (buf > oldbuf); return buf; } #define MASK(n) (1 << ((n) + 2)) static int runlength(puzzle_size r, puzzle_size c, puzzle_size dr, puzzle_size dc, const game_state *state, int colourmask) { int const w = state->params.w, h = state->params.h; int sz = 0; while (TRUE) { int cell = idx(r, c, w); if (out_of_bounds(r, c, w, h)) break; if (state->grid[cell] > 0) { if (!(colourmask & ~(MASK(BLACK) | MASK(WHITE) | MASK(EMPTY)))) break; } else if (!(MASK(state->grid[cell]) & colourmask)) break; ++sz; r += dr; c += dc; } return sz; } static void solver_makemove(puzzle_size r, puzzle_size c, int colour, game_state *state, move **buffer_ptr) { int const cell = idx(r, c, state->params.w); if (out_of_bounds(r, c, state->params.w, state->params.h)) return; if (state->grid[cell] != EMPTY) return; setmember((*buffer_ptr)->square, r); setmember((*buffer_ptr)->square, c); setmember(**buffer_ptr, colour); ++*buffer_ptr; state->grid[cell] = (colour == M_BLACK ? BLACK : WHITE); } static move *solver_reasoning_adjacency(game_state *state, int nclues, const square *clues, move *buf) { int r, c, i; for (r = 0; r < state->params.h; ++r) for (c = 0; c < state->params.w; ++c) { int const cell = idx(r, c, state->params.w); if (state->grid[cell] != BLACK) continue; for (i = 0; i < 4; ++i) solver_makemove(r + dr[i], c + dc[i], M_WHITE, state, &buf); } return buf; } enum {NOT_VISITED = -1}; static int dfs_biconnect_visit(puzzle_size r, puzzle_size c, game_state *state, square *dfs_parent, int *dfs_depth, move **buf); static move *solver_reasoning_connectedness(game_state *state, int nclues, const square *clues, move *buf) { int const w = state->params.w, h = state->params.h, n = w * h; square *const dfs_parent = snewn(n, square); int *const dfs_depth = snewn(n, int); int i; for (i = 0; i < n; ++i) { dfs_parent[i].r = NOT_VISITED; dfs_depth[i] = -n; } for (i = 0; i < n && state->grid[i] == BLACK; ++i); dfs_parent[i].r = i / w; dfs_parent[i].c = i % w; /* `dfs root`.parent == `dfs root` */ dfs_depth[i] = 0; dfs_biconnect_visit(i / w, i % w, state, dfs_parent, dfs_depth, &buf); sfree(dfs_parent); sfree(dfs_depth); return buf; } /* returns the `lowpoint` of (r, c) */ static int dfs_biconnect_visit(puzzle_size r, puzzle_size c, game_state *state, square *dfs_parent, int *dfs_depth, move **buf) { const puzzle_size w = state->params.w, h = state->params.h; int const i = idx(r, c, w), mydepth = dfs_depth[i]; int lowpoint = mydepth, j, nchildren = 0; for (j = 0; j < 4; ++j) { const puzzle_size rr = r + dr[j], cc = c + dc[j]; int const cell = idx(rr, cc, w); if (out_of_bounds(rr, cc, w, h)) continue; if (state->grid[cell] == BLACK) continue; if (dfs_parent[cell].r == NOT_VISITED) { int child_lowpoint; dfs_parent[cell].r = r; dfs_parent[cell].c = c; dfs_depth[cell] = mydepth + 1; child_lowpoint = dfs_biconnect_visit(rr, cc, state, dfs_parent, dfs_depth, buf); if (child_lowpoint >= mydepth && mydepth > 0) solver_makemove(r, c, M_WHITE, state, buf); lowpoint = min(lowpoint, child_lowpoint); ++nchildren; } else if (rr != dfs_parent[i].r || cc != dfs_parent[i].c) { lowpoint = min(lowpoint, dfs_depth[cell]); } } if (mydepth == 0 && nchildren >= 2) solver_makemove(r, c, M_WHITE, state, buf); return lowpoint; } static move *solver_reasoning_not_too_big(game_state *state, int nclues, const square *clues, move *buf) { int const w = state->params.w, runmasks[4] = { ~(MASK(BLACK) | MASK(EMPTY)), MASK(EMPTY), ~(MASK(BLACK) | MASK(EMPTY)), ~(MASK(BLACK)) }; enum {RUN_WHITE, RUN_EMPTY, RUN_BEYOND, RUN_SPACE}; int i, runlengths[4][4]; for (i = 0; i < nclues; ++i) { int j, k, whites, space; const puzzle_size row = clues[i].r, col = clues[i].c; int const clue = state->grid[idx(row, col, w)]; for (j = 0; j < 4; ++j) { puzzle_size r = row + dr[j], c = col + dc[j]; runlengths[RUN_SPACE][j] = 0; for (k = 0; k <= RUN_SPACE; ++k) { int l = runlength(r, c, dr[j], dc[j], state, runmasks[k]); if (k < RUN_SPACE) { runlengths[k][j] = l; r += dr[j] * l; c += dc[j] * l; } runlengths[RUN_SPACE][j] += l; } } whites = 1; for (j = 0; j < 4; ++j) whites += runlengths[RUN_WHITE][j]; for (j = 0; j < 4; ++j) { int const delta = 1 + runlengths[RUN_WHITE][j]; const puzzle_size r = row + delta * dr[j]; const puzzle_size c = col + delta * dc[j]; if (whites == clue) { solver_makemove(r, c, M_BLACK, state, &buf); continue; } if (runlengths[RUN_EMPTY][j] == 1 && whites + runlengths[RUN_EMPTY][j] + runlengths[RUN_BEYOND][j] > clue) { solver_makemove(r, c, M_BLACK, state, &buf); continue; } if (whites + runlengths[RUN_EMPTY][j] + runlengths[RUN_BEYOND][j] > clue) { runlengths[RUN_SPACE][j] = runlengths[RUN_WHITE][j] + runlengths[RUN_EMPTY][j] - 1; if (runlengths[RUN_EMPTY][j] == 1) solver_makemove(r, c, M_BLACK, state, &buf); } } space = 1; for (j = 0; j < 4; ++j) space += runlengths[RUN_SPACE][j]; for (j = 0; j < 4; ++j) { puzzle_size r = row + dr[j], c = col + dc[j]; int k = space - runlengths[RUN_SPACE][j]; if (k >= clue) continue; for (; k < clue; ++k, r += dr[j], c += dc[j]) solver_makemove(r, c, M_WHITE, state, &buf); } } return buf; } static move *solver_reasoning_recursion(game_state *state, int nclues, const square *clues, move *buf) { int const w = state->params.w, n = w * state->params.h; int cell, colour; for (cell = 0; cell < n; ++cell) { int const r = cell / w, c = cell % w; int i; game_state *newstate; move *recursive_result; if (state->grid[cell] != EMPTY) continue; /* FIXME: add enum alias for smallest and largest (or N) */ for (colour = M_BLACK; colour <= M_WHITE; ++colour) { newstate = dup_game(state); newstate->grid[cell] = colour; recursive_result = do_solve(newstate, nclues, clues, buf, DIFF_RECURSION); free_game(newstate); if (recursive_result == NULL) { solver_makemove(r, c, M_BLACK + M_WHITE - colour, state, &buf); return buf; } for (i = 0; i < n && newstate->grid[i] != EMPTY; ++i); if (i == n) return buf; } } return buf; } static square *find_clues(const game_state *state, int *ret_nclues) { int r, c, i, nclues = 0; square *ret = snewn(state->params.w * state->params.h, struct square); for (i = r = 0; r < state->params.h; ++r) for (c = 0; c < state->params.w; ++c, ++i) if (state->grid[i] > 0) { ret[nclues].r = r; ret[nclues].c = c; ++nclues; } *ret_nclues = nclues; return sresize(ret, nclues + (nclues == 0), square); } /* ---------------------------------------------------------------------- * Puzzle generation * * Generating kurodoko instances is rather straightforward: * * - Start with a white grid and add black squares at randomly chosen * locations, unless colouring that square black would violate * either the adjacency or connectedness constraints. * * - For each white square, compute the number it would contain if it * were given as a clue. * * - From a starting point of "give _every_ white square as a clue", * for each white square (in a random order), see if the board is * solvable when that square is not given as a clue. If not, don't * give it as a clue, otherwise do. * * This never fails, but it's only _almost_ what I do. The real final * step is this: * * - From a starting point of "give _every_ white square as a clue", * first remove all clues that are two-way rotationally symmetric * to a black square. If this leaves the puzzle unsolvable, throw * it out and try again. Otherwise, remove all _pairs_ of clues * (that are rotationally symmetric) which can be removed without * rendering the puzzle unsolvable. * * This can fail even if one only removes the black and symmetric * clues; indeed it happens often (avg. once or twice per puzzle) when * generating 1xN instances. (If you add black cells they must be in * the end, and if you only add one, it's ambiguous where). */ /* forward declarations of internal calls */ static void newdesc_choose_black_squares(game_state *state, const int *shuffle_1toN); static void newdesc_compute_clues(game_state *state); static int newdesc_strip_clues(game_state *state, int *shuffle_1toN); static char *newdesc_encode_game_description(int n, puzzle_size *grid); static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { int const w = params->w, h = params->h, n = w * h; puzzle_size *const grid = snewn(n, puzzle_size); int *const shuffle_1toN = snewn(n, int); int i, clues_removed; char *encoding; game_state state; state.params = *params; state.grid = grid; interactive = 0; /* I don't need it, I shouldn't use it*/ for (i = 0; i < n; ++i) shuffle_1toN[i] = i; while (TRUE) { shuffle(shuffle_1toN, n, sizeof (int), rs); newdesc_choose_black_squares(&state, shuffle_1toN); newdesc_compute_clues(&state); shuffle(shuffle_1toN, n, sizeof (int), rs); clues_removed = newdesc_strip_clues(&state, shuffle_1toN); if (clues_removed < 0) continue; else break; } encoding = newdesc_encode_game_description(n, grid); sfree(grid); sfree(shuffle_1toN); return encoding; } static int dfs_count_white(game_state *state, int cell); static void newdesc_choose_black_squares(game_state *state, const int *shuffle_1toN) { int const w = state->params.w, h = state->params.h, n = w * h; int k, any_white_cell, n_black_cells; for (k = 0; k < n; ++k) state->grid[k] = WHITE; any_white_cell = shuffle_1toN[n - 1]; n_black_cells = 0; /* I like the puzzles that result from n / 3, but maybe this * could be made a (generation, i.e. non-full) parameter? */ for (k = 0; k < n / 3; ++k) { int const i = shuffle_1toN[k], c = i % w, r = i / w; int j; for (j = 0; j < 4; ++j) { int const rr = r + dr[j], cc = c + dc[j], cell = idx(rr, cc, w); /* if you're out of bounds, we skip you */ if (out_of_bounds(rr, cc, w, h)) continue; if (state->grid[cell] == BLACK) break; /* I can't be black */ } if (j < 4) continue; /* I have black neighbour: I'm white */ state->grid[i] = BLACK; ++n_black_cells; j = dfs_count_white(state, any_white_cell); if (j + n_black_cells < n) { state->grid[i] = WHITE; --n_black_cells; } } } static void newdesc_compute_clues(game_state *state) { int const w = state->params.w, h = state->params.h; int r, c; for (r = 0; r < h; ++r) { int run_size = 0, c, cc; for (c = 0; c <= w; ++c) { if (c == w || state->grid[idx(r, c, w)] == BLACK) { for (cc = c - run_size; cc < c; ++cc) state->grid[idx(r, cc, w)] += run_size; run_size = 0; } else ++run_size; } } for (c = 0; c < w; ++c) { int run_size = 0, r, rr; for (r = 0; r <= h; ++r) { if (r == h || state->grid[idx(r, c, w)] == BLACK) { for (rr = r - run_size; rr < r; ++rr) state->grid[idx(rr, c, w)] += run_size; run_size = 0; } else ++run_size; } } } #define rotate(x) (n - 1 - (x)) static int newdesc_strip_clues(game_state *state, int *shuffle_1toN) { int const w = state->params.w, n = w * state->params.h; move *const move_buffer = snewn(n, move); move *buf; game_state *dupstate; /* * do a partition/pivot of shuffle_1toN into three groups: * (1) squares rotationally-symmetric to (3) * (2) squares not in (1) or (3) * (3) black squares * * They go from [0, left), [left, right) and [right, n) in * shuffle_1toN (and from there into state->grid[ ]) * * Then, remove clues from the grid one by one in shuffle_1toN * order, until the solver becomes unhappy. If we didn't remove * all of (1), return (-1). Else, we're happy. */ /* do the partition */ int clues_removed, k = 0, left = 0, right = n; for (;; ++k) { while (k < right && state->grid[shuffle_1toN[k]] == BLACK) { --right; SWAP(int, shuffle_1toN[right], shuffle_1toN[k]); assert(state->grid[shuffle_1toN[right]] == BLACK); } if (k >= right) break; assert (k >= left); if (state->grid[rotate(shuffle_1toN[k])] == BLACK) { SWAP(int, shuffle_1toN[k], shuffle_1toN[left]); ++left; } assert (state->grid[rotate(shuffle_1toN[k])] != BLACK || k == left - 1); } for (k = 0; k < left; ++k) { assert (state->grid[rotate(shuffle_1toN[k])] == BLACK); state->grid[shuffle_1toN[k]] = EMPTY; } for (k = left; k < right; ++k) { assert (state->grid[rotate(shuffle_1toN[k])] != BLACK); assert (state->grid[shuffle_1toN[k]] != BLACK); } for (k = right; k < n; ++k) { assert (state->grid[shuffle_1toN[k]] == BLACK); state->grid[shuffle_1toN[k]] = EMPTY; } clues_removed = (left - 0) + (n - right); dupstate = dup_game(state); buf = solve_internal(dupstate, move_buffer, DIFF_RECURSION - 1); free_game(dupstate); if (buf - move_buffer < clues_removed) { /* branch prediction: I don't think I'll go here */ clues_removed = -1; goto ret; } for (k = left; k < right; ++k) { const int i = shuffle_1toN[k], j = rotate(i); int const clue = state->grid[i], clue_rot = state->grid[j]; if (clue == BLACK) continue; state->grid[i] = state->grid[j] = EMPTY; dupstate = dup_game(state); buf = solve_internal(dupstate, move_buffer, DIFF_RECURSION - 1); free_game(dupstate); clues_removed += 2 - (i == j); /* if i is the center square, then i == (j = rotate(i)) * when i and j are one, removing i and j removes only one */ if (buf - move_buffer == clues_removed) continue; /* if the solver is sound, refilling all removed clues means * we have filled all squares, i.e. solved the puzzle. */ state->grid[i] = clue; state->grid[j] = clue_rot; clues_removed -= 2 - (i == j); } ret: sfree(move_buffer); return clues_removed; } static int dfs_count_rec(puzzle_size *grid, int r, int c, int w, int h) { int const cell = idx(r, c, w); if (out_of_bounds(r, c, w, h)) return 0; if (grid[cell] != WHITE) return 0; grid[cell] = EMPTY; return 1 + dfs_count_rec(grid, r + 0, c + 1, w, h) + dfs_count_rec(grid, r + 0, c - 1, w, h) + dfs_count_rec(grid, r + 1, c + 0, w, h) + dfs_count_rec(grid, r - 1, c + 0, w, h); } static int dfs_count_white(game_state *state, int cell) { int const w = state->params.w, h = state->params.h, n = w * h; int const r = cell / w, c = cell % w; int i, k = dfs_count_rec(state->grid, r, c, w, h); for (i = 0; i < n; ++i) if (state->grid[i] == EMPTY) state->grid[i] = WHITE; return k; } static char *validate_params(const game_params *params, int full) { int const w = params->w, h = params->h; if (w < 1) return "Error: width is less than 1"; if (h < 1) return "Error: height is less than 1"; if (w * h < 1) return "Error: size is less than 1"; if (w + h - 1 > SCHAR_MAX) return "Error: w + h is too big"; /* I might be unable to store clues in my puzzle_size *grid; */ if (full) { if (w == 2 && h == 2) return "Error: can't create 2x2 puzzles"; if (w == 1 && h == 2) return "Error: can't create 1x2 puzzles"; if (w == 2 && h == 1) return "Error: can't create 2x1 puzzles"; if (w == 1 && h == 1) return "Error: can't create 1x1 puzzles"; } return NULL; } /* Definition: a puzzle instance is _good_ if: * - it has a unique solution * - the solver can find this solution without using recursion * - the solution contains at least one black square * - the clues are 2-way rotationally symmetric * * (the idea being: the generator can not output any _bad_ puzzles) * * Theorem: validate_params, when full != 0, discards exactly the set * of parameters for which there are _no_ good puzzle instances. * * Proof: it's an immediate consequence of the five lemmas below. * * Observation: not only do puzzles on non-tiny grids exist, the * generator is pretty fast about coming up with them. On my pre-2004 * desktop box, it generates 100 puzzles on the highest preset (16x11) * in 8.383 seconds, or <= 0.1 second per puzzle. * * ---------------------------------------------------------------------- * * Lemma: On a 1x1 grid, there are no good puzzles. * * Proof: the one square can't be a clue because at least one square * is black. But both a white square and a black square satisfy the * solution criteria, so the puzzle is ambiguous (and hence bad). * * Lemma: On a 1x2 grid, there are no good puzzles. * * Proof: let's name the squares l and r. Note that there can be at * most one black square, or adjacency is violated. By assumption at * least one square is black, so let's call that one l. By clue * symmetry, neither l nor r can be given as a clue, so the puzzle * instance is blank and thus ambiguous. * * Corollary: On a 2x1 grid, there are no good puzzles. * Proof: rotate the above proof 90 degrees ;-) * * ---------------------------------------------------------------------- * * Lemma: On a 2x2 grid, there are no soluble puzzles with 2-way * rotational symmetric clues and at least one black square. * * Proof: Let's name the squares a, b, c, and d, with a and b on the * top row, a and c in the left column. Let's consider the case where * a is black. Then no other square can be black: b and c would both * violate the adjacency constraint; d would disconnect b from c. * * So exactly one square is black (and by 4-way rotation symmetry of * the 2x2 square, it doesn't matter which one, so let's stick to a). * By 2-way rotational symmetry of the clues and the rule about not * painting numbers black, neither a nor d can be clues. A blank * puzzle would be ambiguous, so one of {b, c} is a clue; by symmetry, * so is the other one. * * It is readily seen that their clue value is 2. But "a is black" * and "d is black" are both valid solutions in this case, so the * puzzle is ambiguous (and hence bad). * * ---------------------------------------------------------------------- * * Lemma: On a wxh grid with w, h >= 1 and (w > 2 or h > 2), there is * at least one good puzzle. * * Proof: assume that w > h (otherwise rotate the proof again). Paint * the top left and bottom right corners black, and fill a clue into * all the other squares. Present this board to the solver code (or * player, hypothetically), except with the two black squares as blank * squares. * * For an Nx1 puzzle, observe that every clue is N - 2, and there are * N - 2 of them in one connected sequence, so the remaining two * squares can be deduced to be black, which solves the puzzle. * * For any other puzzle, let j be a cell in the same row as a black * cell, but not in the same column (such a cell doesn't exist in 2x3 * puzzles, but we assume w > h and such cells exist in 3x2 puzzles). * * Note that the number of cells in axis parallel `rays' going out * from j exceeds j's clue value by one. Only one such cell is a * non-clue, so it must be black. Similarly for the other corner (let * j' be a cell in the same row as the _other_ black cell, but not in * the same column as _any_ black cell; repeat this argument at j'). * * This fills the grid and satisfies all clues and the adjacency * constraint and doesn't paint on top of any clues. All that is left * to see is connectedness. * * Observe that the white cells in each column form a single connected * `run', and each column contains a white cell adjacent to a white * cell in the column to the right, if that column exists. * * Thus, any cell in the left-most column can reach any other cell: * first go to the target column (by repeatedly going to the cell in * your current column that lets you go right, then going right), then * go up or down to the desired cell. * * As reachability is symmetric (in undirected graphs) and transitive, * any cell can reach any left-column cell, and from there any other * cell. */ /* ---------------------------------------------------------------------- * Game encoding and decoding */ #define NDIGITS_BASE '!' static char *newdesc_encode_game_description(int area, puzzle_size *grid) { char *desc = NULL; int desclen = 0, descsize = 0; int run, i; run = 0; for (i = 0; i <= area; i++) { int n = (i < area ? grid[i] : -1); if (!n) run++; else { if (descsize < desclen + 40) { descsize = desclen * 3 / 2 + 40; desc = sresize(desc, descsize, char); } if (run) { while (run > 0) { int c = 'a' - 1 + run; if (run > 26) c = 'z'; desc[desclen++] = 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 (desclen > 0 && n > 0) desc[desclen++] = '_'; } if (n > 0) desclen += sprintf(desc+desclen, "%d", n); run = 0; } } desc[desclen] = '\0'; return desc; } static char *validate_desc(const game_params *params, const char *desc) { int const n = params->w * params->h; int squares = 0; int range = params->w + params->h - 1; /* maximum cell value */ while (*desc && *desc != ',') { int c = *desc++; if (c >= 'a' && c <= 'z') { squares += c - 'a' + 1; } else if (c == '_') { /* do nothing */; } else if (c > '0' && c <= '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 < n) return "Not enough data to fill grid"; if (squares > n) return "Too much data to fit in grid"; return NULL; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { int i; const char *p; int const n = params->w * params->h; game_state *state = snew(game_state); me = NULL; /* I don't need it, I shouldn't use it */ state->params = *params; /* structure copy */ state->grid = snewn(n, puzzle_size); p = desc; i = 0; while (i < n && *p) { int c = *p++; if (c >= 'a' && c <= 'z') { int squares = c - 'a' + 1; while (squares--) state->grid[i++] = 0; } else if (c == '_') { /* do nothing */; } else if (c > '0' && c <= '9') { int val = atoi(p-1); assert(val >= 1 && val <= params->w+params->h-1); state->grid[i++] = val; while (*p >= '0' && *p <= '9') p++; } } assert(i == n); state->has_cheated = FALSE; state->was_solved = FALSE; return state; } /* ---------------------------------------------------------------------- * User interface: ascii */ static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { int cellsize, r, c, i, w_string, h_string, n_string; char *ret, *buf, *gridline; int const w = state->params.w, h = state->params.h; cellsize = 0; /* or may be used uninitialized */ for (c = 0; c < w; ++c) { for (r = 1; r < h; ++r) { puzzle_size k = state->grid[idx(r, c, w)]; int d; for (d = 0; k; k /= 10, ++d); cellsize = max(cellsize, d); } } ++cellsize; w_string = w * cellsize + 2; /* "|%d|%d|...|\n" */ h_string = 2 * h + 1; /* "+--+--+...+\n%s\n+--+--+...+\n" */ n_string = w_string * h_string; gridline = snewn(w_string + 1, char); /* +1: NUL terminator */ memset(gridline, '-', w_string); for (c = 0; c <= w; ++c) gridline[c * cellsize] = '+'; gridline[w_string - 1] = '\n'; gridline[w_string - 0] = '\0'; buf = ret = snewn(n_string + 1, char); /* +1: NUL terminator */ for (i = r = 0; r < h; ++r) { memcpy(buf, gridline, w_string); buf += w_string; for (c = 0; c < w; ++c, ++i) { char ch; switch (state->grid[i]) { case BLACK: ch = '#'; break; case WHITE: ch = '.'; break; case EMPTY: ch = ' '; break; default: buf += sprintf(buf, "|%*d", cellsize - 1, state->grid[i]); continue; } *buf++ = '|'; memset(buf, ch, cellsize - 1); buf += cellsize - 1; } buf += sprintf(buf, "|\n"); } memcpy(buf, gridline, w_string); buf += w_string; assert (buf - ret == n_string); *buf = '\0'; sfree(gridline); return ret; } /* ---------------------------------------------------------------------- * User interfaces: interactive */ struct game_ui { puzzle_size r, c; /* cursor position */ unsigned int cursor_show: 1; }; static game_ui *new_ui(const game_state *state) { struct game_ui *ui = snew(game_ui); ui->r = ui->c = 0; ui->cursor_show = FALSE; 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) { } typedef struct drawcell { puzzle_size value; unsigned int error: 1; unsigned int cursor: 1; unsigned int flash: 1; } drawcell; struct game_drawstate { int tilesize; drawcell *grid; unsigned int started: 1; }; #define TILESIZE (ds->tilesize) #define BORDER (TILESIZE / 2) #define COORD(x) ((x) * TILESIZE + BORDER) #define FROMCOORD(x) (((x) - BORDER) / TILESIZE) static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { enum {none, forwards, backwards, hint}; int const w = state->params.w, h = state->params.h; int r = ui->r, c = ui->c, action = none, cell; if (IS_CURSOR_SELECT(button) && !ui->cursor_show) return NULL; if (IS_MOUSE_DOWN(button)) { r = FROMCOORD(y + TILESIZE) - 1; /* or (x, y) < TILESIZE) */ c = FROMCOORD(x + TILESIZE) - 1; /* are considered inside */ if (out_of_bounds(r, c, w, h)) return NULL; ui->r = r; ui->c = c; ui->cursor_show = FALSE; } if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { /* * Utterly awful hack, exactly analogous to the one in Slant, * to configure the left and right mouse buttons the opposite * way round. * * The original puzzle submitter thought it would be more * useful to have the left button turn an empty square into a * dotted one, on the grounds that that was what you did most * often; I (SGT) felt instinctively that the left button * ought to place black squares and the right button place * dots, on the grounds that that was consistent with many * other puzzles in which the left button fills in the data * used by the solution checker while the right button places * pencil marks for the user's convenience. * * My first beta-player wasn't sure either, so I thought I'd * pre-emptively put in a 'configuration' mechanism just in * case. */ { static int swap_buttons = -1; if (swap_buttons < 0) { char *env = getenv("RANGE_SWAP_BUTTONS"); swap_buttons = (env && (env[0] == 'y' || env[0] == 'Y')); } if (swap_buttons) { if (button == LEFT_BUTTON) button = RIGHT_BUTTON; else button = LEFT_BUTTON; } } } switch (button) { case CURSOR_SELECT : case LEFT_BUTTON: action = backwards; break; case CURSOR_SELECT2: case RIGHT_BUTTON: action = forwards; break; case 'h': case 'H' : action = hint; break; case CURSOR_UP: case CURSOR_DOWN: case CURSOR_LEFT: case CURSOR_RIGHT: if (ui->cursor_show) { int i; for (i = 0; i < 4 && cursors[i] != button; ++i); assert (i < 4); if (!out_of_bounds(ui->r + dr[i], ui->c + dc[i], w, h)) { ui->r += dr[i]; ui->c += dc[i]; } } else ui->cursor_show = TRUE; return ""; } if (action == hint) { move *end, *buf = snewn(state->params.w * state->params.h, struct move); char *ret = NULL; end = solve_internal(state, buf, DIFF_RECURSION); if (end != NULL && end > buf) { ret = nfmtstr(40, "%c,%d,%d", buf->colour == M_BLACK ? 'B' : 'W', buf->square.r, buf->square.c); /* We used to set a flag here in the game_ui indicating * that the player had used the hint function. I (SGT) * retired it, on grounds of consistency with other games * (most of these games will still flash to indicate * completion if you solved and undid it, so why not if * you got a hint?) and because the flash is as much about * checking you got it all right than about congratulating * you on a job well done. */ } sfree(buf); return ret; } cell = state->grid[idx(r, c, state->params.w)]; if (cell > 0) return NULL; if (action == forwards) switch (cell) { case EMPTY: return nfmtstr(40, "W,%d,%d", r, c); case WHITE: return nfmtstr(40, "B,%d,%d", r, c); case BLACK: return nfmtstr(40, "E,%d,%d", r, c); } else if (action == backwards) switch (cell) { case BLACK: return nfmtstr(40, "W,%d,%d", r, c); case WHITE: return nfmtstr(40, "E,%d,%d", r, c); case EMPTY: return nfmtstr(40, "B,%d,%d", r, c); } return NULL; } static int find_errors(const game_state *state, int *report) { int const w = state->params.w, h = state->params.h, n = w * h; int r, c, i; int nblack = 0, any_white_cell = -1; game_state *dup = dup_game(state); for (i = r = 0; r < h; ++r) for (c = 0; c < w; ++c, ++i) { switch (state->grid[i]) { case BLACK: { int j; ++nblack; for (j = 0; j < 4; ++j) { int const rr = r + dr[j], cc = c + dc[j]; if (out_of_bounds(rr, cc, w, h)) continue; if (state->grid[idx(rr, cc, w)] != BLACK) continue; if (!report) goto found_error; report[i] = TRUE; break; } } break; default: { int j, runs; for (runs = 1, j = 0; j < 4; ++j) { int const rr = r + dr[j], cc = c + dc[j]; runs += runlength(rr, cc, dr[j], dc[j], state, ~MASK(BLACK)); } if (!report) { if (runs != state->grid[i]) goto found_error; } else if (runs < state->grid[i]) report[i] = TRUE; else { for (runs = 1, j = 0; j < 4; ++j) { int const rr = r + dr[j], cc = c + dc[j]; runs += runlength(rr, cc, dr[j], dc[j], state, ~(MASK(BLACK) | MASK(EMPTY))); } if (runs > state->grid[i]) report[i] = TRUE; } } /* note: fallthrough _into_ these cases */ case EMPTY: case WHITE: any_white_cell = i; } } for (i = 0; i < n; ++i) if (dup->grid[i] != BLACK) dup->grid[i] = WHITE; if (nblack + dfs_count_white(dup, any_white_cell) < n) { if (!report) { printf("dfs fail at %d\n", any_white_cell); goto found_error; } for (i = 0; i < n; ++i) if (state->grid[i] != BLACK) report[i] = TRUE; } free_game(dup); return FALSE; /* if report != NULL, this is ignored */ found_error: free_game(dup); return TRUE; } static game_state *execute_move(const game_state *state, const char *move) { signed int r, c, value, nchars, ntok; signed char what_to_do; game_state *ret; assert (move); ret = dup_game(state); if (*move == 'S') { ++move; ret->has_cheated = ret->was_solved = TRUE; } for (; *move; move += nchars) { ntok = sscanf(move, "%c,%d,%d%n", &what_to_do, &r, &c, &nchars); if (ntok < 3) goto failure; switch (what_to_do) { case 'W': value = WHITE; break; case 'E': value = EMPTY; break; case 'B': value = BLACK; break; default: goto failure; } if (out_of_bounds(r, c, ret->params.w, ret->params.h)) goto failure; ret->grid[idx(r, c, ret->params.w)] = value; } if (ret->was_solved == FALSE) ret->was_solved = !find_errors(ret, NULL); return ret; failure: free_game(ret); return NULL; } static void game_changed_state(game_ui *ui, const game_state *oldstate, const game_state *newstate) { } static float game_anim_length(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui) { return 0.0F; } #define FLASH_TIME 0.7F static float game_flash_length(const game_state *from, const game_state *to, int dir, game_ui *ui) { if (!from->was_solved && to->was_solved && !to->has_cheated) return FLASH_TIME; return 0.0F; } static int game_status(const game_state *state) { return state->was_solved ? +1 : 0; } /* ---------------------------------------------------------------------- * Drawing routines. */ #define PREFERRED_TILE_SIZE 32 enum { COL_BACKGROUND = 0, COL_GRID, COL_BLACK = COL_GRID, COL_TEXT = COL_GRID, COL_USER = COL_GRID, COL_ERROR, COL_LOWLIGHT, COL_HIGHLIGHT = COL_ERROR, /* mkhighlight needs it, I don't */ COL_CURSOR = COL_LOWLIGHT, NCOLOURS }; static void game_compute_size(const game_params *params, int tilesize, int *x, int *y) { *x = (1 + params->w) * tilesize; *y = (1 + params->h) * tilesize; } static void game_set_size(drawing *dr, game_drawstate *ds, const game_params *params, int tilesize) { ds->tilesize = tilesize; } #define COLOUR(ret, i, r, g, b) \ ((ret[3*(i)+0] = (r)), (ret[3*(i)+1] = (g)), (ret[3*(i)+2] = (b))) static float *game_colours(frontend *fe, int *ncolours) { float *ret = snewn(3 * NCOLOURS, float); game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT); COLOUR(ret, COL_GRID, 0.0F, 0.0F, 0.0F); COLOUR(ret, COL_ERROR, 1.0F, 0.0F, 0.0F); *ncolours = NCOLOURS; return ret; } static drawcell makecell(puzzle_size value, int error, int cursor, int flash) { drawcell ret; setmember(ret, value); setmember(ret, error); setmember(ret, cursor); setmember(ret, flash); return ret; } static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { int const w = state->params.w, h = state->params.h, n = w * h; struct game_drawstate *ds = snew(struct game_drawstate); int i; ds->tilesize = 0; ds->started = FALSE; ds->grid = snewn(n, drawcell); for (i = 0; i < n; ++i) ds->grid[i] = makecell(w + h, FALSE, FALSE, FALSE); return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->grid); sfree(ds); } #define cmpmember(a, b, field) ((a) . field == (b) . field) static int cell_eq(drawcell a, drawcell b) { return cmpmember(a, b, value) && cmpmember(a, b, error) && cmpmember(a, b, cursor) && cmpmember(a, b, flash); } static void draw_cell(drawing *dr, game_drawstate *ds, int r, int c, drawcell cell); 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 const w = state->params.w, h = state->params.h, n = w * h; int const wpx = (w+1) * ds->tilesize, hpx = (h+1) * ds->tilesize; int const flash = ((int) (flashtime * 5 / FLASH_TIME)) % 2; int r, c, i; int *errors = snewn(n, int); memset(errors, FALSE, n * sizeof (int)); find_errors(state, errors); assert (oldstate == NULL); /* only happens if animating moves */ if (!ds->started) { ds->started = TRUE; draw_rect(dr, 0, 0, wpx, hpx, COL_BACKGROUND); draw_rect(dr, BORDER-1, BORDER-1, ds->tilesize*w+2, ds->tilesize*h+2, COL_GRID); draw_update(dr, 0, 0, wpx, hpx); } for (i = r = 0; r < h; ++r) { for (c = 0; c < w; ++c, ++i) { drawcell cell = makecell(state->grid[i], errors[i], FALSE, flash); if (r == ui->r && c == ui->c && ui->cursor_show) cell.cursor = TRUE; if (!cell_eq(cell, ds->grid[i])) { draw_cell(dr, ds, r, c, cell); ds->grid[i] = cell; } } } sfree(errors); } static void draw_cell(drawing *draw, game_drawstate *ds, int r, int c, drawcell cell) { int const ts = ds->tilesize; int const y = BORDER + ts * r, x = BORDER + ts * c; int const tx = x + (ts / 2), ty = y + (ts / 2); int const dotsz = (ds->tilesize + 9) / 10; int const colour = (cell.value == BLACK ? cell.error ? COL_ERROR : COL_BLACK : cell.flash || cell.cursor ? COL_LOWLIGHT : COL_BACKGROUND); draw_rect (draw, x, y, ts, ts, colour); draw_rect_outline(draw, x, y, ts, ts, COL_GRID); switch (cell.value) { case WHITE: draw_rect(draw, tx - dotsz / 2, ty - dotsz / 2, dotsz, dotsz, cell.error ? COL_ERROR : COL_USER); case BLACK: break; case EMPTY: if (cell.error) draw_circle(draw, tx, ty, dotsz / 2, COL_ERROR, COL_GRID); break; default: { int const colour = (cell.error ? COL_ERROR : COL_GRID); char *msg = nfmtstr(10, "%d", cell.value); draw_text(draw, tx, ty, FONT_VARIABLE, ts * 3 / 5, ALIGN_VCENTRE | ALIGN_HCENTRE, colour, msg); sfree(msg); } } draw_update(draw, x, y, ts, ts); } static int game_timing_state(const game_state *state, game_ui *ui) { puts("warning: game_timing_state was called (this shouldn't happen)"); return FALSE; /* the (non-existing) timer should not be running */ } /* ---------------------------------------------------------------------- * User interface: print */ static void game_print_size(const game_params *params, float *x, float *y) { int print_width, print_height; game_compute_size(params, 800, &print_width, &print_height); *x = print_width / 100.0F; *y = print_height / 100.0F; } static void game_print(drawing *dr, const game_state *state, int tilesize) { int const w = state->params.w, h = state->params.h; game_drawstate ds_obj, *ds = &ds_obj; int r, c, i, colour; ds->tilesize = tilesize; colour = print_mono_colour(dr, 1); assert(colour == COL_BACKGROUND); colour = print_mono_colour(dr, 0); assert(colour == COL_GRID); colour = print_mono_colour(dr, 1); assert(colour == COL_ERROR); colour = print_mono_colour(dr, 0); assert(colour == COL_LOWLIGHT); colour = print_mono_colour(dr, 0); assert(colour == NCOLOURS); for (i = r = 0; r < h; ++r) for (c = 0; c < w; ++c, ++i) draw_cell(dr, ds, r, c, makecell(state->grid[i], FALSE, FALSE, FALSE)); print_line_width(dr, 3 * tilesize / 40); draw_rect_outline(dr, BORDER, BORDER, w*TILESIZE, h*TILESIZE, COL_GRID); } /* And that's about it ;-) **************************************************/ #ifdef COMBINED #define thegame range #endif struct game const thegame = { "Range", "games.range", "range", default_params, game_fetch_preset, 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, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, 0, /* flags */ }; puzzles-r9872/rect.c0000644000175300017530000026741512132232554013544 0ustar simonsimon/* * rect.c: Puzzle from nikoli.co.jp. You have a square grid with * numbers in some squares; you must divide the square grid up into * variously sized rectangles, such that every rectangle contains * exactly one numbered square and the area of each rectangle is * equal to the number contained in it. */ /* * TODO: * * - Improve singleton removal. * + It would be nice to limit the size of the generated * rectangles in accordance with existing constraints such as * the maximum rectangle size and the one about not * generating a rectangle the full width or height of the * grid. * + This could be achieved by making a less random choice * about which of the available options to use. * + Alternatively, we could create our rectangle and then * split it up. */ #include #include #include #include #include #include #include "puzzles.h" enum { COL_BACKGROUND, COL_CORRECT, COL_LINE, COL_TEXT, COL_GRID, COL_DRAG, COL_DRAGERASE, COL_CURSOR, NCOLOURS }; struct game_params { int w, h; float expandfactor; int unique; }; #define INDEX(state, x, y) (((y) * (state)->w) + (x)) #define index(state, a, x, y) ((a) [ INDEX(state,x,y) ]) #define grid(state,x,y) index(state, (state)->grid, x, y) #define vedge(state,x,y) index(state, (state)->vedge, x, y) #define hedge(state,x,y) index(state, (state)->hedge, x, y) #define CRANGE(state,x,y,dx,dy) ( (x) >= dx && (x) < (state)->w && \ (y) >= dy && (y) < (state)->h ) #define RANGE(state,x,y) CRANGE(state,x,y,0,0) #define HRANGE(state,x,y) CRANGE(state,x,y,0,1) #define VRANGE(state,x,y) CRANGE(state,x,y,1,0) #define PREFERRED_TILE_SIZE 24 #define TILE_SIZE (ds->tilesize) #ifdef SMALL_SCREEN #define BORDER (2) #else #define BORDER (TILE_SIZE * 3 / 4) #endif #define CORNER_TOLERANCE 0.15F #define CENTRE_TOLERANCE 0.15F #define FLASH_TIME 0.13F #define COORD(x) ( (x) * TILE_SIZE + BORDER ) #define FROMCOORD(x) ( ((x) - BORDER) / TILE_SIZE ) struct game_state { int w, h; int *grid; /* contains the numbers */ unsigned char *vedge; /* (w+1) x h */ unsigned char *hedge; /* w x (h+1) */ int completed, cheated; unsigned char *correct; }; static game_params *default_params(void) { game_params *ret = snew(game_params); ret->w = ret->h = 7; ret->expandfactor = 0.0F; ret->unique = TRUE; return ret; } static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; int w, h; char buf[80]; switch (i) { case 0: w = 7, h = 7; break; case 1: w = 9, h = 9; break; case 2: w = 11, h = 11; break; case 3: w = 13, h = 13; break; case 4: w = 15, h = 15; break; #ifndef SMALL_SCREEN case 5: w = 17, h = 17; break; case 6: w = 19, h = 19; break; #endif default: return FALSE; } sprintf(buf, "%dx%d", w, h); *name = dupstr(buf); *params = ret = snew(game_params); ret->w = w; ret->h = h; ret->expandfactor = 0.0F; ret->unique = TRUE; 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 *ret, char const *string) { ret->w = ret->h = atoi(string); while (*string && isdigit((unsigned char)*string)) string++; if (*string == 'x') { string++; ret->h = atoi(string); while (*string && isdigit((unsigned char)*string)) string++; } if (*string == 'e') { string++; ret->expandfactor = (float)atof(string); while (*string && (*string == '.' || isdigit((unsigned char)*string))) string++; } if (*string == 'a') { string++; ret->unique = FALSE; } } static char *encode_params(const game_params *params, int full) { char data[256]; sprintf(data, "%dx%d", params->w, params->h); if (full && params->expandfactor) sprintf(data + strlen(data), "e%g", params->expandfactor); if (full && !params->unique) strcat(data, "a"); return dupstr(data); } static config_item *game_configure(const game_params *params) { config_item *ret; char buf[80]; ret = snewn(5, config_item); ret[0].name = "Width"; ret[0].type = C_STRING; sprintf(buf, "%d", params->w); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Expansion factor"; ret[2].type = C_STRING; sprintf(buf, "%g", params->expandfactor); ret[2].sval = dupstr(buf); ret[2].ival = 0; ret[3].name = "Ensure unique solution"; ret[3].type = C_BOOLEAN; ret[3].sval = NULL; ret[3].ival = params->unique; ret[4].name = NULL; ret[4].type = C_END; ret[4].sval = NULL; ret[4].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); ret->expandfactor = (float)atof(cfg[2].sval); ret->unique = cfg[3].ival; return ret; } static char *validate_params(const game_params *params, int full) { if (params->w <= 0 || params->h <= 0) return "Width and height must both be greater than zero"; if (params->w*params->h < 2) return "Grid area must be greater than one"; if (params->expandfactor < 0.0F) return "Expansion factor may not be negative"; return NULL; } struct point { int x, y; }; struct rect { int x, y; int w, h; }; struct rectlist { struct rect *rects; int n; }; struct numberdata { int area; int npoints; struct point *points; }; /* ---------------------------------------------------------------------- * Solver for Rectangles games. * * This solver is souped up beyond the needs of actually _solving_ * a puzzle. It is also designed to cope with uncertainty about * where the numbers have been placed. This is because I run it on * my generated grids _before_ placing the numbers, and have it * tell me where I need to place the numbers to ensure a unique * solution. */ static void remove_rect_placement(int w, int h, struct rectlist *rectpositions, int *overlaps, int rectnum, int placement) { int x, y, xx, yy; #ifdef SOLVER_DIAGNOSTICS printf("ruling out rect %d placement at %d,%d w=%d h=%d\n", rectnum, rectpositions[rectnum].rects[placement].x, rectpositions[rectnum].rects[placement].y, rectpositions[rectnum].rects[placement].w, rectpositions[rectnum].rects[placement].h); #endif /* * Decrement each entry in the overlaps array to reflect the * removal of this rectangle placement. */ for (yy = 0; yy < rectpositions[rectnum].rects[placement].h; yy++) { y = yy + rectpositions[rectnum].rects[placement].y; for (xx = 0; xx < rectpositions[rectnum].rects[placement].w; xx++) { x = xx + rectpositions[rectnum].rects[placement].x; assert(overlaps[(rectnum * h + y) * w + x] != 0); if (overlaps[(rectnum * h + y) * w + x] > 0) overlaps[(rectnum * h + y) * w + x]--; } } /* * Remove the placement from the list of positions for that * rectangle, by interchanging it with the one on the end. */ if (placement < rectpositions[rectnum].n - 1) { struct rect t; t = rectpositions[rectnum].rects[rectpositions[rectnum].n - 1]; rectpositions[rectnum].rects[rectpositions[rectnum].n - 1] = rectpositions[rectnum].rects[placement]; rectpositions[rectnum].rects[placement] = t; } rectpositions[rectnum].n--; } static void remove_number_placement(int w, int h, struct numberdata *number, int index, int *rectbyplace) { /* * Remove the entry from the rectbyplace array. */ rectbyplace[number->points[index].y * w + number->points[index].x] = -1; /* * Remove the placement from the list of candidates for that * number, by interchanging it with the one on the end. */ if (index < number->npoints - 1) { struct point t; t = number->points[number->npoints - 1]; number->points[number->npoints - 1] = number->points[index]; number->points[index] = t; } number->npoints--; } /* * Returns 0 for failure to solve due to inconsistency; 1 for * success; 2 for failure to complete a solution due to either * ambiguity or it being too difficult. */ static int rect_solver(int w, int h, int nrects, struct numberdata *numbers, unsigned char *hedge, unsigned char *vedge, random_state *rs) { struct rectlist *rectpositions; int *overlaps, *rectbyplace, *workspace; int i, ret; /* * Start by setting up a list of candidate positions for each * rectangle. */ rectpositions = snewn(nrects, struct rectlist); for (i = 0; i < nrects; i++) { int rw, rh, area = numbers[i].area; int j, minx, miny, maxx, maxy; struct rect *rlist; int rlistn, rlistsize; /* * For each rectangle, begin by finding the bounding * rectangle of its candidate number placements. */ maxx = maxy = -1; minx = w; miny = h; for (j = 0; j < numbers[i].npoints; j++) { if (minx > numbers[i].points[j].x) minx = numbers[i].points[j].x; if (miny > numbers[i].points[j].y) miny = numbers[i].points[j].y; if (maxx < numbers[i].points[j].x) maxx = numbers[i].points[j].x; if (maxy < numbers[i].points[j].y) maxy = numbers[i].points[j].y; } /* * Now loop over all possible rectangle placements * overlapping a point within that bounding rectangle; * ensure each one actually contains a candidate number * placement, and add it to the list. */ rlist = NULL; rlistn = rlistsize = 0; for (rw = 1; rw <= area && rw <= w; rw++) { int x, y; if (area % rw) continue; rh = area / rw; if (rh > h) continue; for (y = miny - rh + 1; y <= maxy; y++) { if (y < 0 || y+rh > h) continue; for (x = minx - rw + 1; x <= maxx; x++) { if (x < 0 || x+rw > w) continue; /* * See if we can find a candidate number * placement within this rectangle. */ for (j = 0; j < numbers[i].npoints; j++) if (numbers[i].points[j].x >= x && numbers[i].points[j].x < x+rw && numbers[i].points[j].y >= y && numbers[i].points[j].y < y+rh) break; if (j < numbers[i].npoints) { /* * Add this to the list of candidate * placements for this rectangle. */ if (rlistn >= rlistsize) { rlistsize = rlistn + 32; rlist = sresize(rlist, rlistsize, struct rect); } rlist[rlistn].x = x; rlist[rlistn].y = y; rlist[rlistn].w = rw; rlist[rlistn].h = rh; #ifdef SOLVER_DIAGNOSTICS printf("rect %d [area %d]: candidate position at" " %d,%d w=%d h=%d\n", i, area, x, y, rw, rh); #endif rlistn++; } } } } rectpositions[i].rects = rlist; rectpositions[i].n = rlistn; } /* * Next, construct a multidimensional array tracking how many * candidate positions for each rectangle overlap each square. * * Indexing of this array is by the formula * * overlaps[(rectindex * h + y) * w + x] * * A positive or zero value indicates what it sounds as if it * should; -1 indicates that this square _cannot_ be part of * this rectangle; and -2 indicates that it _definitely_ is * (which is distinct from 1, because one might very well know * that _if_ square S is part of rectangle R then it must be * because R is placed in a certain position without knowing * that it definitely _is_). */ overlaps = snewn(nrects * w * h, int); memset(overlaps, 0, nrects * w * h * sizeof(int)); for (i = 0; i < nrects; i++) { int j; for (j = 0; j < rectpositions[i].n; j++) { int xx, yy; for (yy = 0; yy < rectpositions[i].rects[j].h; yy++) for (xx = 0; xx < rectpositions[i].rects[j].w; xx++) overlaps[(i * h + yy+rectpositions[i].rects[j].y) * w + xx+rectpositions[i].rects[j].x]++; } } /* * Also we want an array covering the grid once, to make it * easy to figure out which squares are candidate number * placements for which rectangles. (The existence of this * single array assumes that no square starts off as a * candidate number placement for more than one rectangle. This * assumption is justified, because this solver is _either_ * used to solve real problems - in which case there is a * single placement for every number - _or_ used to decide on * number placements for a new puzzle, in which case each * number's placements are confined to the intended position of * the rectangle containing that number.) */ rectbyplace = snewn(w * h, int); for (i = 0; i < w*h; i++) rectbyplace[i] = -1; for (i = 0; i < nrects; i++) { int j; for (j = 0; j < numbers[i].npoints; j++) { int x = numbers[i].points[j].x; int y = numbers[i].points[j].y; assert(rectbyplace[y * w + x] == -1); rectbyplace[y * w + x] = i; } } workspace = snewn(nrects, int); /* * Now run the actual deduction loop. */ while (1) { int done_something = FALSE; #ifdef SOLVER_DIAGNOSTICS printf("starting deduction loop\n"); for (i = 0; i < nrects; i++) { printf("rect %d overlaps:\n", i); { int x, y; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { printf("%3d", overlaps[(i * h + y) * w + x]); } printf("\n"); } } } printf("rectbyplace:\n"); { int x, y; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { printf("%3d", rectbyplace[y * w + x]); } printf("\n"); } } #endif /* * Housekeeping. Look for rectangles whose number has only * one candidate position left, and mark that square as * known if it isn't already. */ for (i = 0; i < nrects; i++) { if (numbers[i].npoints == 1) { int x = numbers[i].points[0].x; int y = numbers[i].points[0].y; if (overlaps[(i * h + y) * w + x] >= -1) { int j; if (overlaps[(i * h + y) * w + x] <= 0) { ret = 0; /* inconsistency */ goto cleanup; } #ifdef SOLVER_DIAGNOSTICS printf("marking %d,%d as known for rect %d" " (sole remaining number position)\n", x, y, i); #endif for (j = 0; j < nrects; j++) overlaps[(j * h + y) * w + x] = -1; overlaps[(i * h + y) * w + x] = -2; } } } /* * Now look at the intersection of all possible placements * for each rectangle, and mark all squares in that * intersection as known for that rectangle if they aren't * already. */ for (i = 0; i < nrects; i++) { int minx, miny, maxx, maxy, xx, yy, j; minx = miny = 0; maxx = w; maxy = h; for (j = 0; j < rectpositions[i].n; j++) { int x = rectpositions[i].rects[j].x; int y = rectpositions[i].rects[j].y; int w = rectpositions[i].rects[j].w; int h = rectpositions[i].rects[j].h; if (minx < x) minx = x; if (miny < y) miny = y; if (maxx > x+w) maxx = x+w; if (maxy > y+h) maxy = y+h; } for (yy = miny; yy < maxy; yy++) for (xx = minx; xx < maxx; xx++) if (overlaps[(i * h + yy) * w + xx] >= -1) { if (overlaps[(i * h + yy) * w + xx] <= 0) { ret = 0; /* inconsistency */ goto cleanup; } #ifdef SOLVER_DIAGNOSTICS printf("marking %d,%d as known for rect %d" " (intersection of all placements)\n", xx, yy, i); #endif for (j = 0; j < nrects; j++) overlaps[(j * h + yy) * w + xx] = -1; overlaps[(i * h + yy) * w + xx] = -2; } } /* * Rectangle-focused deduction. Look at each rectangle in * turn and try to rule out some of its candidate * placements. */ for (i = 0; i < nrects; i++) { int j; for (j = 0; j < rectpositions[i].n; j++) { int xx, yy, k; int del = FALSE; for (k = 0; k < nrects; k++) workspace[k] = 0; for (yy = 0; yy < rectpositions[i].rects[j].h; yy++) { int y = yy + rectpositions[i].rects[j].y; for (xx = 0; xx < rectpositions[i].rects[j].w; xx++) { int x = xx + rectpositions[i].rects[j].x; if (overlaps[(i * h + y) * w + x] == -1) { /* * This placement overlaps a square * which is _known_ to be part of * another rectangle. Therefore we must * rule it out. */ #ifdef SOLVER_DIAGNOSTICS printf("rect %d placement at %d,%d w=%d h=%d " "contains %d,%d which is known-other\n", i, rectpositions[i].rects[j].x, rectpositions[i].rects[j].y, rectpositions[i].rects[j].w, rectpositions[i].rects[j].h, x, y); #endif del = TRUE; } if (rectbyplace[y * w + x] != -1) { /* * This placement overlaps one of the * candidate number placements for some * rectangle. Count it. */ workspace[rectbyplace[y * w + x]]++; } } } if (!del) { /* * If we haven't ruled this placement out * already, see if it overlaps _all_ of the * candidate number placements for any * rectangle. If so, we can rule it out. */ for (k = 0; k < nrects; k++) if (k != i && workspace[k] == numbers[k].npoints) { #ifdef SOLVER_DIAGNOSTICS printf("rect %d placement at %d,%d w=%d h=%d " "contains all number points for rect %d\n", i, rectpositions[i].rects[j].x, rectpositions[i].rects[j].y, rectpositions[i].rects[j].w, rectpositions[i].rects[j].h, k); #endif del = TRUE; break; } /* * Failing that, see if it overlaps at least * one of the candidate number placements for * itself! (This might not be the case if one * of those number placements has been removed * recently.). */ if (!del && workspace[i] == 0) { #ifdef SOLVER_DIAGNOSTICS printf("rect %d placement at %d,%d w=%d h=%d " "contains none of its own number points\n", i, rectpositions[i].rects[j].x, rectpositions[i].rects[j].y, rectpositions[i].rects[j].w, rectpositions[i].rects[j].h); #endif del = TRUE; } } if (del) { remove_rect_placement(w, h, rectpositions, overlaps, i, j); j--; /* don't skip over next placement */ done_something = TRUE; } } } /* * Square-focused deduction. Look at each square not marked * as known, and see if there are any which can only be * part of a single rectangle. */ { int x, y, n, index; for (y = 0; y < h; y++) for (x = 0; x < w; x++) { /* Known squares are marked as <0 everywhere, so we only need * to check the overlaps entry for rect 0. */ if (overlaps[y * w + x] < 0) continue; /* known already */ n = 0; index = -1; for (i = 0; i < nrects; i++) if (overlaps[(i * h + y) * w + x] > 0) n++, index = i; if (n == 1) { int j; /* * Now we can rule out all placements for * rectangle `index' which _don't_ contain * square x,y. */ #ifdef SOLVER_DIAGNOSTICS printf("square %d,%d can only be in rectangle %d\n", x, y, index); #endif for (j = 0; j < rectpositions[index].n; j++) { struct rect *r = &rectpositions[index].rects[j]; if (x >= r->x && x < r->x + r->w && y >= r->y && y < r->y + r->h) continue; /* this one is OK */ remove_rect_placement(w, h, rectpositions, overlaps, index, j); j--; /* don't skip over next placement */ done_something = TRUE; } } } } /* * If we've managed to deduce anything by normal means, * loop round again and see if there's more to be done. * Only if normal deduction has completely failed us should * we now move on to narrowing down the possible number * placements. */ if (done_something) continue; /* * Now we have done everything we can with the current set * of number placements. So we need to winnow the number * placements so as to narrow down the possibilities. We do * this by searching for a candidate placement (of _any_ * rectangle) which overlaps a candidate placement of the * number for some other rectangle. */ if (rs) { struct rpn { int rect; int placement; int number; } *rpns = NULL; size_t nrpns = 0, rpnsize = 0; int j; for (i = 0; i < nrects; i++) { for (j = 0; j < rectpositions[i].n; j++) { int xx, yy; for (yy = 0; yy < rectpositions[i].rects[j].h; yy++) { int y = yy + rectpositions[i].rects[j].y; for (xx = 0; xx < rectpositions[i].rects[j].w; xx++) { int x = xx + rectpositions[i].rects[j].x; if (rectbyplace[y * w + x] >= 0 && rectbyplace[y * w + x] != i) { /* * Add this to the list of * winnowing possibilities. */ if (nrpns >= rpnsize) { rpnsize = rpnsize * 3 / 2 + 32; rpns = sresize(rpns, rpnsize, struct rpn); } rpns[nrpns].rect = i; rpns[nrpns].placement = j; rpns[nrpns].number = rectbyplace[y * w + x]; nrpns++; } } } } } #ifdef SOLVER_DIAGNOSTICS printf("%d candidate rect placements we could eliminate\n", nrpns); #endif if (nrpns > 0) { /* * Now choose one of these unwanted rectangle * placements, and eliminate it. */ int index = random_upto(rs, nrpns); int k, m; struct rpn rpn = rpns[index]; struct rect r; sfree(rpns); i = rpn.rect; j = rpn.placement; k = rpn.number; r = rectpositions[i].rects[j]; /* * We rule out placement j of rectangle i by means * of removing all of rectangle k's candidate * number placements which do _not_ overlap it. * This will ensure that it is eliminated during * the next pass of rectangle-focused deduction. */ #ifdef SOLVER_DIAGNOSTICS printf("ensuring number for rect %d is within" " rect %d's placement at %d,%d w=%d h=%d\n", k, i, r.x, r.y, r.w, r.h); #endif for (m = 0; m < numbers[k].npoints; m++) { int x = numbers[k].points[m].x; int y = numbers[k].points[m].y; if (x < r.x || x >= r.x + r.w || y < r.y || y >= r.y + r.h) { #ifdef SOLVER_DIAGNOSTICS printf("eliminating number for rect %d at %d,%d\n", k, x, y); #endif remove_number_placement(w, h, &numbers[k], m, rectbyplace); m--; /* don't skip the next one */ done_something = TRUE; } } } } if (!done_something) { #ifdef SOLVER_DIAGNOSTICS printf("terminating deduction loop\n"); #endif break; } } cleanup: ret = 1; for (i = 0; i < nrects; i++) { #ifdef SOLVER_DIAGNOSTICS printf("rect %d has %d possible placements\n", i, rectpositions[i].n); #endif if (rectpositions[i].n <= 0) { ret = 0; /* inconsistency */ } else if (rectpositions[i].n > 1) { ret = 2; /* remaining uncertainty */ } else if (hedge && vedge) { /* * Place the rectangle in its only possible position. */ int x, y; struct rect *r = &rectpositions[i].rects[0]; for (y = 0; y < r->h; y++) { if (r->x > 0) vedge[(r->y+y) * w + r->x] = 1; if (r->x+r->w < w) vedge[(r->y+y) * w + r->x+r->w] = 1; } for (x = 0; x < r->w; x++) { if (r->y > 0) hedge[r->y * w + r->x+x] = 1; if (r->y+r->h < h) hedge[(r->y+r->h) * w + r->x+x] = 1; } } } /* * Free up all allocated storage. */ sfree(workspace); sfree(rectbyplace); sfree(overlaps); for (i = 0; i < nrects; i++) sfree(rectpositions[i].rects); sfree(rectpositions); return ret; } /* ---------------------------------------------------------------------- * Grid generation code. */ /* * This function does one of two things. If passed r==NULL, it * counts the number of possible rectangles which cover the given * square, and returns it in *n. If passed r!=NULL then it _reads_ * *n to find an index, counts the possible rectangles until it * reaches the nth, and writes it into r. * * `scratch' is expected to point to an array of 2 * params->w * ints, used internally as scratch space (and passed in like this * to avoid re-allocating and re-freeing it every time round a * tight loop). */ static void enum_rects(game_params *params, int *grid, struct rect *r, int *n, int sx, int sy, int *scratch) { int rw, rh, mw, mh; int x, y, dx, dy; int maxarea, realmaxarea; int index = 0; int *top, *bottom; /* * Maximum rectangle area is 1/6 of total grid size, unless * this means we can't place any rectangles at all in which * case we set it to 2 at minimum. */ maxarea = params->w * params->h / 6; if (maxarea < 2) maxarea = 2; /* * Scan the grid to find the limits of the region within which * any rectangle containing this point must fall. This will * save us trawling the inside of every rectangle later on to * see if it contains any used squares. */ top = scratch; bottom = scratch + params->w; for (dy = -1; dy <= +1; dy += 2) { int *array = (dy == -1 ? top : bottom); for (dx = -1; dx <= +1; dx += 2) { for (x = sx; x >= 0 && x < params->w; x += dx) { array[x] = -2 * params->h * dy; for (y = sy; y >= 0 && y < params->h; y += dy) { if (index(params, grid, x, y) == -1 && (x == sx || dy*y <= dy*array[x-dx])) array[x] = y; else break; } } } } /* * Now scan again to work out the largest rectangles we can fit * in the grid, so that we can terminate the following loops * early once we get down to not having much space left in the * grid. */ realmaxarea = 0; for (x = 0; x < params->w; x++) { int x2; rh = bottom[x] - top[x] + 1; if (rh <= 0) continue; /* no rectangles can start here */ dx = (x > sx ? -1 : +1); for (x2 = x; x2 >= 0 && x2 < params->w; x2 += dx) if (bottom[x2] < bottom[x] || top[x2] > top[x]) break; rw = abs(x2 - x); if (realmaxarea < rw * rh) realmaxarea = rw * rh; } if (realmaxarea > maxarea) realmaxarea = maxarea; /* * Rectangles which go right the way across the grid are * boring, although they can't be helped in the case of * extremely small grids. (Also they might be generated later * on by the singleton-removal process; we can't help that.) */ mw = params->w - 1; if (mw < 3) mw++; mh = params->h - 1; if (mh < 3) mh++; for (rw = 1; rw <= mw; rw++) for (rh = 1; rh <= mh; rh++) { if (rw * rh > realmaxarea) continue; if (rw * rh == 1) continue; for (x = max(sx - rw + 1, 0); x <= min(sx, params->w - rw); x++) for (y = max(sy - rh + 1, 0); y <= min(sy, params->h - rh); y++) { /* * Check this rectangle against the region we * defined above. */ if (top[x] <= y && top[x+rw-1] <= y && bottom[x] >= y+rh-1 && bottom[x+rw-1] >= y+rh-1) { if (r && index == *n) { r->x = x; r->y = y; r->w = rw; r->h = rh; return; } index++; } } } assert(!r); *n = index; } static void place_rect(game_params *params, int *grid, struct rect r) { int idx = INDEX(params, r.x, r.y); int x, y; for (x = r.x; x < r.x+r.w; x++) for (y = r.y; y < r.y+r.h; y++) { index(params, grid, x, y) = idx; } #ifdef GENERATION_DIAGNOSTICS printf(" placing rectangle at (%d,%d) size %d x %d\n", r.x, r.y, r.w, r.h); #endif } static struct rect find_rect(game_params *params, int *grid, int x, int y) { int idx, w, h; struct rect r; /* * Find the top left of the rectangle. */ idx = index(params, grid, x, y); if (idx < 0) { r.x = x; r.y = y; r.w = r.h = 1; return r; /* 1x1 singleton here */ } y = idx / params->w; x = idx % params->w; /* * Find the width and height of the rectangle. */ for (w = 1; (x+w < params->w && index(params,grid,x+w,y)==idx); w++); for (h = 1; (y+h < params->h && index(params,grid,x,y+h)==idx); h++); r.x = x; r.y = y; r.w = w; r.h = h; return r; } #ifdef GENERATION_DIAGNOSTICS static void display_grid(game_params *params, int *grid, int *numbers, int all) { unsigned char *egrid = snewn((params->w*2+3) * (params->h*2+3), unsigned char); int x, y; int r = (params->w*2+3); memset(egrid, 0, (params->w*2+3) * (params->h*2+3)); for (x = 0; x < params->w; x++) for (y = 0; y < params->h; y++) { int i = index(params, grid, x, y); if (x == 0 || index(params, grid, x-1, y) != i) egrid[(2*y+2) * r + (2*x+1)] = 1; if (x == params->w-1 || index(params, grid, x+1, y) != i) egrid[(2*y+2) * r + (2*x+3)] = 1; if (y == 0 || index(params, grid, x, y-1) != i) egrid[(2*y+1) * r + (2*x+2)] = 1; if (y == params->h-1 || index(params, grid, x, y+1) != i) egrid[(2*y+3) * r + (2*x+2)] = 1; } for (y = 1; y < 2*params->h+2; y++) { for (x = 1; x < 2*params->w+2; x++) { if (!((y|x)&1)) { int k = numbers ? index(params, numbers, x/2-1, y/2-1) : 0; if (k || (all && numbers)) printf("%2d", k); else printf(" "); } else if (!((y&x)&1)) { int v = egrid[y*r+x]; if ((y&1) && v) v = '-'; if ((x&1) && v) v = '|'; if (!v) v = ' '; putchar(v); if (!(x&1)) putchar(v); } else { int c, d = 0; if (egrid[y*r+(x+1)]) d |= 1; if (egrid[(y-1)*r+x]) d |= 2; if (egrid[y*r+(x-1)]) d |= 4; if (egrid[(y+1)*r+x]) d |= 8; c = " ??+?-++?+|+++++"[d]; putchar(c); if (!(x&1)) putchar(c); } } putchar('\n'); } sfree(egrid); } #endif static char *new_game_desc(const game_params *params_in, random_state *rs, char **aux, int interactive) { game_params params_copy = *params_in; /* structure copy */ game_params *params = ¶ms_copy; int *grid, *numbers = NULL; int x, y, y2, y2last, yx, run, i, nsquares; char *desc, *p; int *enum_rects_scratch; game_params params2real, *params2 = ¶ms2real; while (1) { /* * Set up the smaller width and height which we will use to * generate the base grid. */ params2->w = (int)((float)params->w / (1.0F + params->expandfactor)); if (params2->w < 2 && params->w >= 2) params2->w = 2; params2->h = (int)((float)params->h / (1.0F + params->expandfactor)); if (params2->h < 2 && params->h >= 2) params2->h = 2; grid = snewn(params2->w * params2->h, int); enum_rects_scratch = snewn(2 * params2->w, int); nsquares = 0; for (y = 0; y < params2->h; y++) for (x = 0; x < params2->w; x++) { index(params2, grid, x, y) = -1; nsquares++; } /* * Place rectangles until we can't any more. We do this by * finding a square we haven't yet covered, and randomly * choosing a rectangle to cover it. */ while (nsquares > 0) { int square = random_upto(rs, nsquares); int n; struct rect r; x = params2->w; y = params2->h; for (y = 0; y < params2->h; y++) { for (x = 0; x < params2->w; x++) { if (index(params2, grid, x, y) == -1 && square-- == 0) break; } if (x < params2->w) break; } assert(x < params2->w && y < params2->h); /* * Now see how many rectangles fit around this one. */ enum_rects(params2, grid, NULL, &n, x, y, enum_rects_scratch); if (!n) { /* * There are no possible rectangles covering this * square, meaning it must be a singleton. Mark it * -2 so we know not to keep trying. */ index(params2, grid, x, y) = -2; nsquares--; } else { /* * Pick one at random. */ n = random_upto(rs, n); enum_rects(params2, grid, &r, &n, x, y, enum_rects_scratch); /* * Place it. */ place_rect(params2, grid, r); nsquares -= r.w * r.h; } } sfree(enum_rects_scratch); /* * Deal with singleton spaces remaining in the grid, one by * one. * * We do this by making a local change to the layout. There are * several possibilities: * * +-----+-----+ Here, we can remove the singleton by * | | | extending the 1x2 rectangle below it * +--+--+-----+ into a 1x3. * | | | | * | +--+ | * | | | | * | | | | * | | | | * +--+--+-----+ * * +--+--+--+ Here, that trick doesn't work: there's no * | | | 1 x n rectangle with the singleton at one * | | | end. Instead, we extend a 1 x n rectangle * | | | _out_ from the singleton, shaving a layer * +--+--+ | off the end of another rectangle. So if we * | | | | extended up, we'd make our singleton part * | +--+--+ of a 1x3 and generate a 1x2 where the 2x2 * | | | used to be; or we could extend right into * +--+-----+ a 2x1, turning the 1x3 into a 1x2. * * +-----+--+ Here, we can't even do _that_, since any * | | | direction we choose to extend the singleton * +--+--+ | will produce a new singleton as a result of * | | | | truncating one of the size-2 rectangles. * | +--+--+ Fortunately, this case can _only_ occur when * | | | a singleton is surrounded by four size-2s * +--+-----+ in this fashion; so instead we can simply * replace the whole section with a single 3x3. */ for (x = 0; x < params2->w; x++) { for (y = 0; y < params2->h; y++) { if (index(params2, grid, x, y) < 0) { int dirs[4], ndirs; #ifdef GENERATION_DIAGNOSTICS display_grid(params2, grid, NULL, FALSE); printf("singleton at %d,%d\n", x, y); #endif /* * Check in which directions we can feasibly extend * the singleton. We can extend in a particular * direction iff either: * * - the rectangle on that side of the singleton * is not 2x1, and we are at one end of the edge * of it we are touching * * - it is 2x1 but we are on its short side. * * FIXME: we could plausibly choose between these * based on the sizes of the rectangles they would * create? */ ndirs = 0; if (x < params2->w-1) { struct rect r = find_rect(params2, grid, x+1, y); if ((r.w * r.h > 2 && (r.y==y || r.y+r.h-1==y)) || r.h==1) dirs[ndirs++] = 1; /* right */ } if (y > 0) { struct rect r = find_rect(params2, grid, x, y-1); if ((r.w * r.h > 2 && (r.x==x || r.x+r.w-1==x)) || r.w==1) dirs[ndirs++] = 2; /* up */ } if (x > 0) { struct rect r = find_rect(params2, grid, x-1, y); if ((r.w * r.h > 2 && (r.y==y || r.y+r.h-1==y)) || r.h==1) dirs[ndirs++] = 4; /* left */ } if (y < params2->h-1) { struct rect r = find_rect(params2, grid, x, y+1); if ((r.w * r.h > 2 && (r.x==x || r.x+r.w-1==x)) || r.w==1) dirs[ndirs++] = 8; /* down */ } if (ndirs > 0) { int which, dir; struct rect r1, r2; which = random_upto(rs, ndirs); dir = dirs[which]; switch (dir) { case 1: /* right */ assert(x < params2->w+1); #ifdef GENERATION_DIAGNOSTICS printf("extending right\n"); #endif r1 = find_rect(params2, grid, x+1, y); r2.x = x; r2.y = y; r2.w = 1 + r1.w; r2.h = 1; if (r1.y == y) r1.y++; r1.h--; break; case 2: /* up */ assert(y > 0); #ifdef GENERATION_DIAGNOSTICS printf("extending up\n"); #endif r1 = find_rect(params2, grid, x, y-1); r2.x = x; r2.y = r1.y; r2.w = 1; r2.h = 1 + r1.h; if (r1.x == x) r1.x++; r1.w--; break; case 4: /* left */ assert(x > 0); #ifdef GENERATION_DIAGNOSTICS printf("extending left\n"); #endif r1 = find_rect(params2, grid, x-1, y); r2.x = r1.x; r2.y = y; r2.w = 1 + r1.w; r2.h = 1; if (r1.y == y) r1.y++; r1.h--; break; case 8: /* down */ assert(y < params2->h+1); #ifdef GENERATION_DIAGNOSTICS printf("extending down\n"); #endif r1 = find_rect(params2, grid, x, y+1); r2.x = x; r2.y = y; r2.w = 1; r2.h = 1 + r1.h; if (r1.x == x) r1.x++; r1.w--; break; default: /* should never happen */ assert(!"invalid direction"); } if (r1.h > 0 && r1.w > 0) place_rect(params2, grid, r1); place_rect(params2, grid, r2); } else { #ifndef NDEBUG /* * Sanity-check that there really is a 3x3 * rectangle surrounding this singleton and it * contains absolutely everything we could * possibly need. */ { int xx, yy; assert(x > 0 && x < params2->w-1); assert(y > 0 && y < params2->h-1); for (xx = x-1; xx <= x+1; xx++) for (yy = y-1; yy <= y+1; yy++) { struct rect r = find_rect(params2,grid,xx,yy); assert(r.x >= x-1); assert(r.y >= y-1); assert(r.x+r.w-1 <= x+1); assert(r.y+r.h-1 <= y+1); } } #endif #ifdef GENERATION_DIAGNOSTICS printf("need the 3x3 trick\n"); #endif /* * FIXME: If the maximum rectangle area for * this grid is less than 9, we ought to * subdivide the 3x3 in some fashion. There are * five other possibilities: * * - a 6 and a 3 * - a 4, a 3 and a 2 * - three 3s * - a 3 and three 2s (two different arrangements). */ { struct rect r; r.x = x-1; r.y = y-1; r.w = r.h = 3; place_rect(params2, grid, r); } } } } } /* * We have now constructed a grid of the size specified in * params2. Now we extend it into a grid of the size specified * in params. We do this in two passes: we extend it vertically * until it's the right height, then we transpose it, then * extend it vertically again (getting it effectively the right * width), then finally transpose again. */ for (i = 0; i < 2; i++) { int *grid2, *expand, *where; game_params params3real, *params3 = ¶ms3real; #ifdef GENERATION_DIAGNOSTICS printf("before expansion:\n"); display_grid(params2, grid, NULL, TRUE); #endif /* * Set up the new grid. */ grid2 = snewn(params2->w * params->h, int); expand = snewn(params2->h-1, int); where = snewn(params2->w, int); params3->w = params2->w; params3->h = params->h; /* * Decide which horizontal edges are going to get expanded, * and by how much. */ for (y = 0; y < params2->h-1; y++) expand[y] = 0; for (y = params2->h; y < params->h; y++) { x = random_upto(rs, params2->h-1); expand[x]++; } #ifdef GENERATION_DIAGNOSTICS printf("expand[] = {"); for (y = 0; y < params2->h-1; y++) printf(" %d", expand[y]); printf(" }\n"); #endif /* * Perform the expansion. The way this works is that we * alternately: * * - copy a row from grid into grid2 * * - invent some number of additional rows in grid2 where * there was previously only a horizontal line between * rows in grid, and make random decisions about where * among these to place each rectangle edge that ran * along this line. */ for (y = y2 = y2last = 0; y < params2->h; y++) { /* * Copy a single line from row y of grid into row y2 of * grid2. */ for (x = 0; x < params2->w; x++) { int val = index(params2, grid, x, y); if (val / params2->w == y && /* rect starts on this line */ (y2 == 0 || /* we're at the very top, or... */ index(params3, grid2, x, y2-1) / params3->w < y2last /* this rect isn't already started */)) index(params3, grid2, x, y2) = INDEX(params3, val % params2->w, y2); else index(params3, grid2, x, y2) = index(params3, grid2, x, y2-1); } /* * If that was the last line, terminate the loop early. */ if (++y2 == params3->h) break; y2last = y2; /* * Invent some number of additional lines. First walk * along this line working out where to put all the * edges that coincide with it. */ yx = -1; for (x = 0; x < params2->w; x++) { if (index(params2, grid, x, y) != index(params2, grid, x, y+1)) { /* * This is a horizontal edge, so it needs * placing. */ if (x == 0 || (index(params2, grid, x-1, y) != index(params2, grid, x, y) && index(params2, grid, x-1, y+1) != index(params2, grid, x, y+1))) { /* * Here we have the chance to make a new * decision. */ yx = random_upto(rs, expand[y]+1); } else { /* * Here we just reuse the previous value of * yx. */ } } else yx = -1; where[x] = yx; } for (yx = 0; yx < expand[y]; yx++) { /* * Invent a single row. For each square in the row, * we copy the grid entry from the square above it, * unless we're starting the new rectangle here. */ for (x = 0; x < params2->w; x++) { if (yx == where[x]) { int val = index(params2, grid, x, y+1); val %= params2->w; val = INDEX(params3, val, y2); index(params3, grid2, x, y2) = val; } else index(params3, grid2, x, y2) = index(params3, grid2, x, y2-1); } y2++; } } sfree(expand); sfree(where); #ifdef GENERATION_DIAGNOSTICS printf("after expansion:\n"); display_grid(params3, grid2, NULL, TRUE); #endif /* * Transpose. */ params2->w = params3->h; params2->h = params3->w; sfree(grid); grid = snewn(params2->w * params2->h, int); for (x = 0; x < params2->w; x++) for (y = 0; y < params2->h; y++) { int idx1 = INDEX(params2, x, y); int idx2 = INDEX(params3, y, x); int tmp; tmp = grid2[idx2]; tmp = (tmp % params3->w) * params2->w + (tmp / params3->w); grid[idx1] = tmp; } sfree(grid2); { int tmp; tmp = params->w; params->w = params->h; params->h = tmp; } #ifdef GENERATION_DIAGNOSTICS printf("after transposition:\n"); display_grid(params2, grid, NULL, TRUE); #endif } /* * Run the solver to narrow down the possible number * placements. */ { struct numberdata *nd; int nnumbers, i, ret; /* Count the rectangles. */ nnumbers = 0; for (y = 0; y < params->h; y++) { for (x = 0; x < params->w; x++) { int idx = INDEX(params, x, y); if (index(params, grid, x, y) == idx) nnumbers++; } } nd = snewn(nnumbers, struct numberdata); /* Now set up each number's candidate position list. */ i = 0; for (y = 0; y < params->h; y++) { for (x = 0; x < params->w; x++) { int idx = INDEX(params, x, y); if (index(params, grid, x, y) == idx) { struct rect r = find_rect(params, grid, x, y); int j, k, m; nd[i].area = r.w * r.h; nd[i].npoints = nd[i].area; nd[i].points = snewn(nd[i].npoints, struct point); m = 0; for (j = 0; j < r.h; j++) for (k = 0; k < r.w; k++) { nd[i].points[m].x = k + r.x; nd[i].points[m].y = j + r.y; m++; } assert(m == nd[i].npoints); i++; } } } if (params->unique) ret = rect_solver(params->w, params->h, nnumbers, nd, NULL, NULL, rs); else ret = 1; /* allow any number placement at all */ if (ret == 1) { /* * Now place the numbers according to the solver's * recommendations. */ numbers = snewn(params->w * params->h, int); for (y = 0; y < params->h; y++) for (x = 0; x < params->w; x++) { index(params, numbers, x, y) = 0; } for (i = 0; i < nnumbers; i++) { int idx = random_upto(rs, nd[i].npoints); int x = nd[i].points[idx].x; int y = nd[i].points[idx].y; index(params,numbers,x,y) = nd[i].area; } } /* * Clean up. */ for (i = 0; i < nnumbers; i++) sfree(nd[i].points); sfree(nd); /* * If we've succeeded, then terminate the loop. */ if (ret == 1) break; } /* * Give up and go round again. */ sfree(grid); } /* * Store the solution in aux. */ { char *ai; int len; len = 2 + (params->w-1)*params->h + (params->h-1)*params->w; ai = snewn(len, char); ai[0] = 'S'; p = ai+1; for (y = 0; y < params->h; y++) for (x = 1; x < params->w; x++) *p++ = (index(params, grid, x, y) != index(params, grid, x-1, y) ? '1' : '0'); for (y = 1; y < params->h; y++) for (x = 0; x < params->w; x++) *p++ = (index(params, grid, x, y) != index(params, grid, x, y-1) ? '1' : '0'); assert(p - ai == len-1); *p = '\0'; *aux = ai; } #ifdef GENERATION_DIAGNOSTICS display_grid(params, grid, numbers, FALSE); #endif desc = snewn(11 * params->w * params->h, char); p = desc; run = 0; for (i = 0; i <= params->w * params->h; i++) { int n = (i < params->w * params->h ? numbers[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; } } *p = '\0'; sfree(grid); sfree(numbers); return desc; } static char *validate_desc(const game_params *params, const char *desc) { int area = params->w * params->h; int squares = 0; while (*desc) { int n = *desc++; if (n >= 'a' && n <= 'z') { squares += n - 'a' + 1; } else if (n == '_') { /* do nothing */; } else if (n > '0' && n <= '9') { 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"; return NULL; } static unsigned char *get_correct(game_state *state) { unsigned char *ret; int x, y; ret = snewn(state->w * state->h, unsigned char); memset(ret, 0xFF, state->w * state->h); for (x = 0; x < state->w; x++) for (y = 0; y < state->h; y++) if (index(state,ret,x,y) == 0xFF) { int rw, rh; int xx, yy; int num, area, valid; /* * Find a rectangle starting at this point. */ rw = 1; while (x+rw < state->w && !vedge(state,x+rw,y)) rw++; rh = 1; while (y+rh < state->h && !hedge(state,x,y+rh)) rh++; /* * We know what the dimensions of the rectangle * should be if it's there at all. Find out if we * really have a valid rectangle. */ valid = TRUE; /* Check the horizontal edges. */ for (xx = x; xx < x+rw; xx++) { for (yy = y; yy <= y+rh; yy++) { int e = !HRANGE(state,xx,yy) || hedge(state,xx,yy); int ec = (yy == y || yy == y+rh); if (e != ec) valid = FALSE; } } /* Check the vertical edges. */ for (yy = y; yy < y+rh; yy++) { for (xx = x; xx <= x+rw; xx++) { int e = !VRANGE(state,xx,yy) || vedge(state,xx,yy); int ec = (xx == x || xx == x+rw); if (e != ec) valid = FALSE; } } /* * If this is not a valid rectangle with no other * edges inside it, we just mark this square as not * complete and proceed to the next square. */ if (!valid) { index(state, ret, x, y) = 0; continue; } /* * We have a rectangle. Now see what its area is, * and how many numbers are in it. */ num = 0; area = 0; for (xx = x; xx < x+rw; xx++) { for (yy = y; yy < y+rh; yy++) { area++; if (grid(state,xx,yy)) { if (num > 0) valid = FALSE; /* two numbers */ num = grid(state,xx,yy); } } } if (num != area) valid = FALSE; /* * Now fill in the whole rectangle based on the * value of `valid'. */ for (xx = x; xx < x+rw; xx++) { for (yy = y; yy < y+rh; yy++) { index(state, ret, xx, yy) = valid; } } } return ret; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { game_state *state = snew(game_state); int x, y, i, area; state->w = params->w; state->h = params->h; area = state->w * state->h; state->grid = snewn(area, int); state->vedge = snewn(area, unsigned char); state->hedge = snewn(area, unsigned char); state->completed = state->cheated = FALSE; i = 0; while (*desc) { int n = *desc++; if (n >= 'a' && n <= 'z') { int run = n - 'a' + 1; assert(i + run <= area); while (run-- > 0) state->grid[i++] = 0; } else if (n == '_') { /* do nothing */; } else if (n > '0' && n <= '9') { assert(i < area); state->grid[i++] = atoi(desc-1); while (*desc >= '0' && *desc <= '9') desc++; } else { assert(!"We can't get here"); } } assert(i == area); for (y = 0; y < state->h; y++) for (x = 0; x < state->w; x++) vedge(state,x,y) = hedge(state,x,y) = 0; state->correct = get_correct(state); return state; } static game_state *dup_game(const game_state *state) { game_state *ret = snew(game_state); ret->w = state->w; ret->h = state->h; ret->vedge = snewn(state->w * state->h, unsigned char); ret->hedge = snewn(state->w * state->h, unsigned char); ret->grid = snewn(state->w * state->h, int); ret->correct = snewn(ret->w * ret->h, unsigned char); ret->completed = state->completed; ret->cheated = state->cheated; memcpy(ret->grid, state->grid, state->w * state->h * sizeof(int)); memcpy(ret->vedge, state->vedge, state->w*state->h*sizeof(unsigned char)); memcpy(ret->hedge, state->hedge, state->w*state->h*sizeof(unsigned char)); memcpy(ret->correct, state->correct, state->w*state->h*sizeof(unsigned char)); return ret; } static void free_game(game_state *state) { sfree(state->grid); sfree(state->vedge); sfree(state->hedge); sfree(state->correct); sfree(state); } static char *solve_game(const game_state *state, const game_state *currstate, const char *ai, char **error) { unsigned char *vedge, *hedge; int x, y, len; char *ret, *p; int i, j, n; struct numberdata *nd; if (ai) return dupstr(ai); /* * Attempt the in-built solver. */ /* Set up each number's (very short) candidate position list. */ for (i = n = 0; i < state->h * state->w; i++) if (state->grid[i]) n++; nd = snewn(n, struct numberdata); for (i = j = 0; i < state->h * state->w; i++) if (state->grid[i]) { nd[j].area = state->grid[i]; nd[j].npoints = 1; nd[j].points = snewn(1, struct point); nd[j].points[0].x = i % state->w; nd[j].points[0].y = i / state->w; j++; } assert(j == n); vedge = snewn(state->w * state->h, unsigned char); hedge = snewn(state->w * state->h, unsigned char); memset(vedge, 0, state->w * state->h); memset(hedge, 0, state->w * state->h); rect_solver(state->w, state->h, n, nd, hedge, vedge, NULL); /* * Clean up. */ for (i = 0; i < n; i++) sfree(nd[i].points); sfree(nd); len = 2 + (state->w-1)*state->h + (state->h-1)*state->w; ret = snewn(len, char); p = ret; *p++ = 'S'; for (y = 0; y < state->h; y++) for (x = 1; x < state->w; x++) *p++ = vedge[y*state->w+x] ? '1' : '0'; for (y = 1; y < state->h; y++) for (x = 0; x < state->w; x++) *p++ = hedge[y*state->w+x] ? '1' : '0'; *p++ = '\0'; assert(p - ret == len); sfree(vedge); sfree(hedge); return ret; } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { char *ret, *p, buf[80]; int i, x, y, col, maxlen; /* * First determine the number of spaces required to display a * number. We'll use at least two, because one looks a bit * silly. */ col = 2; for (i = 0; i < state->w * state->h; i++) { x = sprintf(buf, "%d", state->grid[i]); if (col < x) col = x; } /* * Now we know the exact total size of the grid we're going to * produce: it's got 2*h+1 rows, each containing w lots of col, * w+1 boundary characters and a trailing newline. */ maxlen = (2*state->h+1) * (state->w * (col+1) + 2); ret = snewn(maxlen+1, char); p = ret; for (y = 0; y <= 2*state->h; y++) { for (x = 0; x <= 2*state->w; x++) { if (x & y & 1) { /* * Display a number. */ int v = grid(state, x/2, y/2); if (v) sprintf(buf, "%*d", col, v); else sprintf(buf, "%*s", col, ""); memcpy(p, buf, col); p += col; } else if (x & 1) { /* * Display a horizontal edge or nothing. */ int h = (y==0 || y==2*state->h ? 1 : HRANGE(state, x/2, y/2) && hedge(state, x/2, y/2)); int i; if (h) h = '-'; else h = ' '; for (i = 0; i < col; i++) *p++ = h; } else if (y & 1) { /* * Display a vertical edge or nothing. */ int v = (x==0 || x==2*state->w ? 1 : VRANGE(state, x/2, y/2) && vedge(state, x/2, y/2)); if (v) *p++ = '|'; else *p++ = ' '; } else { /* * Display a corner, or a vertical edge, or a * horizontal edge, or nothing. */ int hl = (y==0 || y==2*state->h ? 1 : HRANGE(state, (x-1)/2, y/2) && hedge(state, (x-1)/2, y/2)); int hr = (y==0 || y==2*state->h ? 1 : HRANGE(state, (x+1)/2, y/2) && hedge(state, (x+1)/2, y/2)); int vu = (x==0 || x==2*state->w ? 1 : VRANGE(state, x/2, (y-1)/2) && vedge(state, x/2, (y-1)/2)); int vd = (x==0 || x==2*state->w ? 1 : VRANGE(state, x/2, (y+1)/2) && vedge(state, x/2, (y+1)/2)); if (!hl && !hr && !vu && !vd) *p++ = ' '; else if (hl && hr && !vu && !vd) *p++ = '-'; else if (!hl && !hr && vu && vd) *p++ = '|'; else *p++ = '+'; } } *p++ = '\n'; } assert(p - ret == maxlen); *p = '\0'; return ret; } struct game_ui { /* * These coordinates are 2 times the obvious grid coordinates. * Hence, the top left of the grid is (0,0), the grid point to * the right of that is (2,0), the one _below that_ is (2,2) * and so on. This is so that we can specify a drag start point * on an edge (one odd coordinate) or in the middle of a square * (two odd coordinates) rather than always at a corner. * * -1,-1 means no drag is in progress. */ int drag_start_x; int drag_start_y; int drag_end_x; int drag_end_y; /* * This flag is set as soon as a dragging action moves the * mouse pointer away from its starting point, so that even if * the pointer _returns_ to its starting point the action is * treated as a small drag rather than a click. */ int dragged; /* This flag is set if we're doing an erase operation (i.e. * removing edges in the centre of the rectangle without altering * the outlines). */ int erasing; /* * These are the co-ordinates of the top-left and bottom-right squares * in the drag box, respectively, or -1 otherwise. */ int x1; int y1; int x2; int y2; /* * These are the coordinates of a cursor, whether it's visible, and * whether it was used to start a drag. */ int cur_x, cur_y, cur_visible, cur_dragging; }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->drag_start_x = -1; ui->drag_start_y = -1; ui->drag_end_x = -1; ui->drag_end_y = -1; ui->dragged = ui->erasing = FALSE; ui->x1 = -1; ui->y1 = -1; ui->x2 = -1; ui->y2 = -1; ui->cur_x = ui->cur_y = ui->cur_visible = ui->cur_dragging = 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 coord_round(float x, float y, int *xr, int *yr) { float xs, ys, xv, yv, dx, dy, dist; /* * Find the nearest square-centre. */ xs = (float)floor(x) + 0.5F; ys = (float)floor(y) + 0.5F; /* * And find the nearest grid vertex. */ xv = (float)floor(x + 0.5F); yv = (float)floor(y + 0.5F); /* * We allocate clicks in parts of the grid square to either * corners, edges or square centres, as follows: * * +--+--------+--+ * | | | | * +--+ +--+ * | `. ,' | * | +--+ | * | | | | * | +--+ | * | ,' `. | * +--+ +--+ * | | | | * +--+--------+--+ * * (Not to scale!) * * In other words: we measure the square distance (i.e. * max(dx,dy)) from the click to the nearest corner, and if * it's within CORNER_TOLERANCE then we return a corner click. * We measure the square distance from the click to the nearest * centre, and if that's within CENTRE_TOLERANCE we return a * centre click. Failing that, we find which of the two edge * centres is nearer to the click and return that edge. */ /* * Check for corner click. */ dx = (float)fabs(x - xv); dy = (float)fabs(y - yv); dist = (dx > dy ? dx : dy); if (dist < CORNER_TOLERANCE) { *xr = 2 * (int)xv; *yr = 2 * (int)yv; } else { /* * Check for centre click. */ dx = (float)fabs(x - xs); dy = (float)fabs(y - ys); dist = (dx > dy ? dx : dy); if (dist < CENTRE_TOLERANCE) { *xr = 1 + 2 * (int)xs; *yr = 1 + 2 * (int)ys; } else { /* * Failing both of those, see which edge we're closer to. * Conveniently, this is simply done by testing the relative * magnitude of dx and dy (which are currently distances from * the square centre). */ if (dx > dy) { /* Vertical edge: x-coord of corner, * y-coord of square centre. */ *xr = 2 * (int)xv; *yr = 1 + 2 * (int)floor(ys); } else { /* Horizontal edge: x-coord of square centre, * y-coord of corner. */ *xr = 1 + 2 * (int)floor(xs); *yr = 2 * (int)yv; } } } } /* * Returns TRUE if it has made any change to the grid. */ static int grid_draw_rect(const game_state *state, unsigned char *hedge, unsigned char *vedge, int c, int really, int outline, int x1, int y1, int x2, int y2) { int x, y; int changed = FALSE; /* * Draw horizontal edges of rectangles. */ for (x = x1; x < x2; x++) for (y = y1; y <= y2; y++) if (HRANGE(state,x,y)) { int val = index(state,hedge,x,y); if (y == y1 || y == y2) { if (!outline) continue; val = c; } else if (c == 1) val = 0; changed = changed || (index(state,hedge,x,y) != val); if (really) index(state,hedge,x,y) = val; } /* * Draw vertical edges of rectangles. */ for (y = y1; y < y2; y++) for (x = x1; x <= x2; x++) if (VRANGE(state,x,y)) { int val = index(state,vedge,x,y); if (x == x1 || x == x2) { if (!outline) continue; val = c; } else if (c == 1) val = 0; changed = changed || (index(state,vedge,x,y) != val); if (really) index(state,vedge,x,y) = val; } return changed; } static int ui_draw_rect(const game_state *state, const game_ui *ui, unsigned char *hedge, unsigned char *vedge, int c, int really, int outline) { return grid_draw_rect(state, hedge, vedge, c, really, outline, ui->x1, ui->y1, ui->x2, ui->y2); } static void game_changed_state(game_ui *ui, const game_state *oldstate, const game_state *newstate) { } struct game_drawstate { int started; int w, h, tilesize; unsigned long *visible; }; static char *interpret_move(const game_state *from, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { int xc, yc; int startdrag = FALSE, enddrag = FALSE, active = FALSE, erasing = FALSE; char buf[80], *ret; button &= ~MOD_MASK; coord_round(FROMCOORD((float)x), FROMCOORD((float)y), &xc, &yc); if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { if (ui->drag_start_x >= 0 && ui->cur_dragging) { /* * If a keyboard drag is in progress, unceremoniously * cancel it. */ ui->drag_start_x = -1; ui->drag_start_y = -1; ui->drag_end_x = -1; ui->drag_end_y = -1; ui->x1 = -1; ui->y1 = -1; ui->x2 = -1; ui->y2 = -1; ui->dragged = FALSE; } startdrag = TRUE; ui->cur_visible = ui->cur_dragging = FALSE; active = TRUE; erasing = (button == RIGHT_BUTTON); } else if (button == LEFT_RELEASE || button == RIGHT_RELEASE) { /* We assert we should have had a LEFT_BUTTON first. */ if (ui->cur_visible) { ui->cur_visible = FALSE; active = TRUE; } assert(!ui->cur_dragging); enddrag = TRUE; erasing = (button == RIGHT_RELEASE); } else if (IS_CURSOR_MOVE(button)) { move_cursor(button, &ui->cur_x, &ui->cur_y, from->w, from->h, 0); ui->cur_visible = TRUE; active = TRUE; if (!ui->cur_dragging) return ""; coord_round((float)ui->cur_x + 0.5F, (float)ui->cur_y + 0.5F, &xc, &yc); } else if (IS_CURSOR_SELECT(button)) { if (ui->drag_start_x >= 0 && !ui->cur_dragging) { /* * If a mouse drag is in progress, ignore attempts to * start a keyboard one. */ return NULL; } if (!ui->cur_visible) { assert(!ui->cur_dragging); ui->cur_visible = TRUE; return ""; } coord_round((float)ui->cur_x + 0.5F, (float)ui->cur_y + 0.5F, &xc, &yc); erasing = (button == CURSOR_SELECT2); if (ui->cur_dragging) { ui->cur_dragging = FALSE; enddrag = TRUE; active = TRUE; } else { ui->cur_dragging = TRUE; startdrag = TRUE; active = TRUE; } } else if (button != LEFT_DRAG && button != RIGHT_DRAG) { return NULL; } if (startdrag && xc >= 0 && xc <= 2*from->w && yc >= 0 && yc <= 2*from->h) { ui->drag_start_x = xc; ui->drag_start_y = yc; ui->drag_end_x = -1; ui->drag_end_y = -1; ui->dragged = FALSE; ui->erasing = erasing; active = TRUE; } if (ui->drag_start_x >= 0 && (xc != ui->drag_end_x || yc != ui->drag_end_y)) { int t; if (ui->drag_end_x != -1 && ui->drag_end_y != -1) ui->dragged = TRUE; ui->drag_end_x = xc; ui->drag_end_y = yc; active = TRUE; if (xc >= 0 && xc <= 2*from->w && yc >= 0 && yc <= 2*from->h) { ui->x1 = ui->drag_start_x; ui->x2 = ui->drag_end_x; if (ui->x2 < ui->x1) { t = ui->x1; ui->x1 = ui->x2; ui->x2 = t; } ui->y1 = ui->drag_start_y; ui->y2 = ui->drag_end_y; if (ui->y2 < ui->y1) { t = ui->y1; ui->y1 = ui->y2; ui->y2 = t; } ui->x1 = ui->x1 / 2; /* rounds down */ ui->x2 = (ui->x2+1) / 2; /* rounds up */ ui->y1 = ui->y1 / 2; /* rounds down */ ui->y2 = (ui->y2+1) / 2; /* rounds up */ } else { ui->x1 = -1; ui->y1 = -1; ui->x2 = -1; ui->y2 = -1; } } ret = NULL; if (enddrag && (ui->drag_start_x >= 0)) { if (xc >= 0 && xc <= 2*from->w && yc >= 0 && yc <= 2*from->h && erasing == ui->erasing) { if (ui->dragged) { if (ui_draw_rect(from, ui, from->hedge, from->vedge, 1, FALSE, !ui->erasing)) { sprintf(buf, "%c%d,%d,%d,%d", (int)(ui->erasing ? 'E' : 'R'), ui->x1, ui->y1, ui->x2 - ui->x1, ui->y2 - ui->y1); ret = dupstr(buf); } } else { if ((xc & 1) && !(yc & 1) && HRANGE(from,xc/2,yc/2)) { sprintf(buf, "H%d,%d", xc/2, yc/2); ret = dupstr(buf); } if ((yc & 1) && !(xc & 1) && VRANGE(from,xc/2,yc/2)) { sprintf(buf, "V%d,%d", xc/2, yc/2); ret = dupstr(buf); } } } ui->drag_start_x = -1; ui->drag_start_y = -1; ui->drag_end_x = -1; ui->drag_end_y = -1; ui->x1 = -1; ui->y1 = -1; ui->x2 = -1; ui->y2 = -1; ui->dragged = FALSE; active = TRUE; } if (ret) return ret; /* a move has been made */ else if (active) return ""; /* UI activity has occurred */ else return NULL; } static game_state *execute_move(const game_state *from, const char *move) { game_state *ret; int x1, y1, x2, y2, mode; if (move[0] == 'S') { const char *p = move+1; int x, y; ret = dup_game(from); ret->cheated = TRUE; for (y = 0; y < ret->h; y++) for (x = 1; x < ret->w; x++) { vedge(ret, x, y) = (*p == '1'); if (*p) p++; } for (y = 1; y < ret->h; y++) for (x = 0; x < ret->w; x++) { hedge(ret, x, y) = (*p == '1'); if (*p) p++; } sfree(ret->correct); ret->correct = get_correct(ret); return ret; } else if ((move[0] == 'R' || move[0] == 'E') && sscanf(move+1, "%d,%d,%d,%d", &x1, &y1, &x2, &y2) == 4 && x1 >= 0 && x2 >= 0 && x1+x2 <= from->w && y1 >= 0 && y2 >= 0 && y1+y2 <= from->h) { x2 += x1; y2 += y1; mode = move[0]; } else if ((move[0] == 'H' || move[0] == 'V') && sscanf(move+1, "%d,%d", &x1, &y1) == 2 && (move[0] == 'H' ? HRANGE(from, x1, y1) : VRANGE(from, x1, y1))) { mode = move[0]; } else return NULL; /* can't parse move string */ ret = dup_game(from); if (mode == 'R' || mode == 'E') { grid_draw_rect(ret, ret->hedge, ret->vedge, 1, TRUE, mode == 'R', x1, y1, x2, y2); } else if (mode == 'H') { hedge(ret,x1,y1) = !hedge(ret,x1,y1); } else if (mode == 'V') { vedge(ret,x1,y1) = !vedge(ret,x1,y1); } sfree(ret->correct); ret->correct = get_correct(ret); /* * We've made a real change to the grid. Check to see * if the game has been completed. */ if (!ret->completed) { int x, y, ok; ok = TRUE; for (x = 0; x < ret->w; x++) for (y = 0; y < ret->h; y++) if (!index(ret, ret->correct, x, y)) ok = FALSE; if (ok) ret->completed = TRUE; } return ret; } /* ---------------------------------------------------------------------- * Drawing routines. */ #define CORRECT (1L<<16) #define CURSOR (1L<<17) #define COLOUR(k) ( (k)==1 ? COL_LINE : (k)==2 ? COL_DRAG : COL_DRAGERASE ) #define MAX4(x,y,z,w) ( max(max(x,y),max(z,w)) ) 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 = params->w * TILE_SIZE + 2*BORDER + 1; *y = params->h * TILE_SIZE + 2*BORDER + 1; } 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.5F * ret[COL_BACKGROUND * 3 + 0]; ret[COL_GRID * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1]; ret[COL_GRID * 3 + 2] = 0.5F * ret[COL_BACKGROUND * 3 + 2]; ret[COL_DRAG * 3 + 0] = 1.0F; ret[COL_DRAG * 3 + 1] = 0.0F; ret[COL_DRAG * 3 + 2] = 0.0F; ret[COL_DRAGERASE * 3 + 0] = 0.2F; ret[COL_DRAGERASE * 3 + 1] = 0.2F; ret[COL_DRAGERASE * 3 + 2] = 1.0F; ret[COL_CORRECT * 3 + 0] = 0.75F * ret[COL_BACKGROUND * 3 + 0]; ret[COL_CORRECT * 3 + 1] = 0.75F * ret[COL_BACKGROUND * 3 + 1]; ret[COL_CORRECT * 3 + 2] = 0.75F * ret[COL_BACKGROUND * 3 + 2]; ret[COL_LINE * 3 + 0] = 0.0F; ret[COL_LINE * 3 + 1] = 0.0F; ret[COL_LINE * 3 + 2] = 0.0F; ret[COL_TEXT * 3 + 0] = 0.0F; ret[COL_TEXT * 3 + 1] = 0.0F; ret[COL_TEXT * 3 + 2] = 0.0F; ret[COL_CURSOR * 3 + 0] = 1.0F; ret[COL_CURSOR * 3 + 1] = 0.5F; ret[COL_CURSOR * 3 + 2] = 0.5F; *ncolours = NCOLOURS; return ret; } static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { struct game_drawstate *ds = snew(struct game_drawstate); int i; ds->started = FALSE; ds->w = state->w; ds->h = state->h; ds->visible = snewn(ds->w * ds->h, unsigned long); ds->tilesize = 0; /* not decided yet */ for (i = 0; i < ds->w * ds->h; i++) ds->visible[i] = 0xFFFF; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->visible); sfree(ds); } static void draw_tile(drawing *dr, game_drawstate *ds, const game_state *state, int x, int y, unsigned char *hedge, unsigned char *vedge, unsigned char *corners, unsigned long bgflags) { int cx = COORD(x), cy = COORD(y); char str[80]; draw_rect(dr, cx, cy, TILE_SIZE+1, TILE_SIZE+1, COL_GRID); draw_rect(dr, cx+1, cy+1, TILE_SIZE-1, TILE_SIZE-1, (bgflags & CURSOR) ? COL_CURSOR : (bgflags & CORRECT) ? COL_CORRECT : COL_BACKGROUND); if (grid(state,x,y)) { sprintf(str, "%d", grid(state,x,y)); draw_text(dr, cx+TILE_SIZE/2, cy+TILE_SIZE/2, FONT_VARIABLE, TILE_SIZE/2, ALIGN_HCENTRE | ALIGN_VCENTRE, COL_TEXT, str); } /* * Draw edges. */ if (!HRANGE(state,x,y) || index(state,hedge,x,y)) draw_rect(dr, cx, cy, TILE_SIZE+1, 2, HRANGE(state,x,y) ? COLOUR(index(state,hedge,x,y)) : COL_LINE); if (!HRANGE(state,x,y+1) || index(state,hedge,x,y+1)) draw_rect(dr, cx, cy+TILE_SIZE-1, TILE_SIZE+1, 2, HRANGE(state,x,y+1) ? COLOUR(index(state,hedge,x,y+1)) : COL_LINE); if (!VRANGE(state,x,y) || index(state,vedge,x,y)) draw_rect(dr, cx, cy, 2, TILE_SIZE+1, VRANGE(state,x,y) ? COLOUR(index(state,vedge,x,y)) : COL_LINE); if (!VRANGE(state,x+1,y) || index(state,vedge,x+1,y)) draw_rect(dr, cx+TILE_SIZE-1, cy, 2, TILE_SIZE+1, VRANGE(state,x+1,y) ? COLOUR(index(state,vedge,x+1,y)) : COL_LINE); /* * Draw corners. */ if (index(state,corners,x,y)) draw_rect(dr, cx, cy, 2, 2, COLOUR(index(state,corners,x,y))); if (x+1 < state->w && index(state,corners,x+1,y)) draw_rect(dr, cx+TILE_SIZE-1, cy, 2, 2, COLOUR(index(state,corners,x+1,y))); if (y+1 < state->h && index(state,corners,x,y+1)) draw_rect(dr, cx, cy+TILE_SIZE-1, 2, 2, COLOUR(index(state,corners,x,y+1))); if (x+1 < state->w && y+1 < state->h && index(state,corners,x+1,y+1)) draw_rect(dr, cx+TILE_SIZE-1, cy+TILE_SIZE-1, 2, 2, COLOUR(index(state,corners,x+1,y+1))); draw_update(dr, cx, cy, TILE_SIZE+1, TILE_SIZE+1); } 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 x, y; unsigned char *hedge, *vedge, *corners; if (ui->dragged) { hedge = snewn(state->w*state->h, unsigned char); vedge = snewn(state->w*state->h, unsigned char); memcpy(hedge, state->hedge, state->w*state->h); memcpy(vedge, state->vedge, state->w*state->h); ui_draw_rect(state, ui, hedge, vedge, ui->erasing ? 3 : 2, TRUE, TRUE); } else { hedge = state->hedge; vedge = state->vedge; } corners = snewn(state->w * state->h, unsigned char); memset(corners, 0, state->w * state->h); for (x = 0; x < state->w; x++) for (y = 0; y < state->h; y++) { if (x > 0) { int e = index(state, vedge, x, y); if (index(state,corners,x,y) < e) index(state,corners,x,y) = e; if (y+1 < state->h && index(state,corners,x,y+1) < e) index(state,corners,x,y+1) = e; } if (y > 0) { int e = index(state, hedge, x, y); if (index(state,corners,x,y) < e) index(state,corners,x,y) = e; if (x+1 < state->w && index(state,corners,x+1,y) < e) index(state,corners,x+1,y) = e; } } if (!ds->started) { draw_rect(dr, 0, 0, state->w * TILE_SIZE + 2*BORDER + 1, state->h * TILE_SIZE + 2*BORDER + 1, COL_BACKGROUND); draw_rect(dr, COORD(0)-1, COORD(0)-1, ds->w*TILE_SIZE+3, ds->h*TILE_SIZE+3, COL_LINE); ds->started = TRUE; draw_update(dr, 0, 0, state->w * TILE_SIZE + 2*BORDER + 1, state->h * TILE_SIZE + 2*BORDER + 1); } for (x = 0; x < state->w; x++) for (y = 0; y < state->h; y++) { unsigned long c = 0; if (HRANGE(state,x,y)) c |= index(state,hedge,x,y); if (HRANGE(state,x,y+1)) c |= index(state,hedge,x,y+1) << 2; if (VRANGE(state,x,y)) c |= index(state,vedge,x,y) << 4; if (VRANGE(state,x+1,y)) c |= index(state,vedge,x+1,y) << 6; c |= index(state,corners,x,y) << 8; if (x+1 < state->w) c |= index(state,corners,x+1,y) << 10; if (y+1 < state->h) c |= index(state,corners,x,y+1) << 12; if (x+1 < state->w && y+1 < state->h) /* cast to prevent 2<<14 sign-extending on promotion to long */ c |= (unsigned long)index(state,corners,x+1,y+1) << 14; if (index(state, state->correct, x, y) && !flashtime) c |= CORRECT; if (ui->cur_visible && ui->cur_x == x && ui->cur_y == y) c |= CURSOR; if (index(ds,ds->visible,x,y) != c) { draw_tile(dr, ds, state, x, y, hedge, vedge, corners, (c & (CORRECT|CURSOR)) ); index(ds,ds->visible,x,y) = c; } } { char buf[256]; if (ui->dragged && ui->x1 >= 0 && ui->y1 >= 0 && ui->x2 >= 0 && ui->y2 >= 0) { sprintf(buf, "%dx%d ", ui->x2-ui->x1, ui->y2-ui->y1); } else { buf[0] = '\0'; } if (state->cheated) strcat(buf, "Auto-solved."); else if (state->completed) strcat(buf, "COMPLETED!"); status_bar(dr, buf); } if (hedge != state->hedge) { sfree(hedge); sfree(vedge); } sfree(corners); } 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 int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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) { int pw, ph; /* * I'll use 5mm squares by default. */ game_compute_size(params, 500, &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->w, h = state->h; 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, TILE_SIZE / 10); draw_rect_outline(dr, COORD(0), COORD(0), w*TILE_SIZE, h*TILE_SIZE, ink); /* * Grid. We have to make the grid lines particularly thin, * because users will be drawing lines _along_ them and we want * those lines to be visible. */ print_line_width(dr, TILE_SIZE / 256); for (x = 1; x < w; x++) draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), ink); for (y = 1; y < h; y++) draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), ink); /* * Solution. */ print_line_width(dr, TILE_SIZE / 10); for (y = 0; y <= h; y++) for (x = 0; x <= w; x++) { if (HRANGE(state,x,y) && hedge(state,x,y)) draw_line(dr, COORD(x), COORD(y), COORD(x+1), COORD(y), ink); if (VRANGE(state,x,y) && vedge(state,x,y)) draw_line(dr, COORD(x), COORD(y), COORD(x), COORD(y+1), ink); } /* * Clues. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) if (grid(state,x,y)) { char str[80]; sprintf(str, "%d", grid(state,x,y)); draw_text(dr, COORD(x)+TILE_SIZE/2, COORD(y)+TILE_SIZE/2, FONT_VARIABLE, TILE_SIZE/2, ALIGN_HCENTRE | ALIGN_VCENTRE, ink, str); } } #ifdef COMBINED #define thegame rect #endif const struct game thegame = { "Rectangles", "games.rectangles", "rectangles", default_params, game_fetch_preset, 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, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, TRUE, FALSE, game_print_size, game_print, TRUE, /* wants_statusbar */ FALSE, game_timing_state, 0, /* flags */ }; /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/samegame.c0000644000175300017530000014026012132232554014352 0ustar simonsimon/* * 'same game' -- try to remove all the coloured squares by * selecting regions of contiguous colours. */ /* * TODO on grid generation: * * - Generation speed could still be improved. * * 15x10c3 is the only really difficult one of the existing * presets. The others are all either small enough, or have * the great flexibility given by four colours, that they * don't take long at all. * * I still suspect many problems arise from separate * subareas. I wonder if we can also somehow prioritise left- * or rightmost insertions so as to avoid area splitting at * all where feasible? It's not easy, though, because the * current shuffle-then-try-all-options approach to move * choice doesn't leave room for `soft' probabilistic * prioritisation: we either try all class A moves before any * class B ones, or we don't. * * - The current generation algorithm inserts exactly two squares * at a time, with a single exception at the beginning of * generation for grids of odd overall size. An obvious * extension would be to permit larger inverse moves during * generation. * * this might reduce the number of failed generations by * making the insertion algorithm more flexible * * on the other hand, it would be significantly more complex * * if I do this I'll need to take out the odd-subarea * avoidance * * a nice feature of the current algorithm is that the * computer's `intended' solution always receives the minimum * possible score, so that pretty much the player's entire * score represents how much better they did than the * computer. * * - Is it possible we can _temporarily_ tolerate neighbouring * squares of the same colour, until we've finished setting up * our inverse move? * * or perhaps even not choose the colour of our inserted * region until we have finished placing it, and _then_ look * at what colours border on it? * * I don't think this is currently meaningful unless we're * placing more than a domino at a time. * * - possibly write out a full solution so that Solve can somehow * show it step by step? * * aux_info would have to encode the click points * * solve_game() would have to encode not only those click * points but also give a move string which reconstructed the * initial state * * the game_state would include a pointer to a solution move * list, plus an index into that list * * game_changed_state would auto-select the next move if * handed a new state which had a solution move list active * * execute_move, if passed such a state as input, would check * to see whether the move being made was the same as the one * stated by the solution, and if so would advance the move * index. Failing that it would return a game_state without a * solution move list active at all. */ #include #include #include #include #include #include #include "puzzles.h" #define TILE_INNER (ds->tileinner) #define TILE_GAP (ds->tilegap) #define TILE_SIZE (TILE_INNER + TILE_GAP) #define PREFERRED_TILE_SIZE 32 #define BORDER (TILE_SIZE / 2) #define HIGHLIGHT_WIDTH 2 #define FLASH_FRAME 0.13F #define COORD(x) ( (x) * TILE_SIZE + BORDER ) #define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 ) #define X(state, i) ( (i) % (state)->params.w ) #define Y(state, i) ( (i) / (state)->params.w ) #define C(state, x, y) ( (y) * (state)->w + (x) ) enum { COL_BACKGROUND, COL_1, COL_2, COL_3, COL_4, COL_5, COL_6, COL_7, COL_8, COL_9, COL_IMPOSSIBLE, COL_SEL, COL_HIGHLIGHT, COL_LOWLIGHT, NCOLOURS }; /* scoresub is 1 or 2 (for (n-1)^2 or (n-2)^2) */ struct game_params { int w, h, ncols, scoresub; int soluble; /* choose generation algorithm */ }; /* These flags must be unique across all uses; in the game_state, * the game_ui, and the drawstate (as they all get combined in the * drawstate). */ #define TILE_COLMASK 0x00ff #define TILE_SELECTED 0x0100 /* used in ui and drawstate */ #define TILE_JOINRIGHT 0x0200 /* used in drawstate */ #define TILE_JOINDOWN 0x0400 /* used in drawstate */ #define TILE_JOINDIAG 0x0800 /* used in drawstate */ #define TILE_HASSEL 0x1000 /* used in drawstate */ #define TILE_IMPOSSIBLE 0x2000 /* used in drawstate */ #define TILE(gs,x,y) ((gs)->tiles[(gs)->params.w*(y)+(x)]) #define COL(gs,x,y) (TILE(gs,x,y) & TILE_COLMASK) #define ISSEL(gs,x,y) (TILE(gs,x,y) & TILE_SELECTED) #define SWAPTILE(gs,x1,y1,x2,y2) do { \ int t = TILE(gs,x1,y1); \ TILE(gs,x1,y1) = TILE(gs,x2,y2); \ TILE(gs,x2,y2) = t; \ } while (0) static int npoints(const game_params *params, int nsel) { int sdiff = nsel - params->scoresub; return (sdiff > 0) ? sdiff * sdiff : 0; } struct game_state { struct game_params params; int n; int *tiles; /* colour only */ int score; int complete, impossible; }; static game_params *default_params(void) { game_params *ret = snew(game_params); ret->w = 5; ret->h = 5; ret->ncols = 3; ret->scoresub = 2; ret->soluble = TRUE; return ret; } static const struct game_params samegame_presets[] = { { 5, 5, 3, 2, TRUE }, { 10, 5, 3, 2, TRUE }, #ifdef SLOW_SYSTEM { 10, 10, 3, 2, TRUE }, #else { 15, 10, 3, 2, TRUE }, #endif { 15, 10, 4, 2, TRUE }, { 20, 15, 4, 2, TRUE } }; static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char str[80]; if (i < 0 || i >= lenof(samegame_presets)) return FALSE; ret = snew(game_params); *ret = samegame_presets[i]; sprintf(str, "%dx%d, %d colours", ret->w, ret->h, ret->ncols); *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) { char const *p = string; params->w = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; if (*p == 'x') { p++; params->h = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; } else { params->h = params->w; } if (*p == 'c') { p++; params->ncols = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; } else { params->ncols = 3; } if (*p == 's') { p++; params->scoresub = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; } else { params->scoresub = 2; } if (*p == 'r') { p++; params->soluble = FALSE; } } static char *encode_params(const game_params *params, int full) { char ret[80]; sprintf(ret, "%dx%dc%ds%d%s", params->w, params->h, params->ncols, params->scoresub, full && !params->soluble ? "r" : ""); return dupstr(ret); } static config_item *game_configure(const game_params *params) { config_item *ret; char buf[80]; ret = snewn(6, config_item); ret[0].name = "Width"; ret[0].type = C_STRING; sprintf(buf, "%d", params->w); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "No. of colours"; ret[2].type = C_STRING; sprintf(buf, "%d", params->ncols); ret[2].sval = dupstr(buf); ret[2].ival = 0; ret[3].name = "Scoring system"; ret[3].type = C_CHOICES; ret[3].sval = ":(n-1)^2:(n-2)^2"; ret[3].ival = params->scoresub-1; ret[4].name = "Ensure solubility"; ret[4].type = C_BOOLEAN; ret[4].sval = NULL; ret[4].ival = params->soluble; ret[5].name = NULL; ret[5].type = C_END; ret[5].sval = NULL; ret[5].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); ret->ncols = atoi(cfg[2].sval); ret->scoresub = cfg[3].ival + 1; ret->soluble = cfg[4].ival; return ret; } static char *validate_params(const game_params *params, int full) { if (params->w < 1 || params->h < 1) return "Width and height must both be positive"; if (params->ncols > 9) return "Maximum of 9 colours"; if (params->soluble) { if (params->ncols < 3) return "Number of colours must be at least three"; if (params->w * params->h <= 1) return "Grid area must be greater than 1"; } else { if (params->ncols < 2) return "Number of colours must be at least three"; /* ...and we must make sure we can generate at least 2 squares * of each colour so it's theoretically soluble. */ if ((params->w * params->h) < (params->ncols * 2)) return "Too many colours makes given grid size impossible"; } if ((params->scoresub < 1) || (params->scoresub > 2)) return "Scoring system not recognised"; return NULL; } /* * Guaranteed-soluble grid generator. */ static void gen_grid(int w, int h, int nc, int *grid, random_state *rs) { int wh = w*h, tc = nc+1; int i, j, k, c, x, y, pos, n; int *list, *grid2; int ok, failures = 0; /* * We'll use `list' to track the possible places to put our * next insertion. There are up to h places to insert in each * column: in a column of height n there are n+1 places because * we can insert at the very bottom or the very top, but a * column of height h can't have anything at all inserted in it * so we have up to h in each column. Likewise, with n columns * present there are n+1 places to fit a new one in between but * we can't insert a column if there are already w; so there * are a maximum of w new columns too. Total is wh + w. */ list = snewn(wh + w, int); grid2 = snewn(wh, int); do { /* * Start with two or three squares - depending on parity of w*h * - of a random colour. */ for (i = 0; i < wh; i++) grid[i] = 0; j = 2 + (wh % 2); c = 1 + random_upto(rs, nc); if (j <= w) { for (i = 0; i < j; i++) grid[(h-1)*w+i] = c; } else { assert(j <= h); for (i = 0; i < j; i++) grid[(h-1-i)*w] = c; } /* * Now repeatedly insert a two-square blob in the grid, of * whatever colour will go at the position we chose. */ while (1) { n = 0; /* * Build up a list of insertion points. Each point is * encoded as y*w+x; insertion points between columns are * encoded as h*w+x. */ if (grid[wh - 1] == 0) { /* * The final column is empty, so we can insert new * columns. */ for (i = 0; i < w; i++) { list[n++] = wh + i; if (grid[(h-1)*w + i] == 0) break; } } /* * Now look for places to insert within columns. */ for (i = 0; i < w; i++) { if (grid[(h-1)*w+i] == 0) break; /* no more columns */ if (grid[i] != 0) continue; /* this column is full */ for (j = h; j-- > 0 ;) { list[n++] = j*w+i; if (grid[j*w+i] == 0) break; /* this column is exhausted */ } } if (n == 0) break; /* we're done */ #ifdef GENERATION_DIAGNOSTICS printf("initial grid:\n"); { int x,y; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { if (grid[y*w+x] == 0) printf("-"); else printf("%d", grid[y*w+x]); } printf("\n"); } } #endif /* * Now go through the list one element at a time in * random order, and actually attempt to insert * something there. */ while (n-- > 0) { int dirs[4], ndirs, dir; i = random_upto(rs, n+1); pos = list[i]; list[i] = list[n]; x = pos % w; y = pos / w; memcpy(grid2, grid, wh * sizeof(int)); if (y == h) { /* * Insert a column at position x. */ for (i = w-1; i > x; i--) for (j = 0; j < h; j++) grid2[j*w+i] = grid2[j*w+(i-1)]; /* * Clear the new column. */ for (j = 0; j < h; j++) grid2[j*w+x] = 0; /* * Decrement y so that our first square is actually * inserted _in_ the grid rather than just below it. */ y--; } /* * Insert a square within column x at position y. */ for (i = 0; i+1 <= y; i++) grid2[i*w+x] = grid2[(i+1)*w+x]; #ifdef GENERATION_DIAGNOSTICS printf("trying at n=%d (%d,%d)\n", n, x, y); grid2[y*w+x] = tc; { int x,y; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { if (grid2[y*w+x] == 0) printf("-"); else if (grid2[y*w+x] <= nc) printf("%d", grid2[y*w+x]); else printf("*"); } printf("\n"); } } #endif /* * Pick our square colour so that it doesn't match any * of its neighbours. */ { int wrongcol[4], nwrong = 0; /* * List the neighbouring colours. */ if (x > 0) wrongcol[nwrong++] = grid2[y*w+(x-1)]; if (x+1 < w) wrongcol[nwrong++] = grid2[y*w+(x+1)]; if (y > 0) wrongcol[nwrong++] = grid2[(y-1)*w+x]; if (y+1 < h) wrongcol[nwrong++] = grid2[(y+1)*w+x]; /* * Eliminate duplicates. We can afford a shoddy * algorithm here because the problem size is * bounded. */ for (i = j = 0 ;; i++) { int pos = -1, min = 0; if (j > 0) min = wrongcol[j-1]; for (k = i; k < nwrong; k++) if (wrongcol[k] > min && (pos == -1 || wrongcol[k] < wrongcol[pos])) pos = k; if (pos >= 0) { int v = wrongcol[pos]; wrongcol[pos] = wrongcol[j]; wrongcol[j++] = v; } else break; } nwrong = j; /* * If no colour will go here, stop trying. */ if (nwrong == nc) continue; /* * Otherwise, pick a colour from the remaining * ones. */ c = 1 + random_upto(rs, nc - nwrong); for (i = 0; i < nwrong; i++) { if (c >= wrongcol[i]) c++; else break; } } /* * Place the new square. * * Although I've _chosen_ the new region's colour * (so that we can check adjacency), I'm going to * actually place it as an invalid colour (tc) * until I'm sure it's viable. This is so that I * can conveniently check that I really have made a * _valid_ inverse move later on. */ #ifdef GENERATION_DIAGNOSTICS printf("picked colour %d\n", c); #endif grid2[y*w+x] = tc; /* * Now attempt to extend it in one of three ways: left, * right or up. */ ndirs = 0; if (x > 0 && grid2[y*w+(x-1)] != c && grid2[x-1] == 0 && (y+1 >= h || grid2[(y+1)*w+(x-1)] != c) && (y+1 >= h || grid2[(y+1)*w+(x-1)] != 0) && (x <= 1 || grid2[y*w+(x-2)] != c)) dirs[ndirs++] = -1; /* left */ if (x+1 < w && grid2[y*w+(x+1)] != c && grid2[x+1] == 0 && (y+1 >= h || grid2[(y+1)*w+(x+1)] != c) && (y+1 >= h || grid2[(y+1)*w+(x+1)] != 0) && (x+2 >= w || grid2[y*w+(x+2)] != c)) dirs[ndirs++] = +1; /* right */ if (y > 0 && grid2[x] == 0 && (x <= 0 || grid2[(y-1)*w+(x-1)] != c) && (x+1 >= w || grid2[(y-1)*w+(x+1)] != c)) { /* * We add this possibility _twice_, so that the * probability of placing a vertical domino is * about the same as that of a horizontal. This * should yield less bias in the generated * grids. */ dirs[ndirs++] = 0; /* up */ dirs[ndirs++] = 0; /* up */ } if (ndirs == 0) continue; dir = dirs[random_upto(rs, ndirs)]; #ifdef GENERATION_DIAGNOSTICS printf("picked dir %d\n", dir); #endif /* * Insert a square within column (x+dir) at position y. */ for (i = 0; i+1 <= y; i++) grid2[i*w+x+dir] = grid2[(i+1)*w+x+dir]; grid2[y*w+x+dir] = tc; /* * See if we've divided the remaining grid squares * into sub-areas. If so, we need every sub-area to * have an even area or we won't be able to * complete generation. * * If the height is odd and not all columns are * present, we can increase the area of a subarea * by adding a new column in it, so in that * situation we don't mind having as many odd * subareas as there are spare columns. * * If the height is even, we can't fix it at all. */ { int nerrs = 0, nfix = 0; k = 0; /* current subarea size */ for (i = 0; i < w; i++) { if (grid2[(h-1)*w+i] == 0) { if (h % 2) nfix++; continue; } for (j = 0; j < h && grid2[j*w+i] == 0; j++); assert(j < h); if (j == 0) { /* * End of previous subarea. */ if (k % 2) nerrs++; k = 0; } else { k += j; } } if (k % 2) nerrs++; if (nerrs > nfix) continue; /* try a different placement */ } /* * We've made a move. Verify that it is a valid * move and that if made it would indeed yield the * previous grid state. The criteria are: * * (a) removing all the squares of colour tc (and * shuffling the columns up etc) from grid2 * would yield grid * (b) no square of colour tc is adjacent to one * of colour c * (c) all the squares of colour tc form a single * connected component * * We verify the latter property at the same time * as checking that removing all the tc squares * would yield the previous grid. Then we colour * the tc squares in colour c by breadth-first * search, which conveniently permits us to test * that they're all connected. */ { int x1, x2, y1, y2; int ok = TRUE; int fillstart = -1, ntc = 0; #ifdef GENERATION_DIAGNOSTICS { int x,y; printf("testing move (new, old):\n"); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { if (grid2[y*w+x] == 0) printf("-"); else if (grid2[y*w+x] <= nc) printf("%d", grid2[y*w+x]); else printf("*"); } printf(" "); for (x = 0; x < w; x++) { if (grid[y*w+x] == 0) printf("-"); else printf("%d", grid[y*w+x]); } printf("\n"); } } #endif for (x1 = x2 = 0; x2 < w; x2++) { int usedcol = FALSE; for (y1 = y2 = h-1; y2 >= 0; y2--) { if (grid2[y2*w+x2] == tc) { ntc++; if (fillstart == -1) fillstart = y2*w+x2; if ((y2+1 < h && grid2[(y2+1)*w+x2] == c) || (y2-1 >= 0 && grid2[(y2-1)*w+x2] == c) || (x2+1 < w && grid2[y2*w+x2+1] == c) || (x2-1 >= 0 && grid2[y2*w+x2-1] == c)) { #ifdef GENERATION_DIAGNOSTICS printf("adjacency failure at %d,%d\n", x2, y2); #endif ok = FALSE; } continue; } if (grid2[y2*w+x2] == 0) break; usedcol = TRUE; if (grid2[y2*w+x2] != grid[y1*w+x1]) { #ifdef GENERATION_DIAGNOSTICS printf("matching failure at %d,%d vs %d,%d\n", x2, y2, x1, y1); #endif ok = FALSE; } y1--; } /* * If we've reached the top of the column * in grid2, verify that we've also reached * the top of the column in `grid'. */ if (usedcol) { while (y1 >= 0) { if (grid[y1*w+x1] != 0) { #ifdef GENERATION_DIAGNOSTICS printf("junk at column top (%d,%d)\n", x1, y1); #endif ok = FALSE; } y1--; } } if (!ok) break; if (usedcol) x1++; } if (!ok) { assert(!"This should never happen"); /* * If this game is compiled NDEBUG so that * the assertion doesn't bring it to a * crashing halt, the only thing we can do * is to give up, loop round again, and * hope to randomly avoid making whatever * type of move just caused this failure. */ continue; } /* * Now use bfs to fill in the tc section as * colour c. We use `list' to store the set of * squares we have to process. */ i = j = 0; assert(fillstart >= 0); list[i++] = fillstart; #ifdef OUTPUT_SOLUTION printf("M"); #endif while (j < i) { k = list[j]; x = k % w; y = k / w; #ifdef OUTPUT_SOLUTION printf("%s%d", j ? "," : "", k); #endif j++; assert(grid2[k] == tc); grid2[k] = c; if (x > 0 && grid2[k-1] == tc) list[i++] = k-1; if (x+1 < w && grid2[k+1] == tc) list[i++] = k+1; if (y > 0 && grid2[k-w] == tc) list[i++] = k-w; if (y+1 < h && grid2[k+w] == tc) list[i++] = k+w; } #ifdef OUTPUT_SOLUTION printf("\n"); #endif /* * Check that we've filled the same number of * tc squares as we originally found. */ assert(j == ntc); } memcpy(grid, grid2, wh * sizeof(int)); break; /* done it! */ } #ifdef GENERATION_DIAGNOSTICS { int x,y; printf("n=%d\n", n); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { if (grid[y*w+x] == 0) printf("-"); else printf("%d", grid[y*w+x]); } printf("\n"); } } #endif if (n < 0) break; } ok = TRUE; for (i = 0; i < wh; i++) if (grid[i] == 0) { ok = FALSE; failures++; #if defined GENERATION_DIAGNOSTICS || defined SHOW_INCOMPLETE { int x,y; printf("incomplete grid:\n"); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { if (grid[y*w+x] == 0) printf("-"); else printf("%d", grid[y*w+x]); } printf("\n"); } } #endif break; } } while (!ok); #if defined GENERATION_DIAGNOSTICS || defined COUNT_FAILURES printf("%d failures\n", failures); #endif #ifdef GENERATION_DIAGNOSTICS { int x,y; printf("final grid:\n"); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { printf("%d", grid[y*w+x]); } printf("\n"); } } #endif sfree(grid2); sfree(list); } /* * Not-guaranteed-soluble grid generator; kept as a legacy, and in * case someone finds the slightly odd quality of the guaranteed- * soluble grids to be aesthetically displeasing or finds its CPU * utilisation to be excessive. */ static void gen_grid_random(int w, int h, int nc, int *grid, random_state *rs) { int i, j, c; int n = w * h; for (i = 0; i < n; i++) grid[i] = 0; /* * Our sole concession to not gratuitously generating insoluble * grids is to ensure we have at least two of every colour. */ for (c = 1; c <= nc; c++) { for (j = 0; j < 2; j++) { do { i = (int)random_upto(rs, n); } while (grid[i] != 0); grid[i] = c; } } /* * Fill in the rest of the grid at random. */ for (i = 0; i < n; i++) { if (grid[i] == 0) grid[i] = (int)random_upto(rs, nc)+1; } } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { char *ret; int n, i, retlen, *tiles; n = params->w * params->h; tiles = snewn(n, int); if (params->soluble) gen_grid(params->w, params->h, params->ncols, tiles, rs); else gen_grid_random(params->w, params->h, params->ncols, tiles, rs); ret = NULL; retlen = 0; for (i = 0; i < n; i++) { char buf[80]; int k; k = sprintf(buf, "%d,", tiles[i]); ret = sresize(ret, retlen + k + 1, char); strcpy(ret + retlen, buf); retlen += k; } ret[retlen-1] = '\0'; /* delete last comma */ sfree(tiles); return ret; } static char *validate_desc(const game_params *params, const char *desc) { int area = params->w * params->h, i; const char *p = desc; for (i = 0; i < area; i++) { const char *q = p; int n; if (!isdigit((unsigned char)*p)) return "Not enough numbers in string"; while (isdigit((unsigned char)*p)) p++; if (i < area-1 && *p != ',') return "Expected comma after number"; else if (i == area-1 && *p) return "Excess junk at end of string"; n = atoi(q); if (n < 0 || n > params->ncols) return "Colour out of range"; if (*p) p++; /* eat comma */ } return NULL; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { game_state *state = snew(game_state); const char *p = desc; int i; state->params = *params; /* struct copy */ state->n = state->params.w * state->params.h; state->tiles = snewn(state->n, int); for (i = 0; i < state->n; i++) { assert(*p); state->tiles[i] = atoi(p); while (*p && *p != ',') p++; if (*p) p++; /* eat comma */ } state->complete = state->impossible = 0; state->score = 0; return state; } static game_state *dup_game(const game_state *state) { game_state *ret = snew(game_state); *ret = *state; /* structure copy, except... */ ret->tiles = snewn(state->n, int); memcpy(ret->tiles, state->tiles, state->n * sizeof(int)); return ret; } static void free_game(game_state *state) { sfree(state->tiles); sfree(state); } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { return NULL; } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { char *ret, *p; int x, y, maxlen; maxlen = state->params.h * (state->params.w + 1); ret = snewn(maxlen+1, char); p = ret; for (y = 0; y < state->params.h; y++) { for (x = 0; x < state->params.w; x++) { int t = TILE(state,x,y); if (t <= 0) *p++ = ' '; else if (t < 10) *p++ = '0'+t; else *p++ = 'a'+(t-10); } *p++ = '\n'; } assert(p - ret == maxlen); *p = '\0'; return ret; } struct game_ui { struct game_params params; int *tiles; /* selected-ness only */ int nselected; int xsel, ysel, displaysel; }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->params = state->params; /* structure copy */ ui->tiles = snewn(state->n, int); memset(ui->tiles, 0, state->n*sizeof(int)); ui->nselected = 0; ui->xsel = ui->ysel = ui->displaysel = 0; return ui; } static void free_ui(game_ui *ui) { sfree(ui->tiles); sfree(ui); } static char *encode_ui(const game_ui *ui) { return NULL; } static void decode_ui(game_ui *ui, const char *encoding) { } static void sel_clear(game_ui *ui, const game_state *state) { int i; for (i = 0; i < state->n; i++) ui->tiles[i] &= ~TILE_SELECTED; ui->nselected = 0; } static void game_changed_state(game_ui *ui, const game_state *oldstate, const game_state *newstate) { sel_clear(ui, newstate); /* * If the game state has just changed into an unplayable one * (either completed or impossible), we vanish the keyboard- * control cursor. */ if (newstate->complete || newstate->impossible) ui->displaysel = 0; } static char *sel_movedesc(game_ui *ui, const game_state *state) { int i; char *ret, *sep, buf[80]; int retlen, retsize; retsize = 256; ret = snewn(retsize, char); retlen = 0; ret[retlen++] = 'M'; sep = ""; for (i = 0; i < state->n; i++) { if (ui->tiles[i] & TILE_SELECTED) { sprintf(buf, "%s%d", sep, i); sep = ","; if (retlen + (int)strlen(buf) >= retsize) { retsize = retlen + strlen(buf) + 256; ret = sresize(ret, retsize, char); } strcpy(ret + retlen, buf); retlen += strlen(buf); ui->tiles[i] &= ~TILE_SELECTED; } } ui->nselected = 0; assert(retlen < retsize); ret[retlen++] = '\0'; return sresize(ret, retlen, char); } static void sel_expand(game_ui *ui, const game_state *state, int tx, int ty) { int ns = 1, nadded, x, y, c; TILE(ui,tx,ty) |= TILE_SELECTED; do { nadded = 0; for (x = 0; x < state->params.w; x++) { for (y = 0; y < state->params.h; y++) { if (x == tx && y == ty) continue; if (ISSEL(ui,x,y)) continue; c = COL(state,x,y); if ((x > 0) && ISSEL(ui,x-1,y) && COL(state,x-1,y) == c) { TILE(ui,x,y) |= TILE_SELECTED; nadded++; continue; } if ((x+1 < state->params.w) && ISSEL(ui,x+1,y) && COL(state,x+1,y) == c) { TILE(ui,x,y) |= TILE_SELECTED; nadded++; continue; } if ((y > 0) && ISSEL(ui,x,y-1) && COL(state,x,y-1) == c) { TILE(ui,x,y) |= TILE_SELECTED; nadded++; continue; } if ((y+1 < state->params.h) && ISSEL(ui,x,y+1) && COL(state,x,y+1) == c) { TILE(ui,x,y) |= TILE_SELECTED; nadded++; continue; } } } ns += nadded; } while (nadded > 0); if (ns > 1) { ui->nselected = ns; } else { sel_clear(ui, state); } } static int sg_emptycol(game_state *ret, int x) { int y; for (y = 0; y < ret->params.h; y++) { if (COL(ret,x,y)) return 0; } return 1; } static void sg_snuggle(game_state *ret) { int x,y, ndone; /* make all unsupported tiles fall down. */ do { ndone = 0; for (x = 0; x < ret->params.w; x++) { for (y = ret->params.h-1; y > 0; y--) { if (COL(ret,x,y) != 0) continue; if (COL(ret,x,y-1) != 0) { SWAPTILE(ret,x,y,x,y-1); ndone++; } } } } while (ndone); /* shuffle all columns as far left as they can go. */ do { ndone = 0; for (x = 0; x < ret->params.w-1; x++) { if (sg_emptycol(ret,x) && !sg_emptycol(ret,x+1)) { ndone++; for (y = 0; y < ret->params.h; y++) { SWAPTILE(ret,x,y,x+1,y); } } } } while (ndone); } static void sg_check(game_state *ret) { int x,y, complete = 1, impossible = 1; for (x = 0; x < ret->params.w; x++) { for (y = 0; y < ret->params.h; y++) { if (COL(ret,x,y) == 0) continue; complete = 0; if (x+1 < ret->params.w) { if (COL(ret,x,y) == COL(ret,x+1,y)) impossible = 0; } if (y+1 < ret->params.h) { if (COL(ret,x,y) == COL(ret,x,y+1)) impossible = 0; } } } ret->complete = complete; ret->impossible = impossible; } struct game_drawstate { int started, bgcolour; int tileinner, tilegap; int *tiles; /* contains colour and SELECTED. */ }; static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { int tx, ty; char *ret = ""; ui->displaysel = 0; if (button == RIGHT_BUTTON || button == LEFT_BUTTON) { tx = FROMCOORD(x); ty= FROMCOORD(y); } else if (IS_CURSOR_MOVE(button)) { int dx = 0, dy = 0; ui->displaysel = 1; dx = (button == CURSOR_LEFT) ? -1 : ((button == CURSOR_RIGHT) ? +1 : 0); dy = (button == CURSOR_DOWN) ? +1 : ((button == CURSOR_UP) ? -1 : 0); ui->xsel = (ui->xsel + state->params.w + dx) % state->params.w; ui->ysel = (ui->ysel + state->params.h + dy) % state->params.h; return ret; } else if (IS_CURSOR_SELECT(button)) { ui->displaysel = 1; tx = ui->xsel; ty = ui->ysel; } else return NULL; if (tx < 0 || tx >= state->params.w || ty < 0 || ty >= state->params.h) return NULL; if (COL(state, tx, ty) == 0) return NULL; if (ISSEL(ui,tx,ty)) { if (button == RIGHT_BUTTON || button == CURSOR_SELECT2) sel_clear(ui, state); else ret = sel_movedesc(ui, state); } else { sel_clear(ui, state); /* might be no-op */ sel_expand(ui, state, tx, ty); } return ret; } static game_state *execute_move(const game_state *from, const char *move) { int i, n; game_state *ret; if (move[0] == 'M') { ret = dup_game(from); n = 0; move++; while (*move) { i = atoi(move); if (i < 0 || i >= ret->n) { free_game(ret); return NULL; } n++; ret->tiles[i] = 0; while (*move && isdigit((unsigned char)*move)) move++; if (*move == ',') move++; } ret->score += npoints(&ret->params, n); sg_snuggle(ret); /* shifts blanks down and to the left */ sg_check(ret); /* checks for completeness or impossibility */ return ret; } else return NULL; /* couldn't parse move string */ } /* ---------------------------------------------------------------------- * Drawing routines. */ static void game_set_size(drawing *dr, game_drawstate *ds, const game_params *params, int tilesize) { ds->tilegap = 2; ds->tileinner = tilesize - ds->tilegap; } static void game_compute_size(const game_params *params, int tilesize, int *x, int *y) { /* Ick: fake up tile size variables for macro expansion purposes */ game_drawstate ads, *ds = &ads; game_set_size(NULL, ds, params, tilesize); *x = TILE_SIZE * params->w + 2 * BORDER - TILE_GAP; *y = TILE_SIZE * params->h + 2 * BORDER - TILE_GAP; } static float *game_colours(frontend *fe, int *ncolours) { float *ret = snewn(3 * NCOLOURS, float); frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); ret[COL_1 * 3 + 0] = 0.0F; ret[COL_1 * 3 + 1] = 0.0F; ret[COL_1 * 3 + 2] = 1.0F; ret[COL_2 * 3 + 0] = 0.0F; ret[COL_2 * 3 + 1] = 0.5F; ret[COL_2 * 3 + 2] = 0.0F; ret[COL_3 * 3 + 0] = 1.0F; ret[COL_3 * 3 + 1] = 0.0F; ret[COL_3 * 3 + 2] = 0.0F; ret[COL_4 * 3 + 0] = 1.0F; ret[COL_4 * 3 + 1] = 1.0F; ret[COL_4 * 3 + 2] = 0.0F; ret[COL_5 * 3 + 0] = 1.0F; ret[COL_5 * 3 + 1] = 0.0F; ret[COL_5 * 3 + 2] = 1.0F; ret[COL_6 * 3 + 0] = 0.0F; ret[COL_6 * 3 + 1] = 1.0F; ret[COL_6 * 3 + 2] = 1.0F; ret[COL_7 * 3 + 0] = 0.5F; ret[COL_7 * 3 + 1] = 0.5F; ret[COL_7 * 3 + 2] = 1.0F; ret[COL_8 * 3 + 0] = 0.5F; ret[COL_8 * 3 + 1] = 1.0F; ret[COL_8 * 3 + 2] = 0.5F; ret[COL_9 * 3 + 0] = 1.0F; ret[COL_9 * 3 + 1] = 0.5F; ret[COL_9 * 3 + 2] = 0.5F; ret[COL_IMPOSSIBLE * 3 + 0] = 0.0F; ret[COL_IMPOSSIBLE * 3 + 1] = 0.0F; ret[COL_IMPOSSIBLE * 3 + 2] = 0.0F; ret[COL_SEL * 3 + 0] = 1.0F; ret[COL_SEL * 3 + 1] = 1.0F; ret[COL_SEL * 3 + 2] = 1.0F; ret[COL_HIGHLIGHT * 3 + 0] = 1.0F; ret[COL_HIGHLIGHT * 3 + 1] = 1.0F; ret[COL_HIGHLIGHT * 3 + 2] = 1.0F; ret[COL_LOWLIGHT * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 2.0F / 3.0F; ret[COL_LOWLIGHT * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 2.0F / 3.0F; ret[COL_LOWLIGHT * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 2.0F / 3.0F; *ncolours = NCOLOURS; return ret; } static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { struct game_drawstate *ds = snew(struct game_drawstate); int i; ds->started = 0; ds->tileinner = ds->tilegap = 0; /* not decided yet */ ds->tiles = snewn(state->n, int); ds->bgcolour = -1; for (i = 0; i < state->n; i++) ds->tiles[i] = -1; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->tiles); sfree(ds); } /* Drawing routing for the tile at (x,y) is responsible for drawing * itself and the gaps to its right and below. If we're the same colour * as the tile to our right, then we fill in the gap; ditto below, and if * both then we fill the teeny tiny square in the corner as well. */ static void tile_redraw(drawing *dr, game_drawstate *ds, int x, int y, int dright, int dbelow, int tile, int bgcolour) { int outer = bgcolour, inner = outer, col = tile & TILE_COLMASK; if (col) { if (tile & TILE_IMPOSSIBLE) { outer = col; inner = COL_IMPOSSIBLE; } else if (tile & TILE_SELECTED) { outer = COL_SEL; inner = col; } else { outer = inner = col; } } draw_rect(dr, COORD(x), COORD(y), TILE_INNER, TILE_INNER, outer); draw_rect(dr, COORD(x)+TILE_INNER/4, COORD(y)+TILE_INNER/4, TILE_INNER/2, TILE_INNER/2, inner); if (dright) draw_rect(dr, COORD(x)+TILE_INNER, COORD(y), TILE_GAP, TILE_INNER, (tile & TILE_JOINRIGHT) ? outer : bgcolour); if (dbelow) draw_rect(dr, COORD(x), COORD(y)+TILE_INNER, TILE_INNER, TILE_GAP, (tile & TILE_JOINDOWN) ? outer : bgcolour); if (dright && dbelow) draw_rect(dr, COORD(x)+TILE_INNER, COORD(y)+TILE_INNER, TILE_GAP, TILE_GAP, (tile & TILE_JOINDIAG) ? outer : bgcolour); if (tile & TILE_HASSEL) { int sx = COORD(x)+2, sy = COORD(y)+2, ssz = TILE_INNER-5; int scol = (outer == COL_SEL) ? COL_LOWLIGHT : COL_HIGHLIGHT; draw_line(dr, sx, sy, sx+ssz, sy, scol); draw_line(dr, sx+ssz, sy, sx+ssz, sy+ssz, scol); draw_line(dr, sx+ssz, sy+ssz, sx, sy+ssz, scol); draw_line(dr, sx, sy+ssz, sx, sy, scol); } draw_update(dr, COORD(x), COORD(y), TILE_SIZE, TILE_SIZE); } 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 bgcolour, x, y; /* This was entirely cloned from fifteen.c; it should probably be * moved into some generic 'draw-recessed-rectangle' utility fn. */ if (!ds->started) { int coords[10]; draw_rect(dr, 0, 0, TILE_SIZE * state->params.w + 2 * BORDER, TILE_SIZE * state->params.h + 2 * BORDER, COL_BACKGROUND); draw_update(dr, 0, 0, TILE_SIZE * state->params.w + 2 * BORDER, TILE_SIZE * state->params.h + 2 * BORDER); /* * Recessed area containing the whole puzzle. */ coords[0] = COORD(state->params.w) + HIGHLIGHT_WIDTH - 1 - TILE_GAP; coords[1] = COORD(state->params.h) + HIGHLIGHT_WIDTH - 1 - TILE_GAP; coords[2] = COORD(state->params.w) + HIGHLIGHT_WIDTH - 1 - TILE_GAP; coords[3] = COORD(0) - HIGHLIGHT_WIDTH; coords[4] = coords[2] - TILE_SIZE; coords[5] = coords[3] + TILE_SIZE; coords[8] = COORD(0) - HIGHLIGHT_WIDTH; coords[9] = COORD(state->params.h) + HIGHLIGHT_WIDTH - 1 - TILE_GAP; coords[6] = coords[8] + TILE_SIZE; coords[7] = coords[9] - TILE_SIZE; draw_polygon(dr, coords, 5, COL_HIGHLIGHT, COL_HIGHLIGHT); coords[1] = COORD(0) - HIGHLIGHT_WIDTH; coords[0] = COORD(0) - HIGHLIGHT_WIDTH; draw_polygon(dr, coords, 5, COL_LOWLIGHT, COL_LOWLIGHT); ds->started = 1; } if (flashtime > 0.0) { int frame = (int)(flashtime / FLASH_FRAME); bgcolour = (frame % 2 ? COL_LOWLIGHT : COL_HIGHLIGHT); } else bgcolour = COL_BACKGROUND; for (x = 0; x < state->params.w; x++) { for (y = 0; y < state->params.h; y++) { int i = (state->params.w * y) + x; int col = COL(state,x,y), tile = col; int dright = (x+1 < state->params.w); int dbelow = (y+1 < state->params.h); tile |= ISSEL(ui,x,y); if (state->impossible) tile |= TILE_IMPOSSIBLE; if (dright && COL(state,x+1,y) == col) tile |= TILE_JOINRIGHT; if (dbelow && COL(state,x,y+1) == col) tile |= TILE_JOINDOWN; if ((tile & TILE_JOINRIGHT) && (tile & TILE_JOINDOWN) && COL(state,x+1,y+1) == col) tile |= TILE_JOINDIAG; if (ui->displaysel && ui->xsel == x && ui->ysel == y) tile |= TILE_HASSEL; /* For now we're never expecting oldstate at all (because we have * no animation); when we do we might well want to be looking * at the tile colours from oldstate, not state. */ if ((oldstate && COL(oldstate,x,y) != col) || (ds->bgcolour != bgcolour) || (tile != ds->tiles[i])) { tile_redraw(dr, ds, x, y, dright, dbelow, tile, bgcolour); ds->tiles[i] = tile; } } } ds->bgcolour = bgcolour; { char status[255], score[80]; sprintf(score, "Score: %d", state->score); if (state->complete) sprintf(status, "COMPLETE! %s", score); else if (state->impossible) sprintf(status, "Cannot move! %s", score); else if (ui->nselected) sprintf(status, "%s Selected: %d (%d)", score, ui->nselected, npoints(&state->params, ui->nselected)); else sprintf(status, "%s", score); status_bar(dr, status); } } 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->complete && newstate->complete) || (!oldstate->impossible && newstate->impossible)) return 2 * FLASH_FRAME; else return 0.0F; } static int game_status(const game_state *state) { /* * Dead-end situations are assumed to be rescuable by Undo, so we * don't bother to identify them and return -1. */ return state->complete ? +1 : 0; } static int 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 samegame #endif const struct game thegame = { "Same Game", "games.samegame", "samegame", default_params, game_fetch_preset, 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, TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, decode_ui, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, FALSE, FALSE, game_print_size, game_print, TRUE, /* wants_statusbar */ FALSE, game_timing_state, 0, /* flags */ }; puzzles-r9872/signpost.c0000644000175300017530000022324612132232554014447 0ustar simonsimon/* * signpost.c: implementation of the janko game 'arrow path' */ #include #include #include #include #include #include #include "puzzles.h" #define PREFERRED_TILE_SIZE 48 #define TILE_SIZE (ds->tilesize) #define BLITTER_SIZE TILE_SIZE #define BORDER (TILE_SIZE / 2) #define COORD(x) ( (x) * TILE_SIZE + BORDER ) #define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 ) #define INGRID(s,x,y) ((x) >= 0 && (x) < (s)->w && (y) >= 0 && (y) < (s)->h) #define FLASH_SPIN 0.7F #define NBACKGROUNDS 16 enum { COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT, COL_GRID, COL_CURSOR, COL_ERROR, COL_DRAG_ORIGIN, COL_ARROW, COL_ARROW_BG_DIM, COL_NUMBER, COL_NUMBER_SET, COL_NUMBER_SET_MID, COL_B0, /* background colours */ COL_M0 = COL_B0 + 1*NBACKGROUNDS, /* mid arrow colours */ COL_D0 = COL_B0 + 2*NBACKGROUNDS, /* dim arrow colours */ COL_X0 = COL_B0 + 3*NBACKGROUNDS, /* dim arrow colours */ NCOLOURS = COL_B0 + 4*NBACKGROUNDS }; struct game_params { int w, h; int force_corner_start; }; enum { DIR_N = 0, DIR_NE, DIR_E, DIR_SE, DIR_S, DIR_SW, DIR_W, DIR_NW, DIR_MAX }; static const char *dirstrings[8] = { "N ", "NE", "E ", "SE", "S ", "SW", "W ", "NW" }; static const int dxs[DIR_MAX] = { 0, 1, 1, 1, 0, -1, -1, -1 }; static const int dys[DIR_MAX] = { -1, -1, 0, 1, 1, 1, 0, -1 }; #define DIR_OPPOSITE(d) ((d+4)%8) struct game_state { int w, h, n; int completed, used_solve, impossible; int *dirs; /* direction enums, size n */ int *nums; /* numbers, size n */ unsigned int *flags; /* flags, size n */ int *next, *prev; /* links to other cell indexes, size n (-1 absent) */ int *dsf; /* connects regions with a dsf. */ int *numsi; /* for each number, which index is it in? (-1 absent) */ }; #define FLAG_IMMUTABLE 1 #define FLAG_ERROR 2 /* --- Generally useful functions --- */ #define ISREALNUM(state, num) ((num) > 0 && (num) <= (state)->n) static int whichdir(int fromx, int fromy, int tox, int toy) { int i, dx, dy; dx = tox - fromx; dy = toy - fromy; if (dx && dy && abs(dx) != abs(dy)) return -1; if (dx) dx = dx / abs(dx); /* limit to (-1, 0, 1) */ if (dy) dy = dy / abs(dy); /* ditto */ for (i = 0; i < DIR_MAX; i++) { if (dx == dxs[i] && dy == dys[i]) return i; } return -1; } static int whichdiri(game_state *state, int fromi, int toi) { int w = state->w; return whichdir(fromi%w, fromi/w, toi%w, toi/w); } static int ispointing(const game_state *state, int fromx, int fromy, int tox, int toy) { int w = state->w, dir = state->dirs[fromy*w+fromx]; /* (by convention) squares do not point to themselves. */ if (fromx == tox && fromy == toy) return 0; /* the final number points to nothing. */ if (state->nums[fromy*w + fromx] == state->n) return 0; while (1) { if (!INGRID(state, fromx, fromy)) return 0; if (fromx == tox && fromy == toy) return 1; fromx += dxs[dir]; fromy += dys[dir]; } return 0; /* not reached */ } static int ispointingi(game_state *state, int fromi, int toi) { int w = state->w; return ispointing(state, fromi%w, fromi/w, toi%w, toi/w); } /* Taking the number 'num', work out the gap between it and the next * available number up or down (depending on d). Return 1 if the region * at (x,y) will fit in that gap, or 0 otherwise. */ static int move_couldfit(const game_state *state, int num, int d, int x, int y) { int n, gap, i = y*state->w+x, sz; assert(d != 0); /* The 'gap' is the number of missing numbers in the grid between * our number and the next one in the sequence (up or down), or * the end of the sequence (if we happen not to have 1/n present) */ for (n = num + d, gap = 0; ISREALNUM(state, n) && state->numsi[n] == -1; n += d, gap++) ; /* empty loop */ if (gap == 0) { /* no gap, so the only allowable move is that that directly * links the two numbers. */ n = state->nums[i]; return (n == num+d) ? 0 : 1; } if (state->prev[i] == -1 && state->next[i] == -1) return 1; /* single unconnected square, always OK */ sz = dsf_size(state->dsf, i); return (sz > gap) ? 0 : 1; } static int isvalidmove(const game_state *state, int clever, int fromx, int fromy, int tox, int toy) { int w = state->w, from = fromy*w+fromx, to = toy*w+tox; int nfrom, nto; if (!INGRID(state, fromx, fromy) || !INGRID(state, tox, toy)) return 0; /* can only move where we point */ if (!ispointing(state, fromx, fromy, tox, toy)) return 0; nfrom = state->nums[from]; nto = state->nums[to]; /* can't move _from_ the preset final number, or _to_ the preset 1. */ if (((nfrom == state->n) && (state->flags[from] & FLAG_IMMUTABLE)) || ((nto == 1) && (state->flags[to] & FLAG_IMMUTABLE))) return 0; /* can't create a new connection between cells in the same region * as that would create a loop. */ if (dsf_canonify(state->dsf, from) == dsf_canonify(state->dsf, to)) return 0; /* if both cells are actual numbers, can't drag if we're not * one digit apart. */ if (ISREALNUM(state, nfrom) && ISREALNUM(state, nto)) { if (nfrom != nto-1) return 0; } else if (clever && ISREALNUM(state, nfrom)) { if (!move_couldfit(state, nfrom, +1, tox, toy)) return 0; } else if (clever && ISREALNUM(state, nto)) { if (!move_couldfit(state, nto, -1, fromx, fromy)) return 0; } return 1; } static void makelink(game_state *state, int from, int to) { if (state->next[from] != -1) state->prev[state->next[from]] = -1; state->next[from] = to; if (state->prev[to] != -1) state->next[state->prev[to]] = -1; state->prev[to] = from; } static int game_can_format_as_text_now(const game_params *params) { if (params->w * params->h >= 100) return 0; return 1; } static char *game_text_format(const game_state *state) { int len = state->h * 2 * (4*state->w + 1) + state->h + 2; int x, y, i, num, n, set; char *ret, *p; p = ret = snewn(len, char); for (y = 0; y < state->h; y++) { for (x = 0; x < state->h; x++) { i = y*state->w+x; *p++ = dirstrings[state->dirs[i]][0]; *p++ = dirstrings[state->dirs[i]][1]; *p++ = (state->flags[i] & FLAG_IMMUTABLE) ? 'I' : ' '; *p++ = ' '; } *p++ = '\n'; for (x = 0; x < state->h; x++) { i = y*state->w+x; num = state->nums[i]; if (num == 0) { *p++ = ' '; *p++ = ' '; *p++ = ' '; } else { n = num % (state->n+1); set = num / (state->n+1); assert(n <= 99); /* two digits only! */ if (set != 0) *p++ = set+'a'-1; *p++ = (n >= 10) ? ('0' + (n/10)) : ' '; *p++ = '0' + (n%10); if (set == 0) *p++ = ' '; } *p++ = ' '; } *p++ = '\n'; *p++ = '\n'; } *p++ = '\0'; return ret; } static void debug_state(const char *desc, game_state *state) { #ifdef DEBUGGING char *dbg; if (state->n >= 100) { debug(("[ no game_text_format for this size ]")); return; } dbg = game_text_format(state); debug(("%s\n%s", desc, dbg)); sfree(dbg); #endif } static void strip_nums(game_state *state) { int i; for (i = 0; i < state->n; i++) { if (!(state->flags[i] & FLAG_IMMUTABLE)) state->nums[i] = 0; } memset(state->next, -1, state->n*sizeof(int)); memset(state->prev, -1, state->n*sizeof(int)); memset(state->numsi, -1, (state->n+1)*sizeof(int)); dsf_init(state->dsf, state->n); } static int check_nums(game_state *orig, game_state *copy, int only_immutable) { int i, ret = 1; assert(copy->n == orig->n); for (i = 0; i < copy->n; i++) { if (only_immutable && !copy->flags[i] & FLAG_IMMUTABLE) continue; assert(copy->nums[i] >= 0); assert(copy->nums[i] <= copy->n); if (copy->nums[i] != orig->nums[i]) { debug(("check_nums: (%d,%d) copy=%d, orig=%d.", i%orig->w, i/orig->w, copy->nums[i], orig->nums[i])); ret = 0; } } return ret; } /* --- Game parameter/presets functions --- */ static game_params *default_params(void) { game_params *ret = snew(game_params); ret->w = ret->h = 4; ret->force_corner_start = 1; return ret; } static const struct game_params signpost_presets[] = { { 4, 4, 1 }, { 4, 4, 0 }, { 5, 5, 1 }, { 5, 5, 0 }, { 6, 6, 1 }, { 7, 7, 1 } }; static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char buf[80]; if (i < 0 || i >= lenof(signpost_presets)) return FALSE; ret = default_params(); *ret = signpost_presets[i]; *params = ret; sprintf(buf, "%dx%d%s", ret->w, ret->h, ret->force_corner_start ? "" : ", free ends"); *name = dupstr(buf); 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 *ret, char const *string) { ret->w = ret->h = atoi(string); while (*string && isdigit((unsigned char)*string)) string++; if (*string == 'x') { string++; ret->h = atoi(string); while (*string && isdigit((unsigned char)*string)) string++; } ret->force_corner_start = 0; if (*string == 'c') { string++; ret->force_corner_start = 1; } } static char *encode_params(const game_params *params, int full) { char data[256]; if (full) sprintf(data, "%dx%d%s", params->w, params->h, params->force_corner_start ? "c" : ""); else 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(4, config_item); ret[0].name = "Width"; ret[0].type = C_STRING; sprintf(buf, "%d", params->w); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Start and end in corners"; ret[2].type = C_BOOLEAN; ret[2].sval = NULL; ret[2].ival = params->force_corner_start; ret[3].name = NULL; ret[3].type = C_END; ret[3].sval = NULL; ret[3].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); ret->force_corner_start = cfg[2].ival; return ret; } static char *validate_params(const game_params *params, int full) { if (params->w < 2 || params->h < 2) return "Width and height must both be at least two"; if (params->w == 2 && params->h == 2) /* leads to generation hang */ return "Width and height cannot both be two"; return NULL; } /* --- Game description string generation and unpicking --- */ static void blank_game_into(game_state *state) { memset(state->dirs, 0, state->n*sizeof(int)); memset(state->nums, 0, state->n*sizeof(int)); memset(state->flags, 0, state->n*sizeof(unsigned int)); memset(state->next, -1, state->n*sizeof(int)); memset(state->prev, -1, state->n*sizeof(int)); memset(state->numsi, -1, (state->n+1)*sizeof(int)); } static game_state *blank_game(int w, int h) { game_state *state = snew(game_state); memset(state, 0, sizeof(game_state)); state->w = w; state->h = h; state->n = w*h; state->dirs = snewn(state->n, int); state->nums = snewn(state->n, int); state->flags = snewn(state->n, unsigned int); state->next = snewn(state->n, int); state->prev = snewn(state->n, int); state->dsf = snew_dsf(state->n); state->numsi = snewn(state->n+1, int); blank_game_into(state); return state; } static void dup_game_to(game_state *to, const game_state *from) { to->completed = from->completed; to->used_solve = from->used_solve; to->impossible = from->impossible; memcpy(to->dirs, from->dirs, to->n*sizeof(int)); memcpy(to->flags, from->flags, to->n*sizeof(unsigned int)); memcpy(to->nums, from->nums, to->n*sizeof(int)); memcpy(to->next, from->next, to->n*sizeof(int)); memcpy(to->prev, from->prev, to->n*sizeof(int)); memcpy(to->dsf, from->dsf, to->n*sizeof(int)); memcpy(to->numsi, from->numsi, (to->n+1)*sizeof(int)); } static game_state *dup_game(const game_state *state) { game_state *ret = blank_game(state->w, state->h); dup_game_to(ret, state); return ret; } static void free_game(game_state *state) { sfree(state->dirs); sfree(state->nums); sfree(state->flags); sfree(state->next); sfree(state->prev); sfree(state->dsf); sfree(state->numsi); sfree(state); } static void unpick_desc(const game_params *params, const char *desc, game_state **sout, char **mout) { game_state *state = blank_game(params->w, params->h); char *msg = NULL, c; int num = 0, i = 0; while (*desc) { if (i >= state->n) { msg = "Game description longer than expected"; goto done; } c = *desc; if (isdigit((unsigned char)c)) { num = (num*10) + (int)(c-'0'); if (num > state->n) { msg = "Number too large"; goto done; } } else if ((c-'a') >= 0 && (c-'a') < DIR_MAX) { state->nums[i] = num; state->flags[i] = num ? FLAG_IMMUTABLE : 0; num = 0; state->dirs[i] = c - 'a'; i++; } else if (!*desc) { msg = "Game description shorter than expected"; goto done; } else { msg = "Game description contains unexpected characters"; goto done; } desc++; } if (i < state->n) { msg = "Game description shorter than expected"; goto done; } done: if (msg) { /* sth went wrong. */ if (mout) *mout = msg; free_game(state); } else { if (mout) *mout = NULL; if (sout) *sout = state; else free_game(state); } } static char *generate_desc(game_state *state, int issolve) { char *ret, buf[80]; int retlen, i, k; ret = NULL; retlen = 0; if (issolve) { ret = sresize(ret, 2, char); ret[0] = 'S'; ret[1] = '\0'; retlen += 1; } for (i = 0; i < state->n; i++) { if (state->nums[i]) k = sprintf(buf, "%d%c", state->nums[i], (int)(state->dirs[i]+'a')); else k = sprintf(buf, "%c", (int)(state->dirs[i]+'a')); ret = sresize(ret, retlen + k + 1, char); strcpy(ret + retlen, buf); retlen += k; } return ret; } /* --- Game generation --- */ /* Fills in preallocated arrays ai (indices) and ad (directions) * showing all non-numbered cells adjacent to index i, returns length */ /* This function has been somewhat optimised... */ static int cell_adj(game_state *state, int i, int *ai, int *ad) { int n = 0, a, x, y, sx, sy, dx, dy, newi; int w = state->w, h = state->h; sx = i % w; sy = i / w; for (a = 0; a < DIR_MAX; a++) { x = sx; y = sy; dx = dxs[a]; dy = dys[a]; while (1) { x += dx; y += dy; if (x < 0 || y < 0 || x >= w || y >= h) break; newi = y*w + x; if (state->nums[newi] == 0) { ai[n] = newi; ad[n] = a; n++; } } } return n; } static int new_game_fill(game_state *state, random_state *rs, int headi, int taili) { int nfilled, an, ret = 0, j; int *aidx, *adir; aidx = snewn(state->n, int); adir = snewn(state->n, int); debug(("new_game_fill: headi=%d, taili=%d.", headi, taili)); memset(state->nums, 0, state->n*sizeof(int)); state->nums[headi] = 1; state->nums[taili] = state->n; state->dirs[taili] = 0; nfilled = 2; while (nfilled < state->n) { /* Try and expand _from_ headi; keep going if there's only one * place to go to. */ an = cell_adj(state, headi, aidx, adir); do { if (an == 0) goto done; j = random_upto(rs, an); state->dirs[headi] = adir[j]; state->nums[aidx[j]] = state->nums[headi] + 1; nfilled++; headi = aidx[j]; an = cell_adj(state, headi, aidx, adir); } while (an == 1); /* Try and expand _to_ taili; keep going if there's only one * place to go to. */ an = cell_adj(state, taili, aidx, adir); do { if (an == 0) goto done; j = random_upto(rs, an); state->dirs[aidx[j]] = DIR_OPPOSITE(adir[j]); state->nums[aidx[j]] = state->nums[taili] - 1; nfilled++; taili = aidx[j]; an = cell_adj(state, taili, aidx, adir); } while (an == 1); } /* If we get here we have headi and taili set but unconnected * by direction: we need to set headi's direction so as to point * at taili. */ state->dirs[headi] = whichdiri(state, headi, taili); /* it could happen that our last two weren't in line; if that's the * case, we have to start again. */ if (state->dirs[headi] != -1) ret = 1; done: sfree(aidx); sfree(adir); return ret; } /* Better generator: with the 'generate, sprinkle numbers, solve, * repeat' algorithm we're _never_ generating anything greater than * 6x6, and spending all of our time in new_game_fill (and very little * in solve_state). * * So, new generator steps: * generate the grid, at random (same as now). Numbers 1 and N get immutable flag immediately. * squirrel that away for the solved state. * * (solve:) Try and solve it. * If we solved it, we're done: * generate the description from current immutable numbers, * free stuff that needs freeing, * return description + solved state. * If we didn't solve it: * count #tiles in state we've made deductions about. * while (1): * randomise a scratch array. * for each index in scratch (in turn): * if the cell isn't empty, continue (through scratch array) * set number + immutable in state. * try and solve state. * if we've solved it, we're done. * otherwise, count #tiles. If it's more than we had before: * good, break from this loop and re-randomise. * otherwise (number didn't help): * remove number and try next in scratch array. * if we've got to the end of the scratch array, no luck: free everything we need to, and go back to regenerate the grid. */ static int solve_state(game_state *state); static void debug_desc(const char *what, game_state *state) { #if DEBUGGING { char *desc = generate_desc(state, 0); debug(("%s game state: %dx%d:%s", what, state->w, state->h, desc)); sfree(desc); } #endif } /* Expects a fully-numbered game_state on input, and makes sure * FLAG_IMMUTABLE is only set on those numbers we need to solve * (as for a real new-game); returns 1 if it managed * this (such that it could solve it), or 0 if not. */ static int new_game_strip(game_state *state, random_state *rs) { int *scratch, i, j, ret = 1; game_state *copy = dup_game(state); debug(("new_game_strip.")); strip_nums(copy); debug_desc("Stripped", copy); if (solve_state(copy) > 0) { debug(("new_game_strip: soluble immediately after strip.")); free_game(copy); return 1; } scratch = snewn(state->n, int); for (i = 0; i < state->n; i++) scratch[i] = i; shuffle(scratch, state->n, sizeof(int), rs); /* This is scungy. It might just be quick enough. * It goes through, adding set numbers in empty squares * until either we run out of empty squares (in the one * we're half-solving) or else we solve it properly. * NB that we run the entire solver each time, which * strips the grid beforehand; we will save time if we * avoid that. */ for (i = 0; i < state->n; i++) { j = scratch[i]; if (copy->nums[j] > 0 && copy->nums[j] <= state->n) continue; /* already solved to a real number here. */ assert(state->nums[j] <= state->n); debug(("new_game_strip: testing add IMMUTABLE number %d at square (%d,%d).", state->nums[j], j%state->w, j/state->w)); copy->nums[j] = state->nums[j]; copy->flags[j] |= FLAG_IMMUTABLE; state->flags[j] |= FLAG_IMMUTABLE; debug_state("Copy of state: ", copy); strip_nums(copy); if (solve_state(copy) > 0) goto solved; assert(check_nums(state, copy, 1)); } ret = 0; goto done; solved: debug(("new_game_strip: now solved.")); /* Since we added basically at random, try now to remove numbers * and see if we can still solve it; if we can (still), really * remove the number. Make sure we don't remove the anchor numbers * 1 and N. */ for (i = 0; i < state->n; i++) { j = scratch[i]; if ((state->flags[j] & FLAG_IMMUTABLE) && (state->nums[j] != 1 && state->nums[j] != state->n)) { debug(("new_game_strip: testing remove IMMUTABLE number %d at square (%d,%d).", state->nums[j], j%state->w, j/state->w)); state->flags[j] &= ~FLAG_IMMUTABLE; dup_game_to(copy, state); strip_nums(copy); if (solve_state(copy) > 0) { assert(check_nums(state, copy, 0)); debug(("new_game_strip: OK, removing number")); } else { assert(state->nums[j] <= state->n); debug(("new_game_strip: cannot solve, putting IMMUTABLE back.")); copy->nums[j] = state->nums[j]; state->flags[j] |= FLAG_IMMUTABLE; } } } done: debug(("new_game_strip: %ssuccessful.", ret ? "" : "not ")); sfree(scratch); free_game(copy); return ret; } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { game_state *state = blank_game(params->w, params->h); char *ret; int headi, taili; generate: blank_game_into(state); /* keep trying until we fill successfully. */ do { if (params->force_corner_start) { headi = 0; taili = state->n-1; } else { do { headi = random_upto(rs, state->n); taili = random_upto(rs, state->n); } while (headi == taili); } } while (!new_game_fill(state, rs, headi, taili)); debug_state("Filled game:", state); assert(state->nums[headi] <= state->n); assert(state->nums[taili] <= state->n); state->flags[headi] |= FLAG_IMMUTABLE; state->flags[taili] |= FLAG_IMMUTABLE; /* This will have filled in directions and _all_ numbers. * Store the game definition for this, as the solved-state. */ if (!new_game_strip(state, rs)) { goto generate; } strip_nums(state); { game_state *tosolve = dup_game(state); assert(solve_state(tosolve) > 0); free_game(tosolve); } ret = generate_desc(state, 0); free_game(state); return ret; } static char *validate_desc(const game_params *params, const char *desc) { char *ret = NULL; unpick_desc(params, desc, NULL, &ret); return ret; } /* --- Linked-list and numbers array --- */ /* Assuming numbers are always up-to-date, there are only four possibilities * for regions changing after a single valid move: * * 1) two differently-coloured regions being combined (the resulting colouring * should be based on the larger of the two regions) * 2) a numbered region having a single number added to the start (the * region's colour will remain, and the numbers will shift by 1) * 3) a numbered region having a single number added to the end (the * region's colour and numbering remains as-is) * 4) two unnumbered squares being joined (will pick the smallest unused set * of colours to use for the new region). * * There should never be any complications with regions containing 3 colours * being combined, since two of those colours should have been merged on a * previous move. * * Most of the complications are in ensuring we don't accidentally set two * regions with the same colour (e.g. if a region was split). If this happens * we always try and give the largest original portion the original colour. */ #define COLOUR(a) ((a) / (state->n+1)) #define START(c) ((c) * (state->n+1)) struct head_meta { int i; /* position */ int sz; /* size of region */ int start; /* region start number preferred, or 0 if !preference */ int preference; /* 0 if we have no preference (and should just pick one) */ const char *why; }; static void head_number(game_state *state, int i, struct head_meta *head) { int off = 0, ss, j = i, c, n, sz; /* Insist we really were passed the head of a chain. */ assert(state->prev[i] == -1 && state->next[i] != -1); head->i = i; head->sz = dsf_size(state->dsf, i); head->why = NULL; /* Search through this chain looking for real numbers, checking that * they match up (if there are more than one). */ head->preference = 0; while (j != -1) { if (state->flags[j] & FLAG_IMMUTABLE) { ss = state->nums[j] - off; if (!head->preference) { head->start = ss; head->preference = 1; head->why = "contains cell with immutable number"; } else if (head->start != ss) { debug(("head_number: chain with non-sequential numbers!")); state->impossible = 1; } } off++; j = state->next[j]; assert(j != i); /* we have created a loop, obviously wrong */ } if (head->preference) goto done; if (state->nums[i] == 0 && state->nums[state->next[i]] > state->n) { /* (probably) empty cell onto the head of a coloured region: * make sure we start at a 0 offset. */ head->start = START(COLOUR(state->nums[state->next[i]])); head->preference = 1; head->why = "adding blank cell to head of numbered region"; } else if (state->nums[i] <= state->n) { /* if we're 0 we're probably just blank -- but even if we're a * (real) numbered region, we don't have an immutable number * in it (any more) otherwise it'd have been caught above, so * reassign the colour. */ head->start = 0; head->preference = 0; head->why = "lowest available colour group"; } else { c = COLOUR(state->nums[i]); n = 1; sz = dsf_size(state->dsf, i); j = i; while (state->next[j] != -1) { j = state->next[j]; if (state->nums[j] == 0 && state->next[j] == -1) { head->start = START(c); head->preference = 1; head->why = "adding blank cell to end of numbered region"; goto done; } if (COLOUR(state->nums[j]) == c) n++; else { int start_alternate = START(COLOUR(state->nums[j])); if (n < (sz - n)) { head->start = start_alternate; head->preference = 1; head->why = "joining two coloured regions, swapping to larger colour"; } else { head->start = START(c); head->preference = 1; head->why = "joining two coloured regions, taking largest"; } goto done; } } /* If we got here then we may have split a region into * two; make sure we don't assign a colour we've already used. */ if (c == 0) { /* not convinced this shouldn't be an assertion failure here. */ head->start = 0; head->preference = 0; } else { head->start = START(c); head->preference = 1; } head->why = "got to end of coloured region"; } done: assert(head->why != NULL); if (head->preference) debug(("Chain at (%d,%d) numbered for preference at %d (colour %d): %s.", head->i%state->w, head->i/state->w, head->start, COLOUR(head->start), head->why)); else debug(("Chain at (%d,%d) using next available colour: %s.", head->i%state->w, head->i/state->w, head->why)); } #if 0 static void debug_numbers(game_state *state) { int i, w=state->w; for (i = 0; i < state->n; i++) { debug(("(%d,%d) --> (%d,%d) --> (%d,%d)", state->prev[i]==-1 ? -1 : state->prev[i]%w, state->prev[i]==-1 ? -1 : state->prev[i]/w, i%w, i/w, state->next[i]==-1 ? -1 : state->next[i]%w, state->next[i]==-1 ? -1 : state->next[i]/w)); } w = w+1; } #endif static void connect_numbers(game_state *state) { int i, di, dni; dsf_init(state->dsf, state->n); for (i = 0; i < state->n; i++) { if (state->next[i] != -1) { assert(state->prev[state->next[i]] == i); di = dsf_canonify(state->dsf, i); dni = dsf_canonify(state->dsf, state->next[i]); if (di == dni) { debug(("connect_numbers: chain forms a loop.")); state->impossible = 1; } dsf_merge(state->dsf, di, dni); } } } static int compare_heads(const void *a, const void *b) { struct head_meta *ha = (struct head_meta *)a; struct head_meta *hb = (struct head_meta *)b; /* Heads with preferred colours first... */ if (ha->preference && !hb->preference) return -1; if (hb->preference && !ha->preference) return 1; /* ...then heads with low colours first... */ if (ha->start < hb->start) return -1; if (ha->start > hb->start) return 1; /* ... then large regions first... */ if (ha->sz > hb->sz) return -1; if (ha->sz < hb->sz) return 1; /* ... then position. */ if (ha->i > hb->i) return -1; if (ha->i < hb->i) return 1; return 0; } static int lowest_start(game_state *state, struct head_meta *heads, int nheads) { int n, c; /* NB start at 1: colour 0 is real numbers */ for (c = 1; c < state->n; c++) { for (n = 0; n < nheads; n++) { if (COLOUR(heads[n].start) == c) goto used; } return c; used: ; } assert(!"No available colours!"); return 0; } static void update_numbers(game_state *state) { int i, j, n, nnum, nheads; struct head_meta *heads = snewn(state->n, struct head_meta); for (n = 0; n < state->n; n++) state->numsi[n] = -1; for (i = 0; i < state->n; i++) { if (state->flags[i] & FLAG_IMMUTABLE) { assert(state->nums[i] > 0); assert(state->nums[i] <= state->n); state->numsi[state->nums[i]] = i; } else if (state->prev[i] == -1 && state->next[i] == -1) state->nums[i] = 0; } connect_numbers(state); /* Construct an array of the heads of all current regions, together * with their preferred colours. */ nheads = 0; for (i = 0; i < state->n; i++) { /* Look for a cell that is the start of a chain * (has a next but no prev). */ if (state->prev[i] != -1 || state->next[i] == -1) continue; head_number(state, i, &heads[nheads++]); } /* Sort that array: * - heads with preferred colours first, then * - heads with low colours first, then * - large regions first */ qsort(heads, nheads, sizeof(struct head_meta), compare_heads); /* Remove duplicate-coloured regions. */ for (n = nheads-1; n >= 0; n--) { /* order is important! */ if ((n != 0) && (heads[n].start == heads[n-1].start)) { /* We have a duplicate-coloured region: since we're * sorted in size order and this is not the first * of its colour it's not the largest: recolour it. */ heads[n].start = START(lowest_start(state, heads, nheads)); heads[n].preference = -1; /* '-1' means 'was duplicate' */ } else if (!heads[n].preference) { assert(heads[n].start == 0); heads[n].start = START(lowest_start(state, heads, nheads)); } } debug(("Region colouring after duplicate removal:")); for (n = 0; n < nheads; n++) { debug((" Chain at (%d,%d) sz %d numbered at %d (colour %d): %s%s", heads[n].i % state->w, heads[n].i / state->w, heads[n].sz, heads[n].start, COLOUR(heads[n].start), heads[n].why, heads[n].preference == 0 ? " (next available)" : heads[n].preference < 0 ? " (duplicate, next available)" : "")); nnum = heads[n].start; j = heads[n].i; while (j != -1) { if (!(state->flags[j] & FLAG_IMMUTABLE)) { if (nnum > 0 && nnum <= state->n) state->numsi[nnum] = j; state->nums[j] = nnum; } nnum++; j = state->next[j]; assert(j != heads[n].i); /* loop?! */ } } /*debug_numbers(state);*/ sfree(heads); } static int check_completion(game_state *state, int mark_errors) { int n, j, k, error = 0, complete; /* NB This only marks errors that are possible to perpetrate with * the current UI in interpret_move. Things like forming loops in * linked sections and having numbers not add up should be forbidden * by the code elsewhere, so we don't bother marking those (because * it would add lots of tricky drawing code for very little gain). */ if (mark_errors) { for (j = 0; j < state->n; j++) state->flags[j] &= ~FLAG_ERROR; } /* Search for repeated numbers. */ for (j = 0; j < state->n; j++) { if (state->nums[j] > 0 && state->nums[j] <= state->n) { for (k = j+1; k < state->n; k++) { if (state->nums[k] == state->nums[j]) { if (mark_errors) { state->flags[j] |= FLAG_ERROR; state->flags[k] |= FLAG_ERROR; } error = 1; } } } } /* Search and mark numbers n not pointing to n+1; if any numbers * are missing we know we've not completed. */ complete = 1; for (n = 1; n < state->n; n++) { if (state->numsi[n] == -1 || state->numsi[n+1] == -1) complete = 0; else if (!ispointingi(state, state->numsi[n], state->numsi[n+1])) { if (mark_errors) { state->flags[state->numsi[n]] |= FLAG_ERROR; state->flags[state->numsi[n+1]] |= FLAG_ERROR; } error = 1; } else { /* make sure the link is explicitly made here; for instance, this * is nice if the user drags from 2 out (making 3) and a 4 is also * visible; this ensures that the link from 3 to 4 is also made. */ if (mark_errors) makelink(state, state->numsi[n], state->numsi[n+1]); } } /* Search and mark numbers less than 0, or 0 with links. */ for (n = 1; n < state->n; n++) { if ((state->nums[n] < 0) || (state->nums[n] == 0 && (state->next[n] != -1 || state->prev[n] != -1))) { error = 1; if (mark_errors) state->flags[n] |= FLAG_ERROR; } } if (error) return 0; return complete; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { game_state *state = NULL; unpick_desc(params, desc, &state, NULL); if (!state) assert(!"new_game failed to unpick"); update_numbers(state); check_completion(state, 1); /* update any auto-links */ return state; } /* --- Solver --- */ /* If a tile has a single tile it can link _to_, or there's only a single * location that can link to a given tile, fill that link in. */ static int solve_single(game_state *state, game_state *copy, int *from) { int i, j, sx, sy, x, y, d, poss, w=state->w, nlinks = 0; /* The from array is a list of 'which square can link _to_ us'; * we start off with from as '-1' (meaning 'not found'); if we find * something that can link to us it is set to that index, and then if * we find another we set it to -2. */ memset(from, -1, state->n*sizeof(int)); /* poss is 'can I link to anything' with the same meanings. */ for (i = 0; i < state->n; i++) { if (state->next[i] != -1) continue; if (state->nums[i] == state->n) continue; /* no next from last no. */ d = state->dirs[i]; poss = -1; sx = x = i%w; sy = y = i/w; while (1) { x += dxs[d]; y += dys[d]; if (!INGRID(state, x, y)) break; if (!isvalidmove(state, 1, sx, sy, x, y)) continue; /* can't link to somewhere with a back-link we would have to * break (the solver just doesn't work like this). */ j = y*w+x; if (state->prev[j] != -1) continue; if (state->nums[i] > 0 && state->nums[j] > 0 && state->nums[i] <= state->n && state->nums[j] <= state->n && state->nums[j] == state->nums[i]+1) { debug(("Solver: forcing link through existing consecutive numbers.")); poss = j; from[j] = i; break; } /* if there's been a valid move already, we have to move on; * we can't make any deductions here. */ poss = (poss == -1) ? j : -2; /* Modify the from array as described above (which is enumerating * what points to 'j' in a similar way). */ from[j] = (from[j] == -1) ? i : -2; } if (poss == -2) { /*debug(("Solver: (%d,%d) has multiple possible next squares.", sx, sy));*/ ; } else if (poss == -1) { debug(("Solver: nowhere possible for (%d,%d) to link to.", sx, sy)); copy->impossible = 1; return -1; } else { debug(("Solver: linking (%d,%d) to only possible next (%d,%d).", sx, sy, poss%w, poss/w)); makelink(copy, i, poss); nlinks++; } } for (i = 0; i < state->n; i++) { if (state->prev[i] != -1) continue; if (state->nums[i] == 1) continue; /* no prev from 1st no. */ x = i%w; y = i/w; if (from[i] == -1) { debug(("Solver: nowhere possible to link to (%d,%d)", x, y)); copy->impossible = 1; return -1; } else if (from[i] == -2) { /*debug(("Solver: (%d,%d) has multiple possible prev squares.", x, y));*/ ; } else { debug(("Solver: linking only possible prev (%d,%d) to (%d,%d).", from[i]%w, from[i]/w, x, y)); makelink(copy, from[i], i); nlinks++; } } return nlinks; } /* Returns 1 if we managed to solve it, 0 otherwise. */ static int solve_state(game_state *state) { game_state *copy = dup_game(state); int *scratch = snewn(state->n, int), ret; debug_state("Before solver: ", state); while (1) { update_numbers(state); if (solve_single(state, copy, scratch)) { dup_game_to(state, copy); if (state->impossible) break; else continue; } break; } free_game(copy); sfree(scratch); update_numbers(state); ret = state->impossible ? -1 : check_completion(state, 0); debug(("Solver finished: %s", ret < 0 ? "impossible" : ret > 0 ? "solved" : "not solved")); debug_state("After solver: ", state); return ret; } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { game_state *tosolve; char *ret = NULL; int result; tosolve = dup_game(currstate); result = solve_state(tosolve); if (result > 0) ret = generate_desc(tosolve, 1); free_game(tosolve); if (ret) return ret; tosolve = dup_game(state); result = solve_state(tosolve); if (result < 0) *error = "Puzzle is impossible."; else if (result == 0) *error = "Unable to solve puzzle."; else ret = generate_desc(tosolve, 1); free_game(tosolve); return ret; } /* --- UI and move routines. --- */ struct game_ui { int cx, cy, cshow; int dragging, drag_is_from; int sx, sy; /* grid coords of start cell */ int dx, dy; /* pixel coords of drag posn */ }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); /* NB: if this is ever changed to as to require more than a structure * copy to clone, there's code that needs fixing in game_redraw too. */ ui->cx = ui->cy = ui->cshow = 0; ui->dragging = 0; ui->sx = ui->sy = ui->dx = ui->dy = 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) { if (!oldstate->completed && newstate->completed) ui->cshow = ui->dragging = 0; } struct game_drawstate { int tilesize, started, solved; int w, h, n; int *nums, *dirp; unsigned int *f; double angle_offset; int dragging, dx, dy; blitter *dragb; }; static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int mx, int my, int button) { int x = FROMCOORD(mx), y = FROMCOORD(my), w = state->w; char buf[80]; if (IS_CURSOR_MOVE(button)) { move_cursor(button, &ui->cx, &ui->cy, state->w, state->h, 0); ui->cshow = 1; if (ui->dragging) { ui->dx = COORD(ui->cx) + TILE_SIZE/2; ui->dy = COORD(ui->cy) + TILE_SIZE/2; } return ""; } else if (IS_CURSOR_SELECT(button)) { if (!ui->cshow) ui->cshow = 1; else if (ui->dragging) { ui->dragging = FALSE; if (ui->sx == ui->cx && ui->sy == ui->cy) return ""; if (ui->drag_is_from) { if (!isvalidmove(state, 0, ui->sx, ui->sy, ui->cx, ui->cy)) return ""; sprintf(buf, "L%d,%d-%d,%d", ui->sx, ui->sy, ui->cx, ui->cy); } else { if (!isvalidmove(state, 0, ui->cx, ui->cy, ui->sx, ui->sy)) return ""; sprintf(buf, "L%d,%d-%d,%d", ui->cx, ui->cy, ui->sx, ui->sy); } return dupstr(buf); } else { ui->dragging = TRUE; ui->sx = ui->cx; ui->sy = ui->cy; ui->dx = COORD(ui->cx) + TILE_SIZE/2; ui->dy = COORD(ui->cy) + TILE_SIZE/2; ui->drag_is_from = (button == CURSOR_SELECT) ? 1 : 0; } return ""; } if (IS_MOUSE_DOWN(button)) { if (ui->cshow) { ui->cshow = ui->dragging = 0; } assert(!ui->dragging); if (!INGRID(state, x, y)) return NULL; if (button == LEFT_BUTTON) { /* disallow dragging from the final number. */ if ((state->nums[y*w+x] == state->n) && (state->flags[y*w+x] & FLAG_IMMUTABLE)) return NULL; } else if (button == RIGHT_BUTTON) { /* disallow dragging to the first number. */ if ((state->nums[y*w+x] == 1) && (state->flags[y*w+x] & FLAG_IMMUTABLE)) return NULL; } ui->dragging = TRUE; ui->drag_is_from = (button == LEFT_BUTTON) ? 1 : 0; ui->sx = x; ui->sy = y; ui->dx = mx; ui->dy = my; ui->cshow = 0; return ""; } else if (IS_MOUSE_DRAG(button) && ui->dragging) { ui->dx = mx; ui->dy = my; return ""; } else if (IS_MOUSE_RELEASE(button) && ui->dragging) { ui->dragging = FALSE; if (ui->sx == x && ui->sy == y) return ""; /* single click */ if (!INGRID(state, x, y)) { int si = ui->sy*w+ui->sx; if (state->prev[si] == -1 && state->next[si] == -1) return ""; sprintf(buf, "%c%d,%d", (int)(ui->drag_is_from ? 'C' : 'X'), ui->sx, ui->sy); return dupstr(buf); } if (ui->drag_is_from) { if (!isvalidmove(state, 0, ui->sx, ui->sy, x, y)) return ""; sprintf(buf, "L%d,%d-%d,%d", ui->sx, ui->sy, x, y); } else { if (!isvalidmove(state, 0, x, y, ui->sx, ui->sy)) return ""; sprintf(buf, "L%d,%d-%d,%d", x, y, ui->sx, ui->sy); } return dupstr(buf); } /* else if (button == 'H' || button == 'h') return dupstr("H"); */ else if ((button == 'x' || button == 'X') && ui->cshow) { int si = ui->cy*w + ui->cx; if (state->prev[si] == -1 && state->next[si] == -1) return ""; sprintf(buf, "%c%d,%d", (int)((button == 'x') ? 'C' : 'X'), ui->cx, ui->cy); return dupstr(buf); } return NULL; } static void unlink_cell(game_state *state, int si) { debug(("Unlinking (%d,%d).", si%state->w, si/state->w)); if (state->prev[si] != -1) { debug((" ... removing prev link from (%d,%d).", state->prev[si]%state->w, state->prev[si]/state->w)); state->next[state->prev[si]] = -1; state->prev[si] = -1; } if (state->next[si] != -1) { debug((" ... removing next link to (%d,%d).", state->next[si]%state->w, state->next[si]/state->w)); state->prev[state->next[si]] = -1; state->next[si] = -1; } } static game_state *execute_move(const game_state *state, const char *move) { game_state *ret = NULL; int sx, sy, ex, ey, si, ei, w = state->w; char c; debug(("move: %s", move)); if (move[0] == 'S') { game_params p; game_state *tmp; char *valid; int i; p.w = state->w; p.h = state->h; valid = validate_desc(&p, move+1); if (valid) { debug(("execute_move: move not valid: %s", valid)); return NULL; } ret = dup_game(state); tmp = new_game(NULL, &p, move+1); for (i = 0; i < state->n; i++) { ret->prev[i] = tmp->prev[i]; ret->next[i] = tmp->next[i]; } free_game(tmp); ret->used_solve = 1; } else if (sscanf(move, "L%d,%d-%d,%d", &sx, &sy, &ex, &ey) == 4) { if (!isvalidmove(state, 0, sx, sy, ex, ey)) return NULL; ret = dup_game(state); si = sy*w+sx; ei = ey*w+ex; makelink(ret, si, ei); } else if (sscanf(move, "%c%d,%d", &c, &sx, &sy) == 3) { if (c != 'C' && c != 'X') return NULL; if (!INGRID(state, sx, sy)) return NULL; si = sy*w+sx; if (state->prev[si] == -1 && state->next[si] == -1) return NULL; ret = dup_game(state); if (c == 'C') { /* Unlink the single cell we dragged from the board. */ unlink_cell(ret, si); } else { int i, set, sset = state->nums[si] / (state->n+1); for (i = 0; i < state->n; i++) { /* Unlink all cells in the same set as the one we dragged * from the board. */ if (state->nums[i] == 0) continue; set = state->nums[i] / (state->n+1); if (set != sset) continue; unlink_cell(ret, i); } } } else if (strcmp(move, "H") == 0) { ret = dup_game(state); solve_state(ret); } if (ret) { update_numbers(ret); if (check_completion(ret, 1)) ret->completed = 1; } 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, order; } ads, *ds = &ads; ads.tilesize = tilesize; *x = TILE_SIZE * params->w + 2 * BORDER; *y = TILE_SIZE * params->h + 2 * BORDER; } static void game_set_size(drawing *dr, game_drawstate *ds, const game_params *params, int tilesize) { ds->tilesize = tilesize; assert(TILE_SIZE > 0); assert(!ds->dragb); ds->dragb = blitter_new(dr, BLITTER_SIZE, BLITTER_SIZE); } /* Colours chosen from the webby palette to work as a background to black text, * W then some plausible approximation to pastelly ROYGBIV; we then interpolate * between consecutive pairs to give another 8 (and then the drawing routine * will reuse backgrounds). */ static const unsigned long bgcols[8] = { 0xffffff, /* white */ 0xffa07a, /* lightsalmon */ 0x98fb98, /* green */ 0x7fffd4, /* aquamarine */ 0x9370db, /* medium purple */ 0xffa500, /* orange */ 0x87cefa, /* lightskyblue */ 0xffff00, /* yellow */ }; static float *game_colours(frontend *fe, int *ncolours) { float *ret = snewn(3 * NCOLOURS, float); int c, i; game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT); for (i = 0; i < 3; i++) { ret[COL_NUMBER * 3 + i] = 0.0F; ret[COL_ARROW * 3 + i] = 0.0F; ret[COL_CURSOR * 3 + i] = ret[COL_BACKGROUND * 3 + i] / 2.0F; ret[COL_GRID * 3 + i] = ret[COL_BACKGROUND * 3 + i] / 1.3F; } ret[COL_NUMBER_SET * 3 + 0] = 0.0F; ret[COL_NUMBER_SET * 3 + 1] = 0.0F; ret[COL_NUMBER_SET * 3 + 2] = 0.9F; ret[COL_ERROR * 3 + 0] = 1.0F; ret[COL_ERROR * 3 + 1] = 0.0F; ret[COL_ERROR * 3 + 2] = 0.0F; ret[COL_DRAG_ORIGIN * 3 + 0] = 0.2F; ret[COL_DRAG_ORIGIN * 3 + 1] = 1.0F; ret[COL_DRAG_ORIGIN * 3 + 2] = 0.2F; for (c = 0; c < 8; c++) { ret[(COL_B0 + c) * 3 + 0] = (float)((bgcols[c] & 0xff0000) >> 16) / 256.0F; ret[(COL_B0 + c) * 3 + 1] = (float)((bgcols[c] & 0xff00) >> 8) / 256.0F; ret[(COL_B0 + c) * 3 + 2] = (float)((bgcols[c] & 0xff)) / 256.0F; } for (c = 0; c < 8; c++) { for (i = 0; i < 3; i++) { ret[(COL_B0 + 8 + c) * 3 + i] = (ret[(COL_B0 + c) * 3 + i] + ret[(COL_B0 + c + 1) * 3 + i]) / 2.0F; } } #define average(r,a,b,w) do { \ for (i = 0; i < 3; i++) \ ret[(r)*3+i] = ret[(a)*3+i] + w * (ret[(b)*3+i] - ret[(a)*3+i]); \ } while (0) average(COL_ARROW_BG_DIM, COL_BACKGROUND, COL_ARROW, 0.1F); average(COL_NUMBER_SET_MID, COL_B0, COL_NUMBER_SET, 0.3F); for (c = 0; c < NBACKGROUNDS; c++) { /* I assume here that COL_ARROW and COL_NUMBER are the same. * Otherwise I'd need two sets of COL_M*. */ average(COL_M0 + c, COL_B0 + c, COL_NUMBER, 0.3F); average(COL_D0 + c, COL_B0 + c, COL_NUMBER, 0.1F); average(COL_X0 + c, COL_BACKGROUND, COL_B0 + c, 0.5F); } *ncolours = NCOLOURS; return ret; } static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { struct game_drawstate *ds = snew(struct game_drawstate); int i; ds->tilesize = ds->started = ds->solved = 0; ds->w = state->w; ds->h = state->h; ds->n = state->n; ds->nums = snewn(state->n, int); ds->dirp = snewn(state->n, int); ds->f = snewn(state->n, unsigned int); for (i = 0; i < state->n; i++) { ds->nums[i] = 0; ds->dirp[i] = -1; ds->f[i] = 0; } ds->angle_offset = 0.0F; ds->dragging = ds->dx = ds->dy = 0; ds->dragb = NULL; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->nums); sfree(ds->dirp); sfree(ds->f); if (ds->dragb) blitter_free(dr, ds->dragb); sfree(ds); } /* cx, cy are top-left corner. sz is the 'radius' of the arrow. * ang is in radians, clockwise from 0 == straight up. */ static void draw_arrow(drawing *dr, int cx, int cy, int sz, double ang, int cfill, int cout) { int coords[14]; int xdx, ydx, xdy, ydy, xdx3, xdy3; double s = sin(ang), c = cos(ang); xdx3 = (int)(sz * (c/3 + 1) + 0.5) - sz; xdy3 = (int)(sz * (s/3 + 1) + 0.5) - sz; xdx = (int)(sz * (c + 1) + 0.5) - sz; xdy = (int)(sz * (s + 1) + 0.5) - sz; ydx = -xdy; ydy = xdx; coords[2*0 + 0] = cx - ydx; coords[2*0 + 1] = cy - ydy; coords[2*1 + 0] = cx + xdx; coords[2*1 + 1] = cy + xdy; coords[2*2 + 0] = cx + xdx3; coords[2*2 + 1] = cy + xdy3; coords[2*3 + 0] = cx + xdx3 + ydx; coords[2*3 + 1] = cy + xdy3 + ydy; coords[2*4 + 0] = cx - xdx3 + ydx; coords[2*4 + 1] = cy - xdy3 + ydy; coords[2*5 + 0] = cx - xdx3; coords[2*5 + 1] = cy - xdy3; coords[2*6 + 0] = cx - xdx; coords[2*6 + 1] = cy - xdy; draw_polygon(dr, coords, 7, cfill, cout); } static void draw_arrow_dir(drawing *dr, int cx, int cy, int sz, int dir, int cfill, int cout, double angle_offset) { double ang = 2.0 * PI * (double)dir / 8.0 + angle_offset; draw_arrow(dr, cx, cy, sz, ang, cfill, cout); } /* cx, cy are centre coordinates.. */ static void draw_star(drawing *dr, int cx, int cy, int rad, int npoints, int cfill, int cout, double angle_offset) { int *coords, n; double a, r; assert(npoints > 0); coords = snewn(npoints * 2 * 2, int); for (n = 0; n < npoints * 2; n++) { a = 2.0 * PI * ((double)n / ((double)npoints * 2.0)) + angle_offset; r = (n % 2) ? (double)rad/2.0 : (double)rad; /* We're rotating the point at (0, -r) by a degrees */ coords[2*n+0] = cx + (int)( r * sin(a)); coords[2*n+1] = cy + (int)(-r * cos(a)); } draw_polygon(dr, coords, npoints*2, cfill, cout); sfree(coords); } static int num2col(game_drawstate *ds, int num) { int set = num / (ds->n+1); if (num <= 0 || set == 0) return COL_B0; return COL_B0 + 1 + ((set-1) % 15); } #define ARROW_HALFSZ (7 * TILE_SIZE / 32) #define F_CUR 0x001 /* Cursor on this tile. */ #define F_DRAG_SRC 0x002 /* Tile is source of a drag. */ #define F_ERROR 0x004 /* Tile marked in error. */ #define F_IMMUTABLE 0x008 /* Tile (number) is immutable. */ #define F_ARROW_POINT 0x010 /* Tile points to other tile */ #define F_ARROW_INPOINT 0x020 /* Other tile points in here. */ #define F_DIM 0x040 /* Tile is dim */ static void tile_redraw(drawing *dr, game_drawstate *ds, int tx, int ty, int dir, int dirp, int num, unsigned int f, double angle_offset, int print_ink) { int cb = TILE_SIZE / 16, textsz; char buf[20]; int arrowcol, sarrowcol, setcol, textcol; int acx, acy, asz, empty = 0; if (num == 0 && !(f & F_ARROW_POINT) && !(f & F_ARROW_INPOINT)) { empty = 1; /* * We don't display text in empty cells: typically these are * signified by num=0. However, in some cases a cell could * have had the number 0 assigned to it if the user made an * error (e.g. tried to connect a chain of length 5 to the * immutable number 4) so we _do_ display the 0 if the cell * has a link in or a link out. */ } /* Calculate colours. */ if (print_ink >= 0) { /* * We're printing, so just do everything in black. */ arrowcol = textcol = print_ink; setcol = sarrowcol = -1; /* placate optimiser */ } else { setcol = empty ? COL_BACKGROUND : num2col(ds, num); #define dim(fg,bg) ( \ (bg)==COL_BACKGROUND ? COL_ARROW_BG_DIM : \ (bg) + COL_D0 - COL_B0 \ ) #define mid(fg,bg) ( \ (fg)==COL_NUMBER_SET ? COL_NUMBER_SET_MID : \ (bg) + COL_M0 - COL_B0 \ ) #define dimbg(bg) ( \ (bg)==COL_BACKGROUND ? COL_BACKGROUND : \ (bg) + COL_X0 - COL_B0 \ ) if (f & F_DRAG_SRC) arrowcol = COL_DRAG_ORIGIN; else if (f & F_DIM) arrowcol = dim(COL_ARROW, setcol); else if (f & F_ARROW_POINT) arrowcol = mid(COL_ARROW, setcol); else arrowcol = COL_ARROW; if ((f & F_ERROR) && !(f & F_IMMUTABLE)) textcol = COL_ERROR; else { if (f & F_IMMUTABLE) textcol = COL_NUMBER_SET; else textcol = COL_NUMBER; if (f & F_DIM) textcol = dim(textcol, setcol); else if (((f & F_ARROW_POINT) || num==ds->n) && ((f & F_ARROW_INPOINT) || num==1)) textcol = mid(textcol, setcol); } if (f & F_DIM) sarrowcol = dim(COL_ARROW, setcol); else sarrowcol = COL_ARROW; } /* Clear tile background */ if (print_ink < 0) { draw_rect(dr, tx, ty, TILE_SIZE, TILE_SIZE, (f & F_DIM) ? dimbg(setcol) : setcol); } /* Draw large (outwards-pointing) arrow. */ asz = ARROW_HALFSZ; /* 'radius' of arrow/star. */ acx = tx+TILE_SIZE/2+asz; /* centre x */ acy = ty+TILE_SIZE/2+asz; /* centre y */ if (num == ds->n && (f & F_IMMUTABLE)) draw_star(dr, acx, acy, asz, 5, arrowcol, arrowcol, angle_offset); else draw_arrow_dir(dr, acx, acy, asz, dir, arrowcol, arrowcol, angle_offset); if (print_ink < 0 && (f & F_CUR)) draw_rect_corners(dr, acx, acy, asz+1, COL_CURSOR); /* Draw dot iff this tile requires a predecessor and doesn't have one. */ if (print_ink < 0) { acx = tx+TILE_SIZE/2-asz; acy = ty+TILE_SIZE/2+asz; if (!(f & F_ARROW_INPOINT) && num != 1) { draw_circle(dr, acx, acy, asz / 4, sarrowcol, sarrowcol); } } /* Draw text (number or set). */ if (!empty) { int set = (num <= 0) ? 0 : num / (ds->n+1); char *p = buf; if (set == 0 || num <= 0) { sprintf(buf, "%d", num); } else { int n = num % (ds->n+1); p += sizeof(buf) - 1; if (n != 0) { sprintf(buf, "+%d", n); /* Just to get the length... */ p -= strlen(buf); sprintf(p, "+%d", n); } else { *p = '\0'; } do { set--; p--; *p = (char)((set % 26)+'a'); set /= 26; } while (set); } textsz = min(2*asz, (TILE_SIZE - 2 * cb) / (int)strlen(p)); draw_text(dr, tx + cb, ty + TILE_SIZE/4, FONT_VARIABLE, textsz, ALIGN_VCENTRE | ALIGN_HLEFT, textcol, p); } if (print_ink < 0) { draw_rect_outline(dr, tx, ty, TILE_SIZE, TILE_SIZE, COL_GRID); draw_update(dr, tx, ty, TILE_SIZE, TILE_SIZE); } } static void draw_drag_indicator(drawing *dr, game_drawstate *ds, const game_state *state, const game_ui *ui, int validdrag) { int dir, w = ds->w, acol = COL_ARROW; int fx = FROMCOORD(ui->dx), fy = FROMCOORD(ui->dy); double ang; if (validdrag) { /* If we could move here, lock the arrow to the appropriate direction. */ dir = ui->drag_is_from ? state->dirs[ui->sy*w+ui->sx] : state->dirs[fy*w+fx]; ang = (2.0 * PI * dir) / 8.0; /* similar to calculation in draw_arrow_dir. */ } else { /* Draw an arrow pointing away from/towards the origin cell. */ int ox = COORD(ui->sx) + TILE_SIZE/2, oy = COORD(ui->sy) + TILE_SIZE/2; double tana, offset; double xdiff = fabs(ox - ui->dx), ydiff = fabs(oy - ui->dy); if (xdiff == 0) { ang = (oy > ui->dy) ? 0.0F : PI; } else if (ydiff == 0) { ang = (ox > ui->dx) ? 3.0F*PI/2.0F : PI/2.0F; } else { if (ui->dx > ox && ui->dy < oy) { tana = xdiff / ydiff; offset = 0.0F; } else if (ui->dx > ox && ui->dy > oy) { tana = ydiff / xdiff; offset = PI/2.0F; } else if (ui->dx < ox && ui->dy > oy) { tana = xdiff / ydiff; offset = PI; } else { tana = ydiff / xdiff; offset = 3.0F * PI / 2.0F; } ang = atan(tana) + offset; } if (!ui->drag_is_from) ang += PI; /* point to origin, not away from. */ } draw_arrow(dr, ui->dx, ui->dy, ARROW_HALFSZ, ang, acol, acol); } 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 x, y, i, w = ds->w, dirp, force = 0; unsigned int f; double angle_offset = 0.0; game_state *postdrop = NULL; if (flashtime > 0.0F) angle_offset = 2.0 * PI * (flashtime / FLASH_SPIN); if (angle_offset != ds->angle_offset) { ds->angle_offset = angle_offset; force = 1; } if (ds->dragging) { assert(ds->dragb); blitter_load(dr, ds->dragb, ds->dx, ds->dy); draw_update(dr, ds->dx, ds->dy, BLITTER_SIZE, BLITTER_SIZE); ds->dragging = FALSE; } /* If an in-progress drag would make a valid move if finished, we * reflect that move in the board display. We let interpret_move do * most of the heavy lifting for us: we have to copy the game_ui so * as not to stomp on the real UI's drag state. */ if (ui->dragging) { game_ui uicopy = *ui; char *movestr = interpret_move(state, &uicopy, ds, ui->dx, ui->dy, LEFT_RELEASE); if (movestr != NULL && strcmp(movestr, "") != 0) { postdrop = execute_move(state, movestr); sfree(movestr); state = postdrop; } } if (!ds->started) { int aw = TILE_SIZE * state->w; int ah = TILE_SIZE * state->h; draw_rect(dr, 0, 0, aw + 2 * BORDER, ah + 2 * BORDER, COL_BACKGROUND); draw_rect_outline(dr, BORDER - 1, BORDER - 1, aw + 2, ah + 2, COL_GRID); draw_update(dr, 0, 0, aw + 2 * BORDER, ah + 2 * BORDER); } for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { i = y*w + x; f = 0; dirp = -1; if (ui->cshow && x == ui->cx && y == ui->cy) f |= F_CUR; if (ui->dragging) { if (x == ui->sx && y == ui->sy) f |= F_DRAG_SRC; else if (ui->drag_is_from) { if (!ispointing(state, ui->sx, ui->sy, x, y)) f |= F_DIM; } else { if (!ispointing(state, x, y, ui->sx, ui->sy)) f |= F_DIM; } } if (state->impossible || state->nums[i] < 0 || state->flags[i] & FLAG_ERROR) f |= F_ERROR; if (state->flags[i] & FLAG_IMMUTABLE) f |= F_IMMUTABLE; if (state->next[i] != -1) f |= F_ARROW_POINT; if (state->prev[i] != -1) { /* Currently the direction here is from our square _back_ * to its previous. We could change this to give the opposite * sense to the direction. */ f |= F_ARROW_INPOINT; dirp = whichdir(x, y, state->prev[i]%w, state->prev[i]/w); } if (state->nums[i] != ds->nums[i] || f != ds->f[i] || dirp != ds->dirp[i] || force || !ds->started) { int sign; { /* * Trivial and foolish configurable option done on * purest whim. With this option enabled, the * victory flash is done by rotating each square * in the opposite direction from its immediate * neighbours, so that they behave like a field of * interlocking gears. With it disabled, they all * rotate in the same direction. Choose for * yourself which is more brain-twisting :-) */ static int gear_mode = -1; if (gear_mode < 0) { char *env = getenv("SIGNPOST_GEARS"); gear_mode = (env && (env[0] == 'y' || env[0] == 'Y')); } if (gear_mode) sign = 1 - 2 * ((x ^ y) & 1); else sign = 1; } tile_redraw(dr, ds, BORDER + x * TILE_SIZE, BORDER + y * TILE_SIZE, state->dirs[i], dirp, state->nums[i], f, sign * angle_offset, -1); ds->nums[i] = state->nums[i]; ds->f[i] = f; ds->dirp[i] = dirp; } } } if (ui->dragging) { ds->dragging = TRUE; ds->dx = ui->dx - BLITTER_SIZE/2; ds->dy = ui->dy - BLITTER_SIZE/2; blitter_save(dr, ds->dragb, ds->dx, ds->dy); draw_drag_indicator(dr, ds, state, ui, postdrop ? 1 : 0); } if (postdrop) free_game(postdrop); if (!ds->started) ds->started = TRUE; } 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 && !newstate->used_solve) return FLASH_SPIN; else return 0.0F; } static int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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) { int pw, ph; game_compute_size(params, 1300, &pw, &ph); *x = pw / 100.0F; *y = ph / 100.0F; } static void game_print(drawing *dr, const game_state *state, int tilesize) { int ink = print_mono_colour(dr, 0); int x, y; /* Fake up just enough of a drawstate */ game_drawstate ads, *ds = &ads; ds->tilesize = tilesize; ds->n = state->n; /* * Border and grid. */ print_line_width(dr, TILE_SIZE / 40); for (x = 1; x < state->w; x++) draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(state->h), ink); for (y = 1; y < state->h; y++) draw_line(dr, COORD(0), COORD(y), COORD(state->w), COORD(y), ink); print_line_width(dr, 2*TILE_SIZE / 40); draw_rect_outline(dr, COORD(0), COORD(0), TILE_SIZE*state->w, TILE_SIZE*state->h, ink); /* * Arrows and numbers. */ print_line_width(dr, 0); for (y = 0; y < state->h; y++) for (x = 0; x < state->w; x++) tile_redraw(dr, ds, COORD(x), COORD(y), state->dirs[y*state->w+x], 0, state->nums[y*state->w+x], 0, 0.0, ink); } #ifdef COMBINED #define thegame signpost #endif const struct game thegame = { "Signpost", "games.signpost", "signpost", default_params, game_fetch_preset, 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, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, REQUIRE_RBUTTON, /* flags */ }; #ifdef STANDALONE_SOLVER #include #include const char *quis = NULL; int verbose = 0; void usage(FILE *out) { fprintf(out, "usage: %s [--stdin] [--soak] [--seed SEED] |\n", quis); } static void cycle_seed(char **seedstr, random_state *rs) { char newseed[16]; int j; newseed[15] = '\0'; newseed[0] = '1' + (char)random_upto(rs, 9); for (j = 1; j < 15; j++) newseed[j] = '0' + (char)random_upto(rs, 10); sfree(*seedstr); *seedstr = dupstr(newseed); } static void start_soak(game_params *p, char *seedstr) { time_t tt_start, tt_now, tt_last; char *desc, *aux; random_state *rs; long n = 0, nnums = 0, i; game_state *state; tt_start = tt_now = time(NULL); printf("Soak-generating a %dx%d grid.\n", p->w, p->h); while (1) { rs = random_new(seedstr, strlen(seedstr)); desc = thegame.new_desc(p, rs, &aux, 0); state = thegame.new_game(NULL, p, desc); for (i = 0; i < state->n; i++) { if (state->flags[i] & FLAG_IMMUTABLE) nnums++; } thegame.free_game(state); sfree(desc); cycle_seed(&seedstr, rs); random_free(rs); n++; tt_last = time(NULL); if (tt_last > tt_now) { tt_now = tt_last; printf("%ld total, %3.1f/s, %3.1f nums/grid (%3.1f%%).\n", n, (double)n / ((double)tt_now - tt_start), (double)nnums / (double)n, ((double)nnums * 100.0) / ((double)n * (double)p->w * (double)p->h) ); } } } static void process_desc(char *id) { char *desc, *err, *solvestr; game_params *p; game_state *s; printf("%s\n ", id); desc = strchr(id, ':'); if (!desc) { fprintf(stderr, "%s: expecting game description.", quis); exit(1); } *desc++ = '\0'; p = thegame.default_params(); thegame.decode_params(p, id); err = thegame.validate_params(p, 1); if (err) { fprintf(stderr, "%s: %s", quis, err); thegame.free_params(p); return; } err = thegame.validate_desc(p, desc); if (err) { fprintf(stderr, "%s: %s\nDescription: %s\n", quis, err, desc); thegame.free_params(p); return; } s = thegame.new_game(NULL, p, desc); solvestr = thegame.solve(s, s, NULL, &err); if (!solvestr) fprintf(stderr, "%s\n", err); else printf("Puzzle is soluble.\n"); thegame.free_game(s); thegame.free_params(p); } int main(int argc, const char *argv[]) { char *id = NULL, *desc, *err, *aux = NULL; int soak = 0, verbose = 0, stdin_desc = 0, n = 1, i; char *seedstr = NULL, newseed[16]; setvbuf(stdout, NULL, _IONBF, 0); quis = argv[0]; while (--argc > 0) { char *p = (char*)(*++argv); if (!strcmp(p, "-v") || !strcmp(p, "--verbose")) verbose = 1; else if (!strcmp(p, "--stdin")) stdin_desc = 1; else if (!strcmp(p, "-e") || !strcmp(p, "--seed")) { seedstr = dupstr(*++argv); argc--; } else if (!strcmp(p, "-n") || !strcmp(p, "--number")) { n = atoi(*++argv); argc--; } else if (!strcmp(p, "-s") || !strcmp(p, "--soak")) { soak = 1; } else if (*p == '-') { fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p); usage(stderr); exit(1); } else { id = p; } } sprintf(newseed, "%lu", time(NULL)); seedstr = dupstr(newseed); if (id || !stdin_desc) { if (id && strchr(id, ':')) { /* Parameters and description passed on cmd-line: * try and solve it. */ process_desc(id); } else { /* No description passed on cmd-line: decode parameters * (with optional seed too) */ game_params *p = thegame.default_params(); if (id) { char *cmdseed = strchr(id, '#'); if (cmdseed) { *cmdseed++ = '\0'; sfree(seedstr); seedstr = dupstr(cmdseed); } thegame.decode_params(p, id); } err = thegame.validate_params(p, 1); if (err) { fprintf(stderr, "%s: %s", quis, err); thegame.free_params(p); exit(1); } /* We have a set of valid parameters; either soak with it * or generate a single game description and print to stdout. */ if (soak) start_soak(p, seedstr); else { char *pstring = thegame.encode_params(p, 0); for (i = 0; i < n; i++) { random_state *rs = random_new(seedstr, strlen(seedstr)); if (verbose) printf("%s#%s\n", pstring, seedstr); desc = thegame.new_desc(p, rs, &aux, 0); printf("%s:%s\n", pstring, desc); sfree(desc); cycle_seed(&seedstr, rs); random_free(rs); } sfree(pstring); } thegame.free_params(p); } } if (stdin_desc) { char buf[4096]; while (fgets(buf, sizeof(buf), stdin)) { buf[strcspn(buf, "\r\n")] = '\0'; process_desc(buf); } } sfree(seedstr); return 0; } #endif /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/singles.c0000644000175300017530000017011612132232554014242 0ustar simonsimon/* * singles.c: implementation of Hitori ('let me alone') from Nikoli. * * Make single-get able to fetch a specific puzzle ID from menneske.no? * * www.menneske.no solving methods: * * Done: * SC: if you circle a cell, any cells in same row/col with same no --> black * -- solver_op_circle * SB: if you make a cell black, any cells around it --> white * -- solver_op_blacken * ST: 3 identical cells in row, centre is white and outer two black. * SP: 2 identical cells with single-cell gap, middle cell is white. * -- solver_singlesep (both ST and SP) * PI: if you have a pair of same number in row/col, any other * cells of same number must be black. * -- solve_doubles * CC: if you have a black on edge one cell away from corner, cell * on edge diag. adjacent must be white. * CE: if you have 2 black cells of triangle on edge, third cell must * be white. * QM: if you have 3 black cells of diagonal square in middle, fourth * cell must be white. * -- solve_allblackbutone (CC, CE, and QM). * QC: a corner with 4 identical numbers (or 2 and 2) must have the * corner cell (and cell diagonal to that) black. * TC: a corner with 3 identical numbers (with the L either way) * must have the apex of L black, and other two white. * DC: a corner with 2 identical numbers in domino can set a white * cell along wall. * -- solve_corners (QC, TC, DC) * IP: pair with one-offset-pair force whites by offset pair * -- solve_offsetpair * MC: any cells diag. adjacent to black cells that would split board * into separate white regions must be white. * -- solve_removesplits * * Still to do: * * TEP: 3 pairs of dominos parallel to side, can mark 4 white cells * alongside. * DEP: 2 pairs of dominos parallel to side, can mark 2 white cells. * FI: if you have two sets of double-cells packed together, singles * in that row/col must be white (qv. PI) * QuM: four identical cells (or 2 and 2) in middle of grid only have * two possible solutions each. * FDE: doubles one row/column away from edge can force a white cell. * FDM: doubles in centre (next to bits of diag. square) can force a white cell. * MP: two pairs with same number between force number to black. * CnC: if circling a cell leads to impossible board, cell is black. * MC: if we have two possiblilities, can we force a white circle? * */ #include #include #include #include #include #include #include "puzzles.h" #include "latin.h" #ifdef STANDALONE_SOLVER int verbose = 0; #endif #define PREFERRED_TILE_SIZE 32 #define TILE_SIZE (ds->tilesize) #define BORDER (TILE_SIZE / 2) #define CRAD ((TILE_SIZE / 2) - 1) #define TEXTSZ ((14*CRAD/10) - 1) /* 2 * sqrt(2) of CRAD */ #define COORD(x) ( (x) * TILE_SIZE + BORDER ) #define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 ) #define INGRID(s,x,y) ((x) >= 0 && (x) < (s)->w && (y) >= 0 && (y) < (s)->h) #define FLASH_TIME 0.7F enum { COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT, COL_BLACK, COL_WHITE, COL_BLACKNUM, COL_GRID, COL_CURSOR, COL_ERROR, NCOLOURS }; struct game_params { int w, h, diff; }; #define F_BLACK 0x1 #define F_CIRCLE 0x2 #define F_ERROR 0x4 #define F_SCRATCH 0x8 struct game_state { int w, h, n, o; /* n = w*h; o = max(w, h) */ int completed, used_solve, impossible; int *nums; /* size w*h */ unsigned int *flags; /* size w*h */ }; /* top, right, bottom, left */ static const int dxs[4] = { 0, 1, 0, -1 }; static const int dys[4] = { -1, 0, 1, 0 }; /* --- Game parameters and preset functions --- */ #define DIFFLIST(A) \ A(EASY,Easy,e) \ A(TRICKY,Tricky,k) #define ENUM(upper,title,lower) DIFF_ ## upper, #define TITLE(upper,title,lower) #title, #define ENCODE(upper,title,lower) #lower #define CONFIG(upper,title,lower) ":" #title enum { DIFFLIST(ENUM) DIFF_MAX, DIFF_ANY }; static char const *const singles_diffnames[] = { DIFFLIST(TITLE) }; static char const singles_diffchars[] = DIFFLIST(ENCODE); #define DIFFCOUNT lenof(singles_diffchars) #define DIFFCONFIG DIFFLIST(CONFIG) static game_params *default_params(void) { game_params *ret = snew(game_params); ret->w = ret->h = 5; ret->diff = DIFF_EASY; return ret; } static const struct game_params singles_presets[] = { { 5, 5, DIFF_EASY }, { 5, 5, DIFF_TRICKY }, { 6, 6, DIFF_EASY }, { 6, 6, DIFF_TRICKY }, { 8, 8, DIFF_EASY }, { 8, 8, DIFF_TRICKY }, { 10, 10, DIFF_EASY }, { 10, 10, DIFF_TRICKY }, { 12, 12, DIFF_EASY }, { 12, 12, DIFF_TRICKY } }; static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char buf[80]; if (i < 0 || i >= lenof(singles_presets)) return FALSE; ret = default_params(); *ret = singles_presets[i]; *params = ret; sprintf(buf, "%dx%d %s", ret->w, ret->h, singles_diffnames[ret->diff]); *name = dupstr(buf); 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 *ret, char const *string) { char const *p = string; int i; ret->w = ret->h = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; if (*p == 'x') { p++; ret->h = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; } if (*p == 'd') { ret->diff = DIFF_MAX; /* which is invalid */ p++; for (i = 0; i < DIFFCOUNT; i++) { if (*p == singles_diffchars[i]) ret->diff = i; } p++; } } static char *encode_params(const game_params *params, int full) { char data[256]; if (full) sprintf(data, "%dx%dd%c", params->w, params->h, singles_diffchars[params->diff]); else 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(4, config_item); ret[0].name = "Width"; ret[0].type = C_STRING; sprintf(buf, "%d", params->w); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Difficulty"; ret[2].type = C_CHOICES; ret[2].sval = DIFFCONFIG; ret[2].ival = params->diff; ret[3].name = NULL; ret[3].type = C_END; ret[3].sval = NULL; ret[3].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); ret->diff = cfg[2].ival; return ret; } static char *validate_params(const game_params *params, int full) { if (params->w < 2 || params->h < 2) return "Width and neight must be at least two"; if (params->w > 10+26+26 || params->h > 10+26+26) return "Puzzle is too large"; if (full) { if (params->diff < 0 || params->diff >= DIFF_MAX) return "Unknown difficulty rating"; } return NULL; } /* --- Game description string generation and unpicking --- */ static game_state *blank_game(int w, int h) { game_state *state = snew(game_state); memset(state, 0, sizeof(game_state)); state->w = w; state->h = h; state->n = w*h; state->o = max(w,h); state->completed = state->used_solve = state->impossible = 0; state->nums = snewn(state->n, int); state->flags = snewn(state->n, unsigned int); memset(state->nums, 0, state->n*sizeof(int)); memset(state->flags, 0, state->n*sizeof(unsigned int)); return state; } static game_state *dup_game(const game_state *state) { game_state *ret = blank_game(state->w, state->h); ret->completed = state->completed; ret->used_solve = state->used_solve; ret->impossible = state->impossible; memcpy(ret->nums, state->nums, state->n*sizeof(int)); memcpy(ret->flags, state->flags, state->n*sizeof(unsigned int)); return ret; } static void free_game(game_state *state) { sfree(state->nums); sfree(state->flags); sfree(state); } static char n2c(int num) { if (num < 10) return '0' + num; else if (num < 10+26) return 'a' + num - 10; else return 'A' + num - 10 - 26; return '?'; } static int c2n(char c) { if (isdigit((unsigned char)c)) return (int)(c - '0'); else if (c >= 'a' && c <= 'z') return (int)(c - 'a' + 10); else if (c >= 'A' && c <= 'Z') return (int)(c - 'A' + 10 + 26); return -1; } static void unpick_desc(const game_params *params, const char *desc, game_state **sout, char **mout) { game_state *state = blank_game(params->w, params->h); char *msg = NULL; int num = 0, i = 0; if (strlen(desc) != state->n) { msg = "Game description is wrong length"; goto done; } for (i = 0; i < state->n; i++) { num = c2n(desc[i]); if (num <= 0 || num > state->o) { msg = "Game description contains unexpected characters"; goto done; } state->nums[i] = num; } done: if (msg) { /* sth went wrong. */ if (mout) *mout = msg; free_game(state); } else { if (mout) *mout = NULL; if (sout) *sout = state; else free_game(state); } } static char *generate_desc(game_state *state, int issolve) { char *ret = snewn(state->n+1+(issolve?1:0), char); int i, p=0; if (issolve) ret[p++] = 'S'; for (i = 0; i < state->n; i++) ret[p++] = n2c(state->nums[i]); ret[p] = '\0'; return ret; } /* --- Useful game functions (completion, etc.) --- */ static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { int len, x, y, i; char *ret, *p; len = (state->w)*2; /* one row ... */ len = len * (state->h*2); /* ... h rows, including gaps ... */ len += 1; /* ... final NL */ p = ret = snewn(len, char); for (y = 0; y < state->h; y++) { for (x = 0; x < state->w; x++) { i = y*state->w + x; if (x > 0) *p++ = ' '; *p++ = (state->flags[i] & F_BLACK) ? '*' : n2c(state->nums[i]); } *p++ = '\n'; for (x = 0; x < state->w; x++) { i = y*state->w + x; if (x > 0) *p++ = ' '; *p++ = (state->flags[i] & F_CIRCLE) ? '~' : ' '; } *p++ = '\n'; } *p++ = '\0'; assert(p - ret == len); return ret; } static void debug_state(const char *desc, game_state *state) { char *dbg = game_text_format(state); debug(("%s:\n%s", desc, dbg)); sfree(dbg); } static void connect_if_same(game_state *state, int *dsf, int i1, int i2) { int c1, c2; if ((state->flags[i1] & F_BLACK) != (state->flags[i2] & F_BLACK)) return; c1 = dsf_canonify(dsf, i1); c2 = dsf_canonify(dsf, i2); dsf_merge(dsf, c1, c2); } static void connect_dsf(game_state *state, int *dsf) { int x, y, i; /* Construct a dsf array for connected blocks; connections * tracked to right and down. */ dsf_init(dsf, state->n); for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { i = y*state->w + x; if (x < state->w-1) connect_if_same(state, dsf, i, i+1); /* right */ if (y < state->h-1) connect_if_same(state, dsf, i, i+state->w); /* down */ } } } #define CC_MARK_ERRORS 1 #define CC_MUST_FILL 2 static int check_rowcol(game_state *state, int starti, int di, int sz, unsigned flags) { int nerr = 0, n, m, i, j; /* if any circled numbers have identical non-circled numbers on * same row/column, error (non-circled) * if any circled numbers in same column are same number, highlight them. * if any rows/columns have >1 of same number, not complete. */ for (n = 0, i = starti; n < sz; n++, i += di) { if (state->flags[i] & F_BLACK) continue; for (m = n+1, j = i+di; m < sz; m++, j += di) { if (state->flags[j] & F_BLACK) continue; if (state->nums[i] != state->nums[j]) continue; nerr++; /* ok, we have two numbers the same in a row. */ if (!(flags & CC_MARK_ERRORS)) continue; /* If we have two circles in the same row around * two identical numbers, they are _both_ wrong. */ if ((state->flags[i] & F_CIRCLE) && (state->flags[j] & F_CIRCLE)) { state->flags[i] |= F_ERROR; state->flags[j] |= F_ERROR; } /* Otherwise, if we have a circle, any other identical * numbers in that row are obviously wrong. We don't * highlight this, however, since it makes the process * of solving the puzzle too easy (you circle a number * and it promptly tells you which numbers to blacken! */ #if 0 else if (state->flags[i] & F_CIRCLE) state->flags[j] |= F_ERROR; else if (state->flags[j] & F_CIRCLE) state->flags[i] |= F_ERROR; #endif } } return nerr; } static int check_complete(game_state *state, unsigned flags) { int *dsf = snewn(state->n, int); int x, y, i, error = 0, nwhite, w = state->w, h = state->h; if (flags & CC_MARK_ERRORS) { for (i = 0; i < state->n; i++) state->flags[i] &= ~F_ERROR; } connect_dsf(state, dsf); /* If we're the solver we need the grid all to be definitively * black or definitively white (i.e. circled) otherwise the solver * has found an ambiguous grid. */ if (flags & CC_MUST_FILL) { for (i = 0; i < state->n; i++) { if (!(state->flags[i] & F_BLACK) && !(state->flags[i] & F_CIRCLE)) error += 1; } } /* Mark any black squares in groups of >1 as errors. * Count number of white squares. */ nwhite = 0; for (i = 0; i < state->n; i++) { if (state->flags[i] & F_BLACK) { if (dsf_size(dsf, i) > 1) { error += 1; if (flags & CC_MARK_ERRORS) state->flags[i] |= F_ERROR; } } else nwhite += 1; } /* Check attributes of white squares, row- and column-wise. */ for (x = 0; x < w; x++) /* check cols from (x,0) */ error += check_rowcol(state, x, w, h, flags); for (y = 0; y < h; y++) /* check rows from (0,y) */ error += check_rowcol(state, y*w, 1, w, flags); /* mark (all) white regions as an error if there is more than one. * may want to make this less in-your-face (by only marking * the smallest region as an error, for example -- but what if we * have two regions of identical size?) */ for (i = 0; i < state->n; i++) { if (!(state->flags[i] & F_BLACK) && dsf_size(dsf, i) < nwhite) { error += 1; if (flags & CC_MARK_ERRORS) state->flags[i] |= F_ERROR; } } sfree(dsf); return (error > 0) ? 0 : 1; } static char *game_state_diff(const game_state *src, const game_state *dst, int issolve) { char *ret = NULL, buf[80], c; int retlen = 0, x, y, i, k; unsigned int fmask = F_BLACK | F_CIRCLE; assert(src->n == dst->n); if (issolve) { ret = sresize(ret, 3, char); ret[0] = 'S'; ret[1] = ';'; ret[2] = '\0'; retlen += 2; } for (x = 0; x < dst->w; x++) { for (y = 0; y < dst->h; y++) { i = y*dst->w + x; if ((src->flags[i] & fmask) != (dst->flags[i] & fmask)) { assert((dst->flags[i] & fmask) != fmask); if (dst->flags[i] & F_BLACK) c = 'B'; else if (dst->flags[i] & F_CIRCLE) c = 'C'; else c = 'E'; k = sprintf(buf, "%c%d,%d;", (int)c, x, y); ret = sresize(ret, retlen + k + 1, char); strcpy(ret + retlen, buf); retlen += k; } } } return ret; } /* --- Solver --- */ enum { BLACK, CIRCLE }; struct solver_op { int x, y, op; /* op one of BLACK or CIRCLE. */ const char *desc; /* must be non-malloced. */ }; struct solver_state { struct solver_op *ops; int n_ops, n_alloc; int *scratch; }; static struct solver_state *solver_state_new(game_state *state) { struct solver_state *ss = snew(struct solver_state); ss->ops = NULL; ss->n_ops = ss->n_alloc = 0; ss->scratch = snewn(state->n, int); return ss; } static void solver_state_free(struct solver_state *ss) { sfree(ss->scratch); if (ss->ops) sfree(ss->ops); sfree(ss); } static void solver_op_add(struct solver_state *ss, int x, int y, int op, const char *desc) { struct solver_op *sop; if (ss->n_alloc < ss->n_ops + 1) { ss->n_alloc = (ss->n_alloc + 1) * 2; ss->ops = sresize(ss->ops, ss->n_alloc, struct solver_op); } sop = &(ss->ops[ss->n_ops++]); sop->x = x; sop->y = y; sop->op = op; sop->desc = desc; debug(("added solver op %s ('%s') at (%d,%d)\n", op == BLACK ? "BLACK" : "CIRCLE", desc, x, y)); } static void solver_op_circle(game_state *state, struct solver_state *ss, int x, int y) { int i = y*state->w + x; if (!INGRID(state, x, y)) return; if (state->flags[i] & F_BLACK) { debug(("... solver wants to add auto-circle on black (%d,%d)\n", x, y)); state->impossible = 1; return; } /* Only add circle op if it's not already circled. */ if (!(state->flags[i] & F_CIRCLE)) { solver_op_add(ss, x, y, CIRCLE, "SB - adjacent to black square"); } } static void solver_op_blacken(game_state *state, struct solver_state *ss, int x, int y, int num) { int i = y*state->w + x; if (!INGRID(state, x, y)) return; if (state->nums[i] != num) return; if (state->flags[i] & F_CIRCLE) { debug(("... solver wants to add auto-black on circled(%d,%d)\n", x, y)); state->impossible = 1; return; } /* Only add black op if it's not already black. */ if (!(state->flags[i] & F_BLACK)) { solver_op_add(ss, x, y, BLACK, "SC - number on same row/col as circled"); } } static int solver_ops_do(game_state *state, struct solver_state *ss) { int next_op = 0, i, x, y, n_ops = 0; struct solver_op op; /* Care here: solver_op_* may call solver_op_add which may extend the * ss->n_ops. */ while (next_op < ss->n_ops) { op = ss->ops[next_op++]; /* copy this away, it may get reallocated. */ i = op.y*state->w + op.x; if (op.op == BLACK) { if (state->flags[i] & F_CIRCLE) { debug(("Solver wants to blacken circled square (%d,%d)!\n", op.x, op.y)); state->impossible = 1; return n_ops; } if (!(state->flags[i] & F_BLACK)) { debug(("... solver adding black at (%d,%d): %s\n", op.x, op.y, op.desc)); #ifdef STANDALONE_SOLVER if (verbose) printf("Adding black at (%d,%d): %s\n", op.x, op.y, op.desc); #endif state->flags[i] |= F_BLACK; /*debug_state("State after adding black", state);*/ n_ops++; solver_op_circle(state, ss, op.x-1, op.y); solver_op_circle(state, ss, op.x+1, op.y); solver_op_circle(state, ss, op.x, op.y-1); solver_op_circle(state, ss, op.x, op.y+1); } } else { if (state->flags[i] & F_BLACK) { debug(("Solver wants to circle blackened square (%d,%d)!\n", op.x, op.y)); state->impossible = 1; return n_ops; } if (!(state->flags[i] & F_CIRCLE)) { debug(("... solver adding circle at (%d,%d): %s\n", op.x, op.y, op.desc)); #ifdef STANDALONE_SOLVER if (verbose) printf("Adding circle at (%d,%d): %s\n", op.x, op.y, op.desc); #endif state->flags[i] |= F_CIRCLE; /*debug_state("State after adding circle", state);*/ n_ops++; for (x = 0; x < state->w; x++) { if (x != op.x) solver_op_blacken(state, ss, x, op.y, state->nums[i]); } for (y = 0; y < state->h; y++) { if (y != op.y) solver_op_blacken(state, ss, op.x, y, state->nums[i]); } } } } ss->n_ops = 0; return n_ops; } /* If the grid has two identical numbers with one cell between them, the inner * cell _must_ be white (and thus circled); (at least) one of the two must be * black (since they're in the same column or row) and thus the middle cell is * next to a black cell. */ static int solve_singlesep(game_state *state, struct solver_state *ss) { int x, y, i, ir, irr, id, idd, n_ops = ss->n_ops; for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { i = y*state->w + x; /* Cell two to our right? */ ir = i + 1; irr = ir + 1; if (x < (state->w-2) && state->nums[i] == state->nums[irr] && !(state->flags[ir] & F_CIRCLE)) { solver_op_add(ss, x+1, y, CIRCLE, "SP/ST - between identical nums"); } /* Cell two below us? */ id = i + state->w; idd = id + state->w; if (y < (state->h-2) && state->nums[i] == state->nums[idd] && !(state->flags[id] & F_CIRCLE)) { solver_op_add(ss, x, y+1, CIRCLE, "SP/ST - between identical nums"); } } } return ss->n_ops - n_ops; } /* If we have two identical numbers next to each other (in a row or column), * any other identical numbers in that column must be black. */ static int solve_doubles(game_state *state, struct solver_state *ss) { int x, y, i, ii, n_ops = ss->n_ops, xy; for (y = 0, i = 0; y < state->h; y++) { for (x = 0; x < state->w; x++, i++) { assert(i == y*state->w+x); if (state->flags[i] & F_BLACK) continue; ii = i+1; /* check cell to our right. */ if (x < (state->w-1) && !(state->flags[ii] & F_BLACK) && state->nums[i] == state->nums[ii]) { for (xy = 0; xy < state->w; xy++) { if (xy == x || xy == (x+1)) continue; if (state->nums[y*state->w + xy] == state->nums[i] && !(state->flags[y*state->w + xy] & F_BLACK)) solver_op_add(ss, xy, y, BLACK, "PI - same row as pair"); } } ii = i+state->w; /* check cell below us */ if (y < (state->h-1) && !(state->flags[ii] & F_BLACK) && state->nums[i] == state->nums[ii]) { for (xy = 0; xy < state->h; xy++) { if (xy == y || xy == (y+1)) continue; if (state->nums[xy*state->w + x] == state->nums[i] && !(state->flags[xy*state->w + x] & F_BLACK)) solver_op_add(ss, x, xy, BLACK, "PI - same col as pair"); } } } } return ss->n_ops - n_ops; } /* If a white square has all-but-one possible adjacent squares black, the * one square left over must be white. */ static int solve_allblackbutone(game_state *state, struct solver_state *ss) { int x, y, i, n_ops = ss->n_ops, xd, yd, id, ifree; int dis[4], d; dis[0] = -state->w; dis[1] = 1; dis[2] = state->w; dis[3] = -1; for (y = 0, i = 0; y < state->h; y++) { for (x = 0; x < state->w; x++, i++) { assert(i == y*state->w+x); if (state->flags[i] & F_BLACK) continue; ifree = -1; for (d = 0; d < 4; d++) { xd = x + dxs[d]; yd = y + dys[d]; id = i + dis[d]; if (!INGRID(state, xd, yd)) continue; if (state->flags[id] & F_CIRCLE) goto skip; /* this cell already has a way out */ if (!(state->flags[id] & F_BLACK)) { if (ifree != -1) goto skip; /* this cell has >1 white cell around it. */ ifree = id; } } if (ifree != -1) solver_op_add(ss, ifree%state->w, ifree/state->w, CIRCLE, "CC/CE/QM: white cell with single non-black around it"); else { debug(("White cell with no escape at (%d,%d)\n", x, y)); state->impossible = 1; return 0; } skip: ; } } return ss->n_ops - n_ops; } /* If we have 4 numbers the same in a 2x2 corner, the far corner and the * diagonally-adjacent square must both be black. * If we have 3 numbers the same in a 2x2 corner, the apex of the L * thus formed must be black. * If we have 2 numbers the same in a 2x2 corner, the non-same cell * one away from the corner must be white. */ static void solve_corner(game_state *state, struct solver_state *ss, int x, int y, int dx, int dy) { int is[4], ns[4], xx, yy, w = state->w; for (yy = 0; yy < 2; yy++) { for (xx = 0; xx < 2; xx++) { is[yy*2+xx] = (y + dy*yy) * w + (x + dx*xx); ns[yy*2+xx] = state->nums[is[yy*2+xx]]; } } /* order is now (corner, side 1, side 2, inner) */ if (ns[0] == ns[1] && ns[0] == ns[2] && ns[0] == ns[3]) { solver_op_add(ss, is[0]%w, is[0]/w, BLACK, "QC: corner with 4 matching"); solver_op_add(ss, is[3]%w, is[3]/w, BLACK, "QC: corner with 4 matching"); } else if (ns[0] == ns[1] && ns[0] == ns[2]) { /* corner and 2 sides: apex is corner. */ solver_op_add(ss, is[0]%w, is[0]/w, BLACK, "TC: corner apex from 3 matching"); } else if (ns[1] == ns[2] && ns[1] == ns[3]) { /* side, side, fourth: apex is fourth. */ solver_op_add(ss, is[3]%w, is[3]/w, BLACK, "TC: inside apex from 3 matching"); } else if (ns[0] == ns[1] || ns[1] == ns[3]) { /* either way here we match the non-identical side. */ solver_op_add(ss, is[2]%w, is[2]/w, CIRCLE, "DC: corner with 2 matching"); } else if (ns[0] == ns[2] || ns[2] == ns[3]) { /* ditto */ solver_op_add(ss, is[1]%w, is[1]/w, CIRCLE, "DC: corner with 2 matching"); } } static int solve_corners(game_state *state, struct solver_state *ss) { int n_ops = ss->n_ops; solve_corner(state, ss, 0, 0, 1, 1); solve_corner(state, ss, state->w-1, 0, -1, 1); solve_corner(state, ss, state->w-1, state->h-1, -1, -1); solve_corner(state, ss, 0, state->h-1, 1, -1); return ss->n_ops - n_ops; } /* If you have the following situation: * ... * ...x A x x y A x... * ...x B x x B y x... * ... * then both squares marked 'y' must be white. One of the left-most A or B must * be white (since two side-by-side black cells are disallowed), which means * that the corresponding right-most A or B must be black (since you can't * have two of the same number on one line); thus, the adjacent squares * to that right-most A or B must be white, which include the two marked 'y' * in either case. * Obviously this works in any row or column. It also works if A == B. * It doesn't work for the degenerate case: * ...x A A x x * ...x B y x x * where the square marked 'y' isn't necessarily white (consider the left-most A * is black). * * */ static void solve_offsetpair_pair(game_state *state, struct solver_state *ss, int x1, int y1, int x2, int y2) { int ox, oy, w = state->w, ax, ay, an, d, dx[2], dy[2], dn, xd, yd; if (x1 == x2) { /* same column */ ox = 1; oy = 0; } else { assert(y1 == y2); ox = 0; oy = 1; } /* We try adjacent to (x1,y1) and the two diag. adjacent to (x2, y2). * We expect to be called twice, once each way around. */ ax = x1+ox; ay = y1+oy; assert(INGRID(state, ax, ay)); an = state->nums[ay*w + ax]; dx[0] = x2 + ox + oy; dx[1] = x2 + ox - oy; dy[0] = y2 + oy + ox; dy[1] = y2 + oy - ox; for (d = 0; d < 2; d++) { if (INGRID(state, dx[d], dy[d]) && (dx[d] != ax || dy[d] != ay)) { /* The 'dx != ax || dy != ay' removes the degenerate case, * mentioned above. */ dn = state->nums[dy[d]*w + dx[d]]; if (an == dn) { /* We have a match; so (WLOG) the 'A' marked above are at * (x1,y1) and (x2,y2), and the 'B' are at (ax,ay) and (dx,dy). */ debug(("Found offset-pair: %d at (%d,%d) and (%d,%d)\n", state->nums[y1*w + x1], x1, y1, x2, y2)); debug((" and: %d at (%d,%d) and (%d,%d)\n", an, ax, ay, dx[d], dy[d])); xd = dx[d] - x2; yd = dy[d] - y2; solver_op_add(ss, x2 + xd, y2, CIRCLE, "IP: next to offset-pair"); solver_op_add(ss, x2, y2 + yd, CIRCLE, "IP: next to offset-pair"); } } } } static int solve_offsetpair(game_state *state, struct solver_state *ss) { int n_ops = ss->n_ops, x, xx, y, yy, n1, n2; for (x = 0; x < state->w-1; x++) { for (y = 0; y < state->h; y++) { n1 = state->nums[y*state->w + x]; for (yy = y+1; yy < state->h; yy++) { n2 = state->nums[yy*state->w + x]; if (n1 == n2) { solve_offsetpair_pair(state, ss, x, y, x, yy); solve_offsetpair_pair(state, ss, x, yy, x, y); } } } } for (y = 0; y < state->h-1; y++) { for (x = 0; x < state->w; x++) { n1 = state->nums[y*state->w + x]; for (xx = x+1; xx < state->w; xx++) { n2 = state->nums[y*state->w + xx]; if (n1 == n2) { solve_offsetpair_pair(state, ss, x, y, xx, y); solve_offsetpair_pair(state, ss, xx, y, x, y); } } } } return ss->n_ops - n_ops; } static int solve_hassinglewhiteregion(game_state *state, struct solver_state *ss) { int i, j, nwhite = 0, lwhite = -1, szwhite, start, end, next, a, d, x, y; for (i = 0; i < state->n; i++) { if (!(state->flags[i] & F_BLACK)) { nwhite++; lwhite = i; } state->flags[i] &= ~F_SCRATCH; } if (lwhite == -1) { debug(("solve_hassinglewhite: no white squares found!\n")); state->impossible = 1; return 0; } /* We don't use connect_dsf here; it's too slow, and there's a quicker * algorithm if all we want is the size of one region. */ /* Having written this, this algorithm is only about 5% faster than * using a dsf. */ memset(ss->scratch, -1, state->n * sizeof(int)); ss->scratch[0] = lwhite; state->flags[lwhite] |= F_SCRATCH; start = 0; end = next = 1; while (start < end) { for (a = start; a < end; a++) { i = ss->scratch[a]; assert(i != -1); for (d = 0; d < 4; d++) { x = (i % state->w) + dxs[d]; y = (i / state->w) + dys[d]; j = y*state->w + x; if (!INGRID(state, x, y)) continue; if (state->flags[j] & (F_BLACK | F_SCRATCH)) continue; ss->scratch[next++] = j; state->flags[j] |= F_SCRATCH; } } start = end; end = next; } szwhite = next; return (szwhite == nwhite) ? 1 : 0; } static void solve_removesplits_check(game_state *state, struct solver_state *ss, int x, int y) { int i = y*state->w + x, issingle; if (!INGRID(state, x, y)) return; if ((state->flags[i] & F_CIRCLE) || (state->flags[i] & F_BLACK)) return; /* If putting a black square at (x,y) would make the white region * non-contiguous, it must be circled. */ state->flags[i] |= F_BLACK; issingle = solve_hassinglewhiteregion(state, ss); state->flags[i] &= ~F_BLACK; if (!issingle) solver_op_add(ss, x, y, CIRCLE, "MC: black square here would split white region"); } /* For all black squares, search in squares diagonally adjacent to see if * we can rule out putting a black square there (because it would make the * white region non-contiguous). */ /* This function is likely to be somewhat slow. */ static int solve_removesplits(game_state *state, struct solver_state *ss) { int i, x, y, n_ops = ss->n_ops; if (!solve_hassinglewhiteregion(state, ss)) { debug(("solve_removesplits: white region is not contiguous at start!\n")); state->impossible = 1; return 0; } for (i = 0; i < state->n; i++) { if (!(state->flags[i] & F_BLACK)) continue; x = i%state->w; y = i/state->w; solve_removesplits_check(state, ss, x-1, y-1); solve_removesplits_check(state, ss, x+1, y-1); solve_removesplits_check(state, ss, x+1, y+1); solve_removesplits_check(state, ss, x-1, y+1); } return ss->n_ops - n_ops; } /* * This function performs a solver step that isn't implicit in the rules * of the game and is thus treated somewhat differently. * * It marks cells whose number does not exist elsewhere in its row/column * with circles. As it happens the game generator here does mean that this * is always correct, but it's a solving method that people should not have * to rely upon (except in the hidden 'sneaky' difficulty setting) and so * all grids at 'tricky' and above are checked to make sure that the grid * is no easier if this solving step is performed beforehand. * * Calling with ss=NULL just returns the number of sneaky deductions that * would have been made. */ static int solve_sneaky(game_state *state, struct solver_state *ss) { int i, ii, x, xx, y, yy, nunique = 0; /* Clear SCRATCH flags. */ for (i = 0; i < state->n; i++) state->flags[i] &= ~F_SCRATCH; for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { i = y*state->w + x; /* Check for duplicate numbers on our row, mark (both) if so */ for (xx = x; xx < state->w; xx++) { ii = y*state->w + xx; if (i == ii) continue; if (state->nums[i] == state->nums[ii]) { state->flags[i] |= F_SCRATCH; state->flags[ii] |= F_SCRATCH; } } /* Check for duplicate numbers on our col, mark (both) if so */ for (yy = y; yy < state->h; yy++) { ii = yy*state->w + x; if (i == ii) continue; if (state->nums[i] == state->nums[ii]) { state->flags[i] |= F_SCRATCH; state->flags[ii] |= F_SCRATCH; } } } } /* Any cell with no marking has no duplicates on its row or column: * set its CIRCLE. */ for (i = 0; i < state->n; i++) { if (!(state->flags[i] & F_SCRATCH)) { if (ss) solver_op_add(ss, i%state->w, i/state->w, CIRCLE, "SNEAKY: only one of its number in row and col"); nunique += 1; } else state->flags[i] &= ~F_SCRATCH; } return nunique; } static int solve_specific(game_state *state, int diff, int sneaky) { struct solver_state *ss = solver_state_new(state); if (sneaky) solve_sneaky(state, ss); /* Some solver operations we only have to perform once -- * they're only based on the numbers available, and not black * squares or circles which may be added later. */ solve_singlesep(state, ss); /* never sets impossible */ solve_doubles(state, ss); /* ditto */ solve_corners(state, ss); /* ditto */ if (diff >= DIFF_TRICKY) solve_offsetpair(state, ss); /* ditto */ while (1) { if (ss->n_ops > 0) solver_ops_do(state, ss); if (state->impossible) break; if (solve_allblackbutone(state, ss) > 0) continue; if (state->impossible) break; if (diff >= DIFF_TRICKY) { if (solve_removesplits(state, ss) > 0) continue; if (state->impossible) break; } break; } solver_state_free(ss); return state->impossible ? -1 : check_complete(state, CC_MUST_FILL); } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { game_state *solved = dup_game(currstate); char *move = NULL; if (solve_specific(solved, DIFF_ANY, 0) > 0) goto solved; free_game(solved); solved = dup_game(state); if (solve_specific(solved, DIFF_ANY, 0) > 0) goto solved; free_game(solved); *error = "Unable to solve puzzle."; return NULL; solved: move = game_state_diff(currstate, solved, 1); free_game(solved); return move; } /* --- Game generation --- */ /* A correctly completed Hitori board is essentially a latin square * (no duplicated numbers in any row or column) with black squares * added such that no black square touches another, and the white * squares make a contiguous region. * * So we can generate it by: * constructing a latin square * adding black squares at random (minding the constraints) * altering the numbers under the new black squares such that the solver gets a headstart working out where they are. */ static int new_game_is_good(const game_params *params, game_state *state, game_state *tosolve) { int sret, sret_easy = 0; memcpy(tosolve->nums, state->nums, state->n * sizeof(int)); memset(tosolve->flags, 0, state->n * sizeof(unsigned int)); tosolve->completed = tosolve->impossible = 0; /* * We try and solve it twice, once at our requested difficulty level * (ensuring it's soluble at all) and once at the level below (if * it exists), which we hope to fail: if you can also solve it at * the level below then it's too easy and we have to try again. * * With this puzzle in particular there's an extra finesse, which is * that we check that the generated puzzle isn't too easy _with * an extra solver step first_, which is the 'sneaky' mode of deductions * (asserting that any number which fulfils the latin-square rules * on its row/column must be white). This is an artefact of the * generation process and not implicit in the rules, so we don't want * people to be able to use it to make the puzzle easier. */ assert(params->diff < DIFF_MAX); sret = solve_specific(tosolve, params->diff, 0); if (params->diff > DIFF_EASY) { memset(tosolve->flags, 0, state->n * sizeof(unsigned int)); tosolve->completed = tosolve->impossible = 0; /* this is the only time the 'sneaky' flag is set to 1. */ sret_easy = solve_specific(tosolve, params->diff-1, 1); } if (sret <= 0 || sret_easy > 0) { debug(("Generated puzzle %s at chosen difficulty %s\n", sret <= 0 ? "insoluble" : "too easy", singles_diffnames[params->diff])); return 0; } return 1; } #define MAXTRIES 20 static int best_black_col(game_state *state, random_state *rs, int *scratch, int i, int *rownums, int *colnums) { int w = state->w, x = i%w, y = i/w, j, o = state->o; /* Randomise the list of numbers to try. */ for (i = 0; i < o; i++) scratch[i] = i; shuffle(scratch, o, sizeof(int), rs); /* Try each number in turn, first giving preference to removing * latin-square characteristics (i.e. those numbers which only * occur once in a row/column). The '&&' here, although intuitively * wrong, results in a smaller number of 'sneaky' deductions on * solvable boards. */ for (i = 0; i < o; i++) { j = scratch[i] + 1; if (rownums[y*o + j-1] == 1 && colnums[x*o + j-1] == 1) goto found; } /* Then try each number in turn returning the first one that's * not actually unique in its row/column (see comment below) */ for (i = 0; i < o; i++) { j = scratch[i] + 1; if (rownums[y*o + j-1] != 0 || colnums[x*o + j-1] != 0) goto found; } assert(!"unable to place number under black cell."); return 0; found: /* Update column and row counts assuming this number will be placed. */ rownums[y*o + j-1] += 1; colnums[x*o + j-1] += 1; return j; } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { game_state *state = blank_game(params->w, params->h); game_state *tosolve = blank_game(params->w, params->h); int i, j, *scratch, *rownums, *colnums, x, y, ntries; int w = state->w, h = state->h, o = state->o; char *ret; digit *latin; struct solver_state *ss = solver_state_new(state); scratch = snewn(state->n, int); rownums = snewn(h*o, int); colnums = snewn(w*o, int); generate: ss->n_ops = 0; debug(("Starting game generation, size %dx%d\n", w, h)); memset(state->flags, 0, state->n*sizeof(unsigned int)); /* First, generate the latin rectangle. * The order of this, o, is max(w,h). */ latin = latin_generate_rect(w, h, rs); for (i = 0; i < state->n; i++) state->nums[i] = (int)latin[i]; sfree(latin); debug_state("State after latin square", state); /* Add black squares at random, using bits of solver as we go (to lay * white squares), until we can lay no more blacks. */ for (i = 0; i < state->n; i++) scratch[i] = i; shuffle(scratch, state->n, sizeof(int), rs); for (j = 0; j < state->n; j++) { i = scratch[j]; if ((state->flags[i] & F_CIRCLE) || (state->flags[i] & F_BLACK)) { debug(("generator skipping (%d,%d): %s\n", i%w, i/w, (state->flags[i] & F_CIRCLE) ? "CIRCLE" : "BLACK")); continue; /* solver knows this must be one or the other already. */ } /* Add a random black cell... */ solver_op_add(ss, i%w, i/w, BLACK, "Generator: adding random black cell"); solver_ops_do(state, ss); /* ... and do as well as we know how to lay down whites that are now forced. */ solve_allblackbutone(state, ss); solver_ops_do(state, ss); solve_removesplits(state, ss); solver_ops_do(state, ss); if (state->impossible) { debug(("generator made impossible, restarting...\n")); goto generate; } } debug_state("State after adding blacks", state); /* Now we know which squares are white and which are black, we lay numbers * under black squares at random, except that the number must appear in * white cells at least once more in the same column or row as that [black] * square. That's necessary to avoid multiple solutions, where blackening * squares in the finished puzzle becomes optional. We use two arrays: * * rownums[ROW * o + NUM-1] is the no. of white cells containing NUM in y=ROW * colnums[COL * o + NUM-1] is the no. of white cells containing NUM in x=COL */ memset(rownums, 0, h*o * sizeof(int)); memset(colnums, 0, w*o * sizeof(int)); for (i = 0; i < state->n; i++) { if (state->flags[i] & F_BLACK) continue; j = state->nums[i]; x = i%w; y = i/w; rownums[y * o + j-1] += 1; colnums[x * o + j-1] += 1; } ntries = 0; randomise: for (i = 0; i < state->n; i++) { if (!(state->flags[i] & F_BLACK)) continue; state->nums[i] = best_black_col(state, rs, scratch, i, rownums, colnums); } debug_state("State after adding numbers", state); /* DIFF_ANY just returns whatever we first generated, for testing purposes. */ if (params->diff != DIFF_ANY && !new_game_is_good(params, state, tosolve)) { ntries++; if (ntries > MAXTRIES) { debug(("Ran out of randomisation attempts, re-generating.\n")); goto generate; } debug(("Re-randomising numbers under black squares.\n")); goto randomise; } ret = generate_desc(state, 0); free_game(tosolve); free_game(state); solver_state_free(ss); sfree(scratch); sfree(rownums); sfree(colnums); return ret; } static char *validate_desc(const game_params *params, const char *desc) { char *ret = NULL; unpick_desc(params, desc, NULL, &ret); return ret; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { game_state *state = NULL; unpick_desc(params, desc, &state, NULL); if (!state) assert(!"new_game failed to unpick"); return state; } /* --- Game UI and move routines --- */ struct game_ui { int cx, cy, cshow; int show_black_nums; }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->cx = ui->cy = ui->cshow = 0; ui->show_black_nums = 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) { if (!oldstate->completed && newstate->completed) ui->cshow = 0; } #define DS_BLACK 0x1 #define DS_CIRCLE 0x2 #define DS_CURSOR 0x4 #define DS_BLACK_NUM 0x8 #define DS_ERROR 0x10 #define DS_FLASH 0x20 #define DS_IMPOSSIBLE 0x40 struct game_drawstate { int tilesize, started, solved; int w, h, n; unsigned int *flags; }; static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int mx, int my, int button) { char buf[80], c; int i, x = FROMCOORD(mx), y = FROMCOORD(my); enum { NONE, TOGGLE_BLACK, TOGGLE_CIRCLE, UI } action = NONE; if (IS_CURSOR_MOVE(button)) { move_cursor(button, &ui->cx, &ui->cy, state->w, state->h, 1); ui->cshow = 1; action = UI; } else if (IS_CURSOR_SELECT(button)) { x = ui->cx; y = ui->cy; if (!ui->cshow) { action = UI; ui->cshow = 1; } if (button == CURSOR_SELECT) { action = TOGGLE_BLACK; } else if (button == CURSOR_SELECT2) { action = TOGGLE_CIRCLE; } } else if (IS_MOUSE_DOWN(button)) { if (ui->cshow) { ui->cshow = 0; action = UI; } if (!INGRID(state, x, y)) { ui->show_black_nums = 1 - ui->show_black_nums; action = UI; /* this wants to be a per-game option. */ } else if (button == LEFT_BUTTON) { action = TOGGLE_BLACK; } else if (button == RIGHT_BUTTON) { action = TOGGLE_CIRCLE; } } if (action == UI) return ""; if (action == TOGGLE_BLACK || action == TOGGLE_CIRCLE) { i = y * state->w + x; if (state->flags[i] & (F_BLACK | F_CIRCLE)) c = 'E'; else c = (action == TOGGLE_BLACK) ? 'B' : 'C'; sprintf(buf, "%c%d,%d", (int)c, x, y); return dupstr(buf); } return NULL; } static game_state *execute_move(const game_state *state, const char *move) { game_state *ret = dup_game(state); int x, y, i, n; debug(("move: %s\n", move)); while (*move) { char c = *move; if (c == 'B' || c == 'C' || c == 'E') { move++; if (sscanf(move, "%d,%d%n", &x, &y, &n) != 2 || !INGRID(state, x, y)) goto badmove; i = y*ret->w + x; ret->flags[i] &= ~(F_CIRCLE | F_BLACK); /* empty first, always. */ if (c == 'B') ret->flags[i] |= F_BLACK; else if (c == 'C') ret->flags[i] |= F_CIRCLE; move += n; } else if (c == 'S') { move++; ret->used_solve = 1; } else goto badmove; if (*move == ';') move++; else if (*move) goto badmove; } if (check_complete(ret, CC_MARK_ERRORS)) ret->completed = 1; return ret; badmove: free_game(ret); return NULL; } /* ---------------------------------------------------------------------- * 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 = TILE_SIZE * params->w + 2 * BORDER; *y = TILE_SIZE * params->h + 2 * BORDER; } 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); for (i = 0; i < 3; i++) { ret[COL_BLACK * 3 + i] = 0.0F; ret[COL_BLACKNUM * 3 + i] = 0.4F; ret[COL_WHITE * 3 + i] = 1.0F; ret[COL_GRID * 3 + i] = ret[COL_LOWLIGHT * 3 + i]; } ret[COL_CURSOR * 3 + 0] = 0.2F; ret[COL_CURSOR * 3 + 1] = 0.8F; ret[COL_CURSOR * 3 + 2] = 0.0F; ret[COL_ERROR * 3 + 0] = 1.0F; ret[COL_ERROR * 3 + 1] = 0.0F; ret[COL_ERROR * 3 + 2] = 0.0F; *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 = ds->started = ds->solved = 0; ds->w = state->w; ds->h = state->h; ds->n = state->n; ds->flags = snewn(state->n, unsigned int); memset(ds->flags, 0, state->n*sizeof(unsigned int)); return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->flags); sfree(ds); } static void tile_redraw(drawing *dr, game_drawstate *ds, int x, int y, int num, unsigned int f) { int tcol, bg, dnum, cx, cy, tsz; char buf[32]; if (f & DS_BLACK) { bg = (f & DS_ERROR) ? COL_ERROR : COL_BLACK; tcol = COL_BLACKNUM; dnum = (f & DS_BLACK_NUM) ? 1 : 0; } else { bg = (f & DS_FLASH) ? COL_LOWLIGHT : COL_BACKGROUND; tcol = (f & DS_ERROR) ? COL_ERROR : COL_BLACK; dnum = 1; } cx = x + TILE_SIZE/2; cy = y + TILE_SIZE/2; draw_rect(dr, x, y, TILE_SIZE, TILE_SIZE, bg); draw_rect_outline(dr, x, y, TILE_SIZE, TILE_SIZE, (f & DS_IMPOSSIBLE) ? COL_ERROR : COL_GRID); if (f & DS_CIRCLE) { draw_circle(dr, cx, cy, CRAD, tcol, tcol); draw_circle(dr, cx, cy, CRAD-1, bg, tcol); } if (dnum) { sprintf(buf, "%d", num); if (strlen(buf) == 1) tsz = TEXTSZ; else tsz = (CRAD*2 - 1) / strlen(buf); draw_text(dr, cx, cy, FONT_VARIABLE, tsz, ALIGN_VCENTRE | ALIGN_HCENTRE, tcol, buf); } if (f & DS_CURSOR) draw_rect_corners(dr, cx, cy, TEXTSZ/2, COL_CURSOR); draw_update(dr, x, y, TILE_SIZE, TILE_SIZE); } 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 x, y, i, flash; unsigned int f; flash = (int)(flashtime * 5 / FLASH_TIME) % 2; if (!ds->started) { int wsz = TILE_SIZE * state->w + 2 * BORDER; int hsz = TILE_SIZE * state->h + 2 * BORDER; draw_rect(dr, 0, 0, wsz, hsz, COL_BACKGROUND); draw_rect_outline(dr, COORD(0)-1, COORD(0)-1, TILE_SIZE * state->w + 2, TILE_SIZE * state->h + 2, COL_GRID); draw_update(dr, 0, 0, wsz, hsz); } for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { i = y*state->w + x; f = 0; if (flash) f |= DS_FLASH; if (state->impossible) f |= DS_IMPOSSIBLE; if (ui->cshow && x == ui->cx && y == ui->cy) f |= DS_CURSOR; if (state->flags[i] & F_BLACK) { f |= DS_BLACK; if (ui->show_black_nums) f |= DS_BLACK_NUM; } if (state->flags[i] & F_CIRCLE) f |= DS_CIRCLE; if (state->flags[i] & F_ERROR) f |= DS_ERROR; if (!ds->started || ds->flags[i] != f) { tile_redraw(dr, ds, COORD(x), COORD(y), state->nums[i], f); ds->flags[i] = f; } } } ds->started = 1; } 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 && !newstate->used_solve) return FLASH_TIME; return 0.0F; } static int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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) { int pw, ph; /* 8mm squares by default. */ game_compute_size(params, 800, &pw, &ph); *x = pw / 100.0F; *y = ph / 100.0F; } static void game_print(drawing *dr, const game_state *state, int tilesize) { int ink = print_mono_colour(dr, 0); int paper = print_mono_colour(dr, 1); int x, y, ox, oy, i; char buf[32]; /* Ick: fake up `ds->tilesize' for macro expansion purposes */ game_drawstate ads, *ds = &ads; game_set_size(dr, ds, NULL, tilesize); print_line_width(dr, 2 * TILE_SIZE / 40); for (x = 0; x < state->w; x++) { for (y = 0; y < state->h; y++) { ox = COORD(x); oy = COORD(y); i = y*state->w+x; if (state->flags[i] & F_BLACK) { draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE, ink); } else { draw_rect_outline(dr, ox, oy, TILE_SIZE, TILE_SIZE, ink); if (state->flags[i] & DS_CIRCLE) draw_circle(dr, ox+TILE_SIZE/2, oy+TILE_SIZE/2, CRAD, paper, ink); sprintf(buf, "%d", state->nums[i]); draw_text(dr, ox+TILE_SIZE/2, oy+TILE_SIZE/2, FONT_VARIABLE, TEXTSZ/strlen(buf), ALIGN_VCENTRE | ALIGN_HCENTRE, ink, buf); } } } } #ifdef COMBINED #define thegame singles #endif const struct game thegame = { "Singles", "games.singles", "singles", default_params, game_fetch_preset, 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, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, REQUIRE_RBUTTON, /* flags */ }; #ifdef STANDALONE_SOLVER #include #include static void start_soak(game_params *p, random_state *rs) { time_t tt_start, tt_now, tt_last; char *desc, *aux; game_state *s; int i, n = 0, ndiff[DIFF_MAX], diff, sret, nblack = 0, nsneaky = 0; tt_start = tt_now = time(NULL); printf("Soak-testing a %dx%d grid.\n", p->w, p->h); p->diff = DIFF_ANY; memset(ndiff, 0, DIFF_MAX * sizeof(int)); while (1) { n++; desc = new_game_desc(p, rs, &aux, 0); s = new_game(NULL, p, desc); nsneaky += solve_sneaky(s, NULL); for (diff = 0; diff < DIFF_MAX; diff++) { memset(s->flags, 0, s->n * sizeof(unsigned int)); s->completed = s->impossible = 0; sret = solve_specific(s, diff, 0); if (sret > 0) { ndiff[diff]++; break; } else if (sret < 0) fprintf(stderr, "Impossible! %s\n", desc); } for (i = 0; i < s->n; i++) { if (s->flags[i] & F_BLACK) nblack++; } free_game(s); sfree(desc); tt_last = time(NULL); if (tt_last > tt_now) { tt_now = tt_last; printf("%d total, %3.1f/s, bl/sn %3.1f%%/%3.1f%%: ", n, (double)n / ((double)tt_now - tt_start), ((double)nblack * 100.0) / (double)(n * p->w * p->h), ((double)nsneaky * 100.0) / (double)(n * p->w * p->h)); for (diff = 0; diff < DIFF_MAX; diff++) { if (diff > 0) printf(", "); printf("%d (%3.1f%%) %s", ndiff[diff], (double)ndiff[diff] * 100.0 / (double)n, singles_diffnames[diff]); } printf("\n"); } } } int main(int argc, char **argv) { char *id = NULL, *desc, *desc_gen = NULL, *tgame, *err, *aux; game_state *s = NULL; game_params *p = NULL; int soln, soak = 0, ret = 1; time_t seed = time(NULL); random_state *rs = NULL; setvbuf(stdout, NULL, _IONBF, 0); while (--argc > 0) { char *p = *++argv; if (!strcmp(p, "-v")) { verbose = 1; } else if (!strcmp(p, "--soak")) { soak = 1; } else if (!strcmp(p, "--seed")) { if (argc == 0) { fprintf(stderr, "%s: --seed needs an argument", argv[0]); goto done; } seed = (time_t)atoi(*++argv); argc--; } else if (*p == '-') { fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p); return 1; } else { id = p; } } rs = random_new((void*)&seed, sizeof(time_t)); if (!id) { fprintf(stderr, "usage: %s [-v] [--soak] | \n", argv[0]); goto done; } desc = strchr(id, ':'); if (desc) *desc++ = '\0'; p = default_params(); decode_params(p, id); err = validate_params(p, 1); if (err) { fprintf(stderr, "%s: %s", argv[0], err); goto done; } if (soak) { if (desc) { fprintf(stderr, "%s: --soak only needs params, not game desc.\n", argv[0]); goto done; } start_soak(p, rs); } else { if (!desc) desc = desc_gen = new_game_desc(p, rs, &aux, 0); err = validate_desc(p, desc); if (err) { fprintf(stderr, "%s: %s\n", argv[0], err); free_params(p); goto done; } s = new_game(NULL, p, desc); if (verbose) { tgame = game_text_format(s); fputs(tgame, stdout); sfree(tgame); } soln = solve_specific(s, DIFF_ANY, 0); tgame = game_text_format(s); fputs(tgame, stdout); sfree(tgame); printf("Game was %s.\n\n", soln < 0 ? "impossible" : soln > 0 ? "solved" : "not solved"); } ret = 0; done: if (desc_gen) sfree(desc_gen); if (p) free_params(p); if (s) free_game(s); if (rs) random_free(rs); return ret; } #endif /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/sixteen.c0000644000175300017530000006630712132232554014263 0ustar simonsimon/* * sixteen.c: `16-puzzle', a sliding-tiles jigsaw which differs * from the 15-puzzle in that you toroidally rotate a row or column * at a time. */ #include #include #include #include #include #include #include "puzzles.h" #define PREFERRED_TILE_SIZE 48 #define TILE_SIZE (ds->tilesize) #define BORDER TILE_SIZE #define HIGHLIGHT_WIDTH (TILE_SIZE / 20) #define COORD(x) ( (x) * TILE_SIZE + BORDER ) #define FROMCOORD(x) ( ((x) - BORDER + 2*TILE_SIZE) / TILE_SIZE - 2 ) #define ANIM_TIME 0.13F #define FLASH_FRAME 0.13F #define X(state, i) ( (i) % (state)->w ) #define Y(state, i) ( (i) / (state)->w ) #define C(state, x, y) ( (y) * (state)->w + (x) ) enum { COL_BACKGROUND, COL_TEXT, COL_HIGHLIGHT, COL_LOWLIGHT, NCOLOURS }; struct game_params { int w, h; int movetarget; }; struct game_state { int w, h, n; int *tiles; int completed; int used_solve; /* used to suppress completion flash */ int movecount, movetarget; int last_movement_sense; }; static game_params *default_params(void) { game_params *ret = snew(game_params); ret->w = ret->h = 4; ret->movetarget = 0; return ret; } static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; int w, h; char buf[80]; switch (i) { case 0: w = 3, h = 3; break; case 1: w = 4, h = 3; break; case 2: w = 4, h = 4; break; case 3: w = 5, h = 4; break; case 4: w = 5, h = 5; break; default: return FALSE; } sprintf(buf, "%dx%d", w, h); *name = dupstr(buf); *params = ret = snew(game_params); ret->w = w; ret->h = h; ret->movetarget = 0; 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 *ret, char const *string) { ret->w = ret->h = atoi(string); ret->movetarget = 0; while (*string && isdigit((unsigned char)*string)) string++; if (*string == 'x') { string++; ret->h = atoi(string); while (*string && isdigit((unsigned char)*string)) string++; } if (*string == 'm') { string++; ret->movetarget = atoi(string); while (*string && isdigit((unsigned char)*string)) string++; } } static char *encode_params(const game_params *params, int full) { char data[256]; sprintf(data, "%dx%d", params->w, params->h); /* Shuffle limit is part of the limited parameters, because we have to * supply the target move count. */ if (params->movetarget) sprintf(data + strlen(data), "m%d", params->movetarget); 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].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Number of shuffling moves"; ret[2].type = C_STRING; sprintf(buf, "%d", params->movetarget); ret[2].sval = dupstr(buf); ret[2].ival = 0; ret[3].name = NULL; ret[3].type = C_END; ret[3].sval = NULL; ret[3].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); ret->movetarget = atoi(cfg[2].sval); return ret; } static char *validate_params(const game_params *params, int full) { if (params->w < 2 || params->h < 2) return "Width and height must both be at least two"; return NULL; } static int perm_parity(int *perm, int n) { int i, j, ret; ret = 0; for (i = 0; i < n-1; i++) for (j = i+1; j < n; j++) if (perm[i] > perm[j]) ret = !ret; return ret; } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { int stop, n, i, x; int x1, x2, p1, p2; int *tiles, *used; char *ret; int retlen; n = params->w * params->h; tiles = snewn(n, int); if (params->movetarget) { int prevoffset = -1; int max = (params->w > params->h ? params->w : params->h); int *prevmoves = snewn(max, int); /* * Shuffle the old-fashioned way, by making a series of * single moves on the grid. */ for (i = 0; i < n; i++) tiles[i] = i; for (i = 0; i < params->movetarget; i++) { int start, offset, len, direction, index; int j, tmp; /* * Choose a move to make. We can choose from any row * or any column. */ while (1) { j = random_upto(rs, params->w + params->h); if (j < params->w) { /* Column. */ index = j; start = j; offset = params->w; len = params->h; } else { /* Row. */ index = j - params->w; start = index * params->w; offset = 1; len = params->w; } direction = -1 + 2 * random_upto(rs, 2); /* * To at least _try_ to avoid boring cases, check * that this move doesn't directly undo a previous * one, or repeat it so many times as to turn it * into fewer moves in the opposite direction. (For * example, in a row of length 4, we're allowed to * move it the same way twice, but not three * times.) * * We track this for each individual row/column, * and clear all the counters as soon as a * perpendicular move is made. This isn't perfect * (it _can't_ guaranteeably be perfect - there * will always come a move count beyond which a * shorter solution will be possible than the one * which constructed the position) but it should * sort out all the obvious cases. */ if (offset == prevoffset) { tmp = prevmoves[index] + direction; if (abs(2*tmp) > len || abs(tmp) < abs(prevmoves[index])) continue; } /* If we didn't `continue', we've found an OK move to make. */ if (offset != prevoffset) { int i; for (i = 0; i < max; i++) prevmoves[i] = 0; prevoffset = offset; } prevmoves[index] += direction; break; } /* * Make the move. */ if (direction < 0) { start += (len-1) * offset; offset = -offset; } tmp = tiles[start]; for (j = 0; j+1 < len; j++) tiles[start + j*offset] = tiles[start + (j+1)*offset]; tiles[start + (len-1) * offset] = tmp; } sfree(prevmoves); } else { used = snewn(n, int); for (i = 0; i < n; i++) { tiles[i] = -1; used[i] = FALSE; } /* * If both dimensions are odd, there is a parity * constraint. */ if (params->w & params->h & 1) stop = 2; else stop = 0; /* * Place everything except (possibly) the last two tiles. */ for (x = 0, i = n; i > stop; i--) { int k = i > 1 ? random_upto(rs, i) : 0; int j; for (j = 0; j < n; j++) if (!used[j] && (k-- == 0)) break; assert(j < n && !used[j]); used[j] = TRUE; while (tiles[x] >= 0) x++; assert(x < n); tiles[x] = j; } if (stop) { /* * Find the last two locations, and the last two * pieces. */ while (tiles[x] >= 0) x++; assert(x < n); x1 = x; x++; while (tiles[x] >= 0) x++; assert(x < n); x2 = x; for (i = 0; i < n; i++) if (!used[i]) break; p1 = i; for (i = p1+1; i < n; i++) if (!used[i]) break; p2 = i; /* * Try the last two tiles one way round. If that fails, * swap them. */ tiles[x1] = p1; tiles[x2] = p2; if (perm_parity(tiles, n) != 0) { tiles[x1] = p2; tiles[x2] = p1; assert(perm_parity(tiles, n) == 0); } } sfree(used); } /* * Now construct the game description, by describing the tile * array as a simple sequence of comma-separated integers. */ ret = NULL; retlen = 0; for (i = 0; i < n; i++) { char buf[80]; int k; k = sprintf(buf, "%d,", tiles[i]+1); ret = sresize(ret, retlen + k + 1, char); strcpy(ret + retlen, buf); retlen += k; } ret[retlen-1] = '\0'; /* delete last comma */ sfree(tiles); return ret; } static char *validate_desc(const game_params *params, const char *desc) { const char *p; char *err; int i, area; int *used; area = params->w * params->h; p = desc; err = NULL; used = snewn(area, int); for (i = 0; i < area; i++) used[i] = FALSE; for (i = 0; i < area; i++) { const char *q = p; int n; if (*p < '0' || *p > '9') { err = "Not enough numbers in string"; goto leave; } while (*p >= '0' && *p <= '9') p++; if (i < area-1 && *p != ',') { err = "Expected comma after number"; goto leave; } else if (i == area-1 && *p) { err = "Excess junk at end of string"; goto leave; } n = atoi(q); if (n < 1 || n > area) { err = "Number out of range"; goto leave; } if (used[n-1]) { err = "Number used twice"; goto leave; } used[n-1] = TRUE; if (*p) p++; /* eat comma */ } leave: sfree(used); return err; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { game_state *state = snew(game_state); int i; const char *p; state->w = params->w; state->h = params->h; state->n = params->w * params->h; state->tiles = snewn(state->n, int); p = desc; i = 0; for (i = 0; i < state->n; i++) { assert(*p); state->tiles[i] = atoi(p); while (*p && *p != ',') p++; if (*p) p++; /* eat comma */ } assert(!*p); state->completed = state->movecount = 0; state->movetarget = params->movetarget; state->used_solve = FALSE; state->last_movement_sense = 0; return state; } static game_state *dup_game(const game_state *state) { game_state *ret = snew(game_state); ret->w = state->w; ret->h = state->h; ret->n = state->n; ret->tiles = snewn(state->w * state->h, int); memcpy(ret->tiles, state->tiles, state->w * state->h * sizeof(int)); ret->completed = state->completed; ret->movecount = state->movecount; ret->movetarget = state->movetarget; ret->used_solve = state->used_solve; ret->last_movement_sense = state->last_movement_sense; return ret; } static void free_game(game_state *state) { sfree(state->tiles); sfree(state); } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { return dupstr("S"); } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { char *ret, *p, buf[80]; int x, y, col, maxlen; /* * First work out how many characters we need to display each * number. */ col = sprintf(buf, "%d", state->n); /* * Now we know the exact total size of the grid we're going to * produce: it's got h rows, each containing w lots of col, w-1 * spaces and a trailing newline. */ maxlen = state->h * state->w * (col+1); ret = snewn(maxlen+1, char); p = ret; for (y = 0; y < state->h; y++) { for (x = 0; x < state->w; x++) { int v = state->tiles[state->w*y+x]; sprintf(buf, "%*d", col, v); memcpy(p, buf, col); p += col; if (x+1 == state->w) *p++ = '\n'; else *p++ = ' '; } } assert(p - ret == maxlen); *p = '\0'; return ret; } struct game_ui { int cur_x, cur_y; int cur_visible; }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->cur_x = 0; ui->cur_y = -1; ui->cur_visible = FALSE; 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) { } struct game_drawstate { int started; int w, h, bgcolour; int *tiles; int tilesize; int cur_x, cur_y; }; static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { int cx = -1, cy = -1, dx, dy; char buf[80]; button &= ~MOD_MASK; if (IS_CURSOR_MOVE(button)) { /* right/down rotates cursor clockwise, * left/up rotates anticlockwise. */ int cpos, diff; cpos = c2pos(state->w, state->h, ui->cur_x, ui->cur_y); diff = c2diff(state->w, state->h, ui->cur_x, ui->cur_y, button); cpos += diff; pos2c(state->w, state->h, cpos, &ui->cur_x, &ui->cur_y); ui->cur_visible = 1; return ""; } if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { cx = FROMCOORD(x); cy = FROMCOORD(y); ui->cur_visible = 0; } else if (IS_CURSOR_SELECT(button)) { if (ui->cur_visible) { cx = ui->cur_x; cy = ui->cur_y; } else { ui->cur_visible = 1; return ""; } } else { return NULL; } if (cx == -1 && cy >= 0 && cy < state->h) dx = -1, dy = 0; else if (cx == state->w && cy >= 0 && cy < state->h) dx = +1, dy = 0; else if (cy == -1 && cx >= 0 && cx < state->w) dy = -1, dx = 0; else if (cy == state->h && cx >= 0 && cx < state->w) dy = +1, dx = 0; else return ""; /* invalid click location */ /* reverse direction if right hand button is pressed */ if (button == RIGHT_BUTTON || button == CURSOR_SELECT2) { dx = -dx; dy = -dy; } if (dx) sprintf(buf, "R%d,%d", cy, dx); else sprintf(buf, "C%d,%d", cx, dy); return dupstr(buf); } static game_state *execute_move(const game_state *from, const char *move) { int cx, cy, dx, dy; int tx, ty, n; game_state *ret; if (!strcmp(move, "S")) { int i; ret = dup_game(from); /* * Simply replace the grid with a solved one. For this game, * this isn't a useful operation for actually telling the user * what they should have done, but it is useful for * conveniently being able to get hold of a clean state from * which to practise manoeuvres. */ for (i = 0; i < ret->n; i++) ret->tiles[i] = i+1; ret->used_solve = TRUE; ret->completed = ret->movecount = 1; return ret; } if (move[0] == 'R' && sscanf(move+1, "%d,%d", &cy, &dx) == 2 && cy >= 0 && cy < from->h) { cx = dy = 0; n = from->w; } else if (move[0] == 'C' && sscanf(move+1, "%d,%d", &cx, &dy) == 2 && cx >= 0 && cx < from->w) { cy = dx = 0; n = from->h; } else return NULL; ret = dup_game(from); do { tx = (cx - dx + from->w) % from->w; ty = (cy - dy + from->h) % from->h; ret->tiles[C(ret, cx, cy)] = from->tiles[C(from, tx, ty)]; cx = tx; cy = ty; } while (--n > 0); ret->movecount++; ret->last_movement_sense = dx+dy; /* * See if the game has been completed. */ if (!ret->completed) { ret->completed = ret->movecount; for (n = 0; n < ret->n; n++) if (ret->tiles[n] != n+1) ret->completed = FALSE; } 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 = TILE_SIZE * params->w + 2 * BORDER; *y = TILE_SIZE * params->h + 2 * BORDER; } 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); for (i = 0; i < 3; i++) ret[COL_TEXT * 3 + i] = 0.0; *ncolours = NCOLOURS; return ret; } static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { struct game_drawstate *ds = snew(struct game_drawstate); int i; ds->started = FALSE; ds->w = state->w; ds->h = state->h; ds->bgcolour = COL_BACKGROUND; ds->tiles = snewn(ds->w*ds->h, int); ds->tilesize = 0; /* haven't decided yet */ for (i = 0; i < ds->w*ds->h; i++) ds->tiles[i] = -1; ds->cur_x = ds->cur_y = -1; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->tiles); sfree(ds); } static void draw_tile(drawing *dr, game_drawstate *ds, const game_state *state, int x, int y, int tile, int flash_colour) { if (tile == 0) { draw_rect(dr, x, y, TILE_SIZE, TILE_SIZE, flash_colour); } else { int coords[6]; char str[40]; coords[0] = x + TILE_SIZE - 1; coords[1] = y + TILE_SIZE - 1; coords[2] = x + TILE_SIZE - 1; coords[3] = y; coords[4] = x; coords[5] = y + TILE_SIZE - 1; draw_polygon(dr, coords, 3, COL_LOWLIGHT, COL_LOWLIGHT); coords[0] = x; coords[1] = y; draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT); draw_rect(dr, x + HIGHLIGHT_WIDTH, y + HIGHLIGHT_WIDTH, TILE_SIZE - 2*HIGHLIGHT_WIDTH, TILE_SIZE - 2*HIGHLIGHT_WIDTH, flash_colour); sprintf(str, "%d", tile); draw_text(dr, x + TILE_SIZE/2, y + TILE_SIZE/2, FONT_VARIABLE, TILE_SIZE/3, ALIGN_VCENTRE | ALIGN_HCENTRE, COL_TEXT, str); } draw_update(dr, x, y, TILE_SIZE, TILE_SIZE); } static void draw_arrow(drawing *dr, game_drawstate *ds, int x, int y, int xdx, int xdy, int cur) { int coords[14]; int ydy = -xdx, ydx = xdy; #define POINT(n, xx, yy) ( \ coords[2*(n)+0] = x + (xx)*xdx + (yy)*ydx, \ coords[2*(n)+1] = y + (xx)*xdy + (yy)*ydy) POINT(0, TILE_SIZE / 2, 3 * TILE_SIZE / 4); /* top of arrow */ POINT(1, 3 * TILE_SIZE / 4, TILE_SIZE / 2); /* right corner */ POINT(2, 5 * TILE_SIZE / 8, TILE_SIZE / 2); /* right concave */ POINT(3, 5 * TILE_SIZE / 8, TILE_SIZE / 4); /* bottom right */ POINT(4, 3 * TILE_SIZE / 8, TILE_SIZE / 4); /* bottom left */ POINT(5, 3 * TILE_SIZE / 8, TILE_SIZE / 2); /* left concave */ POINT(6, TILE_SIZE / 4, TILE_SIZE / 2); /* left corner */ draw_polygon(dr, coords, 7, cur ? COL_HIGHLIGHT : COL_LOWLIGHT, COL_TEXT); } static void draw_arrow_for_cursor(drawing *dr, game_drawstate *ds, int cur_x, int cur_y, int cur) { if (cur_x == -1 && cur_y == -1) return; /* 'no cursur here */ else if (cur_x == -1) /* LH column. */ draw_arrow(dr, ds, COORD(0), COORD(cur_y+1), 0, -1, cur); else if (cur_x == ds->w) /* RH column */ draw_arrow(dr, ds, COORD(ds->w), COORD(cur_y), 0, +1, cur); else if (cur_y == -1) /* Top row */ draw_arrow(dr, ds, COORD(cur_x), COORD(0), +1, 0, cur); else if (cur_y == ds->h) /* Bottom row */ draw_arrow(dr, ds, COORD(cur_x+1), COORD(ds->h), -1, 0, cur); else assert(!"Invalid cursor position"); draw_update(dr, COORD(cur_x), COORD(cur_y), TILE_SIZE, TILE_SIZE); } 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 i, bgcolour; int cur_x = -1, cur_y = -1; if (flashtime > 0) { int frame = (int)(flashtime / FLASH_FRAME); bgcolour = (frame % 2 ? COL_LOWLIGHT : COL_HIGHLIGHT); } else bgcolour = COL_BACKGROUND; if (!ds->started) { int coords[10]; draw_rect(dr, 0, 0, TILE_SIZE * state->w + 2 * BORDER, TILE_SIZE * state->h + 2 * BORDER, COL_BACKGROUND); draw_update(dr, 0, 0, TILE_SIZE * state->w + 2 * BORDER, TILE_SIZE * state->h + 2 * BORDER); /* * Recessed area containing the whole puzzle. */ coords[0] = COORD(state->w) + HIGHLIGHT_WIDTH - 1; coords[1] = COORD(state->h) + HIGHLIGHT_WIDTH - 1; coords[2] = COORD(state->w) + HIGHLIGHT_WIDTH - 1; coords[3] = COORD(0) - HIGHLIGHT_WIDTH; coords[4] = coords[2] - TILE_SIZE; coords[5] = coords[3] + TILE_SIZE; coords[8] = COORD(0) - HIGHLIGHT_WIDTH; coords[9] = COORD(state->h) + HIGHLIGHT_WIDTH - 1; coords[6] = coords[8] + TILE_SIZE; coords[7] = coords[9] - TILE_SIZE; draw_polygon(dr, coords, 5, COL_HIGHLIGHT, COL_HIGHLIGHT); coords[1] = COORD(0) - HIGHLIGHT_WIDTH; coords[0] = COORD(0) - HIGHLIGHT_WIDTH; draw_polygon(dr, coords, 5, COL_LOWLIGHT, COL_LOWLIGHT); /* * Arrows for making moves. */ for (i = 0; i < state->w; i++) { draw_arrow(dr, ds, COORD(i), COORD(0), +1, 0, 0); draw_arrow(dr, ds, COORD(i+1), COORD(state->h), -1, 0, 0); } for (i = 0; i < state->h; i++) { draw_arrow(dr, ds, COORD(state->w), COORD(i), 0, +1, 0); draw_arrow(dr, ds, COORD(0), COORD(i+1), 0, -1, 0); } ds->started = TRUE; } /* * Cursor (highlighted arrow around edge) */ if (ui->cur_visible) { cur_x = ui->cur_x; cur_y = ui->cur_y; } if (cur_x != ds->cur_x || cur_y != ds->cur_y) { /* Cursor has changed; redraw two (prev and curr) arrows. */ draw_arrow_for_cursor(dr, ds, cur_x, cur_y, 1); draw_arrow_for_cursor(dr, ds, ds->cur_x, ds->cur_y, 0); ds->cur_x = cur_x; ds->cur_y = cur_y; } /* * Now draw each tile. */ clip(dr, COORD(0), COORD(0), TILE_SIZE*state->w, TILE_SIZE*state->h); for (i = 0; i < state->n; i++) { int t, t0; /* * Figure out what should be displayed at this * location. It's either a simple tile, or it's a * transition between two tiles (in which case we say * -1 because it must always be drawn). */ if (oldstate && oldstate->tiles[i] != state->tiles[i]) t = -1; else t = state->tiles[i]; t0 = t; if (ds->bgcolour != bgcolour || /* always redraw when flashing */ ds->tiles[i] != t || ds->tiles[i] == -1 || t == -1) { int x, y, x2, y2; /* * Figure out what to _actually_ draw, and where to * draw it. */ if (t == -1) { int x0, y0, x1, y1, dx, dy; int j; float c; int sense; if (dir < 0) { assert(oldstate); sense = -oldstate->last_movement_sense; } else { sense = state->last_movement_sense; } t = state->tiles[i]; /* * FIXME: must be prepared to draw a double * tile in some situations. */ /* * Find the coordinates of this tile in the old and * new states. */ x1 = COORD(X(state, i)); y1 = COORD(Y(state, i)); for (j = 0; j < oldstate->n; j++) if (oldstate->tiles[j] == state->tiles[i]) break; assert(j < oldstate->n); x0 = COORD(X(state, j)); y0 = COORD(Y(state, j)); dx = (x1 - x0); if (dx != 0 && dx != TILE_SIZE * sense) { dx = (dx < 0 ? dx + TILE_SIZE * state->w : dx - TILE_SIZE * state->w); assert(abs(dx) == TILE_SIZE); } dy = (y1 - y0); if (dy != 0 && dy != TILE_SIZE * sense) { dy = (dy < 0 ? dy + TILE_SIZE * state->h : dy - TILE_SIZE * state->h); assert(abs(dy) == TILE_SIZE); } c = (animtime / ANIM_TIME); if (c < 0.0F) c = 0.0F; if (c > 1.0F) c = 1.0F; x = x0 + (int)(c * dx); y = y0 + (int)(c * dy); x2 = x1 - dx + (int)(c * dx); y2 = y1 - dy + (int)(c * dy); } else { x = COORD(X(state, i)); y = COORD(Y(state, i)); x2 = y2 = -1; } draw_tile(dr, ds, state, x, y, t, bgcolour); if (x2 != -1 || y2 != -1) draw_tile(dr, ds, state, x2, y2, t, bgcolour); } ds->tiles[i] = t0; } unclip(dr); ds->bgcolour = bgcolour; /* * Update the status bar. */ { char statusbuf[256]; /* * Don't show the new status until we're also showing the * new _state_ - after the game animation is complete. */ if (oldstate) state = oldstate; if (state->used_solve) sprintf(statusbuf, "Moves since auto-solve: %d", state->movecount - state->completed); else { sprintf(statusbuf, "%sMoves: %d", (state->completed ? "COMPLETED! " : ""), (state->completed ? state->completed : state->movecount)); if (state->movetarget) sprintf(statusbuf+strlen(statusbuf), " (target %d)", state->movetarget); } status_bar(dr, statusbuf); } } static float game_anim_length(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui) { return ANIM_TIME; } static float game_flash_length(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui) { if (!oldstate->completed && newstate->completed && !oldstate->used_solve && !newstate->used_solve) return 2 * FLASH_FRAME; else return 0.0F; } static int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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 sixteen #endif const struct game thegame = { "Sixteen", "games.sixteen", "sixteen", default_params, game_fetch_preset, 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, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, FALSE, FALSE, game_print_size, game_print, TRUE, /* wants_statusbar */ FALSE, game_timing_state, 0, /* flags */ }; /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/slant.c0000644000175300017530000017475212132232554013731 0ustar simonsimon/* * slant.c: Puzzle from nikoli.co.jp involving drawing a diagonal * line through each square of a grid. */ /* * In this puzzle you have a grid of squares, each of which must * contain a diagonal line; you also have clue numbers placed at * _points_ of that grid, which means there's a (w+1) x (h+1) array * of possible clue positions. * * I'm therefore going to adopt a rigid convention throughout this * source file of using w and h for the dimensions of the grid of * squares, and W and H for the dimensions of the grid of points. * Thus, W == w+1 and H == h+1 always. * * Clue arrays will be W*H `signed char's, and the clue at each * point will be a number from 0 to 4, or -1 if there's no clue. * * Solution arrays will be W*H `signed char's, and the number at * each point will be +1 for a forward slash (/), -1 for a * backslash (\), and 0 for unknown. */ #include #include #include #include #include #include #include #include "puzzles.h" enum { COL_BACKGROUND, COL_GRID, COL_INK, COL_SLANT1, COL_SLANT2, COL_ERROR, COL_CURSOR, COL_FILLEDSQUARE, NCOLOURS }; /* * In standalone solver mode, `verbose' is a variable which can be * set by command-line option; in debugging mode it's simply always * true. */ #if defined STANDALONE_SOLVER #define SOLVER_DIAGNOSTICS int verbose = FALSE; #elif defined SOLVER_DIAGNOSTICS #define verbose TRUE #endif /* * 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(EASY,Easy,e) \ A(HARD,Hard,h) #define ENUM(upper,title,lower) DIFF_ ## upper, #define TITLE(upper,title,lower) #title, #define ENCODE(upper,title,lower) #lower #define CONFIG(upper,title,lower) ":" #title enum { DIFFLIST(ENUM) DIFFCOUNT }; static char const *const slant_diffnames[] = { DIFFLIST(TITLE) }; static char const slant_diffchars[] = DIFFLIST(ENCODE); #define DIFFCONFIG DIFFLIST(CONFIG) struct game_params { int w, h, diff; }; typedef struct game_clues { int w, h; signed char *clues; int *tmpdsf; int refcount; } game_clues; #define ERR_VERTEX 1 #define ERR_SQUARE 2 struct game_state { struct game_params p; game_clues *clues; signed char *soln; unsigned char *errors; int completed; int used_solve; /* used to suppress completion flash */ }; static game_params *default_params(void) { game_params *ret = snew(game_params); ret->w = ret->h = 8; ret->diff = DIFF_EASY; return ret; } static const struct game_params slant_presets[] = { {5, 5, DIFF_EASY}, {5, 5, DIFF_HARD}, {8, 8, DIFF_EASY}, {8, 8, DIFF_HARD}, {12, 10, DIFF_EASY}, {12, 10, DIFF_HARD}, }; static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char str[80]; if (i < 0 || i >= lenof(slant_presets)) return FALSE; ret = snew(game_params); *ret = slant_presets[i]; sprintf(str, "%dx%d %s", ret->w, ret->h, slant_diffnames[ret->diff]); *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 *ret, char const *string) { ret->w = ret->h = atoi(string); while (*string && isdigit((unsigned char)*string)) string++; if (*string == 'x') { string++; ret->h = atoi(string); while (*string && isdigit((unsigned char)*string)) string++; } if (*string == 'd') { int i; string++; for (i = 0; i < DIFFCOUNT; i++) if (*string == slant_diffchars[i]) ret->diff = i; if (*string) string++; } } static char *encode_params(const game_params *params, int full) { char data[256]; sprintf(data, "%dx%d", params->w, params->h); if (full) sprintf(data + strlen(data), "d%c", slant_diffchars[params->diff]); 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].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Difficulty"; ret[2].type = C_CHOICES; ret[2].sval = DIFFCONFIG; ret[2].ival = params->diff; ret[3].name = NULL; ret[3].type = C_END; ret[3].sval = NULL; ret[3].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); ret->diff = cfg[2].ival; return ret; } static char *validate_params(const game_params *params, int full) { /* * (At least at the time of writing this comment) The grid * generator is actually capable of handling even zero grid * dimensions without crashing. Puzzles with a zero-area grid * are a bit boring, though, because they're already solved :-) * And puzzles with a dimension of 1 can't be made Hard, which * means the simplest thing is to forbid them altogether. */ if (params->w < 2 || params->h < 2) return "Width and height must both be at least two"; return NULL; } /* * Scratch space for solver. */ struct solver_scratch { /* * Disjoint set forest which tracks the connected sets of * points. */ int *connected; /* * Counts the number of possible exits from each connected set * of points. (That is, the number of possible _simultaneous_ * exits: an unconnected point labelled 2 has an exit count of * 2 even if all four possible edges are still under * consideration.) */ int *exits; /* * Tracks whether each connected set of points includes a * border point. */ unsigned char *border; /* * Another disjoint set forest. This one tracks _squares_ which * are known to slant in the same direction. */ int *equiv; /* * Stores slash values which we know for an equivalence class. * When we fill in a square, we set slashval[canonify(x)] to * the same value as soln[x], so that we can then spot other * squares equivalent to it and fill them in immediately via * their known equivalence. */ signed char *slashval; /* * Stores possible v-shapes. This array is w by h in size, but * not every bit of every entry is meaningful. The bits mean: * * - bit 0 for a square means that that square and the one to * its right might form a v-shape between them * - bit 1 for a square means that that square and the one to * its right might form a ^-shape between them * - bit 2 for a square means that that square and the one * below it might form a >-shape between them * - bit 3 for a square means that that square and the one * below it might form a <-shape between them * * Any starting 1 or 3 clue rules out four bits in this array * immediately; a 2 clue propagates any ruled-out bit past it * (if the two squares on one side of a 2 cannot be a v-shape, * then neither can the two on the other side be the same * v-shape); we can rule out further bits during play using * partially filled 2 clues; whenever a pair of squares is * known not to be _either_ kind of v-shape, we can mark them * as equivalent. */ unsigned char *vbitmap; /* * Useful to have this information automatically passed to * solver subroutines. (This pointer is not dynamically * allocated by new_scratch and free_scratch.) */ const signed char *clues; }; static struct solver_scratch *new_scratch(int w, int h) { int W = w+1, H = h+1; struct solver_scratch *ret = snew(struct solver_scratch); ret->connected = snewn(W*H, int); ret->exits = snewn(W*H, int); ret->border = snewn(W*H, unsigned char); ret->equiv = snewn(w*h, int); ret->slashval = snewn(w*h, signed char); ret->vbitmap = snewn(w*h, unsigned char); return ret; } static void free_scratch(struct solver_scratch *sc) { sfree(sc->vbitmap); sfree(sc->slashval); sfree(sc->equiv); sfree(sc->border); sfree(sc->exits); sfree(sc->connected); sfree(sc); } /* * Wrapper on dsf_merge() which updates the `exits' and `border' * arrays. */ static void merge_vertices(int *connected, struct solver_scratch *sc, int i, int j) { int exits = -1, border = FALSE; /* initialise to placate optimiser */ if (sc) { i = dsf_canonify(connected, i); j = dsf_canonify(connected, j); /* * We have used one possible exit from each of the two * classes. Thus, the viable exit count of the new class is * the sum of the old exit counts minus two. */ exits = sc->exits[i] + sc->exits[j] - 2; border = sc->border[i] || sc->border[j]; } dsf_merge(connected, i, j); if (sc) { i = dsf_canonify(connected, i); sc->exits[i] = exits; sc->border[i] = border; } } /* * Called when we have just blocked one way out of a particular * point. If that point is a non-clue point (thus has a variable * number of exits), we have therefore decreased its potential exit * count, so we must decrement the exit count for the group as a * whole. */ static void decr_exits(struct solver_scratch *sc, int i) { if (sc->clues[i] < 0) { i = dsf_canonify(sc->connected, i); sc->exits[i]--; } } static void fill_square(int w, int h, int x, int y, int v, signed char *soln, int *connected, struct solver_scratch *sc) { int W = w+1 /*, H = h+1 */; assert(x >= 0 && x < w && y >= 0 && y < h); if (soln[y*w+x] != 0) { return; /* do nothing */ } #ifdef SOLVER_DIAGNOSTICS if (verbose) printf(" placing %c in %d,%d\n", v == -1 ? '\\' : '/', x, y); #endif soln[y*w+x] = v; if (sc) { int c = dsf_canonify(sc->equiv, y*w+x); sc->slashval[c] = v; } if (v < 0) { merge_vertices(connected, sc, y*W+x, (y+1)*W+(x+1)); if (sc) { decr_exits(sc, y*W+(x+1)); decr_exits(sc, (y+1)*W+x); } } else { merge_vertices(connected, sc, y*W+(x+1), (y+1)*W+x); if (sc) { decr_exits(sc, y*W+x); decr_exits(sc, (y+1)*W+(x+1)); } } } static int vbitmap_clear(int w, int h, struct solver_scratch *sc, int x, int y, int vbits, char *reason, ...) { int done_something = FALSE; int vbit; for (vbit = 1; vbit <= 8; vbit <<= 1) if (vbits & sc->vbitmap[y*w+x] & vbit) { done_something = TRUE; #ifdef SOLVER_DIAGNOSTICS if (verbose) { va_list ap; printf("ruling out %c shape at (%d,%d)-(%d,%d) (", "!v^!>!!!<"[vbit], x, y, x+((vbit&0x3)!=0), y+((vbit&0xC)!=0)); va_start(ap, reason); vprintf(reason, ap); va_end(ap); printf(")\n"); } #endif sc->vbitmap[y*w+x] &= ~vbit; } return done_something; } /* * Solver. Returns 0 for impossibility, 1 for success, 2 for * ambiguity or failure to converge. */ static int slant_solve(int w, int h, const signed char *clues, signed char *soln, struct solver_scratch *sc, int difficulty) { int W = w+1, H = h+1; int x, y, i, j; int done_something; /* * Clear the output. */ memset(soln, 0, w*h); sc->clues = clues; /* * Establish a disjoint set forest for tracking connectedness * between grid points. */ dsf_init(sc->connected, W*H); /* * Establish a disjoint set forest for tracking which squares * are known to slant in the same direction. */ dsf_init(sc->equiv, w*h); /* * Clear the slashval array. */ memset(sc->slashval, 0, w*h); /* * Set up the vbitmap array. Initially all types of v are possible. */ memset(sc->vbitmap, 0xF, w*h); /* * Initialise the `exits' and `border' arrays. These are used * to do second-order loop avoidance: the dual of the no loops * constraint is that every point must be somehow connected to * the border of the grid (otherwise there would be a solid * loop around it which prevented this). * * I define a `dead end' to be a connected group of points * which contains no border point, and which can form at most * one new connection outside itself. Then I forbid placing an * edge so that it connects together two dead-end groups, since * this would yield a non-border-connected isolated subgraph * with no further scope to extend it. */ for (y = 0; y < H; y++) for (x = 0; x < W; x++) { if (y == 0 || y == H-1 || x == 0 || x == W-1) sc->border[y*W+x] = TRUE; else sc->border[y*W+x] = FALSE; if (clues[y*W+x] < 0) sc->exits[y*W+x] = 4; else sc->exits[y*W+x] = clues[y*W+x]; } /* * Repeatedly try to deduce something until we can't. */ do { done_something = FALSE; /* * Any clue point with the number of remaining lines equal * to zero or to the number of remaining undecided * neighbouring squares can be filled in completely. */ for (y = 0; y < H; y++) for (x = 0; x < W; x++) { struct { int pos, slash; } neighbours[4]; int nneighbours; int nu, nl, c, s, eq, eq2, last, meq, mj1, mj2; if ((c = clues[y*W+x]) < 0) continue; /* * We have a clue point. Start by listing its * neighbouring squares, in order around the point, * together with the type of slash that would be * required in that square to connect to the point. */ nneighbours = 0; if (x > 0 && y > 0) { neighbours[nneighbours].pos = (y-1)*w+(x-1); neighbours[nneighbours].slash = -1; nneighbours++; } if (x > 0 && y < h) { neighbours[nneighbours].pos = y*w+(x-1); neighbours[nneighbours].slash = +1; nneighbours++; } if (x < w && y < h) { neighbours[nneighbours].pos = y*w+x; neighbours[nneighbours].slash = -1; nneighbours++; } if (x < w && y > 0) { neighbours[nneighbours].pos = (y-1)*w+x; neighbours[nneighbours].slash = +1; nneighbours++; } /* * Count up the number of undecided neighbours, and * also the number of lines already present. * * If we're not on DIFF_EASY, then in this loop we * also track whether we've seen two adjacent empty * squares belonging to the same equivalence class * (meaning they have the same type of slash). If * so, we count them jointly as one line. */ nu = 0; nl = c; last = neighbours[nneighbours-1].pos; if (soln[last] == 0) eq = dsf_canonify(sc->equiv, last); else eq = -1; meq = mj1 = mj2 = -1; for (i = 0; i < nneighbours; i++) { j = neighbours[i].pos; s = neighbours[i].slash; if (soln[j] == 0) { nu++; /* undecided */ if (meq < 0 && difficulty > DIFF_EASY) { eq2 = dsf_canonify(sc->equiv, j); if (eq == eq2 && last != j) { /* * We've found an equivalent pair. * Mark it. This also inhibits any * further equivalence tracking * around this square, since we can * only handle one pair (and in * particular we want to avoid * being misled by two overlapping * equivalence pairs). */ meq = eq; mj1 = last; mj2 = j; nl--; /* count one line */ nu -= 2; /* and lose two undecideds */ } else eq = eq2; } } else { eq = -1; if (soln[j] == s) nl--; /* here's a line */ } last = j; } /* * Check the counts. */ if (nl < 0 || nl > nu) { /* * No consistent value for this at all! */ #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("need %d / %d lines around clue point at %d,%d!\n", nl, nu, x, y); #endif return 0; /* impossible */ } if (nu > 0 && (nl == 0 || nl == nu)) { #ifdef SOLVER_DIAGNOSTICS if (verbose) { if (meq >= 0) printf("partially (since %d,%d == %d,%d) ", mj1%w, mj1/w, mj2%w, mj2/w); printf("%s around clue point at %d,%d\n", nl ? "filling" : "emptying", x, y); } #endif for (i = 0; i < nneighbours; i++) { j = neighbours[i].pos; s = neighbours[i].slash; if (soln[j] == 0 && j != mj1 && j != mj2) fill_square(w, h, j%w, j/w, (nl ? s : -s), soln, sc->connected, sc); } done_something = TRUE; } else if (nu == 2 && nl == 1 && difficulty > DIFF_EASY) { /* * If we have precisely two undecided squares * and precisely one line to place between * them, _and_ those squares are adjacent, then * we can mark them as equivalent to one * another. * * This even applies if meq >= 0: if we have a * 2 clue point and two of its neighbours are * already marked equivalent, we can indeed * mark the other two as equivalent. * * We don't bother with this on DIFF_EASY, * since we wouldn't have used the results * anyway. */ last = -1; for (i = 0; i < nneighbours; i++) { j = neighbours[i].pos; if (soln[j] == 0 && j != mj1 && j != mj2) { if (last < 0) last = i; else if (last == i-1 || (last == 0 && i == 3)) break; /* found a pair */ } } if (i < nneighbours) { int sv1, sv2; assert(last >= 0); /* * neighbours[last] and neighbours[i] are * the pair. Mark them equivalent. */ #ifdef SOLVER_DIAGNOSTICS if (verbose) { if (meq >= 0) printf("since %d,%d == %d,%d, ", mj1%w, mj1/w, mj2%w, mj2/w); } #endif mj1 = neighbours[last].pos; mj2 = neighbours[i].pos; #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("clue point at %d,%d implies %d,%d == %d," "%d\n", x, y, mj1%w, mj1/w, mj2%w, mj2/w); #endif mj1 = dsf_canonify(sc->equiv, mj1); sv1 = sc->slashval[mj1]; mj2 = dsf_canonify(sc->equiv, mj2); sv2 = sc->slashval[mj2]; if (sv1 != 0 && sv2 != 0 && sv1 != sv2) { #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("merged two equivalence classes with" " different slash values!\n"); #endif return 0; } sv1 = sv1 ? sv1 : sv2; dsf_merge(sc->equiv, mj1, mj2); mj1 = dsf_canonify(sc->equiv, mj1); sc->slashval[mj1] = sv1; } } } if (done_something) continue; /* * Failing that, we now apply the second condition, which * is that no square may be filled in such a way as to form * a loop. Also in this loop (since it's over squares * rather than points), we check slashval to see if we've * already filled in another square in the same equivalence * class. * * The slashval check is disabled on DIFF_EASY, as is dead * end avoidance. Only _immediate_ loop avoidance remains. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) { int fs, bs, v; int c1, c2; #ifdef SOLVER_DIAGNOSTICS char *reason = ""; #endif if (soln[y*w+x]) continue; /* got this one already */ fs = FALSE; bs = FALSE; if (difficulty > DIFF_EASY) v = sc->slashval[dsf_canonify(sc->equiv, y*w+x)]; else v = 0; /* * Try to rule out connectivity between (x,y) and * (x+1,y+1); if successful, we will deduce that we * must have a forward slash. */ c1 = dsf_canonify(sc->connected, y*W+x); c2 = dsf_canonify(sc->connected, (y+1)*W+(x+1)); if (c1 == c2) { fs = TRUE; #ifdef SOLVER_DIAGNOSTICS reason = "simple loop avoidance"; #endif } if (difficulty > DIFF_EASY && !sc->border[c1] && !sc->border[c2] && sc->exits[c1] <= 1 && sc->exits[c2] <= 1) { fs = TRUE; #ifdef SOLVER_DIAGNOSTICS reason = "dead end avoidance"; #endif } if (v == +1) { fs = TRUE; #ifdef SOLVER_DIAGNOSTICS reason = "equivalence to an already filled square"; #endif } /* * Now do the same between (x+1,y) and (x,y+1), to * see if we are required to have a backslash. */ c1 = dsf_canonify(sc->connected, y*W+(x+1)); c2 = dsf_canonify(sc->connected, (y+1)*W+x); if (c1 == c2) { bs = TRUE; #ifdef SOLVER_DIAGNOSTICS reason = "simple loop avoidance"; #endif } if (difficulty > DIFF_EASY && !sc->border[c1] && !sc->border[c2] && sc->exits[c1] <= 1 && sc->exits[c2] <= 1) { bs = TRUE; #ifdef SOLVER_DIAGNOSTICS reason = "dead end avoidance"; #endif } if (v == -1) { bs = TRUE; #ifdef SOLVER_DIAGNOSTICS reason = "equivalence to an already filled square"; #endif } if (fs && bs) { /* * No consistent value for this at all! */ #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("%d,%d has no consistent slash!\n", x, y); #endif return 0; /* impossible */ } if (fs) { #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("employing %s\n", reason); #endif fill_square(w, h, x, y, +1, soln, sc->connected, sc); done_something = TRUE; } else if (bs) { #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("employing %s\n", reason); #endif fill_square(w, h, x, y, -1, soln, sc->connected, sc); done_something = TRUE; } } if (done_something) continue; /* * Now see what we can do with the vbitmap array. All * vbitmap deductions are disabled at Easy level. */ if (difficulty <= DIFF_EASY) continue; for (y = 0; y < h; y++) for (x = 0; x < w; x++) { int s, c; /* * Any line already placed in a square must rule * out any type of v which contradicts it. */ if ((s = soln[y*w+x]) != 0) { if (x > 0) done_something |= vbitmap_clear(w, h, sc, x-1, y, (s < 0 ? 0x1 : 0x2), "contradicts known edge at (%d,%d)",x,y); if (x+1 < w) done_something |= vbitmap_clear(w, h, sc, x, y, (s < 0 ? 0x2 : 0x1), "contradicts known edge at (%d,%d)",x,y); if (y > 0) done_something |= vbitmap_clear(w, h, sc, x, y-1, (s < 0 ? 0x4 : 0x8), "contradicts known edge at (%d,%d)",x,y); if (y+1 < h) done_something |= vbitmap_clear(w, h, sc, x, y, (s < 0 ? 0x8 : 0x4), "contradicts known edge at (%d,%d)",x,y); } /* * If both types of v are ruled out for a pair of * adjacent squares, mark them as equivalent. */ if (x+1 < w && !(sc->vbitmap[y*w+x] & 0x3)) { int n1 = y*w+x, n2 = y*w+(x+1); if (dsf_canonify(sc->equiv, n1) != dsf_canonify(sc->equiv, n2)) { dsf_merge(sc->equiv, n1, n2); done_something = TRUE; #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("(%d,%d) and (%d,%d) must be equivalent" " because both v-shapes are ruled out\n", x, y, x+1, y); #endif } } if (y+1 < h && !(sc->vbitmap[y*w+x] & 0xC)) { int n1 = y*w+x, n2 = (y+1)*w+x; if (dsf_canonify(sc->equiv, n1) != dsf_canonify(sc->equiv, n2)) { dsf_merge(sc->equiv, n1, n2); done_something = TRUE; #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("(%d,%d) and (%d,%d) must be equivalent" " because both v-shapes are ruled out\n", x, y, x, y+1); #endif } } /* * The remaining work in this loop only works * around non-edge clue points. */ if (y == 0 || x == 0) continue; if ((c = clues[y*W+x]) < 0) continue; /* * x,y marks a clue point not on the grid edge. See * if this clue point allows us to rule out any v * shapes. */ if (c == 1) { /* * A 1 clue can never have any v shape pointing * at it. */ done_something |= vbitmap_clear(w, h, sc, x-1, y-1, 0x5, "points at 1 clue at (%d,%d)", x, y); done_something |= vbitmap_clear(w, h, sc, x-1, y, 0x2, "points at 1 clue at (%d,%d)", x, y); done_something |= vbitmap_clear(w, h, sc, x, y-1, 0x8, "points at 1 clue at (%d,%d)", x, y); } else if (c == 3) { /* * A 3 clue can never have any v shape pointing * away from it. */ done_something |= vbitmap_clear(w, h, sc, x-1, y-1, 0xA, "points away from 3 clue at (%d,%d)", x, y); done_something |= vbitmap_clear(w, h, sc, x-1, y, 0x1, "points away from 3 clue at (%d,%d)", x, y); done_something |= vbitmap_clear(w, h, sc, x, y-1, 0x4, "points away from 3 clue at (%d,%d)", x, y); } else if (c == 2) { /* * If a 2 clue has any kind of v ruled out on * one side of it, the same v is ruled out on * the other side. */ done_something |= vbitmap_clear(w, h, sc, x-1, y-1, (sc->vbitmap[(y )*w+(x-1)] & 0x3) ^ 0x3, "propagated by 2 clue at (%d,%d)", x, y); done_something |= vbitmap_clear(w, h, sc, x-1, y-1, (sc->vbitmap[(y-1)*w+(x )] & 0xC) ^ 0xC, "propagated by 2 clue at (%d,%d)", x, y); done_something |= vbitmap_clear(w, h, sc, x-1, y, (sc->vbitmap[(y-1)*w+(x-1)] & 0x3) ^ 0x3, "propagated by 2 clue at (%d,%d)", x, y); done_something |= vbitmap_clear(w, h, sc, x, y-1, (sc->vbitmap[(y-1)*w+(x-1)] & 0xC) ^ 0xC, "propagated by 2 clue at (%d,%d)", x, y); } #undef CLEARBITS } } while (done_something); /* * Solver can make no more progress. See if the grid is full. */ for (i = 0; i < w*h; i++) if (!soln[i]) return 2; /* failed to converge */ return 1; /* success */ } /* * Filled-grid generator. */ static void slant_generate(int w, int h, signed char *soln, random_state *rs) { int W = w+1, H = h+1; int x, y, i; int *connected, *indices; /* * Clear the output. */ memset(soln, 0, w*h); /* * Establish a disjoint set forest for tracking connectedness * between grid points. */ connected = snew_dsf(W*H); /* * Prepare a list of the squares in the grid, and fill them in * in a random order. */ indices = snewn(w*h, int); for (i = 0; i < w*h; i++) indices[i] = i; shuffle(indices, w*h, sizeof(*indices), rs); /* * Fill in each one in turn. */ for (i = 0; i < w*h; i++) { int fs, bs, v; y = indices[i] / w; x = indices[i] % w; fs = (dsf_canonify(connected, y*W+x) == dsf_canonify(connected, (y+1)*W+(x+1))); bs = (dsf_canonify(connected, (y+1)*W+x) == dsf_canonify(connected, y*W+(x+1))); /* * It isn't possible to get into a situation where we * aren't allowed to place _either_ type of slash in a * square. Thus, filled-grid generation never has to * backtrack. * * Proof (thanks to Gareth Taylor): * * If it were possible, it would have to be because there * was an existing path (not using this square) between the * top-left and bottom-right corners of this square, and * another between the other two. These two paths would * have to cross at some point. * * Obviously they can't cross in the middle of a square, so * they must cross by sharing a point in common. But this * isn't possible either: if you chessboard-colour all the * points on the grid, you find that any continuous * diagonal path is entirely composed of points of the same * colour. And one of our two hypothetical paths is between * two black points, and the other is between two white * points - therefore they can have no point in common. [] */ assert(!(fs && bs)); v = fs ? +1 : bs ? -1 : 2 * random_upto(rs, 2) - 1; fill_square(w, h, x, y, v, soln, connected, NULL); } sfree(indices); sfree(connected); } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { int w = params->w, h = params->h, W = w+1, H = h+1; signed char *soln, *tmpsoln, *clues; int *clueindices; struct solver_scratch *sc; int x, y, v, i, j; char *desc; soln = snewn(w*h, signed char); tmpsoln = snewn(w*h, signed char); clues = snewn(W*H, signed char); clueindices = snewn(W*H, int); sc = new_scratch(w, h); do { /* * Create the filled grid. */ slant_generate(w, h, soln, rs); /* * Fill in the complete set of clues. */ for (y = 0; y < H; y++) for (x = 0; x < W; x++) { v = 0; if (x > 0 && y > 0 && soln[(y-1)*w+(x-1)] == -1) v++; if (x > 0 && y < h && soln[y*w+(x-1)] == +1) v++; if (x < w && y > 0 && soln[(y-1)*w+x] == +1) v++; if (x < w && y < h && soln[y*w+x] == -1) v++; clues[y*W+x] = v; } /* * With all clue points filled in, all puzzles are easy: we can * simply process the clue points in lexicographic order, and * at each clue point we will always have at most one square * undecided, which we can then fill in uniquely. */ assert(slant_solve(w, h, clues, tmpsoln, sc, DIFF_EASY) == 1); /* * Remove as many clues as possible while retaining solubility. * * In DIFF_HARD mode, we prioritise the removal of obvious * starting points (4s, 0s, border 2s and corner 1s), on * the grounds that having as few of these as possible * seems like a good thing. In particular, we can often get * away without _any_ completely obvious starting points, * which is even better. */ for (i = 0; i < W*H; i++) clueindices[i] = i; shuffle(clueindices, W*H, sizeof(*clueindices), rs); for (j = 0; j < 2; j++) { for (i = 0; i < W*H; i++) { int pass, yb, xb; y = clueindices[i] / W; x = clueindices[i] % W; v = clues[y*W+x]; /* * Identify which pass we should process this point * in. If it's an obvious start point, _or_ we're * in DIFF_EASY, then it goes in pass 0; otherwise * pass 1. */ xb = (x == 0 || x == W-1); yb = (y == 0 || y == H-1); if (params->diff == DIFF_EASY || v == 4 || v == 0 || (v == 2 && (xb||yb)) || (v == 1 && xb && yb)) pass = 0; else pass = 1; if (pass == j) { clues[y*W+x] = -1; if (slant_solve(w, h, clues, tmpsoln, sc, params->diff) != 1) clues[y*W+x] = v; /* put it back */ } } } /* * And finally, verify that the grid is of _at least_ the * requested difficulty, by running the solver one level * down and verifying that it can't manage it. */ } while (params->diff > 0 && slant_solve(w, h, clues, tmpsoln, sc, params->diff - 1) <= 1); /* * Now we have the clue set as it will be presented to the * user. Encode it in a game desc. */ { char *p; int run, i; desc = snewn(W*H+1, char); p = desc; run = 0; for (i = 0; i <= W*H; i++) { int n = (i < W*H ? clues[i] : -2); if (n == -1) run++; else { if (run) { while (run > 0) { int c = 'a' - 1 + run; if (run > 26) c = 'z'; *p++ = c; run -= c - ('a' - 1); } } if (n >= 0) *p++ = '0' + n; run = 0; } } assert(p - desc <= W*H); *p++ = '\0'; desc = sresize(desc, p - desc, char); } /* * Encode the solution as an aux_info. */ { char *auxbuf; *aux = auxbuf = snewn(w*h+1, char); for (i = 0; i < w*h; i++) auxbuf[i] = soln[i] < 0 ? '\\' : '/'; auxbuf[w*h] = '\0'; } free_scratch(sc); sfree(clueindices); sfree(clues); sfree(tmpsoln); sfree(soln); return desc; } static char *validate_desc(const game_params *params, const char *desc) { int w = params->w, h = params->h, W = w+1, H = h+1; int area = W*H; int squares = 0; while (*desc) { int n = *desc++; if (n >= 'a' && n <= 'z') { squares += n - 'a' + 1; } else if (n >= '0' && n <= '4') { squares++; } 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"; return NULL; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { int w = params->w, h = params->h, W = w+1, H = h+1; game_state *state = snew(game_state); int area = W*H; int squares = 0; state->p = *params; state->soln = snewn(w*h, signed char); memset(state->soln, 0, w*h); state->completed = state->used_solve = FALSE; state->errors = snewn(W*H, unsigned char); memset(state->errors, 0, W*H); state->clues = snew(game_clues); state->clues->w = w; state->clues->h = h; state->clues->clues = snewn(W*H, signed char); state->clues->refcount = 1; state->clues->tmpdsf = snewn(W*H*2+W+H, int); memset(state->clues->clues, -1, W*H); while (*desc) { int n = *desc++; if (n >= 'a' && n <= 'z') { squares += n - 'a' + 1; } else if (n >= '0' && n <= '4') { state->clues->clues[squares++] = n - '0'; } else assert(!"can't get here"); } assert(squares == area); return state; } static game_state *dup_game(const game_state *state) { int w = state->p.w, h = state->p.h, W = w+1, H = h+1; game_state *ret = snew(game_state); ret->p = state->p; ret->clues = state->clues; ret->clues->refcount++; ret->completed = state->completed; ret->used_solve = state->used_solve; ret->soln = snewn(w*h, signed char); memcpy(ret->soln, state->soln, w*h); ret->errors = snewn(W*H, unsigned char); memcpy(ret->errors, state->errors, W*H); return ret; } static void free_game(game_state *state) { sfree(state->errors); sfree(state->soln); assert(state->clues); if (--state->clues->refcount <= 0) { sfree(state->clues->clues); sfree(state->clues->tmpdsf); sfree(state->clues); } sfree(state); } /* * Utility function to return the current degree of a vertex. If * `anti' is set, it returns the number of filled-in edges * surrounding the point which _don't_ connect to it; thus 4 minus * its anti-degree is the maximum degree it could have if all the * empty spaces around it were filled in. * * (Yes, _4_ minus its anti-degree even if it's a border vertex.) * * If ret > 0, *sx and *sy are set to the coordinates of one of the * squares that contributed to it. */ static int vertex_degree(int w, int h, signed char *soln, int x, int y, int anti, int *sx, int *sy) { int ret = 0; assert(x >= 0 && x <= w && y >= 0 && y <= h); if (x > 0 && y > 0 && soln[(y-1)*w+(x-1)] - anti < 0) { if (sx) *sx = x-1; if (sy) *sy = y-1; ret++; } if (x > 0 && y < h && soln[y*w+(x-1)] + anti > 0) { if (sx) *sx = x-1; if (sy) *sy = y; ret++; } if (x < w && y > 0 && soln[(y-1)*w+x] + anti > 0) { if (sx) *sx = x; if (sy) *sy = y-1; ret++; } if (x < w && y < h && soln[y*w+x] - anti < 0) { if (sx) *sx = x; if (sy) *sy = y; ret++; } return anti ? 4 - ret : ret; } static int check_completion(game_state *state) { int w = state->p.w, h = state->p.h, W = w+1, H = h+1; int x, y, err = FALSE; int *dsf; memset(state->errors, 0, W*H); /* * To detect loops in the grid, we iterate through each edge * building up a dsf of connected components of the space * around the edges; if there's more than one such component, * we have a loop, and in particular we can then easily * identify and highlight every edge forming part of a loop * because it separates two nonequivalent regions. * * We use the `tmpdsf' scratch space in the shared clues * structure, to avoid mallocing too often. * * For these purposes, the grid is considered to be divided * into diamond-shaped regions surrounding an orthogonal edge. * This means we have W*h vertical edges and w*H horizontal * ones; so our vertical edges are indexed in the dsf as * (y*W+x) (0<=yclues->tmpdsf; dsf_init(dsf, W*h + w*H); /* Start by identifying all the outer edges with each other. */ for (y = 0; y < h; y++) { dsf_merge(dsf, 0, y*W+0); dsf_merge(dsf, 0, y*W+w); } for (x = 0; x < w; x++) { dsf_merge(dsf, 0, W*h + 0*w+x); dsf_merge(dsf, 0, W*h + h*w+x); } /* Now go through the actual grid. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) { if (state->soln[y*w+x] >= 0) { /* * There isn't a \ in this square, so we can unify * the top edge with the left, and the bottom with * the right. */ dsf_merge(dsf, y*W+x, W*h + y*w+x); dsf_merge(dsf, y*W+(x+1), W*h + (y+1)*w+x); } if (state->soln[y*w+x] <= 0) { /* * There isn't a / in this square, so we can unify * the top edge with the right, and the bottom * with the left. */ dsf_merge(dsf, y*W+x, W*h + (y+1)*w+x); dsf_merge(dsf, y*W+(x+1), W*h + y*w+x); } } /* Now go through again and mark the appropriate edges as erroneous. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) { int erroneous = 0; if (state->soln[y*w+x] > 0) { /* * A / separates the top and left edges (which * must already have been identified with each * other) from the bottom and right (likewise). * Hence it is erroneous if and only if the top * and right edges are nonequivalent. */ erroneous = (dsf_canonify(dsf, y*W+(x+1)) != dsf_canonify(dsf, W*h + y*w+x)); } else if (state->soln[y*w+x] < 0) { /* * A \ separates the top and right edges (which * must already have been identified with each * other) from the bottom and left (likewise). * Hence it is erroneous if and only if the top * and left edges are nonequivalent. */ erroneous = (dsf_canonify(dsf, y*W+x) != dsf_canonify(dsf, W*h + y*w+x)); } if (erroneous) { state->errors[y*W+x] |= ERR_SQUARE; err = TRUE; } } /* * Now go through and check the degree of each clue vertex, and * mark it with ERR_VERTEX if it cannot be fulfilled. */ for (y = 0; y < H; y++) for (x = 0; x < W; x++) { int c; if ((c = state->clues->clues[y*W+x]) < 0) continue; /* * Check to see if there are too many connections to * this vertex _or_ too many non-connections. Either is * grounds for marking the vertex as erroneous. */ if (vertex_degree(w, h, state->soln, x, y, FALSE, NULL, NULL) > c || vertex_degree(w, h, state->soln, x, y, TRUE, NULL, NULL) > 4-c) { state->errors[y*W+x] |= ERR_VERTEX; err = TRUE; } } /* * Now our actual victory condition is that (a) none of the * above code marked anything as erroneous, and (b) every * square has an edge in it. */ if (err) return FALSE; for (y = 0; y < h; y++) for (x = 0; x < w; x++) if (state->soln[y*w+x] == 0) return FALSE; return TRUE; } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { int w = state->p.w, h = state->p.h; signed char *soln; int bs, ret; int free_soln = FALSE; char *move, buf[80]; int movelen, movesize; int x, y; if (aux) { /* * If we already have the solution, save ourselves some * time. */ soln = (signed char *)aux; bs = (signed char)'\\'; free_soln = FALSE; } else { struct solver_scratch *sc = new_scratch(w, h); soln = snewn(w*h, signed char); bs = -1; ret = slant_solve(w, h, state->clues->clues, soln, sc, DIFF_HARD); free_scratch(sc); if (ret != 1) { sfree(soln); if (ret == 0) *error = "This puzzle is not self-consistent"; else *error = "Unable to find a unique solution for this puzzle"; return NULL; } free_soln = TRUE; } /* * Construct a move string which turns the current state into * the solved state. */ movesize = 256; move = snewn(movesize, char); movelen = 0; move[movelen++] = 'S'; move[movelen] = '\0'; for (y = 0; y < h; y++) for (x = 0; x < w; x++) { int v = (soln[y*w+x] == bs ? -1 : +1); if (state->soln[y*w+x] != v) { int len = sprintf(buf, ";%c%d,%d", (int)(v < 0 ? '\\' : '/'), x, y); if (movelen + len >= movesize) { movesize = movelen + len + 256; move = sresize(move, movesize, char); } strcpy(move + movelen, buf); movelen += len; } } if (free_soln) sfree(soln); return move; } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { int w = state->p.w, h = state->p.h, W = w+1, H = h+1; int x, y, len; char *ret, *p; /* * There are h+H rows of w+W columns. */ len = (h+H) * (w+W+1) + 1; ret = snewn(len, char); p = ret; for (y = 0; y < H; y++) { for (x = 0; x < W; x++) { if (state->clues->clues[y*W+x] >= 0) *p++ = state->clues->clues[y*W+x] + '0'; else *p++ = '+'; if (x < w) *p++ = '-'; } *p++ = '\n'; if (y < h) { for (x = 0; x < W; x++) { *p++ = '|'; if (x < w) { if (state->soln[y*w+x] != 0) *p++ = (state->soln[y*w+x] < 0 ? '\\' : '/'); else *p++ = ' '; } } *p++ = '\n'; } } *p++ = '\0'; assert(p - ret == len); return ret; } struct game_ui { int cur_x, cur_y, cur_visible; }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->cur_x = ui->cur_y = ui->cur_visible = 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) { } #define PREFERRED_TILESIZE 32 #define TILESIZE (ds->tilesize) #define BORDER TILESIZE #define CLUE_RADIUS (TILESIZE / 3) #define CLUE_TEXTSIZE (TILESIZE / 2) #define COORD(x) ( (x) * TILESIZE + BORDER ) #define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 ) #define FLASH_TIME 0.30F /* * Bit fields in the `grid' and `todraw' elements of the drawstate. */ #define BACKSLASH 0x00000001L #define FORWSLASH 0x00000002L #define L_T 0x00000004L #define ERR_L_T 0x00000008L #define L_B 0x00000010L #define ERR_L_B 0x00000020L #define T_L 0x00000040L #define ERR_T_L 0x00000080L #define T_R 0x00000100L #define ERR_T_R 0x00000200L #define C_TL 0x00000400L #define ERR_C_TL 0x00000800L #define FLASH 0x00001000L #define ERRSLASH 0x00002000L #define ERR_TL 0x00004000L #define ERR_TR 0x00008000L #define ERR_BL 0x00010000L #define ERR_BR 0x00020000L #define CURSOR 0x00040000L struct game_drawstate { int tilesize; int started; long *grid; long *todraw; }; static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { int w = state->p.w, h = state->p.h; int v; char buf[80]; enum { CLOCKWISE, ANTICLOCKWISE, NONE } action = NONE; if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { /* * This is an utterly awful hack which I should really sort out * by means of a proper configuration mechanism. One Slant * player has observed that they prefer the mouse buttons to * function exactly the opposite way round, so here's a * mechanism for environment-based configuration. I cache the * result in a global variable - yuck! - to avoid repeated * lookups. */ { static int swap_buttons = -1; if (swap_buttons < 0) { char *env = getenv("SLANT_SWAP_BUTTONS"); swap_buttons = (env && (env[0] == 'y' || env[0] == 'Y')); } if (swap_buttons) { if (button == LEFT_BUTTON) button = RIGHT_BUTTON; else button = LEFT_BUTTON; } } action = (button == LEFT_BUTTON) ? CLOCKWISE : ANTICLOCKWISE; x = FROMCOORD(x); y = FROMCOORD(y); if (x < 0 || y < 0 || x >= w || y >= h) return NULL; } else if (IS_CURSOR_SELECT(button)) { if (!ui->cur_visible) { ui->cur_visible = 1; return ""; } x = ui->cur_x; y = ui->cur_y; action = (button == CURSOR_SELECT2) ? ANTICLOCKWISE : CLOCKWISE; } else if (IS_CURSOR_MOVE(button)) { move_cursor(button, &ui->cur_x, &ui->cur_y, w, h, 0); ui->cur_visible = 1; return ""; } if (action != NONE) { if (action == CLOCKWISE) { /* * Left-clicking cycles blank -> \ -> / -> blank. */ v = state->soln[y*w+x] - 1; if (v == -2) v = +1; } else { /* * Right-clicking cycles blank -> / -> \ -> blank. */ v = state->soln[y*w+x] + 1; if (v == +2) v = -1; } sprintf(buf, "%c%d,%d", (int)(v==-1 ? '\\' : v==+1 ? '/' : 'C'), x, y); return dupstr(buf); } return NULL; } static game_state *execute_move(const game_state *state, const char *move) { int w = state->p.w, h = state->p.h; char c; int x, y, n; game_state *ret = dup_game(state); while (*move) { c = *move; if (c == 'S') { ret->used_solve = TRUE; move++; } else if (c == '\\' || c == '/' || c == 'C') { move++; if (sscanf(move, "%d,%d%n", &x, &y, &n) != 2 || x < 0 || y < 0 || x >= w || y >= h) { free_game(ret); return NULL; } ret->soln[y*w+x] = (c == '\\' ? -1 : c == '/' ? +1 : 0); move += n; } else { free_game(ret); return NULL; } if (*move == ';') move++; else if (*move) { free_game(ret); return NULL; } } /* * We never clear the `completed' flag, but we must always * re-run the completion check because it also highlights * errors in the grid. */ ret->completed = check_completion(ret) || ret->completed; 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 = 2 * BORDER + params->w * TILESIZE + 1; *y = 2 * BORDER + params->h * TILESIZE + 1; } 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); /* CURSOR colour is a background highlight. */ game_mkhighlight(fe, ret, COL_BACKGROUND, COL_CURSOR, -1); ret[COL_FILLEDSQUARE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0]; ret[COL_FILLEDSQUARE * 3 + 1] = ret[COL_BACKGROUND * 3 + 1]; ret[COL_FILLEDSQUARE * 3 + 2] = ret[COL_BACKGROUND * 3 + 2]; ret[COL_GRID * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.7F; ret[COL_GRID * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.7F; ret[COL_GRID * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 0.7F; ret[COL_INK * 3 + 0] = 0.0F; ret[COL_INK * 3 + 1] = 0.0F; ret[COL_INK * 3 + 2] = 0.0F; ret[COL_SLANT1 * 3 + 0] = 0.0F; ret[COL_SLANT1 * 3 + 1] = 0.0F; ret[COL_SLANT1 * 3 + 2] = 0.0F; ret[COL_SLANT2 * 3 + 0] = 0.0F; ret[COL_SLANT2 * 3 + 1] = 0.0F; ret[COL_SLANT2 * 3 + 2] = 0.0F; ret[COL_ERROR * 3 + 0] = 1.0F; ret[COL_ERROR * 3 + 1] = 0.0F; ret[COL_ERROR * 3 + 2] = 0.0F; *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; int i; struct game_drawstate *ds = snew(struct game_drawstate); ds->tilesize = 0; ds->started = FALSE; ds->grid = snewn((w+2)*(h+2), long); ds->todraw = snewn((w+2)*(h+2), long); for (i = 0; i < (w+2)*(h+2); i++) ds->grid[i] = ds->todraw[i] = -1; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->todraw); sfree(ds->grid); sfree(ds); } static void draw_clue(drawing *dr, game_drawstate *ds, int x, int y, long v, long err, int bg, int colour) { char p[2]; int ccol = colour >= 0 ? colour : ((x ^ y) & 1) ? COL_SLANT1 : COL_SLANT2; int tcol = colour >= 0 ? colour : err ? COL_ERROR : COL_INK; if (v < 0) return; p[0] = (char)v + '0'; p[1] = '\0'; draw_circle(dr, COORD(x), COORD(y), CLUE_RADIUS, bg >= 0 ? bg : COL_BACKGROUND, ccol); draw_text(dr, COORD(x), COORD(y), FONT_VARIABLE, CLUE_TEXTSIZE, ALIGN_VCENTRE|ALIGN_HCENTRE, tcol, p); } static void draw_tile(drawing *dr, game_drawstate *ds, game_clues *clues, int x, int y, long v) { int w = clues->w, h = clues->h, W = w+1 /*, H = h+1 */; int chesscolour = (x ^ y) & 1; int fscol = chesscolour ? COL_SLANT2 : COL_SLANT1; int bscol = chesscolour ? COL_SLANT1 : COL_SLANT2; clip(dr, COORD(x), COORD(y), TILESIZE, TILESIZE); draw_rect(dr, COORD(x), COORD(y), TILESIZE, TILESIZE, (v & FLASH) ? COL_GRID : (v & CURSOR) ? COL_CURSOR : (v & (BACKSLASH | FORWSLASH)) ? COL_FILLEDSQUARE : COL_BACKGROUND); /* * Draw the grid lines. */ if (x >= 0 && x < w && y >= 0) draw_rect(dr, COORD(x), COORD(y), TILESIZE+1, 1, COL_GRID); if (x >= 0 && x < w && y < h) draw_rect(dr, COORD(x), COORD(y+1), TILESIZE+1, 1, COL_GRID); if (y >= 0 && y < h && x >= 0) draw_rect(dr, COORD(x), COORD(y), 1, TILESIZE+1, COL_GRID); if (y >= 0 && y < h && x < w) draw_rect(dr, COORD(x+1), COORD(y), 1, TILESIZE+1, COL_GRID); if (x == -1 && y == -1) draw_rect(dr, COORD(x+1), COORD(y+1), 1, 1, COL_GRID); if (x == -1 && y == h) draw_rect(dr, COORD(x+1), COORD(y), 1, 1, COL_GRID); if (x == w && y == -1) draw_rect(dr, COORD(x), COORD(y+1), 1, 1, COL_GRID); if (x == w && y == h) draw_rect(dr, COORD(x), COORD(y), 1, 1, COL_GRID); /* * Draw the slash. */ if (v & BACKSLASH) { int scol = (v & ERRSLASH) ? COL_ERROR : bscol; draw_line(dr, COORD(x), COORD(y), COORD(x+1), COORD(y+1), scol); draw_line(dr, COORD(x)+1, COORD(y), COORD(x+1), COORD(y+1)-1, scol); draw_line(dr, COORD(x), COORD(y)+1, COORD(x+1)-1, COORD(y+1), scol); } else if (v & FORWSLASH) { int scol = (v & ERRSLASH) ? COL_ERROR : fscol; draw_line(dr, COORD(x+1), COORD(y), COORD(x), COORD(y+1), scol); draw_line(dr, COORD(x+1)-1, COORD(y), COORD(x), COORD(y+1)-1, scol); draw_line(dr, COORD(x+1), COORD(y)+1, COORD(x)+1, COORD(y+1), scol); } /* * Draw dots on the grid corners that appear if a slash is in a * neighbouring cell. */ if (v & (L_T | BACKSLASH)) draw_rect(dr, COORD(x), COORD(y)+1, 1, 1, (v & ERR_L_T ? COL_ERROR : bscol)); if (v & (L_B | FORWSLASH)) draw_rect(dr, COORD(x), COORD(y+1)-1, 1, 1, (v & ERR_L_B ? COL_ERROR : fscol)); if (v & (T_L | BACKSLASH)) draw_rect(dr, COORD(x)+1, COORD(y), 1, 1, (v & ERR_T_L ? COL_ERROR : bscol)); if (v & (T_R | FORWSLASH)) draw_rect(dr, COORD(x+1)-1, COORD(y), 1, 1, (v & ERR_T_R ? COL_ERROR : fscol)); if (v & (C_TL | BACKSLASH)) draw_rect(dr, COORD(x), COORD(y), 1, 1, (v & ERR_C_TL ? COL_ERROR : bscol)); /* * And finally the clues at the corners. */ if (x >= 0 && y >= 0) draw_clue(dr, ds, x, y, clues->clues[y*W+x], v & ERR_TL, -1, -1); if (x < w && y >= 0) draw_clue(dr, ds, x+1, y, clues->clues[y*W+(x+1)], v & ERR_TR, -1, -1); if (x >= 0 && y < h) draw_clue(dr, ds, x, y+1, clues->clues[(y+1)*W+x], v & ERR_BL, -1, -1); if (x < w && y < h) draw_clue(dr, ds, x+1, y+1, clues->clues[(y+1)*W+(x+1)], v & ERR_BR, -1, -1); unclip(dr); draw_update(dr, COORD(x), COORD(y), 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, W = w+1, H = h+1; int x, y; int flashing; if (flashtime > 0) flashing = (int)(flashtime * 3 / FLASH_TIME) != 1; else flashing = FALSE; if (!ds->started) { int ww, wh; game_compute_size(&state->p, TILESIZE, &ww, &wh); draw_rect(dr, 0, 0, ww, wh, COL_BACKGROUND); draw_update(dr, 0, 0, ww, wh); ds->started = TRUE; } /* * Loop over the grid and work out where all the slashes are. * We need to do this because a slash in one square affects the * drawing of the next one along. */ for (y = -1; y <= h; y++) for (x = -1; x <= w; x++) { if (x >= 0 && x < w && y >= 0 && y < h) ds->todraw[(y+1)*(w+2)+(x+1)] = flashing ? FLASH : 0; else ds->todraw[(y+1)*(w+2)+(x+1)] = 0; } for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { int err = state->errors[y*W+x] & ERR_SQUARE; if (state->soln[y*w+x] < 0) { ds->todraw[(y+1)*(w+2)+(x+1)] |= BACKSLASH; ds->todraw[(y+2)*(w+2)+(x+1)] |= T_R; ds->todraw[(y+1)*(w+2)+(x+2)] |= L_B; ds->todraw[(y+2)*(w+2)+(x+2)] |= C_TL; if (err) { ds->todraw[(y+1)*(w+2)+(x+1)] |= ERRSLASH | ERR_T_L | ERR_L_T | ERR_C_TL; ds->todraw[(y+2)*(w+2)+(x+1)] |= ERR_T_R; ds->todraw[(y+1)*(w+2)+(x+2)] |= ERR_L_B; ds->todraw[(y+2)*(w+2)+(x+2)] |= ERR_C_TL; } } else if (state->soln[y*w+x] > 0) { ds->todraw[(y+1)*(w+2)+(x+1)] |= FORWSLASH; ds->todraw[(y+1)*(w+2)+(x+2)] |= L_T | C_TL; ds->todraw[(y+2)*(w+2)+(x+1)] |= T_L | C_TL; if (err) { ds->todraw[(y+1)*(w+2)+(x+1)] |= ERRSLASH | ERR_L_B | ERR_T_R; ds->todraw[(y+1)*(w+2)+(x+2)] |= ERR_L_T | ERR_C_TL; ds->todraw[(y+2)*(w+2)+(x+1)] |= ERR_T_L | ERR_C_TL; } } if (ui->cur_visible && ui->cur_x == x && ui->cur_y == y) ds->todraw[(y+1)*(w+2)+(x+1)] |= CURSOR; } } for (y = 0; y < H; y++) for (x = 0; x < W; x++) if (state->errors[y*W+x] & ERR_VERTEX) { ds->todraw[y*(w+2)+x] |= ERR_BR; ds->todraw[y*(w+2)+(x+1)] |= ERR_BL; ds->todraw[(y+1)*(w+2)+x] |= ERR_TR; ds->todraw[(y+1)*(w+2)+(x+1)] |= ERR_TL; } /* * Now go through and draw the grid squares. */ for (y = -1; y <= h; y++) { for (x = -1; x <= w; x++) { if (ds->todraw[(y+1)*(w+2)+(x+1)] != ds->grid[(y+1)*(w+2)+(x+1)]) { draw_tile(dr, ds, state->clues, x, y, ds->todraw[(y+1)*(w+2)+(x+1)]); ds->grid[(y+1)*(w+2)+(x+1)] = ds->todraw[(y+1)*(w+2)+(x+1)]; } } } } 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->used_solve && !newstate->used_solve) return FLASH_TIME; return 0.0F; } static int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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) { int pw, ph; /* * I'll use 6mm squares by default. */ game_compute_size(params, 600, &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->p.w, h = state->p.h, W = w+1; int ink = print_mono_colour(dr, 0); int paper = print_mono_colour(dr, 1); 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, TILESIZE / 16); draw_rect_outline(dr, COORD(0), COORD(0), w*TILESIZE, h*TILESIZE, ink); /* * Grid. */ print_line_width(dr, TILESIZE / 24); for (x = 1; x < w; x++) draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), ink); for (y = 1; y < h; y++) draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), ink); /* * Solution. */ print_line_width(dr, TILESIZE / 12); for (y = 0; y < h; y++) for (x = 0; x < w; x++) if (state->soln[y*w+x]) { int ly, ry; /* * To prevent nasty line-ending artefacts at * corners, I'll do something slightly cunning * here. */ clip(dr, COORD(x), COORD(y), TILESIZE, TILESIZE); if (state->soln[y*w+x] < 0) ly = y-1, ry = y+2; else ry = y-1, ly = y+2; draw_line(dr, COORD(x-1), COORD(ly), COORD(x+2), COORD(ry), ink); unclip(dr); } /* * Clues. */ print_line_width(dr, TILESIZE / 24); for (y = 0; y <= h; y++) for (x = 0; x <= w; x++) draw_clue(dr, ds, x, y, state->clues->clues[y*W+x], FALSE, paper, ink); } #ifdef COMBINED #define thegame slant #endif const struct game thegame = { "Slant", "games.slant", "slant", default_params, game_fetch_preset, 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, 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_status, TRUE, FALSE, game_print_size, game_print, FALSE, /* 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, *err; int grade = FALSE; int ret, diff, really_verbose = FALSE; struct solver_scratch *sc; while (--argc > 0) { char *p = *++argv; if (!strcmp(p, "-v")) { really_verbose = 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); sc = new_scratch(p->w, p->h); /* * When solving an Easy 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 */ for (diff = 0; diff < DIFFCOUNT; diff++) { ret = slant_solve(p->w, p->h, s->clues->clues, s->soln, sc, diff); if (ret < 2) break; } if (diff == DIFFCOUNT) { if (grade) printf("Difficulty rating: harder than Hard, or ambiguous\n"); else printf("Unable to find a unique solution\n"); } else { if (grade) { if (ret == 0) printf("Difficulty rating: impossible (no solution exists)\n"); else if (ret == 1) printf("Difficulty rating: %s\n", slant_diffnames[diff]); } else { verbose = really_verbose; ret = slant_solve(p->w, p->h, s->clues->clues, s->soln, sc, diff); if (ret == 0) printf("Puzzle is inconsistent\n"); else fputs(game_text_format(s), stdout); } } return 0; } #endif /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/solo.c0000644000175300017530000047000212132232554013547 0ustar simonsimon/* * solo.c: the number-placing puzzle most popularly known as `Sudoku'. * * TODO: * * - reports from users are that `Trivial'-mode puzzles are still * rather hard compared to newspapers' easy ones, so some better * low-end difficulty grading would be nice * + it's possible that really easy puzzles always have * _several_ things you can do, so don't make you hunt too * hard for the one deduction you can currently make * + it's also possible that easy puzzles require fewer * cross-eliminations: perhaps there's a higher incidence of * things you can deduce by looking only at (say) rows, * rather than things you have to check both rows and columns * for * + but really, what I need to do is find some really easy * puzzles and _play_ them, to see what's actually easy about * them * + while I'm revamping this area, filling in the _last_ * number in a nearly-full row or column should certainly be * permitted even at the lowest difficulty level. * + also Owen noticed that `Basic' grids requiring numeric * elimination are actually very hard, so I wonder if a * difficulty gradation between that and positional- * elimination-only might be in order * + but it's not good to have _too_ many difficulty levels, or * it'll take too long to randomly generate a given level. * * - it might still be nice to do some prioritisation on the * removal of numbers from the grid * + one possibility is to try to minimise the maximum number * of filled squares in any block, which in particular ought * to enforce never leaving a completely filled block in the * puzzle as presented. * * - alternative interface modes * + sudoku.com's Windows program has a palette of possible * entries; you select a palette entry first and then click * on the square you want it to go in, thus enabling * mouse-only play. Useful for PDAs! I don't think it's * actually incompatible with the current highlight-then-type * approach: you _either_ highlight a palette entry and then * click, _or_ you highlight a square and then type. At most * one thing is ever highlighted at a time, so there's no way * to confuse the two. * + then again, I don't actually like sudoku.com's interface; * it's too much like a paint package whereas I prefer to * think of Solo as a text editor. * + another PDA-friendly possibility is a drag interface: * _drag_ numbers from the palette into the grid squares. * Thought experiments suggest I'd prefer that to the * sudoku.com approach, but I haven't actually tried it. */ /* * Solo puzzles need to be square overall (since each row and each * column must contain one of every digit), but they need not be * subdivided the same way internally. I am going to adopt a * convention whereby I _always_ refer to `r' as the number of rows * of _big_ divisions, and `c' as the number of columns of _big_ * divisions. Thus, a 2c by 3r puzzle looks something like this: * * 4 5 1 | 2 6 3 * 6 3 2 | 5 4 1 * ------+------ (Of course, you can't subdivide it the other way * 1 4 5 | 6 3 2 or you'll get clashes; observe that the 4 in the * 3 2 6 | 4 1 5 top left would conflict with the 4 in the second * ------+------ box down on the left-hand side.) * 5 1 4 | 3 2 6 * 2 6 3 | 1 5 4 * * The need for a strong naming convention should now be clear: * each small box is two rows of digits by three columns, while the * overall puzzle has three rows of small boxes by two columns. So * I will (hopefully) consistently use `r' to denote the number of * rows _of small boxes_ (here 3), which is also the number of * columns of digits in each small box; and `c' vice versa (here * 2). * * I'm also going to choose arbitrarily to list c first wherever * possible: the above is a 2x3 puzzle, not a 3x2 one. */ #include #include #include #include #include #include #ifdef STANDALONE_SOLVER #include int solver_show_working, solver_recurse_depth; #endif #include "puzzles.h" /* * To save space, I store digits internally as unsigned char. This * imposes a hard limit of 255 on the order of the puzzle. Since * even a 5x5 takes unacceptably long to generate, I don't see this * as a serious limitation unless something _really_ impressive * happens in computing technology; but here's a typedef anyway for * general good practice. */ typedef unsigned char digit; #define ORDER_MAX 255 #define PREFERRED_TILE_SIZE 48 #define TILE_SIZE (ds->tilesize) #define BORDER (TILE_SIZE / 2) #define GRIDEXTRA max((TILE_SIZE / 32),1) #define FLASH_TIME 0.4F enum { SYMM_NONE, SYMM_ROT2, SYMM_ROT4, SYMM_REF2, SYMM_REF2D, SYMM_REF4, SYMM_REF4D, SYMM_REF8 }; enum { DIFF_BLOCK, DIFF_SIMPLE, DIFF_INTERSECT, DIFF_SET, DIFF_EXTREME, DIFF_RECURSIVE, DIFF_AMBIGUOUS, DIFF_IMPOSSIBLE }; enum { DIFF_KSINGLE, DIFF_KMINMAX, DIFF_KSUMS, DIFF_KINTERSECT }; enum { COL_BACKGROUND, COL_XDIAGONALS, COL_GRID, COL_CLUE, COL_USER, COL_HIGHLIGHT, COL_ERROR, COL_PENCIL, COL_KILLER, NCOLOURS }; /* * To determine all possible ways to reach a given sum by adding two or * three numbers from 1..9, each of which occurs exactly once in the sum, * these arrays contain a list of bitmasks for each sum value, where if * bit N is set, it means that N occurs in the sum. Each list is * terminated by a zero if it is shorter than the size of the array. */ #define MAX_2SUMS 5 #define MAX_3SUMS 8 #define MAX_4SUMS 12 unsigned long sum_bits2[18][MAX_2SUMS]; unsigned long sum_bits3[25][MAX_3SUMS]; unsigned long sum_bits4[31][MAX_4SUMS]; static int find_sum_bits(unsigned long *array, int idx, int value_left, int addends_left, int min_addend, unsigned long bitmask_so_far) { int i; assert(addends_left >= 2); for (i = min_addend; i < value_left; i++) { unsigned long new_bitmask = bitmask_so_far | (1L << i); assert(bitmask_so_far != new_bitmask); if (addends_left == 2) { int j = value_left - i; if (j <= i) break; if (j > 9) continue; array[idx++] = new_bitmask | (1L << j); } else idx = find_sum_bits(array, idx, value_left - i, addends_left - 1, i + 1, new_bitmask); } return idx; } static void precompute_sum_bits(void) { int i; for (i = 3; i < 31; i++) { int j; if (i < 18) { j = find_sum_bits(sum_bits2[i], 0, i, 2, 1, 0); assert (j <= MAX_2SUMS); if (j < MAX_2SUMS) sum_bits2[i][j] = 0; } if (i < 25) { j = find_sum_bits(sum_bits3[i], 0, i, 3, 1, 0); assert (j <= MAX_3SUMS); if (j < MAX_3SUMS) sum_bits3[i][j] = 0; } j = find_sum_bits(sum_bits4[i], 0, i, 4, 1, 0); assert (j <= MAX_4SUMS); if (j < MAX_4SUMS) sum_bits4[i][j] = 0; } } struct game_params { /* * For a square puzzle, `c' and `r' indicate the puzzle * parameters as described above. * * A jigsaw-style puzzle is indicated by r==1, in which case c * can be whatever it likes (there is no constraint on * compositeness - a 7x7 jigsaw sudoku makes perfect sense). */ int c, r, symm, diff, kdiff; int xtype; /* require all digits in X-diagonals */ int killer; }; struct block_structure { int refcount; /* * For text formatting, we do need c and r here. */ int c, r, area; /* * For any square index, whichblock[i] gives its block index. * * For 0 <= b,i < cr, blocks[b][i] gives the index of the ith * square in block b. nr_squares[b] gives the number of squares * in block b (also the number of valid elements in blocks[b]). * * blocks_data holds the data pointed to by blocks. * * nr_squares may be NULL for block structures where all blocks are * the same size. */ int *whichblock, **blocks, *nr_squares, *blocks_data; int nr_blocks, max_nr_squares; #ifdef STANDALONE_SOLVER /* * Textual descriptions of each block. For normal Sudoku these * are of the form "(1,3)"; for jigsaw they are "starting at * (5,7)". So the sensible usage in both cases is to say * "elimination within block %s" with one of these strings. * * Only blocknames itself needs individually freeing; it's all * one block. */ char **blocknames; #endif }; struct game_state { /* * For historical reasons, I use `cr' to denote the overall * width/height of the puzzle. It was a natural notation when * all puzzles were divided into blocks in a grid, but doesn't * really make much sense given jigsaw puzzles. However, the * obvious `n' is heavily used in the solver to describe the * index of a number being placed, so `cr' will have to stay. */ int cr; struct block_structure *blocks; struct block_structure *kblocks; /* Blocks for killer puzzles. */ int xtype, killer; digit *grid, *kgrid; unsigned char *pencil; /* c*r*c*r elements */ unsigned char *immutable; /* marks which digits are clues */ int completed, cheated; }; static game_params *default_params(void) { game_params *ret = snew(game_params); ret->c = ret->r = 3; ret->xtype = FALSE; ret->killer = FALSE; ret->symm = SYMM_ROT2; /* a plausible default */ ret->diff = DIFF_BLOCK; /* so is this */ ret->kdiff = DIFF_KINTERSECT; /* so is this */ 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 int game_fetch_preset(int i, char **name, game_params **params) { static struct { char *title; game_params params; } presets[] = { { "2x2 Trivial", { 2, 2, SYMM_ROT2, DIFF_BLOCK, DIFF_KMINMAX, FALSE, FALSE } }, { "2x3 Basic", { 2, 3, SYMM_ROT2, DIFF_SIMPLE, DIFF_KMINMAX, FALSE, FALSE } }, { "3x3 Trivial", { 3, 3, SYMM_ROT2, DIFF_BLOCK, DIFF_KMINMAX, FALSE, FALSE } }, { "3x3 Basic", { 3, 3, SYMM_ROT2, DIFF_SIMPLE, DIFF_KMINMAX, FALSE, FALSE } }, { "3x3 Basic X", { 3, 3, SYMM_ROT2, DIFF_SIMPLE, DIFF_KMINMAX, TRUE } }, { "3x3 Intermediate", { 3, 3, SYMM_ROT2, DIFF_INTERSECT, DIFF_KMINMAX, FALSE, FALSE } }, { "3x3 Advanced", { 3, 3, SYMM_ROT2, DIFF_SET, DIFF_KMINMAX, FALSE, FALSE } }, { "3x3 Advanced X", { 3, 3, SYMM_ROT2, DIFF_SET, DIFF_KMINMAX, TRUE } }, { "3x3 Extreme", { 3, 3, SYMM_ROT2, DIFF_EXTREME, DIFF_KMINMAX, FALSE, FALSE } }, { "3x3 Unreasonable", { 3, 3, SYMM_ROT2, DIFF_RECURSIVE, DIFF_KMINMAX, FALSE, FALSE } }, { "3x3 Killer", { 3, 3, SYMM_NONE, DIFF_BLOCK, DIFF_KINTERSECT, FALSE, TRUE } }, { "9 Jigsaw Basic", { 9, 1, SYMM_ROT2, DIFF_SIMPLE, DIFF_KMINMAX, FALSE, FALSE } }, { "9 Jigsaw Basic X", { 9, 1, SYMM_ROT2, DIFF_SIMPLE, DIFF_KMINMAX, TRUE } }, { "9 Jigsaw Advanced", { 9, 1, SYMM_ROT2, DIFF_SET, DIFF_KMINMAX, FALSE, FALSE } }, #ifndef SLOW_SYSTEM { "3x4 Basic", { 3, 4, SYMM_ROT2, DIFF_SIMPLE, DIFF_KMINMAX, FALSE, FALSE } }, { "4x4 Basic", { 4, 4, SYMM_ROT2, DIFF_SIMPLE, DIFF_KMINMAX, FALSE, FALSE } }, #endif }; if (i < 0 || i >= lenof(presets)) return FALSE; *name = dupstr(presets[i].title); *params = dup_params(&presets[i].params); return TRUE; } static void decode_params(game_params *ret, char const *string) { int seen_r = FALSE; ret->c = ret->r = atoi(string); ret->xtype = FALSE; ret->killer = FALSE; while (*string && isdigit((unsigned char)*string)) string++; if (*string == 'x') { string++; ret->r = atoi(string); seen_r = TRUE; while (*string && isdigit((unsigned char)*string)) string++; } while (*string) { if (*string == 'j') { string++; if (seen_r) ret->c *= ret->r; ret->r = 1; } else if (*string == 'x') { string++; ret->xtype = TRUE; } else if (*string == 'k') { string++; ret->killer = TRUE; } else if (*string == 'r' || *string == 'm' || *string == 'a') { int sn, sc, sd; sc = *string++; if (sc == 'm' && *string == 'd') { sd = TRUE; string++; } else { sd = FALSE; } sn = atoi(string); while (*string && isdigit((unsigned char)*string)) string++; if (sc == 'm' && sn == 8) ret->symm = SYMM_REF8; if (sc == 'm' && sn == 4) ret->symm = sd ? SYMM_REF4D : SYMM_REF4; if (sc == 'm' && sn == 2) ret->symm = sd ? SYMM_REF2D : SYMM_REF2; if (sc == 'r' && sn == 4) ret->symm = SYMM_ROT4; if (sc == 'r' && sn == 2) ret->symm = SYMM_ROT2; if (sc == 'a') ret->symm = SYMM_NONE; } else if (*string == 'd') { string++; if (*string == 't') /* trivial */ string++, ret->diff = DIFF_BLOCK; else if (*string == 'b') /* basic */ string++, ret->diff = DIFF_SIMPLE; else if (*string == 'i') /* intermediate */ string++, ret->diff = DIFF_INTERSECT; else if (*string == 'a') /* advanced */ string++, ret->diff = DIFF_SET; else if (*string == 'e') /* extreme */ string++, ret->diff = DIFF_EXTREME; else if (*string == 'u') /* unreasonable */ string++, ret->diff = DIFF_RECURSIVE; } else string++; /* eat unknown character */ } } static char *encode_params(const game_params *params, int full) { char str[80]; if (params->r > 1) sprintf(str, "%dx%d", params->c, params->r); else sprintf(str, "%dj", params->c); if (params->xtype) strcat(str, "x"); if (params->killer) strcat(str, "k"); if (full) { switch (params->symm) { case SYMM_REF8: strcat(str, "m8"); break; case SYMM_REF4: strcat(str, "m4"); break; case SYMM_REF4D: strcat(str, "md4"); break; case SYMM_REF2: strcat(str, "m2"); break; case SYMM_REF2D: strcat(str, "md2"); break; case SYMM_ROT4: strcat(str, "r4"); break; /* case SYMM_ROT2: strcat(str, "r2"); break; [default] */ case SYMM_NONE: strcat(str, "a"); break; } switch (params->diff) { /* case DIFF_BLOCK: strcat(str, "dt"); break; [default] */ case DIFF_SIMPLE: strcat(str, "db"); break; case DIFF_INTERSECT: strcat(str, "di"); break; case DIFF_SET: strcat(str, "da"); break; case DIFF_EXTREME: strcat(str, "de"); break; case DIFF_RECURSIVE: strcat(str, "du"); break; } } return dupstr(str); } static config_item *game_configure(const game_params *params) { config_item *ret; char buf[80]; ret = snewn(8, config_item); ret[0].name = "Columns of sub-blocks"; ret[0].type = C_STRING; sprintf(buf, "%d", params->c); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Rows of sub-blocks"; ret[1].type = C_STRING; sprintf(buf, "%d", params->r); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "\"X\" (require every number in each main diagonal)"; ret[2].type = C_BOOLEAN; ret[2].sval = NULL; ret[2].ival = params->xtype; ret[3].name = "Jigsaw (irregularly shaped sub-blocks)"; ret[3].type = C_BOOLEAN; ret[3].sval = NULL; ret[3].ival = (params->r == 1); ret[4].name = "Killer (digit sums)"; ret[4].type = C_BOOLEAN; ret[4].sval = NULL; ret[4].ival = params->killer; ret[5].name = "Symmetry"; ret[5].type = C_CHOICES; ret[5].sval = ":None:2-way rotation:4-way rotation:2-way mirror:" "2-way diagonal mirror:4-way mirror:4-way diagonal mirror:" "8-way mirror"; ret[5].ival = params->symm; ret[6].name = "Difficulty"; ret[6].type = C_CHOICES; ret[6].sval = ":Trivial:Basic:Intermediate:Advanced:Extreme:Unreasonable"; ret[6].ival = params->diff; ret[7].name = NULL; ret[7].type = C_END; ret[7].sval = NULL; ret[7].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->c = atoi(cfg[0].sval); ret->r = atoi(cfg[1].sval); ret->xtype = cfg[2].ival; if (cfg[3].ival) { ret->c *= ret->r; ret->r = 1; } ret->killer = cfg[4].ival; ret->symm = cfg[5].ival; ret->diff = cfg[6].ival; ret->kdiff = DIFF_KINTERSECT; return ret; } static char *validate_params(const game_params *params, int full) { if (params->c < 2) return "Both dimensions must be at least 2"; if (params->c > ORDER_MAX || params->r > ORDER_MAX) return "Dimensions greater than "STR(ORDER_MAX)" are not supported"; if ((params->c * params->r) > 31) return "Unable to support more than 31 distinct symbols in a puzzle"; if (params->killer && params->c * params->r > 9) return "Killer puzzle dimensions must be smaller than 10."; return NULL; } /* * ---------------------------------------------------------------------- * Block structure functions. */ static struct block_structure *alloc_block_structure(int c, int r, int area, int max_nr_squares, int nr_blocks) { int i; struct block_structure *b = snew(struct block_structure); b->refcount = 1; b->nr_blocks = nr_blocks; b->max_nr_squares = max_nr_squares; b->c = c; b->r = r; b->area = area; b->whichblock = snewn(area, int); b->blocks_data = snewn(nr_blocks * max_nr_squares, int); b->blocks = snewn(nr_blocks, int *); b->nr_squares = snewn(nr_blocks, int); for (i = 0; i < nr_blocks; i++) b->blocks[i] = b->blocks_data + i*max_nr_squares; #ifdef STANDALONE_SOLVER b->blocknames = (char **)smalloc(c*r*(sizeof(char *)+80)); for (i = 0; i < c * r; i++) b->blocknames[i] = NULL; #endif return b; } static void free_block_structure(struct block_structure *b) { if (--b->refcount == 0) { sfree(b->whichblock); sfree(b->blocks); sfree(b->blocks_data); #ifdef STANDALONE_SOLVER sfree(b->blocknames); #endif sfree(b->nr_squares); sfree(b); } } static struct block_structure *dup_block_structure(struct block_structure *b) { struct block_structure *nb; int i; nb = alloc_block_structure(b->c, b->r, b->area, b->max_nr_squares, b->nr_blocks); memcpy(nb->nr_squares, b->nr_squares, b->nr_blocks * sizeof *b->nr_squares); memcpy(nb->whichblock, b->whichblock, b->area * sizeof *b->whichblock); memcpy(nb->blocks_data, b->blocks_data, b->nr_blocks * b->max_nr_squares * sizeof *b->blocks_data); for (i = 0; i < b->nr_blocks; i++) nb->blocks[i] = nb->blocks_data + i*nb->max_nr_squares; #ifdef STANDALONE_SOLVER memcpy(nb->blocknames, b->blocknames, b->c * b->r *(sizeof(char *)+80)); { int i; for (i = 0; i < b->c * b->r; i++) if (b->blocknames[i] == NULL) nb->blocknames[i] = NULL; else nb->blocknames[i] = ((char *)nb->blocknames) + (b->blocknames[i] - (char *)b->blocknames); } #endif return nb; } static void split_block(struct block_structure *b, int *squares, int nr_squares) { int i, j; int previous_block = b->whichblock[squares[0]]; int newblock = b->nr_blocks; assert(b->max_nr_squares >= nr_squares); assert(b->nr_squares[previous_block] > nr_squares); b->nr_blocks++; b->blocks_data = sresize(b->blocks_data, b->nr_blocks * b->max_nr_squares, int); b->nr_squares = sresize(b->nr_squares, b->nr_blocks, int); sfree(b->blocks); b->blocks = snewn(b->nr_blocks, int *); for (i = 0; i < b->nr_blocks; i++) b->blocks[i] = b->blocks_data + i*b->max_nr_squares; for (i = 0; i < nr_squares; i++) { assert(b->whichblock[squares[i]] == previous_block); b->whichblock[squares[i]] = newblock; b->blocks[newblock][i] = squares[i]; } for (i = j = 0; i < b->nr_squares[previous_block]; i++) { int k; int sq = b->blocks[previous_block][i]; for (k = 0; k < nr_squares; k++) if (squares[k] == sq) break; if (k == nr_squares) b->blocks[previous_block][j++] = sq; } b->nr_squares[previous_block] -= nr_squares; b->nr_squares[newblock] = nr_squares; } static void remove_from_block(struct block_structure *blocks, int b, int n) { int i, j; blocks->whichblock[n] = -1; for (i = j = 0; i < blocks->nr_squares[b]; i++) if (blocks->blocks[b][i] != n) blocks->blocks[b][j++] = blocks->blocks[b][i]; assert(j+1 == i); blocks->nr_squares[b]--; } /* ---------------------------------------------------------------------- * Solver. * * This solver is used for two purposes: * + to check solubility of a grid as we gradually remove numbers * from it * + to solve an externally generated puzzle when the user selects * `Solve'. * * It supports a variety of specific modes of reasoning. By * enabling or disabling subsets of these modes we can arrange a * range of difficulty levels. */ /* * Modes of reasoning currently supported: * * - Positional elimination: a number must go in a particular * square because all the other empty squares in a given * row/col/blk are ruled out. * * - Killer minmax elimination: for killer-type puzzles, a number * is impossible if choosing it would cause the sum in a killer * region to be guaranteed to be too large or too small. * * - Numeric elimination: a square must have a particular number * in because all the other numbers that could go in it are * ruled out. * * - Intersectional analysis: given two domains which overlap * (hence one must be a block, and the other can be a row or * col), if the possible locations for a particular number in * one of the domains can be narrowed down to the overlap, then * that number can be ruled out everywhere but the overlap in * the other domain too. * * - Set elimination: if there is a subset of the empty squares * within a domain such that the union of the possible numbers * in that subset has the same size as the subset itself, then * those numbers can be ruled out everywhere else in the domain. * (For example, if there are five empty squares and the * possible numbers in each are 12, 23, 13, 134 and 1345, then * the first three empty squares form such a subset: the numbers * 1, 2 and 3 _must_ be in those three squares in some * permutation, and hence we can deduce none of them can be in * the fourth or fifth squares.) * + You can also see this the other way round, concentrating * on numbers rather than squares: if there is a subset of * the unplaced numbers within a domain such that the union * of all their possible positions has the same size as the * subset itself, then all other numbers can be ruled out for * those positions. However, it turns out that this is * exactly equivalent to the first formulation at all times: * there is a 1-1 correspondence between suitable subsets of * the unplaced numbers and suitable subsets of the unfilled * places, found by taking the _complement_ of the union of * the numbers' possible positions (or the spaces' possible * contents). * * - Forcing chains (see comment for solver_forcing().) * * - Recursion. If all else fails, we pick one of the currently * most constrained empty squares and take a random guess at its * contents, then continue solving on that basis and see if we * get any further. */ struct solver_usage { int cr; struct block_structure *blocks, *kblocks, *extra_cages; /* * We set up a cubic array, indexed by x, y and digit; each * element of this array is TRUE or FALSE according to whether * or not that digit _could_ in principle go in that position. * * The way to index this array is cube[(y*cr+x)*cr+n-1]; there * are macros below to help with this. */ unsigned char *cube; /* * This is the grid in which we write down our final * deductions. y-coordinates in here are _not_ transformed. */ digit *grid; /* * For killer-type puzzles, kclues holds the secondary clue for * each cage. For derived cages, the clue is in extra_clues. */ digit *kclues, *extra_clues; /* * Now we keep track, at a slightly higher level, of what we * have yet to work out, to prevent doing the same deduction * many times. */ /* row[y*cr+n-1] TRUE if digit n has been placed in row y */ unsigned char *row; /* col[x*cr+n-1] TRUE if digit n has been placed in row x */ unsigned char *col; /* blk[i*cr+n-1] TRUE if digit n has been placed in block i */ unsigned char *blk; /* diag[i*cr+n-1] TRUE if digit n has been placed in diagonal i */ unsigned char *diag; /* diag 0 is \, 1 is / */ int *regions; int nr_regions; int **sq2region; }; #define cubepos2(xy,n) ((xy)*usage->cr+(n)-1) #define cubepos(x,y,n) cubepos2((y)*usage->cr+(x),n) #define cube(x,y,n) (usage->cube[cubepos(x,y,n)]) #define cube2(xy,n) (usage->cube[cubepos2(xy,n)]) #define ondiag0(xy) ((xy) % (cr+1) == 0) #define ondiag1(xy) ((xy) % (cr-1) == 0 && (xy) > 0 && (xy) < cr*cr-1) #define diag0(i) ((i) * (cr+1)) #define diag1(i) ((i+1) * (cr-1)) /* * Function called when we are certain that a particular square has * a particular number in it. The y-coordinate passed in here is * transformed. */ static void solver_place(struct solver_usage *usage, int x, int y, int n) { int cr = usage->cr; int sqindex = y*cr+x; int i, bi; assert(cube(x,y,n)); /* * Rule out all other numbers in this square. */ for (i = 1; i <= cr; i++) if (i != n) cube(x,y,i) = FALSE; /* * Rule out this number in all other positions in the row. */ for (i = 0; i < cr; i++) if (i != y) cube(x,i,n) = FALSE; /* * Rule out this number in all other positions in the column. */ for (i = 0; i < cr; i++) if (i != x) cube(i,y,n) = FALSE; /* * Rule out this number in all other positions in the block. */ bi = usage->blocks->whichblock[sqindex]; for (i = 0; i < cr; i++) { int bp = usage->blocks->blocks[bi][i]; if (bp != sqindex) cube2(bp,n) = FALSE; } /* * Enter the number in the result grid. */ usage->grid[sqindex] = n; /* * Cross out this number from the list of numbers left to place * in its row, its column and its block. */ usage->row[y*cr+n-1] = usage->col[x*cr+n-1] = usage->blk[bi*cr+n-1] = TRUE; if (usage->diag) { if (ondiag0(sqindex)) { for (i = 0; i < cr; i++) if (diag0(i) != sqindex) cube2(diag0(i),n) = FALSE; usage->diag[n-1] = TRUE; } if (ondiag1(sqindex)) { for (i = 0; i < cr; i++) if (diag1(i) != sqindex) cube2(diag1(i),n) = FALSE; usage->diag[cr+n-1] = TRUE; } } } #if defined STANDALONE_SOLVER && defined __GNUC__ /* * Forward-declare the functions taking printf-like format arguments * with __attribute__((format)) so as to ensure the argument syntax * gets debugged. */ struct solver_scratch; static int solver_elim(struct solver_usage *usage, int *indices, char *fmt, ...) __attribute__((format(printf,3,4))); static int solver_intersect(struct solver_usage *usage, int *indices1, int *indices2, char *fmt, ...) __attribute__((format(printf,4,5))); static int solver_set(struct solver_usage *usage, struct solver_scratch *scratch, int *indices, char *fmt, ...) __attribute__((format(printf,4,5))); #endif static int solver_elim(struct solver_usage *usage, int *indices #ifdef STANDALONE_SOLVER , char *fmt, ... #endif ) { int cr = usage->cr; int fpos, m, i; /* * Count the number of set bits within this section of the * cube. */ m = 0; fpos = -1; for (i = 0; i < cr; i++) if (usage->cube[indices[i]]) { fpos = indices[i]; m++; } if (m == 1) { int x, y, n; assert(fpos >= 0); n = 1 + fpos % cr; x = fpos / cr; y = x / cr; x %= cr; if (!usage->grid[y*cr+x]) { #ifdef STANDALONE_SOLVER if (solver_show_working) { va_list ap; printf("%*s", solver_recurse_depth*4, ""); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); printf(":\n%*s placing %d at (%d,%d)\n", solver_recurse_depth*4, "", n, 1+x, 1+y); } #endif solver_place(usage, x, y, n); return +1; } } else if (m == 0) { #ifdef STANDALONE_SOLVER if (solver_show_working) { va_list ap; printf("%*s", solver_recurse_depth*4, ""); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); printf(":\n%*s no possibilities available\n", solver_recurse_depth*4, ""); } #endif return -1; } return 0; } static int solver_intersect(struct solver_usage *usage, int *indices1, int *indices2 #ifdef STANDALONE_SOLVER , char *fmt, ... #endif ) { int cr = usage->cr; int ret, i, j; /* * Loop over the first domain and see if there's any set bit * not also in the second. */ for (i = j = 0; i < cr; i++) { int p = indices1[i]; while (j < cr && indices2[j] < p) j++; if (usage->cube[p]) { if (j < cr && indices2[j] == p) continue; /* both domains contain this index */ else return 0; /* there is, so we can't deduce */ } } /* * We have determined that all set bits in the first domain are * within its overlap with the second. So loop over the second * domain and remove all set bits that aren't also in that * overlap; return +1 iff we actually _did_ anything. */ ret = 0; for (i = j = 0; i < cr; i++) { int p = indices2[i]; while (j < cr && indices1[j] < p) j++; if (usage->cube[p] && (j >= cr || indices1[j] != p)) { #ifdef STANDALONE_SOLVER if (solver_show_working) { int px, py, pn; if (!ret) { va_list ap; printf("%*s", solver_recurse_depth*4, ""); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); printf(":\n"); } pn = 1 + p % cr; px = p / cr; py = px / cr; px %= cr; printf("%*s ruling out %d at (%d,%d)\n", solver_recurse_depth*4, "", pn, 1+px, 1+py); } #endif ret = +1; /* we did something */ usage->cube[p] = 0; } } return ret; } struct solver_scratch { unsigned char *grid, *rowidx, *colidx, *set; int *neighbours, *bfsqueue; int *indexlist, *indexlist2; #ifdef STANDALONE_SOLVER int *bfsprev; #endif }; static int solver_set(struct solver_usage *usage, struct solver_scratch *scratch, int *indices #ifdef STANDALONE_SOLVER , char *fmt, ... #endif ) { int cr = usage->cr; int i, j, n, count; unsigned char *grid = scratch->grid; unsigned char *rowidx = scratch->rowidx; unsigned char *colidx = scratch->colidx; unsigned char *set = scratch->set; /* * We are passed a cr-by-cr matrix of booleans. Our first job * is to winnow it by finding any definite placements - i.e. * any row with a solitary 1 - and discarding that row and the * column containing the 1. */ memset(rowidx, TRUE, cr); memset(colidx, TRUE, cr); for (i = 0; i < cr; i++) { int count = 0, first = -1; for (j = 0; j < cr; j++) if (usage->cube[indices[i*cr+j]]) first = j, count++; /* * If count == 0, then there's a row with no 1s at all and * the puzzle is internally inconsistent. However, we ought * to have caught this already during the simpler reasoning * methods, so we can safely fail an assertion if we reach * this point here. */ assert(count > 0); if (count == 1) rowidx[i] = colidx[first] = FALSE; } /* * Convert each of rowidx/colidx from a list of 0s and 1s to a * list of the indices of the 1s. */ for (i = j = 0; i < cr; i++) if (rowidx[i]) rowidx[j++] = i; n = j; for (i = j = 0; i < cr; i++) if (colidx[i]) colidx[j++] = i; assert(n == j); /* * And create the smaller matrix. */ for (i = 0; i < n; i++) for (j = 0; j < n; j++) grid[i*cr+j] = usage->cube[indices[rowidx[i]*cr+colidx[j]]]; /* * Having done that, we now have a matrix in which every row * has at least two 1s in. Now we search to see if we can find * a rectangle of zeroes (in the set-theoretic sense of * `rectangle', i.e. a subset of rows crossed with a subset of * columns) whose width and height add up to n. */ memset(set, 0, n); count = 0; while (1) { /* * We have a candidate set. If its size is <=1 or >=n-1 * then we move on immediately. */ if (count > 1 && count < n-1) { /* * The number of rows we need is n-count. See if we can * find that many rows which each have a zero in all * the positions listed in `set'. */ int rows = 0; for (i = 0; i < n; i++) { int ok = TRUE; for (j = 0; j < n; j++) if (set[j] && grid[i*cr+j]) { ok = FALSE; break; } if (ok) rows++; } /* * We expect never to be able to get _more_ than * n-count suitable rows: this would imply that (for * example) there are four numbers which between them * have at most three possible positions, and hence it * indicates a faulty deduction before this point or * even a bogus clue. */ if (rows > n - count) { #ifdef STANDALONE_SOLVER if (solver_show_working) { va_list ap; printf("%*s", solver_recurse_depth*4, ""); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); printf(":\n%*s contradiction reached\n", solver_recurse_depth*4, ""); } #endif return -1; } if (rows >= n - count) { int progress = FALSE; /* * We've got one! Now, for each row which _doesn't_ * satisfy the criterion, eliminate all its set * bits in the positions _not_ listed in `set'. * Return +1 (meaning progress has been made) if we * successfully eliminated anything at all. * * This involves referring back through * rowidx/colidx in order to work out which actual * positions in the cube to meddle with. */ for (i = 0; i < n; i++) { int ok = TRUE; for (j = 0; j < n; j++) if (set[j] && grid[i*cr+j]) { ok = FALSE; break; } if (!ok) { for (j = 0; j < n; j++) if (!set[j] && grid[i*cr+j]) { int fpos = indices[rowidx[i]*cr+colidx[j]]; #ifdef STANDALONE_SOLVER if (solver_show_working) { int px, py, pn; if (!progress) { va_list ap; printf("%*s", solver_recurse_depth*4, ""); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); printf(":\n"); } pn = 1 + fpos % cr; px = fpos / cr; py = px / cr; px %= cr; printf("%*s ruling out %d at (%d,%d)\n", solver_recurse_depth*4, "", pn, 1+px, 1+py); } #endif progress = TRUE; usage->cube[fpos] = FALSE; } } } if (progress) { return +1; } } } /* * Binary increment: change the rightmost 0 to a 1, and * change all 1s to the right of it to 0s. */ i = n; while (i > 0 && set[i-1]) set[--i] = 0, count--; if (i > 0) set[--i] = 1, count++; else break; /* done */ } return 0; } /* * Look for forcing chains. A forcing chain is a path of * pairwise-exclusive squares (i.e. each pair of adjacent squares * in the path are in the same row, column or block) with the * following properties: * * (a) Each square on the path has precisely two possible numbers. * * (b) Each pair of squares which are adjacent on the path share * at least one possible number in common. * * (c) Each square in the middle of the path shares _both_ of its * numbers with at least one of its neighbours (not the same * one with both neighbours). * * These together imply that at least one of the possible number * choices at one end of the path forces _all_ the rest of the * numbers along the path. In order to make real use of this, we * need further properties: * * (c) Ruling out some number N from the square at one end of the * path forces the square at the other end to take the same * number N. * * (d) The two end squares are both in line with some third * square. * * (e) That third square currently has N as a possibility. * * If we can find all of that lot, we can deduce that at least one * of the two ends of the forcing chain has number N, and that * therefore the mutually adjacent third square does not. * * To find forcing chains, we're going to start a bfs at each * suitable square, once for each of its two possible numbers. */ static int solver_forcing(struct solver_usage *usage, struct solver_scratch *scratch) { int cr = usage->cr; int *bfsqueue = scratch->bfsqueue; #ifdef STANDALONE_SOLVER int *bfsprev = scratch->bfsprev; #endif unsigned char *number = scratch->grid; int *neighbours = scratch->neighbours; int x, y; for (y = 0; y < cr; y++) for (x = 0; x < cr; x++) { int count, t, n; /* * If this square doesn't have exactly two candidate * numbers, don't try it. * * In this loop we also sum the candidate numbers, * which is a nasty hack to allow us to quickly find * `the other one' (since we will shortly know there * are exactly two). */ for (count = t = 0, n = 1; n <= cr; n++) if (cube(x, y, n)) count++, t += n; if (count != 2) continue; /* * Now attempt a bfs for each candidate. */ for (n = 1; n <= cr; n++) if (cube(x, y, n)) { int orign, currn, head, tail; /* * Begin a bfs. */ orign = n; memset(number, cr+1, cr*cr); head = tail = 0; bfsqueue[tail++] = y*cr+x; #ifdef STANDALONE_SOLVER bfsprev[y*cr+x] = -1; #endif number[y*cr+x] = t - n; while (head < tail) { int xx, yy, nneighbours, xt, yt, i; xx = bfsqueue[head++]; yy = xx / cr; xx %= cr; currn = number[yy*cr+xx]; /* * Find neighbours of yy,xx. */ nneighbours = 0; for (yt = 0; yt < cr; yt++) neighbours[nneighbours++] = yt*cr+xx; for (xt = 0; xt < cr; xt++) neighbours[nneighbours++] = yy*cr+xt; xt = usage->blocks->whichblock[yy*cr+xx]; for (yt = 0; yt < cr; yt++) neighbours[nneighbours++] = usage->blocks->blocks[xt][yt]; if (usage->diag) { int sqindex = yy*cr+xx; if (ondiag0(sqindex)) { for (i = 0; i < cr; i++) neighbours[nneighbours++] = diag0(i); } if (ondiag1(sqindex)) { for (i = 0; i < cr; i++) neighbours[nneighbours++] = diag1(i); } } /* * Try visiting each of those neighbours. */ for (i = 0; i < nneighbours; i++) { int cc, tt, nn; xt = neighbours[i] % cr; yt = neighbours[i] / cr; /* * We need this square to not be * already visited, and to include * currn as a possible number. */ if (number[yt*cr+xt] <= cr) continue; if (!cube(xt, yt, currn)) continue; /* * Don't visit _this_ square a second * time! */ if (xt == xx && yt == yy) continue; /* * To continue with the bfs, we need * this square to have exactly two * possible numbers. */ for (cc = tt = 0, nn = 1; nn <= cr; nn++) if (cube(xt, yt, nn)) cc++, tt += nn; if (cc == 2) { bfsqueue[tail++] = yt*cr+xt; #ifdef STANDALONE_SOLVER bfsprev[yt*cr+xt] = yy*cr+xx; #endif number[yt*cr+xt] = tt - currn; } /* * One other possibility is that this * might be the square in which we can * make a real deduction: if it's * adjacent to x,y, and currn is equal * to the original number we ruled out. */ if (currn == orign && (xt == x || yt == y || (usage->blocks->whichblock[yt*cr+xt] == usage->blocks->whichblock[y*cr+x]) || (usage->diag && ((ondiag0(yt*cr+xt) && ondiag0(y*cr+x)) || (ondiag1(yt*cr+xt) && ondiag1(y*cr+x)))))) { #ifdef STANDALONE_SOLVER if (solver_show_working) { char *sep = ""; int xl, yl; printf("%*sforcing chain, %d at ends of ", solver_recurse_depth*4, "", orign); xl = xx; yl = yy; while (1) { printf("%s(%d,%d)", sep, 1+xl, 1+yl); xl = bfsprev[yl*cr+xl]; if (xl < 0) break; yl = xl / cr; xl %= cr; sep = "-"; } printf("\n%*s ruling out %d at (%d,%d)\n", solver_recurse_depth*4, "", orign, 1+xt, 1+yt); } #endif cube(xt, yt, orign) = FALSE; return 1; } } } } } return 0; } static int solver_killer_minmax(struct solver_usage *usage, struct block_structure *cages, digit *clues, int b #ifdef STANDALONE_SOLVER , const char *extra #endif ) { int cr = usage->cr; int i; int ret = 0; int nsquares = cages->nr_squares[b]; if (clues[b] == 0) return 0; for (i = 0; i < nsquares; i++) { int n, x = cages->blocks[b][i]; for (n = 1; n <= cr; n++) if (cube2(x, n)) { int maxval = 0, minval = 0; int j; for (j = 0; j < nsquares; j++) { int m; int y = cages->blocks[b][j]; if (i == j) continue; for (m = 1; m <= cr; m++) if (cube2(y, m)) { minval += m; break; } for (m = cr; m > 0; m--) if (cube2(y, m)) { maxval += m; break; } } if (maxval + n < clues[b]) { cube2(x, n) = FALSE; ret = 1; #ifdef STANDALONE_SOLVER if (solver_show_working) printf("%*s ruling out %d at (%d,%d) as too low %s\n", solver_recurse_depth*4, "killer minmax analysis", n, 1 + x%cr, 1 + x/cr, extra); #endif } if (minval + n > clues[b]) { cube2(x, n) = FALSE; ret = 1; #ifdef STANDALONE_SOLVER if (solver_show_working) printf("%*s ruling out %d at (%d,%d) as too high %s\n", solver_recurse_depth*4, "killer minmax analysis", n, 1 + x%cr, 1 + x/cr, extra); #endif } } } return ret; } static int solver_killer_sums(struct solver_usage *usage, int b, struct block_structure *cages, int clue, int cage_is_region #ifdef STANDALONE_SOLVER , const char *cage_type #endif ) { int cr = usage->cr; int i, ret, max_sums; int nsquares = cages->nr_squares[b]; unsigned long *sumbits, possible_addends; if (clue == 0) { assert(nsquares == 0); return 0; } assert(nsquares > 0); if (nsquares < 2 || nsquares > 4) return 0; if (!cage_is_region) { int known_row = -1, known_col = -1, known_block = -1; /* * Verify that the cage lies entirely within one region, * so that using the precomputed sums is valid. */ for (i = 0; i < nsquares; i++) { int x = cages->blocks[b][i]; assert(usage->grid[x] == 0); if (i == 0) { known_row = x/cr; known_col = x%cr; known_block = usage->blocks->whichblock[x]; } else { if (known_row != x/cr) known_row = -1; if (known_col != x%cr) known_col = -1; if (known_block != usage->blocks->whichblock[x]) known_block = -1; } } if (known_block == -1 && known_col == -1 && known_row == -1) return 0; } if (nsquares == 2) { if (clue < 3 || clue > 17) return -1; sumbits = sum_bits2[clue]; max_sums = MAX_2SUMS; } else if (nsquares == 3) { if (clue < 6 || clue > 24) return -1; sumbits = sum_bits3[clue]; max_sums = MAX_3SUMS; } else { if (clue < 10 || clue > 30) return -1; sumbits = sum_bits4[clue]; max_sums = MAX_4SUMS; } /* * For every possible way to get the sum, see if there is * one square in the cage that disallows all the required * addends. If we find one such square, this way to compute * the sum is impossible. */ possible_addends = 0; for (i = 0; i < max_sums; i++) { int j; unsigned long bits = sumbits[i]; if (bits == 0) break; for (j = 0; j < nsquares; j++) { int n; unsigned long square_bits = bits; int x = cages->blocks[b][j]; for (n = 1; n <= cr; n++) if (!cube2(x, n)) square_bits &= ~(1L << n); if (square_bits == 0) { break; } } if (j == nsquares) possible_addends |= bits; } /* * Now we know which addends can possibly be used to * compute the sum. Remove all other digits from the * set of possibilities. */ if (possible_addends == 0) return -1; ret = 0; for (i = 0; i < nsquares; i++) { int n; int x = cages->blocks[b][i]; for (n = 1; n <= cr; n++) { if (!cube2(x, n)) continue; if ((possible_addends & (1 << n)) == 0) { cube2(x, n) = FALSE; ret = 1; #ifdef STANDALONE_SOLVER if (solver_show_working) { printf("%*s using %s\n", solver_recurse_depth*4, "killer sums analysis", cage_type); printf("%*s ruling out %d at (%d,%d) due to impossible %d-sum\n", solver_recurse_depth*4, "", n, 1 + x%cr, 1 + x/cr, nsquares); } #endif } } } return ret; } static int filter_whole_cages(struct solver_usage *usage, int *squares, int n, int *filtered_sum) { int b, i, j, off; *filtered_sum = 0; /* First, filter squares with a clue. */ for (i = j = 0; i < n; i++) if (usage->grid[squares[i]]) *filtered_sum += usage->grid[squares[i]]; else squares[j++] = squares[i]; n = j; /* * Filter all cages that are covered entirely by the list of * squares. */ off = 0; for (b = 0; b < usage->kblocks->nr_blocks && off < n; b++) { int b_squares = usage->kblocks->nr_squares[b]; int matched = 0; if (b_squares == 0) continue; /* * Find all squares of block b that lie in our list, * and make them contiguous at off, which is the current position * in the output list. */ for (i = 0; i < b_squares; i++) { for (j = off; j < n; j++) if (squares[j] == usage->kblocks->blocks[b][i]) { int t = squares[off + matched]; squares[off + matched] = squares[j]; squares[j] = t; matched++; break; } } /* If so, filter out all squares of b from the list. */ if (matched != usage->kblocks->nr_squares[b]) { off += matched; continue; } memmove(squares + off, squares + off + matched, (n - off - matched) * sizeof *squares); n -= matched; *filtered_sum += usage->kclues[b]; } assert(off == n); return off; } static struct solver_scratch *solver_new_scratch(struct solver_usage *usage) { struct solver_scratch *scratch = snew(struct solver_scratch); int cr = usage->cr; scratch->grid = snewn(cr*cr, unsigned char); scratch->rowidx = snewn(cr, unsigned char); scratch->colidx = snewn(cr, unsigned char); scratch->set = snewn(cr, unsigned char); scratch->neighbours = snewn(5*cr, int); scratch->bfsqueue = snewn(cr*cr, int); #ifdef STANDALONE_SOLVER scratch->bfsprev = snewn(cr*cr, int); #endif scratch->indexlist = snewn(cr*cr, int); /* used for set elimination */ scratch->indexlist2 = snewn(cr, int); /* only used for intersect() */ return scratch; } static void solver_free_scratch(struct solver_scratch *scratch) { #ifdef STANDALONE_SOLVER sfree(scratch->bfsprev); #endif sfree(scratch->bfsqueue); sfree(scratch->neighbours); sfree(scratch->set); sfree(scratch->colidx); sfree(scratch->rowidx); sfree(scratch->grid); sfree(scratch->indexlist); sfree(scratch->indexlist2); sfree(scratch); } /* * Used for passing information about difficulty levels between the solver * and its callers. */ struct difficulty { /* Maximum levels allowed. */ int maxdiff, maxkdiff; /* Levels reached by the solver. */ int diff, kdiff; }; static void solver(int cr, struct block_structure *blocks, struct block_structure *kblocks, int xtype, digit *grid, digit *kgrid, struct difficulty *dlev) { struct solver_usage *usage; struct solver_scratch *scratch; int x, y, b, i, n, ret; int diff = DIFF_BLOCK; int kdiff = DIFF_KSINGLE; /* * Set up a usage structure as a clean slate (everything * possible). */ usage = snew(struct solver_usage); usage->cr = cr; usage->blocks = blocks; if (kblocks) { usage->kblocks = dup_block_structure(kblocks); usage->extra_cages = alloc_block_structure (kblocks->c, kblocks->r, cr * cr, cr, cr * cr); usage->extra_clues = snewn(cr*cr, digit); } else { usage->kblocks = usage->extra_cages = NULL; usage->extra_clues = NULL; } usage->cube = snewn(cr*cr*cr, unsigned char); usage->grid = grid; /* write straight back to the input */ if (kgrid) { int nclues; assert(kblocks); nclues = kblocks->nr_blocks; /* * Allow for expansion of the killer regions, the absolute * limit is obviously one region per square. */ usage->kclues = snewn(cr*cr, digit); for (i = 0; i < nclues; i++) { for (n = 0; n < kblocks->nr_squares[i]; n++) if (kgrid[kblocks->blocks[i][n]] != 0) usage->kclues[i] = kgrid[kblocks->blocks[i][n]]; assert(usage->kclues[i] > 0); } memset(usage->kclues + nclues, 0, cr*cr - nclues); } else { usage->kclues = NULL; } memset(usage->cube, TRUE, cr*cr*cr); usage->row = snewn(cr * cr, unsigned char); usage->col = snewn(cr * cr, unsigned char); usage->blk = snewn(cr * cr, unsigned char); memset(usage->row, FALSE, cr * cr); memset(usage->col, FALSE, cr * cr); memset(usage->blk, FALSE, cr * cr); if (xtype) { usage->diag = snewn(cr * 2, unsigned char); memset(usage->diag, FALSE, cr * 2); } else usage->diag = NULL; usage->nr_regions = cr * 3 + (xtype ? 2 : 0); usage->regions = snewn(cr * usage->nr_regions, int); usage->sq2region = snewn(cr * cr * 3, int *); for (n = 0; n < cr; n++) { for (i = 0; i < cr; i++) { x = n*cr+i; y = i*cr+n; b = usage->blocks->blocks[n][i]; usage->regions[cr*n*3 + i] = x; usage->regions[cr*n*3 + cr + i] = y; usage->regions[cr*n*3 + 2*cr + i] = b; usage->sq2region[x*3] = usage->regions + cr*n*3; usage->sq2region[y*3 + 1] = usage->regions + cr*n*3 + cr; usage->sq2region[b*3 + 2] = usage->regions + cr*n*3 + 2*cr; } } scratch = solver_new_scratch(usage); /* * Place all the clue numbers we are given. */ for (x = 0; x < cr; x++) for (y = 0; y < cr; y++) { int n = grid[y*cr+x]; if (n) { if (!cube(x,y,n)) { diff = DIFF_IMPOSSIBLE; goto got_result; } solver_place(usage, x, y, grid[y*cr+x]); } } /* * Now loop over the grid repeatedly trying all permitted modes * of reasoning. The loop terminates if we complete an * iteration without making any progress; we then return * failure or success depending on whether the grid is full or * not. */ while (1) { /* * I'd like to write `continue;' inside each of the * following loops, so that the solver returns here after * making some progress. However, I can't specify that I * want to continue an outer loop rather than the innermost * one, so I'm apologetically resorting to a goto. */ cont: /* * Blockwise positional elimination. */ for (b = 0; b < cr; b++) for (n = 1; n <= cr; n++) if (!usage->blk[b*cr+n-1]) { for (i = 0; i < cr; i++) scratch->indexlist[i] = cubepos2(usage->blocks->blocks[b][i],n); ret = solver_elim(usage, scratch->indexlist #ifdef STANDALONE_SOLVER , "positional elimination," " %d in block %s", n, usage->blocks->blocknames[b] #endif ); if (ret < 0) { diff = DIFF_IMPOSSIBLE; goto got_result; } else if (ret > 0) { diff = max(diff, DIFF_BLOCK); goto cont; } } if (usage->kclues != NULL) { int changed = FALSE; /* * First, bring the kblocks into a more useful form: remove * all filled-in squares, and reduce the sum by their values. * Walk in reverse order, since otherwise remove_from_block * can move element past our loop counter. */ for (b = 0; b < usage->kblocks->nr_blocks; b++) for (i = usage->kblocks->nr_squares[b] -1; i >= 0; i--) { int x = usage->kblocks->blocks[b][i]; int t = usage->grid[x]; if (t == 0) continue; remove_from_block(usage->kblocks, b, x); if (t > usage->kclues[b]) { diff = DIFF_IMPOSSIBLE; goto got_result; } usage->kclues[b] -= t; /* * Since cages are regions, this tells us something * about the other squares in the cage. */ for (n = 0; n < usage->kblocks->nr_squares[b]; n++) { cube2(usage->kblocks->blocks[b][n], t) = FALSE; } } /* * The most trivial kind of solver for killer puzzles: fill * single-square cages. */ for (b = 0; b < usage->kblocks->nr_blocks; b++) { int squares = usage->kblocks->nr_squares[b]; if (squares == 1) { int v = usage->kclues[b]; if (v < 1 || v > cr) { diff = DIFF_IMPOSSIBLE; goto got_result; } x = usage->kblocks->blocks[b][0] % cr; y = usage->kblocks->blocks[b][0] / cr; if (!cube(x, y, v)) { diff = DIFF_IMPOSSIBLE; goto got_result; } solver_place(usage, x, y, v); #ifdef STANDALONE_SOLVER if (solver_show_working) { printf("%*s placing %d at (%d,%d)\n", solver_recurse_depth*4, "killer single-square cage", v, 1 + x%cr, 1 + x/cr); } #endif changed = TRUE; } } if (changed) { kdiff = max(kdiff, DIFF_KSINGLE); goto cont; } } if (dlev->maxkdiff >= DIFF_KINTERSECT && usage->kclues != NULL) { int changed = FALSE; /* * Now, create the extra_cages information. Every full region * (row, column, or block) has the same sum total (45 for 3x3 * puzzles. After we try to cover these regions with cages that * lie entirely within them, any squares that remain must bring * the total to this known value, and so they form additional * cages which aren't immediately evident in the displayed form * of the puzzle. */ usage->extra_cages->nr_blocks = 0; for (i = 0; i < 3; i++) { for (n = 0; n < cr; n++) { int *region = usage->regions + cr*n*3 + i*cr; int sum = cr * (cr + 1) / 2; int nsquares = cr; int filtered; int n_extra = usage->extra_cages->nr_blocks; int *extra_list = usage->extra_cages->blocks[n_extra]; memcpy(extra_list, region, cr * sizeof *extra_list); nsquares = filter_whole_cages(usage, extra_list, nsquares, &filtered); sum -= filtered; if (nsquares == cr || nsquares == 0) continue; if (dlev->maxdiff >= DIFF_RECURSIVE) { if (sum <= 0) { dlev->diff = DIFF_IMPOSSIBLE; goto got_result; } } assert(sum > 0); if (nsquares == 1) { if (sum > cr) { diff = DIFF_IMPOSSIBLE; goto got_result; } x = extra_list[0] % cr; y = extra_list[0] / cr; if (!cube(x, y, sum)) { diff = DIFF_IMPOSSIBLE; goto got_result; } solver_place(usage, x, y, sum); changed = TRUE; #ifdef STANDALONE_SOLVER if (solver_show_working) { printf("%*s placing %d at (%d,%d)\n", solver_recurse_depth*4, "killer single-square deduced cage", sum, 1 + x, 1 + y); } #endif } b = usage->kblocks->whichblock[extra_list[0]]; for (x = 1; x < nsquares; x++) if (usage->kblocks->whichblock[extra_list[x]] != b) break; if (x == nsquares) { assert(usage->kblocks->nr_squares[b] > nsquares); split_block(usage->kblocks, extra_list, nsquares); assert(usage->kblocks->nr_squares[usage->kblocks->nr_blocks - 1] == nsquares); usage->kclues[usage->kblocks->nr_blocks - 1] = sum; usage->kclues[b] -= sum; } else { usage->extra_cages->nr_squares[n_extra] = nsquares; usage->extra_cages->nr_blocks++; usage->extra_clues[n_extra] = sum; } } } if (changed) { kdiff = max(kdiff, DIFF_KINTERSECT); goto cont; } } /* * Another simple killer-type elimination. For every square in a * cage, find the minimum and maximum possible sums of all the * other squares in the same cage, and rule out possibilities * for the given square based on whether they are guaranteed to * cause the sum to be either too high or too low. * This is a special case of trying all possible sums across a * region, which is a recursive algorithm. We should probably * implement it for a higher difficulty level. */ if (dlev->maxkdiff >= DIFF_KMINMAX && usage->kclues != NULL) { int changed = FALSE; for (b = 0; b < usage->kblocks->nr_blocks; b++) { int ret = solver_killer_minmax(usage, usage->kblocks, usage->kclues, b #ifdef STANDALONE_SOLVER , "" #endif ); if (ret < 0) { diff = DIFF_IMPOSSIBLE; goto got_result; } else if (ret > 0) changed = TRUE; } for (b = 0; b < usage->extra_cages->nr_blocks; b++) { int ret = solver_killer_minmax(usage, usage->extra_cages, usage->extra_clues, b #ifdef STANDALONE_SOLVER , "using deduced cages" #endif ); if (ret < 0) { diff = DIFF_IMPOSSIBLE; goto got_result; } else if (ret > 0) changed = TRUE; } if (changed) { kdiff = max(kdiff, DIFF_KMINMAX); goto cont; } } /* * Try to use knowledge of which numbers can be used to generate * a given sum. * This can only be used if a cage lies entirely within a region. */ if (dlev->maxkdiff >= DIFF_KSUMS && usage->kclues != NULL) { int changed = FALSE; for (b = 0; b < usage->kblocks->nr_blocks; b++) { int ret = solver_killer_sums(usage, b, usage->kblocks, usage->kclues[b], TRUE #ifdef STANDALONE_SOLVER , "regular clues" #endif ); if (ret > 0) { changed = TRUE; kdiff = max(kdiff, DIFF_KSUMS); } else if (ret < 0) { diff = DIFF_IMPOSSIBLE; goto got_result; } } for (b = 0; b < usage->extra_cages->nr_blocks; b++) { int ret = solver_killer_sums(usage, b, usage->extra_cages, usage->extra_clues[b], FALSE #ifdef STANDALONE_SOLVER , "deduced clues" #endif ); if (ret > 0) { changed = TRUE; kdiff = max(kdiff, DIFF_KSUMS); } else if (ret < 0) { diff = DIFF_IMPOSSIBLE; goto got_result; } } if (changed) goto cont; } if (dlev->maxdiff <= DIFF_BLOCK) break; /* * Row-wise positional elimination. */ for (y = 0; y < cr; y++) for (n = 1; n <= cr; n++) if (!usage->row[y*cr+n-1]) { for (x = 0; x < cr; x++) scratch->indexlist[x] = cubepos(x, y, n); ret = solver_elim(usage, scratch->indexlist #ifdef STANDALONE_SOLVER , "positional elimination," " %d in row %d", n, 1+y #endif ); if (ret < 0) { diff = DIFF_IMPOSSIBLE; goto got_result; } else if (ret > 0) { diff = max(diff, DIFF_SIMPLE); goto cont; } } /* * Column-wise positional elimination. */ for (x = 0; x < cr; x++) for (n = 1; n <= cr; n++) if (!usage->col[x*cr+n-1]) { for (y = 0; y < cr; y++) scratch->indexlist[y] = cubepos(x, y, n); ret = solver_elim(usage, scratch->indexlist #ifdef STANDALONE_SOLVER , "positional elimination," " %d in column %d", n, 1+x #endif ); if (ret < 0) { diff = DIFF_IMPOSSIBLE; goto got_result; } else if (ret > 0) { diff = max(diff, DIFF_SIMPLE); goto cont; } } /* * X-diagonal positional elimination. */ if (usage->diag) { for (n = 1; n <= cr; n++) if (!usage->diag[n-1]) { for (i = 0; i < cr; i++) scratch->indexlist[i] = cubepos2(diag0(i), n); ret = solver_elim(usage, scratch->indexlist #ifdef STANDALONE_SOLVER , "positional elimination," " %d in \\-diagonal", n #endif ); if (ret < 0) { diff = DIFF_IMPOSSIBLE; goto got_result; } else if (ret > 0) { diff = max(diff, DIFF_SIMPLE); goto cont; } } for (n = 1; n <= cr; n++) if (!usage->diag[cr+n-1]) { for (i = 0; i < cr; i++) scratch->indexlist[i] = cubepos2(diag1(i), n); ret = solver_elim(usage, scratch->indexlist #ifdef STANDALONE_SOLVER , "positional elimination," " %d in /-diagonal", n #endif ); if (ret < 0) { diff = DIFF_IMPOSSIBLE; goto got_result; } else if (ret > 0) { diff = max(diff, DIFF_SIMPLE); goto cont; } } } /* * Numeric elimination. */ for (x = 0; x < cr; x++) for (y = 0; y < cr; y++) if (!usage->grid[y*cr+x]) { for (n = 1; n <= cr; n++) scratch->indexlist[n-1] = cubepos(x, y, n); ret = solver_elim(usage, scratch->indexlist #ifdef STANDALONE_SOLVER , "numeric elimination at (%d,%d)", 1+x, 1+y #endif ); if (ret < 0) { diff = DIFF_IMPOSSIBLE; goto got_result; } else if (ret > 0) { diff = max(diff, DIFF_SIMPLE); goto cont; } } if (dlev->maxdiff <= DIFF_SIMPLE) break; /* * Intersectional analysis, rows vs blocks. */ for (y = 0; y < cr; y++) for (b = 0; b < cr; b++) for (n = 1; n <= cr; n++) { if (usage->row[y*cr+n-1] || usage->blk[b*cr+n-1]) continue; for (i = 0; i < cr; i++) { scratch->indexlist[i] = cubepos(i, y, n); scratch->indexlist2[i] = cubepos2(usage->blocks->blocks[b][i], n); } /* * solver_intersect() never returns -1. */ if (solver_intersect(usage, scratch->indexlist, scratch->indexlist2 #ifdef STANDALONE_SOLVER , "intersectional analysis," " %d in row %d vs block %s", n, 1+y, usage->blocks->blocknames[b] #endif ) || solver_intersect(usage, scratch->indexlist2, scratch->indexlist #ifdef STANDALONE_SOLVER , "intersectional analysis," " %d in block %s vs row %d", n, usage->blocks->blocknames[b], 1+y #endif )) { diff = max(diff, DIFF_INTERSECT); goto cont; } } /* * Intersectional analysis, columns vs blocks. */ for (x = 0; x < cr; x++) for (b = 0; b < cr; b++) for (n = 1; n <= cr; n++) { if (usage->col[x*cr+n-1] || usage->blk[b*cr+n-1]) continue; for (i = 0; i < cr; i++) { scratch->indexlist[i] = cubepos(x, i, n); scratch->indexlist2[i] = cubepos2(usage->blocks->blocks[b][i], n); } if (solver_intersect(usage, scratch->indexlist, scratch->indexlist2 #ifdef STANDALONE_SOLVER , "intersectional analysis," " %d in column %d vs block %s", n, 1+x, usage->blocks->blocknames[b] #endif ) || solver_intersect(usage, scratch->indexlist2, scratch->indexlist #ifdef STANDALONE_SOLVER , "intersectional analysis," " %d in block %s vs column %d", n, usage->blocks->blocknames[b], 1+x #endif )) { diff = max(diff, DIFF_INTERSECT); goto cont; } } if (usage->diag) { /* * Intersectional analysis, \-diagonal vs blocks. */ for (b = 0; b < cr; b++) for (n = 1; n <= cr; n++) { if (usage->diag[n-1] || usage->blk[b*cr+n-1]) continue; for (i = 0; i < cr; i++) { scratch->indexlist[i] = cubepos2(diag0(i), n); scratch->indexlist2[i] = cubepos2(usage->blocks->blocks[b][i], n); } if (solver_intersect(usage, scratch->indexlist, scratch->indexlist2 #ifdef STANDALONE_SOLVER , "intersectional analysis," " %d in \\-diagonal vs block %s", n, usage->blocks->blocknames[b] #endif ) || solver_intersect(usage, scratch->indexlist2, scratch->indexlist #ifdef STANDALONE_SOLVER , "intersectional analysis," " %d in block %s vs \\-diagonal", n, usage->blocks->blocknames[b] #endif )) { diff = max(diff, DIFF_INTERSECT); goto cont; } } /* * Intersectional analysis, /-diagonal vs blocks. */ for (b = 0; b < cr; b++) for (n = 1; n <= cr; n++) { if (usage->diag[cr+n-1] || usage->blk[b*cr+n-1]) continue; for (i = 0; i < cr; i++) { scratch->indexlist[i] = cubepos2(diag1(i), n); scratch->indexlist2[i] = cubepos2(usage->blocks->blocks[b][i], n); } if (solver_intersect(usage, scratch->indexlist, scratch->indexlist2 #ifdef STANDALONE_SOLVER , "intersectional analysis," " %d in /-diagonal vs block %s", n, usage->blocks->blocknames[b] #endif ) || solver_intersect(usage, scratch->indexlist2, scratch->indexlist #ifdef STANDALONE_SOLVER , "intersectional analysis," " %d in block %s vs /-diagonal", n, usage->blocks->blocknames[b] #endif )) { diff = max(diff, DIFF_INTERSECT); goto cont; } } } if (dlev->maxdiff <= DIFF_INTERSECT) break; /* * Blockwise set elimination. */ for (b = 0; b < cr; b++) { for (i = 0; i < cr; i++) for (n = 1; n <= cr; n++) scratch->indexlist[i*cr+n-1] = cubepos2(usage->blocks->blocks[b][i], n); ret = solver_set(usage, scratch, scratch->indexlist #ifdef STANDALONE_SOLVER , "set elimination, block %s", usage->blocks->blocknames[b] #endif ); if (ret < 0) { diff = DIFF_IMPOSSIBLE; goto got_result; } else if (ret > 0) { diff = max(diff, DIFF_SET); goto cont; } } /* * Row-wise set elimination. */ for (y = 0; y < cr; y++) { for (x = 0; x < cr; x++) for (n = 1; n <= cr; n++) scratch->indexlist[x*cr+n-1] = cubepos(x, y, n); ret = solver_set(usage, scratch, scratch->indexlist #ifdef STANDALONE_SOLVER , "set elimination, row %d", 1+y #endif ); if (ret < 0) { diff = DIFF_IMPOSSIBLE; goto got_result; } else if (ret > 0) { diff = max(diff, DIFF_SET); goto cont; } } /* * Column-wise set elimination. */ for (x = 0; x < cr; x++) { for (y = 0; y < cr; y++) for (n = 1; n <= cr; n++) scratch->indexlist[y*cr+n-1] = cubepos(x, y, n); ret = solver_set(usage, scratch, scratch->indexlist #ifdef STANDALONE_SOLVER , "set elimination, column %d", 1+x #endif ); if (ret < 0) { diff = DIFF_IMPOSSIBLE; goto got_result; } else if (ret > 0) { diff = max(diff, DIFF_SET); goto cont; } } if (usage->diag) { /* * \-diagonal set elimination. */ for (i = 0; i < cr; i++) for (n = 1; n <= cr; n++) scratch->indexlist[i*cr+n-1] = cubepos2(diag0(i), n); ret = solver_set(usage, scratch, scratch->indexlist #ifdef STANDALONE_SOLVER , "set elimination, \\-diagonal" #endif ); if (ret < 0) { diff = DIFF_IMPOSSIBLE; goto got_result; } else if (ret > 0) { diff = max(diff, DIFF_SET); goto cont; } /* * /-diagonal set elimination. */ for (i = 0; i < cr; i++) for (n = 1; n <= cr; n++) scratch->indexlist[i*cr+n-1] = cubepos2(diag1(i), n); ret = solver_set(usage, scratch, scratch->indexlist #ifdef STANDALONE_SOLVER , "set elimination, /-diagonal" #endif ); if (ret < 0) { diff = DIFF_IMPOSSIBLE; goto got_result; } else if (ret > 0) { diff = max(diff, DIFF_SET); goto cont; } } if (dlev->maxdiff <= DIFF_SET) break; /* * Row-vs-column set elimination on a single number. */ for (n = 1; n <= cr; n++) { for (y = 0; y < cr; y++) for (x = 0; x < cr; x++) scratch->indexlist[y*cr+x] = cubepos(x, y, n); ret = solver_set(usage, scratch, scratch->indexlist #ifdef STANDALONE_SOLVER , "positional set elimination, number %d", n #endif ); if (ret < 0) { diff = DIFF_IMPOSSIBLE; goto got_result; } else if (ret > 0) { diff = max(diff, DIFF_EXTREME); goto cont; } } /* * Forcing chains. */ if (solver_forcing(usage, scratch)) { diff = max(diff, DIFF_EXTREME); goto cont; } /* * If we reach here, we have made no deductions in this * iteration, so the algorithm terminates. */ break; } /* * Last chance: if we haven't fully solved the puzzle yet, try * recursing based on guesses for a particular square. We pick * one of the most constrained empty squares we can find, which * has the effect of pruning the search tree as much as * possible. */ if (dlev->maxdiff >= DIFF_RECURSIVE) { int best, bestcount; best = -1; bestcount = cr+1; for (y = 0; y < cr; y++) for (x = 0; x < cr; x++) if (!grid[y*cr+x]) { int count; /* * An unfilled square. Count the number of * possible digits in it. */ count = 0; for (n = 1; n <= cr; n++) if (cube(x,y,n)) count++; /* * We should have found any impossibilities * already, so this can safely be an assert. */ assert(count > 1); if (count < bestcount) { bestcount = count; best = y*cr+x; } } if (best != -1) { int i, j; digit *list, *ingrid, *outgrid; diff = DIFF_IMPOSSIBLE; /* no solution found yet */ /* * Attempt recursion. */ y = best / cr; x = best % cr; list = snewn(cr, digit); ingrid = snewn(cr * cr, digit); outgrid = snewn(cr * cr, digit); memcpy(ingrid, grid, cr * cr); /* Make a list of the possible digits. */ for (j = 0, n = 1; n <= cr; n++) if (cube(x,y,n)) list[j++] = n; #ifdef STANDALONE_SOLVER if (solver_show_working) { char *sep = ""; printf("%*srecursing on (%d,%d) [", solver_recurse_depth*4, "", x + 1, y + 1); for (i = 0; i < j; i++) { printf("%s%d", sep, list[i]); sep = " or "; } printf("]\n"); } #endif /* * And step along the list, recursing back into the * main solver at every stage. */ for (i = 0; i < j; i++) { memcpy(outgrid, ingrid, cr * cr); outgrid[y*cr+x] = list[i]; #ifdef STANDALONE_SOLVER if (solver_show_working) printf("%*sguessing %d at (%d,%d)\n", solver_recurse_depth*4, "", list[i], x + 1, y + 1); solver_recurse_depth++; #endif solver(cr, blocks, kblocks, xtype, outgrid, kgrid, dlev); #ifdef STANDALONE_SOLVER solver_recurse_depth--; if (solver_show_working) { printf("%*sretracting %d at (%d,%d)\n", solver_recurse_depth*4, "", list[i], x + 1, y + 1); } #endif /* * If we have our first solution, copy it into the * grid we will return. */ if (diff == DIFF_IMPOSSIBLE && dlev->diff != DIFF_IMPOSSIBLE) memcpy(grid, outgrid, cr*cr); if (dlev->diff == DIFF_AMBIGUOUS) diff = DIFF_AMBIGUOUS; else if (dlev->diff == DIFF_IMPOSSIBLE) /* do not change our return value */; else { /* the recursion turned up exactly one solution */ if (diff == DIFF_IMPOSSIBLE) diff = DIFF_RECURSIVE; else diff = DIFF_AMBIGUOUS; } /* * As soon as we've found more than one solution, * give up immediately. */ if (diff == DIFF_AMBIGUOUS) break; } sfree(outgrid); sfree(ingrid); sfree(list); } } else { /* * We're forbidden to use recursion, so we just see whether * our grid is fully solved, and return DIFF_IMPOSSIBLE * otherwise. */ for (y = 0; y < cr; y++) for (x = 0; x < cr; x++) if (!grid[y*cr+x]) diff = DIFF_IMPOSSIBLE; } got_result: dlev->diff = diff; dlev->kdiff = kdiff; #ifdef STANDALONE_SOLVER if (solver_show_working) printf("%*s%s found\n", solver_recurse_depth*4, "", diff == DIFF_IMPOSSIBLE ? "no solution" : diff == DIFF_AMBIGUOUS ? "multiple solutions" : "one solution"); #endif sfree(usage->sq2region); sfree(usage->regions); sfree(usage->cube); sfree(usage->row); sfree(usage->col); sfree(usage->blk); if (usage->kblocks) { free_block_structure(usage->kblocks); free_block_structure(usage->extra_cages); sfree(usage->extra_clues); } if (usage->kclues) sfree(usage->kclues); sfree(usage); solver_free_scratch(scratch); } /* ---------------------------------------------------------------------- * End of solver code. */ /* ---------------------------------------------------------------------- * Killer set generator. */ /* ---------------------------------------------------------------------- * Solo filled-grid generator. * * This grid generator works by essentially trying to solve a grid * starting from no clues, and not worrying that there's more than * one possible solution. Unfortunately, it isn't computationally * feasible to do this by calling the above solver with an empty * grid, because that one needs to allocate a lot of scratch space * at every recursion level. Instead, I have a much simpler * algorithm which I shamelessly copied from a Python solver * written by Andrew Wilkinson (which is GPLed, but I've reused * only ideas and no code). It mostly just does the obvious * recursive thing: pick an empty square, put one of the possible * digits in it, recurse until all squares are filled, backtrack * and change some choices if necessary. * * The clever bit is that every time it chooses which square to * fill in next, it does so by counting the number of _possible_ * numbers that can go in each square, and it prioritises so that * it picks a square with the _lowest_ number of possibilities. The * idea is that filling in lots of the obvious bits (particularly * any squares with only one possibility) will cut down on the list * of possibilities for other squares and hence reduce the enormous * search space as much as possible as early as possible. * * The use of bit sets implies that we support puzzles up to a size of * 32x32 (less if anyone finds a 16-bit machine to compile this on). */ /* * Internal data structure used in gridgen to keep track of * progress. */ struct gridgen_coord { int x, y, r; }; struct gridgen_usage { int cr; struct block_structure *blocks, *kblocks; /* grid is a copy of the input grid, modified as we go along */ digit *grid; /* * Bitsets. In each of them, bit n is set if digit n has been placed * in the corresponding region. row, col and blk are used for all * puzzles. cge is used only for killer puzzles, and diag is used * only for x-type puzzles. * All of these have cr entries, except diag which only has 2, * and cge, which has as many entries as kblocks. */ unsigned int *row, *col, *blk, *cge, *diag; /* This lists all the empty spaces remaining in the grid. */ struct gridgen_coord *spaces; int nspaces; /* If we need randomisation in the solve, this is our random state. */ random_state *rs; }; static void gridgen_place(struct gridgen_usage *usage, int x, int y, digit n) { unsigned int bit = 1 << n; int cr = usage->cr; usage->row[y] |= bit; usage->col[x] |= bit; usage->blk[usage->blocks->whichblock[y*cr+x]] |= bit; if (usage->cge) usage->cge[usage->kblocks->whichblock[y*cr+x]] |= bit; if (usage->diag) { if (ondiag0(y*cr+x)) usage->diag[0] |= bit; if (ondiag1(y*cr+x)) usage->diag[1] |= bit; } usage->grid[y*cr+x] = n; } static void gridgen_remove(struct gridgen_usage *usage, int x, int y, digit n) { unsigned int mask = ~(1 << n); int cr = usage->cr; usage->row[y] &= mask; usage->col[x] &= mask; usage->blk[usage->blocks->whichblock[y*cr+x]] &= mask; if (usage->cge) usage->cge[usage->kblocks->whichblock[y*cr+x]] &= mask; if (usage->diag) { if (ondiag0(y*cr+x)) usage->diag[0] &= mask; if (ondiag1(y*cr+x)) usage->diag[1] &= mask; } usage->grid[y*cr+x] = 0; } #define N_SINGLE 32 /* * The real recursive step in the generating function. * * Return values: 1 means solution found, 0 means no solution * found on this branch. */ static int gridgen_real(struct gridgen_usage *usage, digit *grid, int *steps) { int cr = usage->cr; int i, j, n, sx, sy, bestm, bestr, ret; int *digits; unsigned int used; /* * Firstly, check for completion! If there are no spaces left * in the grid, we have a solution. */ if (usage->nspaces == 0) return TRUE; /* * Next, abandon generation if we went over our steps limit. */ if (*steps <= 0) return FALSE; (*steps)--; /* * Otherwise, there must be at least one space. Find the most * constrained space, using the `r' field as a tie-breaker. */ bestm = cr+1; /* so that any space will beat it */ bestr = 0; used = ~0; i = sx = sy = -1; for (j = 0; j < usage->nspaces; j++) { int x = usage->spaces[j].x, y = usage->spaces[j].y; unsigned int used_xy; int m; m = usage->blocks->whichblock[y*cr+x]; used_xy = usage->row[y] | usage->col[x] | usage->blk[m]; if (usage->cge != NULL) used_xy |= usage->cge[usage->kblocks->whichblock[y*cr+x]]; if (usage->cge != NULL) used_xy |= usage->cge[usage->kblocks->whichblock[y*cr+x]]; if (usage->diag != NULL) { if (ondiag0(y*cr+x)) used_xy |= usage->diag[0]; if (ondiag1(y*cr+x)) used_xy |= usage->diag[1]; } /* * Find the number of digits that could go in this space. */ m = 0; for (n = 1; n <= cr; n++) { unsigned int bit = 1 << n; if ((used_xy & bit) == 0) m++; } if (m < bestm || (m == bestm && usage->spaces[j].r < bestr)) { bestm = m; bestr = usage->spaces[j].r; sx = x; sy = y; i = j; used = used_xy; } } /* * Swap that square into the final place in the spaces array, * so that decrementing nspaces will remove it from the list. */ if (i != usage->nspaces-1) { struct gridgen_coord t; t = usage->spaces[usage->nspaces-1]; usage->spaces[usage->nspaces-1] = usage->spaces[i]; usage->spaces[i] = t; } /* * Now we've decided which square to start our recursion at, * simply go through all possible values, shuffling them * randomly first if necessary. */ digits = snewn(bestm, int); j = 0; for (n = 1; n <= cr; n++) { unsigned int bit = 1 << n; if ((used & bit) == 0) digits[j++] = n; } if (usage->rs) shuffle(digits, j, sizeof(*digits), usage->rs); /* And finally, go through the digit list and actually recurse. */ ret = FALSE; for (i = 0; i < j; i++) { n = digits[i]; /* Update the usage structure to reflect the placing of this digit. */ gridgen_place(usage, sx, sy, n); usage->nspaces--; /* Call the solver recursively. Stop when we find a solution. */ if (gridgen_real(usage, grid, steps)) { ret = TRUE; break; } /* Revert the usage structure. */ gridgen_remove(usage, sx, sy, n); usage->nspaces++; } sfree(digits); return ret; } /* * Entry point to generator. You give it parameters and a starting * grid, which is simply an array of cr*cr digits. */ static int gridgen(int cr, struct block_structure *blocks, struct block_structure *kblocks, int xtype, digit *grid, random_state *rs, int maxsteps) { struct gridgen_usage *usage; int x, y, ret; /* * Clear the grid to start with. */ memset(grid, 0, cr*cr); /* * Create a gridgen_usage structure. */ usage = snew(struct gridgen_usage); usage->cr = cr; usage->blocks = blocks; usage->grid = grid; usage->row = snewn(cr, unsigned int); usage->col = snewn(cr, unsigned int); usage->blk = snewn(cr, unsigned int); if (kblocks != NULL) { usage->kblocks = kblocks; usage->cge = snewn(usage->kblocks->nr_blocks, unsigned int); memset(usage->cge, FALSE, kblocks->nr_blocks * sizeof *usage->cge); } else { usage->cge = NULL; } memset(usage->row, 0, cr * sizeof *usage->row); memset(usage->col, 0, cr * sizeof *usage->col); memset(usage->blk, 0, cr * sizeof *usage->blk); if (xtype) { usage->diag = snewn(2, unsigned int); memset(usage->diag, 0, 2 * sizeof *usage->diag); } else { usage->diag = NULL; } /* * Begin by filling in the whole top row with randomly chosen * numbers. This cannot introduce any bias or restriction on * the available grids, since we already know those numbers * are all distinct so all we're doing is choosing their * labels. */ for (x = 0; x < cr; x++) grid[x] = x+1; shuffle(grid, cr, sizeof(*grid), rs); for (x = 0; x < cr; x++) gridgen_place(usage, x, 0, grid[x]); usage->spaces = snewn(cr * cr, struct gridgen_coord); usage->nspaces = 0; usage->rs = rs; /* * Initialise the list of grid spaces, taking care to leave * out the row I've already filled in above. */ for (y = 1; y < cr; y++) { for (x = 0; x < cr; x++) { usage->spaces[usage->nspaces].x = x; usage->spaces[usage->nspaces].y = y; usage->spaces[usage->nspaces].r = random_bits(rs, 31); usage->nspaces++; } } /* * Run the real generator function. */ ret = gridgen_real(usage, grid, &maxsteps); /* * Clean up the usage structure now we have our answer. */ sfree(usage->spaces); sfree(usage->cge); sfree(usage->blk); sfree(usage->col); sfree(usage->row); sfree(usage); return ret; } /* ---------------------------------------------------------------------- * End of grid generator code. */ /* * Check whether a grid contains a valid complete puzzle. */ static int check_valid(int cr, struct block_structure *blocks, struct block_structure *kblocks, int xtype, digit *grid) { unsigned char *used; int x, y, i, j, n; used = snewn(cr, unsigned char); /* * Check that each row contains precisely one of everything. */ for (y = 0; y < cr; y++) { memset(used, FALSE, cr); for (x = 0; x < cr; x++) if (grid[y*cr+x] > 0 && grid[y*cr+x] <= cr) used[grid[y*cr+x]-1] = TRUE; for (n = 0; n < cr; n++) if (!used[n]) { sfree(used); return FALSE; } } /* * Check that each column contains precisely one of everything. */ for (x = 0; x < cr; x++) { memset(used, FALSE, cr); for (y = 0; y < cr; y++) if (grid[y*cr+x] > 0 && grid[y*cr+x] <= cr) used[grid[y*cr+x]-1] = TRUE; for (n = 0; n < cr; n++) if (!used[n]) { sfree(used); return FALSE; } } /* * Check that each block contains precisely one of everything. */ for (i = 0; i < cr; i++) { memset(used, FALSE, cr); for (j = 0; j < cr; j++) if (grid[blocks->blocks[i][j]] > 0 && grid[blocks->blocks[i][j]] <= cr) used[grid[blocks->blocks[i][j]]-1] = TRUE; for (n = 0; n < cr; n++) if (!used[n]) { sfree(used); return FALSE; } } /* * Check that each Killer cage, if any, contains at most one of * everything. */ if (kblocks) { for (i = 0; i < kblocks->nr_blocks; i++) { memset(used, FALSE, cr); for (j = 0; j < kblocks->nr_squares[i]; j++) if (grid[kblocks->blocks[i][j]] > 0 && grid[kblocks->blocks[i][j]] <= cr) { if (used[grid[kblocks->blocks[i][j]]-1]) { sfree(used); return FALSE; } used[grid[kblocks->blocks[i][j]]-1] = TRUE; } } } /* * Check that each diagonal contains precisely one of everything. */ if (xtype) { memset(used, FALSE, cr); for (i = 0; i < cr; i++) if (grid[diag0(i)] > 0 && grid[diag0(i)] <= cr) used[grid[diag0(i)]-1] = TRUE; for (n = 0; n < cr; n++) if (!used[n]) { sfree(used); return FALSE; } for (i = 0; i < cr; i++) if (grid[diag1(i)] > 0 && grid[diag1(i)] <= cr) used[grid[diag1(i)]-1] = TRUE; for (n = 0; n < cr; n++) if (!used[n]) { sfree(used); return FALSE; } } sfree(used); return TRUE; } static int symmetries(const game_params *params, int x, int y, int *output, int s) { int c = params->c, r = params->r, cr = c*r; int i = 0; #define ADD(x,y) (*output++ = (x), *output++ = (y), i++) ADD(x, y); switch (s) { case SYMM_NONE: break; /* just x,y is all we need */ case SYMM_ROT2: ADD(cr - 1 - x, cr - 1 - y); break; case SYMM_ROT4: ADD(cr - 1 - y, x); ADD(y, cr - 1 - x); ADD(cr - 1 - x, cr - 1 - y); break; case SYMM_REF2: ADD(cr - 1 - x, y); break; case SYMM_REF2D: ADD(y, x); break; case SYMM_REF4: ADD(cr - 1 - x, y); ADD(x, cr - 1 - y); ADD(cr - 1 - x, cr - 1 - y); break; case SYMM_REF4D: ADD(y, x); ADD(cr - 1 - x, cr - 1 - y); ADD(cr - 1 - y, cr - 1 - x); break; case SYMM_REF8: ADD(cr - 1 - x, y); ADD(x, cr - 1 - y); ADD(cr - 1 - x, cr - 1 - y); ADD(y, x); ADD(y, cr - 1 - x); ADD(cr - 1 - y, x); ADD(cr - 1 - y, cr - 1 - x); break; } #undef ADD return i; } static char *encode_solve_move(int cr, digit *grid) { int i, len; char *ret, *p, *sep; /* * It's surprisingly easy to work out _exactly_ how long this * string needs to be. To decimal-encode all the numbers from 1 * to n: * * - every number has a units digit; total is n. * - all numbers above 9 have a tens digit; total is max(n-9,0). * - all numbers above 99 have a hundreds digit; total is max(n-99,0). * - and so on. */ len = 0; for (i = 1; i <= cr; i *= 10) len += max(cr - i + 1, 0); len += cr; /* don't forget the commas */ len *= cr; /* there are cr rows of these */ /* * Now len is one bigger than the total size of the * comma-separated numbers (because we counted an * additional leading comma). We need to have a leading S * and a trailing NUL, so we're off by one in total. */ len++; ret = snewn(len, char); p = ret; *p++ = 'S'; sep = ""; for (i = 0; i < cr*cr; i++) { p += sprintf(p, "%s%d", sep, grid[i]); sep = ","; } *p++ = '\0'; assert(p - ret == len); return ret; } static void dsf_to_blocks(int *dsf, struct block_structure *blocks, int min_expected, int max_expected) { int cr = blocks->c * blocks->r, area = cr * cr; int i, nb = 0; for (i = 0; i < area; i++) blocks->whichblock[i] = -1; for (i = 0; i < area; i++) { int j = dsf_canonify(dsf, i); if (blocks->whichblock[j] < 0) blocks->whichblock[j] = nb++; blocks->whichblock[i] = blocks->whichblock[j]; } assert(nb >= min_expected && nb <= max_expected); blocks->nr_blocks = nb; } static void make_blocks_from_whichblock(struct block_structure *blocks) { int i; for (i = 0; i < blocks->nr_blocks; i++) { blocks->blocks[i][blocks->max_nr_squares-1] = 0; blocks->nr_squares[i] = 0; } for (i = 0; i < blocks->area; i++) { int b = blocks->whichblock[i]; int j = blocks->blocks[b][blocks->max_nr_squares-1]++; assert(j < blocks->max_nr_squares); blocks->blocks[b][j] = i; blocks->nr_squares[b]++; } } static char *encode_block_structure_desc(char *p, struct block_structure *blocks) { int i, currrun = 0; int c = blocks->c, r = blocks->r, cr = c * r; /* * Encode the block structure. We do this by encoding * the pattern of dividing lines: first we iterate * over the cr*(cr-1) internal vertical grid lines in * ordinary reading order, then over the cr*(cr-1) * internal horizontal ones in transposed reading * order. * * We encode the number of non-lines between the * lines; _ means zero (two adjacent divisions), a * means 1, ..., y means 25, and z means 25 non-lines * _and no following line_ (so that za means 26, zb 27 * etc). */ for (i = 0; i <= 2*cr*(cr-1); i++) { int x, y, p0, p1, edge; if (i == 2*cr*(cr-1)) { edge = TRUE; /* terminating virtual edge */ } else { if (i < cr*(cr-1)) { y = i/(cr-1); x = i%(cr-1); p0 = y*cr+x; p1 = y*cr+x+1; } else { x = i/(cr-1) - cr; y = i%(cr-1); p0 = y*cr+x; p1 = (y+1)*cr+x; } edge = (blocks->whichblock[p0] != blocks->whichblock[p1]); } if (edge) { while (currrun > 25) *p++ = 'z', currrun -= 25; if (currrun) *p++ = 'a'-1 + currrun; else *p++ = '_'; currrun = 0; } else currrun++; } return p; } 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; } /* * Conservatively stimate the number of characters required for * encoding a grid of a certain area. */ static int grid_encode_space (int area) { int t, count; for (count = 1, t = area; t > 26; t -= 26) count++; return count * area; } /* * Conservatively stimate the number of characters required for * encoding a given blocks structure. */ static int blocks_encode_space(struct block_structure *blocks) { int cr = blocks->c * blocks->r, area = cr * cr; return grid_encode_space(area); } static char *encode_puzzle_desc(const game_params *params, digit *grid, struct block_structure *blocks, digit *kgrid, struct block_structure *kblocks) { int c = params->c, r = params->r, cr = c*r; int area = cr*cr; char *p, *desc; int space; space = grid_encode_space(area) + 1; if (r == 1) space += blocks_encode_space(blocks) + 1; if (params->killer) { space += blocks_encode_space(kblocks) + 1; space += grid_encode_space(area) + 1; } desc = snewn(space, char); p = encode_grid(desc, grid, area); if (r == 1) { *p++ = ','; p = encode_block_structure_desc(p, blocks); } if (params->killer) { *p++ = ','; p = encode_block_structure_desc(p, kblocks); *p++ = ','; p = encode_grid(p, kgrid, area); } assert(p - desc < space); *p++ = '\0'; desc = sresize(desc, p - desc, char); return desc; } static void merge_blocks(struct block_structure *b, int n1, int n2) { int i; /* Move data towards the lower block number. */ if (n2 < n1) { int t = n2; n2 = n1; n1 = t; } /* Merge n2 into n1, and move the last block into n2's position. */ for (i = 0; i < b->nr_squares[n2]; i++) b->whichblock[b->blocks[n2][i]] = n1; memcpy(b->blocks[n1] + b->nr_squares[n1], b->blocks[n2], b->nr_squares[n2] * sizeof **b->blocks); b->nr_squares[n1] += b->nr_squares[n2]; n1 = b->nr_blocks - 1; if (n2 != n1) { memcpy(b->blocks[n2], b->blocks[n1], b->nr_squares[n1] * sizeof **b->blocks); for (i = 0; i < b->nr_squares[n1]; i++) b->whichblock[b->blocks[n1][i]] = n2; b->nr_squares[n2] = b->nr_squares[n1]; } b->nr_blocks = n1; } static int merge_some_cages(struct block_structure *b, int cr, int area, digit *grid, random_state *rs) { /* * Make a list of all the pairs of adjacent blocks. */ int i, j, k; struct pair { int b1, b2; } *pairs; int npairs; pairs = snewn(b->nr_blocks * b->nr_blocks, struct pair); npairs = 0; for (i = 0; i < b->nr_blocks; i++) { for (j = i+1; j < b->nr_blocks; j++) { /* * Rule the merger out of consideration if it's * obviously not viable. */ if (b->nr_squares[i] + b->nr_squares[j] > b->max_nr_squares) continue; /* we couldn't merge these anyway */ /* * See if these two blocks have a pair of squares * adjacent to each other. */ for (k = 0; k < b->nr_squares[i]; k++) { int xy = b->blocks[i][k]; int y = xy / cr, x = xy % cr; if ((y > 0 && b->whichblock[xy - cr] == j) || (y+1 < cr && b->whichblock[xy + cr] == j) || (x > 0 && b->whichblock[xy - 1] == j) || (x+1 < cr && b->whichblock[xy + 1] == j)) { /* * Yes! Add this pair to our list. */ pairs[npairs].b1 = i; pairs[npairs].b2 = j; break; } } } } /* * Now go through that list in random order until we find a pair * of blocks we can merge. */ while (npairs > 0) { int n1, n2; unsigned int digits_found; /* * Pick a random pair, and remove it from the list. */ i = random_upto(rs, npairs); n1 = pairs[i].b1; n2 = pairs[i].b2; if (i != npairs-1) pairs[i] = pairs[npairs-1]; npairs--; /* Guarantee that the merged cage would still be a region. */ digits_found = 0; for (i = 0; i < b->nr_squares[n1]; i++) digits_found |= 1 << grid[b->blocks[n1][i]]; for (i = 0; i < b->nr_squares[n2]; i++) if (digits_found & (1 << grid[b->blocks[n2][i]])) break; if (i != b->nr_squares[n2]) continue; /* * Got one! Do the merge. */ merge_blocks(b, n1, n2); sfree(pairs); return TRUE; } sfree(pairs); return FALSE; } static void compute_kclues(struct block_structure *cages, digit *kclues, digit *grid, int area) { int i; memset(kclues, 0, area * sizeof *kclues); for (i = 0; i < cages->nr_blocks; i++) { int j, sum = 0; for (j = 0; j < area; j++) if (cages->whichblock[j] == i) sum += grid[j]; for (j = 0; j < area; j++) if (cages->whichblock[j] == i) break; assert (j != area); kclues[j] = sum; } } static struct block_structure *gen_killer_cages(int cr, random_state *rs, int remove_singletons) { int nr; int x, y, area = cr * cr; int n_singletons = 0; struct block_structure *b = alloc_block_structure (1, cr, area, cr, area); for (x = 0; x < area; x++) b->whichblock[x] = -1; nr = 0; for (y = 0; y < cr; y++) for (x = 0; x < cr; x++) { int rnd; int xy = y*cr+x; if (b->whichblock[xy] != -1) continue; b->whichblock[xy] = nr; rnd = random_bits(rs, 4); if (xy + 1 < area && (rnd >= 4 || (!remove_singletons && rnd >= 1))) { int xy2 = xy + 1; if (x + 1 == cr || b->whichblock[xy2] != -1 || (xy + cr < area && random_bits(rs, 1) == 0)) xy2 = xy + cr; if (xy2 >= area) n_singletons++; else b->whichblock[xy2] = nr; } else n_singletons++; nr++; } b->nr_blocks = nr; make_blocks_from_whichblock(b); for (x = y = 0; x < b->nr_blocks; x++) if (b->nr_squares[x] == 1) y++; assert(y == n_singletons); if (n_singletons > 0 && remove_singletons) { int n; for (n = 0; n < b->nr_blocks;) { int xy, x, y, xy2, other; if (b->nr_squares[n] > 1) { n++; continue; } xy = b->blocks[n][0]; x = xy % cr; y = xy / cr; if (xy + 1 == area) xy2 = xy - 1; else if (x + 1 < cr && (y + 1 == cr || random_bits(rs, 1) == 0)) xy2 = xy + 1; else xy2 = xy + cr; other = b->whichblock[xy2]; if (b->nr_squares[other] == 1) n_singletons--; n_singletons--; merge_blocks(b, n, other); if (n < other) n++; } assert(n_singletons == 0); } return b; } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { int c = params->c, r = params->r, cr = c*r; int area = cr*cr; struct block_structure *blocks, *kblocks; digit *grid, *grid2, *kgrid; struct xy { int x, y; } *locs; int nlocs; char *desc; int coords[16], ncoords; int x, y, i, j; struct difficulty dlev; precompute_sum_bits(); /* * Adjust the maximum difficulty level to be consistent with * the puzzle size: all 2x2 puzzles appear to be Trivial * (DIFF_BLOCK) so we cannot hold out for even a Basic * (DIFF_SIMPLE) one. */ dlev.maxdiff = params->diff; dlev.maxkdiff = params->kdiff; if (c == 2 && r == 2) dlev.maxdiff = DIFF_BLOCK; grid = snewn(area, digit); locs = snewn(area, struct xy); grid2 = snewn(area, digit); blocks = alloc_block_structure (c, r, area, cr, cr); kblocks = NULL; kgrid = (params->killer) ? snewn(area, digit) : NULL; #ifdef STANDALONE_SOLVER assert(!"This should never happen, so we don't need to create blocknames"); #endif /* * Loop until we get a grid of the required difficulty. This is * nasty, but it seems to be unpleasantly hard to generate * difficult grids otherwise. */ while (1) { /* * Generate a random solved state, starting by * constructing the block structure. */ if (r == 1) { /* jigsaw mode */ int *dsf = divvy_rectangle(cr, cr, cr, rs); dsf_to_blocks (dsf, blocks, cr, cr); sfree(dsf); } else { /* basic Sudoku mode */ for (y = 0; y < cr; y++) for (x = 0; x < cr; x++) blocks->whichblock[y*cr+x] = (y/c) * c + (x/r); } make_blocks_from_whichblock(blocks); if (params->killer) { if (kblocks) free_block_structure(kblocks); kblocks = gen_killer_cages(cr, rs, params->kdiff > DIFF_KSINGLE); } if (!gridgen(cr, blocks, kblocks, params->xtype, grid, rs, area*area)) continue; assert(check_valid(cr, blocks, kblocks, params->xtype, grid)); /* * Save the solved grid in aux. */ { /* * We might already have written *aux the last time we * went round this loop, in which case we should free * the old aux before overwriting it with the new one. */ if (*aux) { sfree(*aux); } *aux = encode_solve_move(cr, grid); } /* * Now we have a solved grid. For normal puzzles, we start removing * things from it while preserving solubility. Killer puzzles are * different: we just pass the empty grid to the solver, and use * the puzzle if it comes back solved. */ if (params->killer) { struct block_structure *good_cages = NULL; struct block_structure *last_cages = NULL; int ntries = 0; memcpy(grid2, grid, area); for (;;) { compute_kclues(kblocks, kgrid, grid2, area); memset(grid, 0, area * sizeof *grid); solver(cr, blocks, kblocks, params->xtype, grid, kgrid, &dlev); if (dlev.diff == dlev.maxdiff && dlev.kdiff == dlev.maxkdiff) { /* * We have one that matches our difficulty. Store it for * later, but keep going. */ if (good_cages) free_block_structure(good_cages); ntries = 0; good_cages = dup_block_structure(kblocks); if (!merge_some_cages(kblocks, cr, area, grid2, rs)) break; } else if (dlev.diff > dlev.maxdiff || dlev.kdiff > dlev.maxkdiff) { /* * Give up after too many tries and either use the good one we * found, or generate a new grid. */ if (++ntries > 50) break; /* * The difficulty level got too high. If we have a good * one, use it, otherwise go back to the last one that * was at a lower difficulty and restart the process from * there. */ if (good_cages != NULL) { free_block_structure(kblocks); kblocks = dup_block_structure(good_cages); if (!merge_some_cages(kblocks, cr, area, grid2, rs)) break; } else { if (last_cages == NULL) break; free_block_structure(kblocks); kblocks = last_cages; last_cages = NULL; } } else { if (last_cages) free_block_structure(last_cages); last_cages = dup_block_structure(kblocks); if (!merge_some_cages(kblocks, cr, area, grid2, rs)) break; } } if (last_cages) free_block_structure(last_cages); if (good_cages != NULL) { free_block_structure(kblocks); kblocks = good_cages; compute_kclues(kblocks, kgrid, grid2, area); memset(grid, 0, area * sizeof *grid); break; } continue; } /* * Find the set of equivalence classes of squares permitted * by the selected symmetry. We do this by enumerating all * the grid squares which have no symmetric companion * sorting lower than themselves. */ nlocs = 0; for (y = 0; y < cr; y++) for (x = 0; x < cr; x++) { int i = y*cr+x; int j; ncoords = symmetries(params, x, y, coords, params->symm); for (j = 0; j < ncoords; j++) if (coords[2*j+1]*cr+coords[2*j] < i) break; if (j == ncoords) { locs[nlocs].x = x; locs[nlocs].y = y; nlocs++; } } /* * Now shuffle that list. */ shuffle(locs, nlocs, sizeof(*locs), rs); /* * Now loop over the shuffled list and, for each element, * see whether removing that element (and its reflections) * from the grid will still leave the grid soluble. */ for (i = 0; i < nlocs; i++) { x = locs[i].x; y = locs[i].y; memcpy(grid2, grid, area); ncoords = symmetries(params, x, y, coords, params->symm); for (j = 0; j < ncoords; j++) grid2[coords[2*j+1]*cr+coords[2*j]] = 0; solver(cr, blocks, kblocks, params->xtype, grid2, kgrid, &dlev); if (dlev.diff <= dlev.maxdiff && (!params->killer || dlev.kdiff <= dlev.maxkdiff)) { for (j = 0; j < ncoords; j++) grid[coords[2*j+1]*cr+coords[2*j]] = 0; } } memcpy(grid2, grid, area); solver(cr, blocks, kblocks, params->xtype, grid2, kgrid, &dlev); if (dlev.diff == dlev.maxdiff && (!params->killer || dlev.kdiff == dlev.maxkdiff)) break; /* found one! */ } sfree(grid2); sfree(locs); /* * Now we have the grid as it will be presented to the user. * Encode it in a game desc. */ desc = encode_puzzle_desc(params, grid, blocks, kgrid, kblocks); sfree(grid); free_block_structure(blocks); if (params->killer) { free_block_structure(kblocks); sfree(kgrid); } return desc; } 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; } /* * Create a DSF from a spec found in *pdesc. Update this to point past the * end of the block spec, and return an error string or NULL if everything * is OK. The DSF is stored in *PDSF. */ static char *spec_to_dsf(const char **pdesc, int **pdsf, int cr, int area) { const char *desc = *pdesc; int pos = 0; int *dsf; *pdsf = dsf = snew_dsf(area); while (*desc && *desc != ',') { int c, adv; if (*desc == '_') c = 0; else if (*desc >= 'a' && *desc <= 'z') c = *desc - 'a' + 1; else { sfree(dsf); return "Invalid character in game description"; } desc++; adv = (c != 25); /* 'z' is a special case */ while (c-- > 0) { int p0, p1; /* * Non-edge; merge the two dsf classes on either * side of it. */ assert(pos < 2*cr*(cr-1)); if (pos < cr*(cr-1)) { int y = pos/(cr-1); int x = pos%(cr-1); p0 = y*cr+x; p1 = y*cr+x+1; } else { int x = pos/(cr-1) - cr; int y = pos%(cr-1); p0 = y*cr+x; p1 = (y+1)*cr+x; } dsf_merge(dsf, p0, p1); pos++; } if (adv) pos++; } *pdesc = desc; /* * When desc is exhausted, we expect to have gone exactly * one space _past_ the end of the grid, due to the dummy * edge at the end. */ if (pos != 2*cr*(cr-1)+1) { sfree(dsf); return "Not enough data in block structure specification"; } return NULL; } static 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 char *validate_block_desc(const char **pdesc, int cr, int area, int min_nr_blocks, int max_nr_blocks, int min_nr_squares, int max_nr_squares) { char *err; int *dsf; err = spec_to_dsf(pdesc, &dsf, cr, area); if (err) { return err; } if (min_nr_squares == max_nr_squares) { assert(min_nr_blocks == max_nr_blocks); assert(min_nr_blocks * min_nr_squares == area); } /* * Now we've got our dsf. Verify that it matches * expectations. */ { int *canons, *counts; int i, j, c, ncanons = 0; canons = snewn(max_nr_blocks, int); counts = snewn(max_nr_blocks, int); for (i = 0; i < area; i++) { j = dsf_canonify(dsf, i); for (c = 0; c < ncanons; c++) if (canons[c] == j) { counts[c]++; if (counts[c] > max_nr_squares) { sfree(dsf); sfree(canons); sfree(counts); return "A jigsaw block is too big"; } break; } if (c == ncanons) { if (ncanons >= max_nr_blocks) { sfree(dsf); sfree(canons); sfree(counts); return "Too many distinct jigsaw blocks"; } canons[ncanons] = j; counts[ncanons] = 1; ncanons++; } } if (ncanons < min_nr_blocks) { sfree(dsf); sfree(canons); sfree(counts); return "Not enough distinct jigsaw blocks"; } for (c = 0; c < ncanons; c++) { if (counts[c] < min_nr_squares) { sfree(dsf); sfree(canons); sfree(counts); return "A jigsaw block is too small"; } } sfree(canons); sfree(counts); } sfree(dsf); return NULL; } static char *validate_desc(const game_params *params, const char *desc) { int cr = params->c * params->r, area = cr*cr; char *err; err = validate_grid_desc(&desc, cr, area); if (err) return err; if (params->r == 1) { /* * Now we expect a suffix giving the jigsaw block * structure. Parse it and validate that it divides the * grid into the right number of regions which are the * right size. */ if (*desc != ',') return "Expected jigsaw block structure in game description"; desc++; err = validate_block_desc(&desc, cr, area, cr, cr, cr, cr); if (err) return err; } if (params->killer) { if (*desc != ',') return "Expected killer block structure in game description"; desc++; err = validate_block_desc(&desc, cr, area, cr, area, 2, cr); if (err) return err; if (*desc != ',') return "Expected killer clue grid in game description"; desc++; err = validate_grid_desc(&desc, cr * area, area); if (err) return err; } if (*desc) return "Unexpected data at end of game description"; return NULL; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { game_state *state = snew(game_state); int c = params->c, r = params->r, cr = c*r, area = cr * cr; int i; precompute_sum_bits(); state->cr = cr; state->xtype = params->xtype; state->killer = params->killer; state->grid = snewn(area, digit); state->pencil = snewn(area * cr, unsigned char); memset(state->pencil, 0, area * cr); state->immutable = snewn(area, unsigned char); memset(state->immutable, FALSE, area); state->blocks = alloc_block_structure (c, r, area, cr, cr); if (params->killer) { state->kblocks = alloc_block_structure (c, r, area, cr, area); state->kgrid = snewn(area, digit); } else { state->kblocks = NULL; state->kgrid = NULL; } state->completed = state->cheated = FALSE; desc = spec_to_grid(desc, state->grid, area); for (i = 0; i < area; i++) if (state->grid[i] != 0) state->immutable[i] = TRUE; if (r == 1) { char *err; int *dsf; assert(*desc == ','); desc++; err = spec_to_dsf(&desc, &dsf, cr, area); assert(err == NULL); dsf_to_blocks(dsf, state->blocks, cr, cr); sfree(dsf); } else { int x, y; for (y = 0; y < cr; y++) for (x = 0; x < cr; x++) state->blocks->whichblock[y*cr+x] = (y/c) * c + (x/r); } make_blocks_from_whichblock(state->blocks); if (params->killer) { char *err; int *dsf; assert(*desc == ','); desc++; err = spec_to_dsf(&desc, &dsf, cr, area); assert(err == NULL); dsf_to_blocks(dsf, state->kblocks, cr, area); sfree(dsf); make_blocks_from_whichblock(state->kblocks); assert(*desc == ','); desc++; desc = spec_to_grid(desc, state->kgrid, area); } assert(!*desc); #ifdef STANDALONE_SOLVER /* * Set up the block names for solver diagnostic output. */ { char *p = (char *)(state->blocks->blocknames + cr); if (r == 1) { for (i = 0; i < area; i++) { int j = state->blocks->whichblock[i]; if (!state->blocks->blocknames[j]) { state->blocks->blocknames[j] = p; p += 1 + sprintf(p, "starting at (%d,%d)", 1 + i%cr, 1 + i/cr); } } } else { int bx, by; for (by = 0; by < r; by++) for (bx = 0; bx < c; bx++) { state->blocks->blocknames[by*c+bx] = p; p += 1 + sprintf(p, "(%d,%d)", bx+1, by+1); } } assert(p - (char *)state->blocks->blocknames < (int)(cr*(sizeof(char *)+80))); for (i = 0; i < cr; i++) assert(state->blocks->blocknames[i]); } #endif return state; } static game_state *dup_game(const game_state *state) { game_state *ret = snew(game_state); int cr = state->cr, area = cr * cr; ret->cr = state->cr; ret->xtype = state->xtype; ret->killer = state->killer; ret->blocks = state->blocks; ret->blocks->refcount++; ret->kblocks = state->kblocks; if (ret->kblocks) ret->kblocks->refcount++; ret->grid = snewn(area, digit); memcpy(ret->grid, state->grid, area); if (state->killer) { ret->kgrid = snewn(area, digit); memcpy(ret->kgrid, state->kgrid, area); } else ret->kgrid = NULL; ret->pencil = snewn(area * cr, unsigned char); memcpy(ret->pencil, state->pencil, area * cr); ret->immutable = snewn(area, unsigned char); memcpy(ret->immutable, state->immutable, area); ret->completed = state->completed; ret->cheated = state->cheated; return ret; } static void free_game(game_state *state) { free_block_structure(state->blocks); if (state->kblocks) free_block_structure(state->kblocks); sfree(state->immutable); sfree(state->pencil); sfree(state->grid); if (state->kgrid) sfree(state->kgrid); sfree(state); } static char *solve_game(const game_state *state, const game_state *currstate, const char *ai, char **error) { int cr = state->cr; char *ret; digit *grid; struct difficulty dlev; /* * If we already have the solution in ai, save ourselves some * time. */ if (ai) return dupstr(ai); grid = snewn(cr*cr, digit); memcpy(grid, state->grid, cr*cr); dlev.maxdiff = DIFF_RECURSIVE; dlev.maxkdiff = DIFF_KINTERSECT; solver(cr, state->blocks, state->kblocks, state->xtype, grid, state->kgrid, &dlev); *error = NULL; if (dlev.diff == DIFF_IMPOSSIBLE) *error = "No solution exists for this puzzle"; else if (dlev.diff == DIFF_AMBIGUOUS) *error = "Multiple solutions exist for this puzzle"; if (*error) { sfree(grid); return NULL; } ret = encode_solve_move(cr, grid); sfree(grid); return ret; } static char *grid_text_format(int cr, struct block_structure *blocks, int xtype, digit *grid) { int vmod, hmod; int x, y; int totallen, linelen, nlines; char *ret, *p, ch; /* * For non-jigsaw Sudoku, we format in the way we always have, * by having the digits unevenly spaced so that the dividing * lines can fit in: * * . . | . . * . . | . . * ----+---- * . . | . . * . . | . . * * For jigsaw puzzles, however, we must leave space between * _all_ pairs of digits for an optional dividing line, so we * have to move to the rather ugly * * . . . . * ------+------ * . . | . . * +---+ * . . | . | . * ------+ | * . . . | . * * We deal with both cases using the same formatting code; we * simply invent a vmod value such that there's a vertical * dividing line before column i iff i is divisible by vmod * (so it's r in the first case and 1 in the second), and hmod * likewise for horizontal dividing lines. */ if (blocks->r != 1) { vmod = blocks->r; hmod = blocks->c; } else { vmod = hmod = 1; } /* * Line length: we have cr digits, each with a space after it, * and (cr-1)/vmod dividing lines, each with a space after it. * The final space is replaced by a newline, but that doesn't * affect the length. */ linelen = 2*(cr + (cr-1)/vmod); /* * Number of lines: we have cr rows of digits, and (cr-1)/hmod * dividing rows. */ nlines = cr + (cr-1)/hmod; /* * Allocate the space. */ totallen = linelen * nlines; ret = snewn(totallen+1, char); /* leave room for terminating NUL */ /* * Write the text. */ p = ret; for (y = 0; y < cr; y++) { /* * Row of digits. */ for (x = 0; x < cr; x++) { /* * Digit. */ digit d = grid[y*cr+x]; if (d == 0) { /* * Empty space: we usually write a dot, but we'll * highlight spaces on the X-diagonals (in X mode) * by using underscores instead. */ if (xtype && (ondiag0(y*cr+x) || ondiag1(y*cr+x))) ch = '_'; else ch = '.'; } else if (d <= 9) { ch = '0' + d; } else { ch = 'a' + d-10; } *p++ = ch; if (x == cr-1) { *p++ = '\n'; continue; } *p++ = ' '; if ((x+1) % vmod) continue; /* * Optional dividing line. */ if (blocks->whichblock[y*cr+x] != blocks->whichblock[y*cr+x+1]) ch = '|'; else ch = ' '; *p++ = ch; *p++ = ' '; } if (y == cr-1 || (y+1) % hmod) continue; /* * Dividing row. */ for (x = 0; x < cr; x++) { int dwid; int tl, tr, bl, br; /* * Division between two squares. This varies * complicatedly in length. */ dwid = 2; /* digit and its following space */ if (x == cr-1) dwid--; /* no following space at end of line */ if (x > 0 && x % vmod == 0) dwid++; /* preceding space after a divider */ if (blocks->whichblock[y*cr+x] != blocks->whichblock[(y+1)*cr+x]) ch = '-'; else ch = ' '; while (dwid-- > 0) *p++ = ch; if (x == cr-1) { *p++ = '\n'; break; } if ((x+1) % vmod) continue; /* * Corner square. This is: * - a space if all four surrounding squares are in * the same block * - a vertical line if the two left ones are in one * block and the two right in another * - a horizontal line if the two top ones are in one * block and the two bottom in another * - a plus sign in all other cases. (If we had a * richer character set available we could break * this case up further by doing fun things with * line-drawing T-pieces.) */ tl = blocks->whichblock[y*cr+x]; tr = blocks->whichblock[y*cr+x+1]; bl = blocks->whichblock[(y+1)*cr+x]; br = blocks->whichblock[(y+1)*cr+x+1]; if (tl == tr && tr == bl && bl == br) ch = ' '; else if (tl == bl && tr == br) ch = '|'; else if (tl == tr && bl == br) ch = '-'; else ch = '+'; *p++ = ch; } } assert(p - ret == totallen); *p = '\0'; return ret; } static int game_can_format_as_text_now(const game_params *params) { /* * Formatting Killer puzzles as text is currently unsupported. I * can't think of any sensible way of doing it which doesn't * involve expanding the puzzle to such a large scale as to make * it unusable. */ if (params->killer) return FALSE; return TRUE; } static char *game_text_format(const game_state *state) { assert(!state->kblocks); return grid_text_format(state->cr, state->blocks, state->xtype, state->grid); } struct game_ui { /* * These are the coordinates of the currently highlighted * square on the grid, if hshow = 1. */ int hx, hy; /* * This indicates whether the current highlight is a * pencil-mark one or a real one. */ int 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. */ int 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. */ int hcursor; }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->hx = ui->hy = 0; ui->hpencil = ui->hshow = ui->hcursor = 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 cr = newstate->cr; /* * 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 * cr + ui->hx] != 0) { ui->hshow = 0; } } struct game_drawstate { int started; int cr, xtype; int tilesize; digit *grid; unsigned char *pencil; unsigned char *hl; /* This is scratch space used within a single call to game_redraw. */ int nregions, *entered_items; }; static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { int cr = state->cr; int tx, ty; char buf[80]; button &= ~MOD_MASK; tx = (x + TILE_SIZE - BORDER) / TILE_SIZE - 1; ty = (y + TILE_SIZE - BORDER) / TILE_SIZE - 1; if (tx >= 0 && tx < cr && ty >= 0 && ty < cr) { if (button == LEFT_BUTTON) { if (state->immutable[ty*cr+tx]) { ui->hshow = 0; } else if (tx == ui->hx && ty == ui->hy && ui->hshow && ui->hpencil == 0) { ui->hshow = 0; } else { ui->hx = tx; ui->hy = ty; ui->hshow = 1; ui->hpencil = 0; } ui->hcursor = 0; return ""; /* UI activity occurred */ } if (button == RIGHT_BUTTON) { /* * Pencil-mode highlighting for non filled squares. */ if (state->grid[ty*cr+tx] == 0) { if (tx == ui->hx && ty == ui->hy && ui->hshow && ui->hpencil) { ui->hshow = 0; } else { ui->hpencil = 1; ui->hx = tx; ui->hy = ty; ui->hshow = 1; } } else { ui->hshow = 0; } ui->hcursor = 0; return ""; /* UI activity occurred */ } } if (IS_CURSOR_MOVE(button)) { move_cursor(button, &ui->hx, &ui->hy, cr, cr, 0); ui->hshow = ui->hcursor = 1; return ""; } if (ui->hshow && (button == CURSOR_SELECT)) { ui->hpencil = 1 - ui->hpencil; ui->hcursor = 1; return ""; } if (ui->hshow && ((button >= '0' && button <= '9' && button - '0' <= cr) || (button >= 'a' && button <= 'z' && button - 'a' + 10 <= cr) || (button >= 'A' && button <= 'Z' && button - 'A' + 10 <= cr) || button == CURSOR_SELECT2 || button == '\b')) { int n = button - '0'; if (button >= 'A' && button <= 'Z') n = button - 'A' + 10; if (button >= 'a' && button <= 'z') n = button - 'a' + 10; if (button == CURSOR_SELECT2 || button == '\b') n = 0; /* * Can't overwrite this square. This can only happen here * if we're using the cursor keys. */ if (state->immutable[ui->hy*cr+ui->hx]) return NULL; /* * Can't make pencil marks in a filled square. Again, this * can only become highlighted if we're using cursor keys. */ if (ui->hpencil && state->grid[ui->hy*cr+ui->hx]) return NULL; sprintf(buf, "%c%d,%d,%d", (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n); if (!ui->hcursor) ui->hshow = 0; return dupstr(buf); } return NULL; } static game_state *execute_move(const game_state *from, const char *move) { int cr = from->cr; game_state *ret; int x, y, n; if (move[0] == 'S') { const char *p; ret = dup_game(from); ret->completed = ret->cheated = TRUE; p = move+1; for (n = 0; n < cr*cr; n++) { ret->grid[n] = atoi(p); if (!*p || ret->grid[n] < 1 || ret->grid[n] > cr) { free_game(ret); return NULL; } while (*p && isdigit((unsigned char)*p)) p++; if (*p == ',') p++; } return ret; } else if ((move[0] == 'P' || move[0] == 'R') && sscanf(move+1, "%d,%d,%d", &x, &y, &n) == 3 && x >= 0 && x < cr && y >= 0 && y < cr && n >= 0 && n <= cr) { ret = dup_game(from); if (move[0] == 'P' && n > 0) { int index = (y*cr+x) * cr + (n-1); ret->pencil[index] = !ret->pencil[index]; } else { ret->grid[y*cr+x] = n; memset(ret->pencil + (y*cr+x)*cr, 0, cr); /* * We've made a real change to the grid. Check to see * if the game has been completed. */ if (!ret->completed && check_valid(cr, ret->blocks, ret->kblocks, ret->xtype, ret->grid)) { ret->completed = TRUE; } } return ret; } else return NULL; /* couldn't parse move string */ } /* ---------------------------------------------------------------------- * Drawing routines. */ #define SIZE(cr) ((cr) * TILE_SIZE + 2*BORDER + 1) #define GETTILESIZE(cr, w) ( (double)(w-1) / (double)(cr+1) ) 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 = SIZE(params->c * params->r); *y = SIZE(params->c * params->r); } 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_XDIAGONALS * 3 + 0] = 0.9F * ret[COL_BACKGROUND * 3 + 0]; ret[COL_XDIAGONALS * 3 + 1] = 0.9F * ret[COL_BACKGROUND * 3 + 1]; ret[COL_XDIAGONALS * 3 + 2] = 0.9F * ret[COL_BACKGROUND * 3 + 2]; ret[COL_GRID * 3 + 0] = 0.0F; ret[COL_GRID * 3 + 1] = 0.0F; ret[COL_GRID * 3 + 2] = 0.0F; ret[COL_CLUE * 3 + 0] = 0.0F; ret[COL_CLUE * 3 + 1] = 0.0F; ret[COL_CLUE * 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_KILLER * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0]; ret[COL_KILLER * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1]; ret[COL_KILLER * 3 + 2] = 0.1F * ret[COL_BACKGROUND * 3 + 2]; *ncolours = NCOLOURS; return ret; } static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { struct game_drawstate *ds = snew(struct game_drawstate); int cr = state->cr; ds->started = FALSE; ds->cr = cr; ds->xtype = state->xtype; ds->grid = snewn(cr*cr, digit); memset(ds->grid, cr+2, cr*cr); ds->pencil = snewn(cr*cr*cr, digit); memset(ds->pencil, 0, cr*cr*cr); ds->hl = snewn(cr*cr, unsigned char); memset(ds->hl, 0, cr*cr); /* * ds->entered_items needs one row of cr entries per entity in * which digits may not be duplicated. That's one for each row, * each column, each block, each diagonal, and each Killer cage. */ ds->nregions = cr*3 + 2; if (state->kblocks) ds->nregions += state->kblocks->nr_blocks; ds->entered_items = snewn(cr * ds->nregions, int); ds->tilesize = 0; /* not decided yet */ return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->hl); sfree(ds->pencil); sfree(ds->grid); sfree(ds->entered_items); sfree(ds); } static void draw_number(drawing *dr, game_drawstate *ds, const game_state *state, int x, int y, int hl) { int cr = state->cr; int tx, ty, tw, th; int cx, cy, cw, ch; int col_killer = (hl & 32 ? COL_ERROR : COL_KILLER); char str[20]; if (ds->grid[y*cr+x] == state->grid[y*cr+x] && ds->hl[y*cr+x] == hl && !memcmp(ds->pencil+(y*cr+x)*cr, state->pencil+(y*cr+x)*cr, cr)) return; /* no change required */ tx = BORDER + x * TILE_SIZE + 1 + GRIDEXTRA; ty = BORDER + y * TILE_SIZE + 1 + GRIDEXTRA; cx = tx; cy = ty; cw = tw = TILE_SIZE-1-2*GRIDEXTRA; ch = th = TILE_SIZE-1-2*GRIDEXTRA; if (x > 0 && state->blocks->whichblock[y*cr+x] == state->blocks->whichblock[y*cr+x-1]) cx -= GRIDEXTRA, cw += GRIDEXTRA; if (x+1 < cr && state->blocks->whichblock[y*cr+x] == state->blocks->whichblock[y*cr+x+1]) cw += GRIDEXTRA; if (y > 0 && state->blocks->whichblock[y*cr+x] == state->blocks->whichblock[(y-1)*cr+x]) cy -= GRIDEXTRA, ch += GRIDEXTRA; if (y+1 < cr && state->blocks->whichblock[y*cr+x] == state->blocks->whichblock[(y+1)*cr+x]) ch += GRIDEXTRA; clip(dr, cx, cy, cw, ch); /* background needs erasing */ draw_rect(dr, cx, cy, cw, ch, ((hl & 15) == 1 ? COL_HIGHLIGHT : (ds->xtype && (ondiag0(y*cr+x) || ondiag1(y*cr+x))) ? COL_XDIAGONALS : COL_BACKGROUND)); /* * Draw the corners of thick lines in corner-adjacent squares, * which jut into this square by one pixel. */ if (x > 0 && y > 0 && state->blocks->whichblock[y*cr+x] != state->blocks->whichblock[(y-1)*cr+x-1]) draw_rect(dr, tx-GRIDEXTRA, ty-GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID); if (x+1 < cr && y > 0 && state->blocks->whichblock[y*cr+x] != state->blocks->whichblock[(y-1)*cr+x+1]) draw_rect(dr, tx+TILE_SIZE-1-2*GRIDEXTRA, ty-GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID); if (x > 0 && y+1 < cr && state->blocks->whichblock[y*cr+x] != state->blocks->whichblock[(y+1)*cr+x-1]) draw_rect(dr, tx-GRIDEXTRA, ty+TILE_SIZE-1-2*GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID); if (x+1 < cr && y+1 < cr && state->blocks->whichblock[y*cr+x] != state->blocks->whichblock[(y+1)*cr+x+1]) draw_rect(dr, tx+TILE_SIZE-1-2*GRIDEXTRA, ty+TILE_SIZE-1-2*GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID); /* pencil-mode highlight */ if ((hl & 15) == 2) { 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); } if (state->kblocks) { int t = GRIDEXTRA * 3; int kcx, kcy, kcw, kch; int kl, kt, kr, kb; int has_left = 0, has_right = 0, has_top = 0, has_bottom = 0; /* * In non-jigsaw mode, the Killer cages are placed at a * fixed offset from the outer edge of the cell dividing * lines, so that they look right whether those lines are * thick or thin. In jigsaw mode, however, doing this will * sometimes cause the cage outlines in adjacent squares to * fail to match up with each other, so we must offset a * fixed amount from the _centre_ of the cell dividing * lines. */ if (state->blocks->r == 1) { kcx = tx; kcy = ty; kcw = tw; kch = th; } else { kcx = cx; kcy = cy; kcw = cw; kch = ch; } kl = kcx - 1; kt = kcy - 1; kr = kcx + kcw; kb = kcy + kch; /* * First, draw the lines dividing this area from neighbouring * different areas. */ if (x == 0 || state->kblocks->whichblock[y*cr+x] != state->kblocks->whichblock[y*cr+x-1]) has_left = 1, kl += t; if (x+1 >= cr || state->kblocks->whichblock[y*cr+x] != state->kblocks->whichblock[y*cr+x+1]) has_right = 1, kr -= t; if (y == 0 || state->kblocks->whichblock[y*cr+x] != state->kblocks->whichblock[(y-1)*cr+x]) has_top = 1, kt += t; if (y+1 >= cr || state->kblocks->whichblock[y*cr+x] != state->kblocks->whichblock[(y+1)*cr+x]) has_bottom = 1, kb -= t; if (has_top) draw_line(dr, kl, kt, kr, kt, col_killer); if (has_bottom) draw_line(dr, kl, kb, kr, kb, col_killer); if (has_left) draw_line(dr, kl, kt, kl, kb, col_killer); if (has_right) draw_line(dr, kr, kt, kr, kb, col_killer); /* * Now, take care of the corners (just as for the normal borders). * We only need a corner if there wasn't a full edge. */ if (x > 0 && y > 0 && !has_left && !has_top && state->kblocks->whichblock[y*cr+x] != state->kblocks->whichblock[(y-1)*cr+x-1]) { draw_line(dr, kl, kt + t, kl + t, kt + t, col_killer); draw_line(dr, kl + t, kt, kl + t, kt + t, col_killer); } if (x+1 < cr && y > 0 && !has_right && !has_top && state->kblocks->whichblock[y*cr+x] != state->kblocks->whichblock[(y-1)*cr+x+1]) { draw_line(dr, kcx + kcw - t, kt + t, kcx + kcw, kt + t, col_killer); draw_line(dr, kcx + kcw - t, kt, kcx + kcw - t, kt + t, col_killer); } if (x > 0 && y+1 < cr && !has_left && !has_bottom && state->kblocks->whichblock[y*cr+x] != state->kblocks->whichblock[(y+1)*cr+x-1]) { draw_line(dr, kl, kcy + kch - t, kl + t, kcy + kch - t, col_killer); draw_line(dr, kl + t, kcy + kch - t, kl + t, kcy + kch, col_killer); } if (x+1 < cr && y+1 < cr && !has_right && !has_bottom && state->kblocks->whichblock[y*cr+x] != state->kblocks->whichblock[(y+1)*cr+x+1]) { draw_line(dr, kcx + kcw - t, kcy + kch - t, kcx + kcw - t, kcy + kch, col_killer); draw_line(dr, kcx + kcw - t, kcy + kch - t, kcx + kcw, kcy + kch - t, col_killer); } } if (state->killer && state->kgrid[y*cr+x]) { sprintf (str, "%d", state->kgrid[y*cr+x]); draw_text(dr, tx + GRIDEXTRA * 4, ty + GRIDEXTRA * 4 + TILE_SIZE/4, FONT_VARIABLE, TILE_SIZE/4, ALIGN_VNORMAL | ALIGN_HLEFT, col_killer, str); } /* new number needs drawing? */ if (state->grid[y*cr+x]) { str[1] = '\0'; str[0] = state->grid[y*cr+x] + '0'; if (str[0] > '9') str[0] += 'a' - ('9'+1); draw_text(dr, tx + TILE_SIZE/2, ty + TILE_SIZE/2, FONT_VARIABLE, TILE_SIZE/2, ALIGN_VCENTRE | ALIGN_HCENTRE, state->immutable[y*cr+x] ? COL_CLUE : (hl & 16) ? COL_ERROR : COL_USER, str); } 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 = npencil = 0; i < cr; i++) if (state->pencil[(y*cr+x)*cr+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 + TILE_SIZE - GRIDEXTRA; pt = ty + GRIDEXTRA; pb = pt + TILE_SIZE - GRIDEXTRA; if (state->killer) { /* * Make space for the Killer cages. We do this * unconditionally, for uniformity between squares, * rather than making it depend on whether a Killer * cage edge is actually present on any given side. */ pl += GRIDEXTRA * 3; pr -= GRIDEXTRA * 3; pt += GRIDEXTRA * 3; pb -= GRIDEXTRA * 3; if (state->kgrid[y*cr+x] != 0) { /* Make further space for the Killer number. */ pt += TILE_SIZE/4; /* minph--; */ } } /* * 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 + (TILE_SIZE - fontsize * pw) / 2; pt = ty + (TILE_SIZE - fontsize * ph) / 2; /* * And move it down a bit if it's collided with the * Killer cage number. */ if (state->killer && state->kgrid[y*cr+x] != 0) { pt = max(pt, ty + GRIDEXTRA * 3 + TILE_SIZE/4); } /* * Now actually draw the pencil marks. */ for (i = j = 0; i < cr; i++) if (state->pencil[(y*cr+x)*cr+i]) { int dx = j % pw, dy = j / pw; str[1] = '\0'; str[0] = i + '1'; if (str[0] > '9') str[0] += 'a' - ('9'+1); 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); ds->grid[y*cr+x] = state->grid[y*cr+x]; memcpy(ds->pencil+(y*cr+x)*cr, state->pencil+(y*cr+x)*cr, cr); ds->hl[y*cr+x] = hl; } 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 cr = state->cr; int x, y; 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(cr), SIZE(cr), COL_BACKGROUND); /* * Draw the grid. We draw it as a big thick rectangle of * COL_GRID initially; individual calls to draw_number() * will poke the right-shaped holes in it. */ draw_rect(dr, BORDER-GRIDEXTRA, BORDER-GRIDEXTRA, cr*TILE_SIZE+1+2*GRIDEXTRA, cr*TILE_SIZE+1+2*GRIDEXTRA, COL_GRID); } /* * This array is used to keep track of rows, columns and boxes * which contain a number more than once. */ for (x = 0; x < cr * ds->nregions; x++) ds->entered_items[x] = 0; for (x = 0; x < cr; x++) for (y = 0; y < cr; y++) { digit d = state->grid[y*cr+x]; if (d) { int box, kbox; /* Rows */ ds->entered_items[x*cr+d-1]++; /* Columns */ ds->entered_items[(y+cr)*cr+d-1]++; /* Blocks */ box = state->blocks->whichblock[y*cr+x]; ds->entered_items[(box+2*cr)*cr+d-1]++; /* Diagonals */ if (ds->xtype) { if (ondiag0(y*cr+x)) ds->entered_items[(3*cr)*cr+d-1]++; if (ondiag1(y*cr+x)) ds->entered_items[(3*cr+1)*cr+d-1]++; } /* Killer cages */ if (state->kblocks) { kbox = state->kblocks->whichblock[y*cr+x]; ds->entered_items[(kbox+3*cr+2)*cr+d-1]++; } } } /* * Draw any numbers which need redrawing. */ for (x = 0; x < cr; x++) { for (y = 0; y < cr; y++) { int highlight = 0; digit d = state->grid[y*cr+x]; if (flashtime > 0 && (flashtime <= FLASH_TIME/3 || flashtime >= FLASH_TIME*2/3)) highlight = 1; /* Highlight active input areas. */ if (x == ui->hx && y == ui->hy && ui->hshow) highlight = ui->hpencil ? 2 : 1; /* Mark obvious errors (ie, numbers which occur more than once * in a single row, column, or box). */ if (d && (ds->entered_items[x*cr+d-1] > 1 || ds->entered_items[(y+cr)*cr+d-1] > 1 || ds->entered_items[(state->blocks->whichblock[y*cr+x] +2*cr)*cr+d-1] > 1 || (ds->xtype && ((ondiag0(y*cr+x) && ds->entered_items[(3*cr)*cr+d-1] > 1) || (ondiag1(y*cr+x) && ds->entered_items[(3*cr+1)*cr+d-1]>1)))|| (state->kblocks && ds->entered_items[(state->kblocks->whichblock[y*cr+x] +3*cr+2)*cr+d-1] > 1))) highlight |= 16; if (d && state->kblocks) { int i, b = state->kblocks->whichblock[y*cr+x]; int n_squares = state->kblocks->nr_squares[b]; int sum = 0, clue = 0; for (i = 0; i < n_squares; i++) { int xy = state->kblocks->blocks[b][i]; if (state->grid[xy] == 0) break; sum += state->grid[xy]; if (state->kgrid[xy]) { assert(clue == 0); clue = state->kgrid[xy]; } } if (i == n_squares) { assert(clue != 0); if (sum != clue) highlight |= 32; } } draw_number(dr, ds, state, x, y, highlight); } } /* * Update the _entire_ grid if necessary. */ if (!ds->started) { draw_update(dr, 0, 0, SIZE(cr), SIZE(cr)); ds->started = TRUE; } } 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 int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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; /* * I'll use 9mm squares by default. They should be quite big * for this game, because players will want to jot down no end * of pencil marks in the squares. */ game_compute_size(params, 900, &pw, &ph); *x = pw / 100.0F; *y = ph / 100.0F; } /* * Subfunction to draw the thick lines between cells. In order to do * this using the line-drawing rather than rectangle-drawing API (so * as to get line thicknesses to scale correctly) and yet have * correctly mitred joins between lines, we must do this by tracing * the boundary of each sub-block and drawing it in one go as a * single polygon. * * This subfunction is also reused with thinner dotted lines to * outline the Killer cages, this time offsetting the outline toward * the interior of the affected squares. */ static void outline_block_structure(drawing *dr, game_drawstate *ds, const game_state *state, struct block_structure *blocks, int ink, int inset) { int cr = state->cr; int *coords; int bi, i, n; int x, y, dx, dy, sx, sy, sdx, sdy; /* * Maximum perimeter of a k-omino is 2k+2. (Proof: start * with k unconnected squares, with total perimeter 4k. * Now repeatedly join two disconnected components * together into a larger one; every time you do so you * remove at least two unit edges, and you require k-1 of * these operations to create a single connected piece, so * you must have at most 4k-2(k-1) = 2k+2 unit edges left * afterwards.) */ coords = snewn(4*cr+4, int); /* 2k+2 points, 2 coords per point */ /* * Iterate over all the blocks. */ for (bi = 0; bi < blocks->nr_blocks; bi++) { if (blocks->nr_squares[bi] == 0) continue; /* * For each block, find a starting square within it * which has a boundary at the left. */ for (i = 0; i < cr; i++) { int j = blocks->blocks[bi][i]; if (j % cr == 0 || blocks->whichblock[j-1] != bi) break; } assert(i < cr); /* every block must have _some_ leftmost square */ x = blocks->blocks[bi][i] % cr; y = blocks->blocks[bi][i] / cr; dx = -1; dy = 0; /* * Now begin tracing round the perimeter. At all * times, (x,y) describes some square within the * block, and (x+dx,y+dy) is some adjacent square * outside it; so the edge between those two squares * is always an edge of the block. */ sx = x, sy = y, sdx = dx, sdy = dy; /* save starting position */ n = 0; do { int cx, cy, tx, ty, nin; /* * Advance to the next edge, by looking at the two * squares beyond it. If they're both outside the block, * we turn right (by leaving x,y the same and rotating * dx,dy clockwise); if they're both inside, we turn * left (by rotating dx,dy anticlockwise and contriving * to leave x+dx,y+dy unchanged); if one of each, we go * straight on (and may enforce by assertion that * they're one of each the _right_ way round). */ nin = 0; tx = x - dy + dx; ty = y + dx + dy; nin += (tx >= 0 && tx < cr && ty >= 0 && ty < cr && blocks->whichblock[ty*cr+tx] == bi); tx = x - dy; ty = y + dx; nin += (tx >= 0 && tx < cr && ty >= 0 && ty < cr && blocks->whichblock[ty*cr+tx] == bi); if (nin == 0) { /* * Turn right. */ int tmp; tmp = dx; dx = -dy; dy = tmp; } else if (nin == 2) { /* * Turn left. */ int tmp; x += dx; y += dy; tmp = dx; dx = dy; dy = -tmp; x -= dx; y -= dy; } else { /* * Go straight on. */ x -= dy; y += dx; } /* * Now enforce by assertion that we ended up * somewhere sensible. */ assert(x >= 0 && x < cr && y >= 0 && y < cr && blocks->whichblock[y*cr+x] == bi); assert(x+dx < 0 || x+dx >= cr || y+dy < 0 || y+dy >= cr || blocks->whichblock[(y+dy)*cr+(x+dx)] != bi); /* * Record the point we just went past at one end of the * edge. To do this, we translate (x,y) down and right * by half a unit (so they're describing a point in the * _centre_ of the square) and then translate back again * in a manner rotated by dy and dx. */ assert(n < 2*cr+2); cx = ((2*x+1) + dy + dx) / 2; cy = ((2*y+1) - dx + dy) / 2; coords[2*n+0] = BORDER + cx * TILE_SIZE; coords[2*n+1] = BORDER + cy * TILE_SIZE; coords[2*n+0] -= dx * inset; coords[2*n+1] -= dy * inset; if (nin == 0) { /* * We turned right, so inset this corner back along * the edge towards the centre of the square. */ coords[2*n+0] -= dy * inset; coords[2*n+1] += dx * inset; } else if (nin == 2) { /* * We turned left, so inset this corner further * _out_ along the edge into the next square. */ coords[2*n+0] += dy * inset; coords[2*n+1] -= dx * inset; } n++; } while (x != sx || y != sy || dx != sdx || dy != sdy); /* * That's our polygon; now draw it. */ draw_polygon(dr, coords, n, -1, ink); } sfree(coords); } static void game_print(drawing *dr, const game_state *state, int tilesize) { int cr = state->cr; 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 * TILE_SIZE / 40); draw_rect_outline(dr, BORDER, BORDER, cr*TILE_SIZE, cr*TILE_SIZE, ink); /* * Highlight X-diagonal squares. */ if (state->xtype) { int i; int xhighlight = print_grey_colour(dr, 0.90F); for (i = 0; i < cr; i++) draw_rect(dr, BORDER + i*TILE_SIZE, BORDER + i*TILE_SIZE, TILE_SIZE, TILE_SIZE, xhighlight); for (i = 0; i < cr; i++) if (i*2 != cr-1) /* avoid redoing centre square, just for fun */ draw_rect(dr, BORDER + i*TILE_SIZE, BORDER + (cr-1-i)*TILE_SIZE, TILE_SIZE, TILE_SIZE, xhighlight); } /* * Main grid. */ for (x = 1; x < cr; x++) { print_line_width(dr, TILE_SIZE / 40); draw_line(dr, BORDER+x*TILE_SIZE, BORDER, BORDER+x*TILE_SIZE, BORDER+cr*TILE_SIZE, ink); } for (y = 1; y < cr; y++) { print_line_width(dr, TILE_SIZE / 40); draw_line(dr, BORDER, BORDER+y*TILE_SIZE, BORDER+cr*TILE_SIZE, BORDER+y*TILE_SIZE, ink); } /* * Thick lines between cells. */ print_line_width(dr, 3 * TILE_SIZE / 40); outline_block_structure(dr, ds, state, state->blocks, ink, 0); /* * Killer cages and their totals. */ if (state->kblocks) { print_line_width(dr, TILE_SIZE / 40); print_line_dotted(dr, TRUE); outline_block_structure(dr, ds, state, state->kblocks, ink, 5 * TILE_SIZE / 40); print_line_dotted(dr, FALSE); for (y = 0; y < cr; y++) for (x = 0; x < cr; x++) if (state->kgrid[y*cr+x]) { char str[20]; sprintf(str, "%d", state->kgrid[y*cr+x]); draw_text(dr, BORDER+x*TILE_SIZE + 7*TILE_SIZE/40, BORDER+y*TILE_SIZE + 16*TILE_SIZE/40, FONT_VARIABLE, TILE_SIZE/4, ALIGN_VNORMAL | ALIGN_HLEFT, ink, str); } } /* * Standard (non-Killer) clue numbers. */ for (y = 0; y < cr; y++) for (x = 0; x < cr; x++) if (state->grid[y*cr+x]) { char str[2]; str[1] = '\0'; str[0] = state->grid[y*cr+x] + '0'; if (str[0] > '9') str[0] += 'a' - ('9'+1); draw_text(dr, BORDER + x*TILE_SIZE + TILE_SIZE/2, BORDER + y*TILE_SIZE + TILE_SIZE/2, FONT_VARIABLE, TILE_SIZE/2, ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str); } } #ifdef COMBINED #define thegame solo #endif const struct game thegame = { "Solo", "games.solo", "solo", default_params, game_fetch_preset, 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, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */ }; #ifdef STANDALONE_SOLVER int main(int argc, char **argv) { game_params *p; game_state *s; char *id = NULL, *desc, *err; int grade = FALSE; struct difficulty dlev; while (--argc > 0) { char *p = *++argv; if (!strcmp(p, "-v")) { solver_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); dlev.maxdiff = DIFF_RECURSIVE; dlev.maxkdiff = DIFF_KINTERSECT; solver(s->cr, s->blocks, s->kblocks, s->xtype, s->grid, s->kgrid, &dlev); if (grade) { printf("Difficulty rating: %s\n", dlev.diff==DIFF_BLOCK ? "Trivial (blockwise positional elimination only)": dlev.diff==DIFF_SIMPLE ? "Basic (row/column/number elimination required)": dlev.diff==DIFF_INTERSECT ? "Intermediate (intersectional analysis required)": dlev.diff==DIFF_SET ? "Advanced (set elimination required)": dlev.diff==DIFF_EXTREME ? "Extreme (complex non-recursive techniques required)": dlev.diff==DIFF_RECURSIVE ? "Unreasonable (guesswork and backtracking required)": dlev.diff==DIFF_AMBIGUOUS ? "Ambiguous (multiple solutions exist)": dlev.diff==DIFF_IMPOSSIBLE ? "Impossible (no solution exists)": "INTERNAL ERROR: unrecognised difficulty code"); if (p->killer) printf("Killer difficulty: %s\n", dlev.kdiff==DIFF_KSINGLE ? "Trivial (single square cages only)": dlev.kdiff==DIFF_KMINMAX ? "Simple (maximum sum analysis required)": dlev.kdiff==DIFF_KSUMS ? "Intermediate (sum possibilities)": dlev.kdiff==DIFF_KINTERSECT ? "Advanced (sum region intersections)": "INTERNAL ERROR: unrecognised difficulty code"); } else { printf("%s\n", grid_text_format(s->cr, s->blocks, s->xtype, s->grid)); } return 0; } #endif /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/tdq.c0000644000175300017530000000367111707014702013367 0ustar simonsimon/* * tdq.c: implement a 'to-do queue', a simple de-duplicating to-do * list mechanism. */ #include #include "puzzles.h" /* * Implementation: a tdq consists of a circular buffer of size n * storing the integers currently in the queue, plus an array of n * booleans indicating whether each integer is already there. * * Using a circular buffer of size n to store between 0 and n items * inclusive has an obvious failure mode: if the input and output * pointers are the same, how do you know whether that means the * buffer is full or empty? * * In this application we have a simple way to tell: in the former * case, the flags array is all 1s, and in the latter case it's all * 0s. So we could spot that case and check, say, flags[0]. * * However, it's even easier to simply determine whether the queue is * non-empty by testing flags[buffer[op]] - that way we don't even * _have_ to compare ip against op. */ struct tdq { int n; int *queue; int ip, op; /* in pointer, out pointer */ char *flags; }; tdq *tdq_new(int n) { int i; tdq *tdq = snew(struct tdq); tdq->queue = snewn(n, int); tdq->flags = snewn(n, char); for (i = 0; i < n; i++) { tdq->queue[i] = 0; tdq->flags[i] = 0; } tdq->n = n; tdq->ip = tdq->op = 0; return tdq; } void tdq_free(tdq *tdq) { sfree(tdq->queue); sfree(tdq->flags); sfree(tdq); } void tdq_add(tdq *tdq, int k) { assert((unsigned)k < (unsigned)tdq->n); if (!tdq->flags[k]) { tdq->queue[tdq->ip] = k; tdq->flags[k] = 1; if (++tdq->ip == tdq->n) tdq->ip = 0; } } int tdq_remove(tdq *tdq) { int ret = tdq->queue[tdq->op]; if (!tdq->flags[ret]) return -1; tdq->flags[ret] = 0; if (++tdq->op == tdq->n) tdq->op = 0; return ret; } void tdq_fill(tdq *tdq) { int i; for (i = 0; i < tdq->n; i++) tdq_add(tdq, i); } puzzles-r9872/tents.c0000644000175300017530000023767312132232554013747 0ustar simonsimon/* * tents.c: Puzzle involving placing tents next to trees subject to * some confusing conditions. * * TODO: * * - it might be nice to make setter-provided tent/nontent clues * inviolable? * * on the other hand, this would introduce considerable extra * complexity and size into the game state; also inviolable * clues would have to be marked as such somehow, in an * intrusive and annoying manner. Since they're never * generated by _my_ generator, I'm currently more inclined * not to bother. * * - more difficult levels at the top end? * * for example, sometimes we can deduce that two BLANKs in * the same row are each adjacent to the same unattached tree * and to nothing else, implying that they can't both be * tents; this enables us to rule out some extra combinations * in the row-based deduction loop, and hence deduce more * from the number in that row than we could otherwise do. * * that by itself doesn't seem worth implementing a new * difficulty level for, but if I can find a few more things * like that then it might become worthwhile. * * I wonder if there's a sensible heuristic for where to * guess which would make a recursive solver viable? */ #include #include #include #include #include #include #include "puzzles.h" #include "maxflow.h" /* * Design discussion * ----------------- * * The rules of this puzzle as available on the WWW are poorly * specified. The bits about tents having to be orthogonally * adjacent to trees, tents not being even diagonally adjacent to * one another, and the number of tents in each row and column * being given are simple enough; the difficult bit is the * tent-to-tree matching. * * Some sources use simplistic wordings such as `each tree is * exactly connected to only one tent', which is extremely unclear: * it's easy to read erroneously as `each tree is _orthogonally * adjacent_ to exactly one tent', which is definitely incorrect. * Even the most coherent sources I've found don't do a much better * job of stating the rule. * * A more precise statement of the rule is that it must be possible * to find a bijection f between tents and trees such that each * tree T is orthogonally adjacent to the tent f(T), but that a * tent is permitted to be adjacent to other trees in addition to * its own. This slightly non-obvious criterion is what gives this * puzzle most of its subtlety. * * However, there's a particularly subtle ambiguity left over. Is * the bijection between tents and trees required to be _unique_? * In other words, is that bijection conceptually something the * player should be able to exhibit as part of the solution (even * if they aren't actually required to do so)? Or is it sufficient * to have a unique _placement_ of the tents which gives rise to at * least one suitable bijection? * * The puzzle shown to the right of this .T. 2 *T* 2 * paragraph illustrates the problem. There T.T 0 -> T-T 0 * are two distinct bijections available. .T. 2 *T* 2 * The answer to the above question will * determine whether it's a valid puzzle. 202 202 * * This is an important question, because it affects both the * player and the generator. Eventually I found all the instances * of this puzzle I could Google up, solved them all by hand, and * verified that in all cases the tree/tent matching was uniquely * determined given the tree and tent positions. Therefore, the * puzzle as implemented in this source file takes the following * policy: * * - When checking a user-supplied solution for correctness, only * verify that there exists _at least_ one matching. * - When generating a puzzle, enforce that there must be * _exactly_ one. * * Algorithmic implications * ------------------------ * * Another way of phrasing the tree/tent matching criterion is to * say that the bipartite adjacency graph between trees and tents * has a perfect matching. That is, if you construct a graph which * has a vertex per tree and a vertex per tent, and an edge between * any tree and tent which are orthogonally adjacent, it is * possible to find a set of N edges of that graph (where N is the * number of trees and also the number of tents) which between them * connect every tree to every tent. * * The most efficient known algorithms for finding such a matching * given a graph, as far as I'm aware, are the Munkres assignment * algorithm (also known as the Hungarian algorithm) and the * Ford-Fulkerson algorithm (for finding optimal flows in * networks). Each of these takes O(N^3) running time; so we're * talking O(N^3) time to verify any candidate solution to this * puzzle. That's just about OK if you're doing it once per mouse * click (and in fact not even that, since the sensible thing to do * is check all the _other_ puzzle criteria and only wade into this * quagmire if none are violated); but if the solver had to keep * doing N^3 work internally, then it would probably end up with * more like N^5 or N^6 running time, and grid generation would * become very clunky. * * Fortunately, I've been able to prove a very useful property of * _unique_ perfect matchings, by adapting the proof of Hall's * Marriage Theorem. For those unaware of Hall's Theorem, I'll * recap it and its proof: it states that a bipartite graph * contains a perfect matching iff every set of vertices on the * left side of the graph have a neighbourhood _at least_ as big on * the right. * * This condition is obviously satisfied if a perfect matching does * exist; each left-side node has a distinct right-side node which * is the one assigned to it by the matching, and thus any set of n * left vertices must have a combined neighbourhood containing at * least the n corresponding right vertices, and possibly others * too. Alternatively, imagine if you had (say) three left-side * nodes all of which were connected to only two right-side nodes * between them: any perfect matching would have to assign one of * those two right nodes to each of the three left nodes, and still * give the three left nodes a different right node each. This is * of course impossible. * * To prove the converse (that if every subset of left vertices * satisfies the Hall condition then a perfect matching exists), * consider trying to find a proper subset of the left vertices * which _exactly_ satisfies the Hall condition: that is, its right * neighbourhood is precisely the same size as it. If we can find * such a subset, then we can split the bipartite graph into two * smaller ones: one consisting of the left subset and its right * neighbourhood, the other consisting of everything else. Edges * from the left side of the former graph to the right side of the * latter do not exist, by construction; edges from the right side * of the former to the left of the latter cannot be part of any * perfect matching because otherwise the left subset would not be * left with enough distinct right vertices to connect to (this is * exactly the same deduction used in Solo's set analysis). You can * then prove (left as an exercise) that both these smaller graphs * still satisfy the Hall condition, and therefore the proof will * follow by induction. * * There's one other possibility, which is the case where _no_ * proper subset of the left vertices has a right neighbourhood of * exactly the same size. That is, every left subset has a strictly * _larger_ right neighbourhood. In this situation, we can simply * remove an _arbitrary_ edge from the graph. This cannot reduce * the size of any left subset's right neighbourhood by more than * one, so if all neighbourhoods were strictly bigger than they * needed to be initially, they must now still be _at least as big_ * as they need to be. So we can keep throwing out arbitrary edges * until we find a set which exactly satisfies the Hall condition, * and then proceed as above. [] * * That's Hall's theorem. I now build on this by examining the * circumstances in which a bipartite graph can have a _unique_ * perfect matching. It is clear that in the second case, where no * left subset exactly satisfies the Hall condition and so we can * remove an arbitrary edge, there cannot be a unique perfect * matching: given one perfect matching, we choose our arbitrary * removed edge to be one of those contained in it, and then we can * still find a perfect matching in the remaining graph, which will * be a distinct perfect matching in the original. * * So it is a necessary condition for a unique perfect matching * that there must be at least one proper left subset which * _exactly_ satisfies the Hall condition. But now consider the * smaller graph constructed by taking that left subset and its * neighbourhood: if the graph as a whole had a unique perfect * matching, then so must this smaller one, which means we can find * a proper left subset _again_, and so on. Repeating this process * must eventually reduce us to a graph with only one left-side * vertex (so there are no proper subsets at all); this vertex must * be connected to only one right-side vertex, and hence must be so * in the original graph as well (by construction). So we can * discard this vertex pair from the graph, and any other edges * that involved it (which will by construction be from other left * vertices only), and the resulting smaller graph still has a * unique perfect matching which means we can do the same thing * again. * * In other words, given any bipartite graph with a unique perfect * matching, we can find that matching by the following extremely * simple algorithm: * * - Find a left-side vertex which is only connected to one * right-side vertex. * - Assign those vertices to one another, and therefore discard * any other edges connecting to that right vertex. * - Repeat until all vertices have been matched. * * This algorithm can be run in O(V+E) time (where V is the number * of vertices and E is the number of edges in the graph), and the * only way it can fail is if there is not a unique perfect * matching (either because there is no matching at all, or because * it isn't unique; but it can't distinguish those cases). * * Thus, the internal solver in this source file can be confident * that if the tree/tent matching is uniquely determined by the * tree and tent positions, it can find it using only this kind of * obvious and simple operation: assign a tree to a tent if it * cannot possibly belong to any other tent, and vice versa. If the * solver were _only_ trying to determine the matching, even that * `vice versa' wouldn't be required; but it can come in handy when * not all the tents have been placed yet. I can therefore be * reasonably confident that as long as my solver doesn't need to * cope with grids that have a non-unique matching, it will also * not need to do anything complicated like set analysis between * trees and tents. */ /* * In standalone solver mode, `verbose' is a variable which can be * set by command-line option; in debugging mode it's simply always * true. */ #if defined STANDALONE_SOLVER #define SOLVER_DIAGNOSTICS int verbose = FALSE; #elif defined SOLVER_DIAGNOSTICS #define verbose TRUE #endif /* * 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(EASY,Easy,e) \ A(TRICKY,Tricky,t) #define ENUM(upper,title,lower) DIFF_ ## upper, #define TITLE(upper,title,lower) #title, #define ENCODE(upper,title,lower) #lower #define CONFIG(upper,title,lower) ":" #title enum { DIFFLIST(ENUM) DIFFCOUNT }; static char const *const tents_diffnames[] = { DIFFLIST(TITLE) }; static char const tents_diffchars[] = DIFFLIST(ENCODE); #define DIFFCONFIG DIFFLIST(CONFIG) enum { COL_BACKGROUND, COL_GRID, COL_GRASS, COL_TREETRUNK, COL_TREELEAF, COL_TENT, COL_ERROR, COL_ERRTEXT, COL_ERRTRUNK, NCOLOURS }; enum { BLANK, TREE, TENT, NONTENT, MAGIC }; struct game_params { int w, h; int diff; }; struct numbers { int refcount; int *numbers; }; struct game_state { game_params p; char *grid; struct numbers *numbers; int completed, used_solve; }; static game_params *default_params(void) { game_params *ret = snew(game_params); ret->w = ret->h = 8; ret->diff = DIFF_EASY; return ret; } static const struct game_params tents_presets[] = { {8, 8, DIFF_EASY}, {8, 8, DIFF_TRICKY}, {10, 10, DIFF_EASY}, {10, 10, DIFF_TRICKY}, {15, 15, DIFF_EASY}, {15, 15, DIFF_TRICKY}, }; static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char str[80]; if (i < 0 || i >= lenof(tents_presets)) return FALSE; ret = snew(game_params); *ret = tents_presets[i]; sprintf(str, "%dx%d %s", ret->w, ret->h, tents_diffnames[ret->diff]); *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 == 'd') { int i; string++; for (i = 0; i < DIFFCOUNT; i++) if (*string == tents_diffchars[i]) params->diff = i; if (*string) string++; } } static char *encode_params(const game_params *params, int full) { char buf[120]; sprintf(buf, "%dx%d", params->w, params->h); if (full) sprintf(buf + strlen(buf), "d%c", tents_diffchars[params->diff]); return dupstr(buf); } 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].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Difficulty"; ret[2].type = C_CHOICES; ret[2].sval = DIFFCONFIG; ret[2].ival = params->diff; ret[3].name = NULL; ret[3].type = C_END; ret[3].sval = NULL; ret[3].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); ret->diff = cfg[2].ival; return ret; } static char *validate_params(const game_params *params, int full) { /* * Generating anything under 4x4 runs into trouble of one kind * or another. */ if (params->w < 4 || params->h < 4) return "Width and height must both be at least four"; return NULL; } /* * Scratch space for solver. */ enum { N, U, L, R, D, MAXDIR }; /* link directions */ #define dx(d) ( ((d)==R) - ((d)==L) ) #define dy(d) ( ((d)==D) - ((d)==U) ) #define F(d) ( U + D - (d) ) struct solver_scratch { char *links; /* mapping between trees and tents */ int *locs; char *place, *mrows, *trows; }; static struct solver_scratch *new_scratch(int w, int h) { struct solver_scratch *ret = snew(struct solver_scratch); ret->links = snewn(w*h, char); ret->locs = snewn(max(w, h), int); ret->place = snewn(max(w, h), char); ret->mrows = snewn(3 * max(w, h), char); ret->trows = snewn(3 * max(w, h), char); return ret; } static void free_scratch(struct solver_scratch *sc) { sfree(sc->trows); sfree(sc->mrows); sfree(sc->place); sfree(sc->locs); sfree(sc->links); sfree(sc); } /* * Solver. Returns 0 for impossibility, 1 for success, 2 for * ambiguity or failure to converge. */ static int tents_solve(int w, int h, const char *grid, int *numbers, char *soln, struct solver_scratch *sc, int diff) { int x, y, d, i, j; char *mrow, *trow, *trow1, *trow2; /* * Set up solver data. */ memset(sc->links, N, w*h); /* * Set up solution array. */ memcpy(soln, grid, w*h); /* * Main solver loop. */ while (1) { int done_something = FALSE; /* * Any tent which has only one unattached tree adjacent to * it can be tied to that tree. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) if (soln[y*w+x] == TENT && !sc->links[y*w+x]) { int linkd = 0; for (d = 1; d < MAXDIR; d++) { int x2 = x + dx(d), y2 = y + dy(d); if (x2 >= 0 && x2 < w && y2 >= 0 && y2 < h && soln[y2*w+x2] == TREE && !sc->links[y2*w+x2]) { if (linkd) break; /* found more than one */ else linkd = d; } } if (d == MAXDIR && linkd == 0) { #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("tent at %d,%d cannot link to anything\n", x, y); #endif return 0; /* no solution exists */ } else if (d == MAXDIR) { int x2 = x + dx(linkd), y2 = y + dy(linkd); #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("tent at %d,%d can only link to tree at" " %d,%d\n", x, y, x2, y2); #endif sc->links[y*w+x] = linkd; sc->links[y2*w+x2] = F(linkd); done_something = TRUE; } } if (done_something) continue; if (diff < 0) break; /* don't do anything else! */ /* * Mark a blank square as NONTENT if it is not orthogonally * adjacent to any unmatched tree. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) if (soln[y*w+x] == BLANK) { int can_be_tent = FALSE; for (d = 1; d < MAXDIR; d++) { int x2 = x + dx(d), y2 = y + dy(d); if (x2 >= 0 && x2 < w && y2 >= 0 && y2 < h && soln[y2*w+x2] == TREE && !sc->links[y2*w+x2]) can_be_tent = TRUE; } if (!can_be_tent) { #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("%d,%d cannot be a tent (no adjacent" " unmatched tree)\n", x, y); #endif soln[y*w+x] = NONTENT; done_something = TRUE; } } if (done_something) continue; /* * Mark a blank square as NONTENT if it is (perhaps * diagonally) adjacent to any other tent. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) if (soln[y*w+x] == BLANK) { int dx, dy, imposs = FALSE; for (dy = -1; dy <= +1; dy++) for (dx = -1; dx <= +1; dx++) if (dy || dx) { int x2 = x + dx, y2 = y + dy; if (x2 >= 0 && x2 < w && y2 >= 0 && y2 < h && soln[y2*w+x2] == TENT) imposs = TRUE; } if (imposs) { #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("%d,%d cannot be a tent (adjacent tent)\n", x, y); #endif soln[y*w+x] = NONTENT; done_something = TRUE; } } if (done_something) continue; /* * Any tree which has exactly one {unattached tent, BLANK} * adjacent to it must have its tent in that square. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) if (soln[y*w+x] == TREE && !sc->links[y*w+x]) { int linkd = 0, linkd2 = 0, nd = 0; for (d = 1; d < MAXDIR; d++) { int x2 = x + dx(d), y2 = y + dy(d); if (!(x2 >= 0 && x2 < w && y2 >= 0 && y2 < h)) continue; if (soln[y2*w+x2] == BLANK || (soln[y2*w+x2] == TENT && !sc->links[y2*w+x2])) { if (linkd) linkd2 = d; else linkd = d; nd++; } } if (nd == 0) { #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("tree at %d,%d cannot link to anything\n", x, y); #endif return 0; /* no solution exists */ } else if (nd == 1) { int x2 = x + dx(linkd), y2 = y + dy(linkd); #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("tree at %d,%d can only link to tent at" " %d,%d\n", x, y, x2, y2); #endif soln[y2*w+x2] = TENT; sc->links[y*w+x] = linkd; sc->links[y2*w+x2] = F(linkd); done_something = TRUE; } else if (nd == 2 && (!dx(linkd) != !dx(linkd2)) && diff >= DIFF_TRICKY) { /* * If there are two possible places where * this tree's tent can go, and they are * diagonally separated rather than being * on opposite sides of the tree, then the * square (other than the tree square) * which is adjacent to both of them must * be a non-tent. */ int x2 = x + dx(linkd) + dx(linkd2); int y2 = y + dy(linkd) + dy(linkd2); assert(x2 >= 0 && x2 < w && y2 >= 0 && y2 < h); if (soln[y2*w+x2] == BLANK) { #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("possible tent locations for tree at" " %d,%d rule out tent at %d,%d\n", x, y, x2, y2); #endif soln[y2*w+x2] = NONTENT; done_something = TRUE; } } } if (done_something) continue; /* * If localised deductions about the trees and tents * themselves haven't helped us, it's time to resort to the * numbers round the grid edge. For each row and column, we * go through all possible combinations of locations for * the unplaced tents, rule out any which have adjacent * tents, and spot any square which is given the same state * by all remaining combinations. */ for (i = 0; i < w+h; i++) { int start, step, len, start1, start2, n, k; if (i < w) { /* * This is the number for a column. */ start = i; step = w; len = h; if (i > 0) start1 = start - 1; else start1 = -1; if (i+1 < w) start2 = start + 1; else start2 = -1; } else { /* * This is the number for a row. */ start = (i-w)*w; step = 1; len = w; if (i > w) start1 = start - w; else start1 = -1; if (i+1 < w+h) start2 = start + w; else start2 = -1; } if (diff < DIFF_TRICKY) { /* * In Easy mode, we don't look at the effect of one * row on the next (i.e. ruling out a square if all * possibilities for an adjacent row place a tent * next to it). */ start1 = start2 = -1; } k = numbers[i]; /* * Count and store the locations of the free squares, * and also count the number of tents already placed. */ n = 0; for (j = 0; j < len; j++) { if (soln[start+j*step] == TENT) k--; /* one fewer tent to place */ else if (soln[start+j*step] == BLANK) sc->locs[n++] = j; } if (n == 0) continue; /* nothing left to do here */ /* * Now we know we're placing k tents in n squares. Set * up the first possibility. */ for (j = 0; j < n; j++) sc->place[j] = (j < k ? TENT : NONTENT); /* * We're aiming to find squares in this row which are * invariant over all valid possibilities. Thus, we * maintain the current state of that invariance. We * start everything off at MAGIC to indicate that it * hasn't been set up yet. */ mrow = sc->mrows; trow = sc->trows; trow1 = sc->trows + len; trow2 = sc->trows + 2*len; memset(mrow, MAGIC, 3*len); /* * And iterate over all possibilities. */ while (1) { int p, valid; /* * See if this possibility is valid. The only way * it can fail to be valid is if it contains two * adjacent tents. (Other forms of invalidity, such * as containing a tent adjacent to one already * placed, will have been dealt with already by * other parts of the solver.) */ valid = TRUE; for (j = 0; j+1 < n; j++) if (sc->place[j] == TENT && sc->place[j+1] == TENT && sc->locs[j+1] == sc->locs[j]+1) { valid = FALSE; break; } if (valid) { /* * Merge this valid combination into mrow. */ memset(trow, MAGIC, len); memset(trow+len, BLANK, 2*len); for (j = 0; j < n; j++) { trow[sc->locs[j]] = sc->place[j]; if (sc->place[j] == TENT) { int jj; for (jj = sc->locs[j]-1; jj <= sc->locs[j]+1; jj++) if (jj >= 0 && jj < len) trow1[jj] = trow2[jj] = NONTENT; } } for (j = 0; j < 3*len; j++) { if (trow[j] == MAGIC) continue; if (mrow[j] == MAGIC || mrow[j] == trow[j]) { /* * Either this is the first valid * placement we've found at all, or * this square's contents are * consistent with every previous valid * combination. */ mrow[j] = trow[j]; } else { /* * This square's contents fail to match * what they were in a different * combination, so we cannot deduce * anything about this square. */ mrow[j] = BLANK; } } } /* * Find the next combination of k choices from n. * We do this by finding the rightmost tent which * can be moved one place right, doing so, and * shunting all tents to the right of that as far * left as they can go. */ p = 0; for (j = n-1; j > 0; j--) { if (sc->place[j] == TENT) p++; if (sc->place[j] == NONTENT && sc->place[j-1] == TENT) { sc->place[j-1] = NONTENT; sc->place[j] = TENT; while (p--) sc->place[++j] = TENT; while (++j < n) sc->place[j] = NONTENT; break; } } if (j <= 0) break; /* we've finished */ } /* * It's just possible that _no_ placement was valid, in * which case we have an internally inconsistent * puzzle. */ if (mrow[sc->locs[0]] == MAGIC) return 0; /* inconsistent */ /* * Now go through mrow and see if there's anything * we've deduced which wasn't already mentioned in soln. */ for (j = 0; j < len; j++) { int whichrow; for (whichrow = 0; whichrow < 3; whichrow++) { char *mthis = mrow + whichrow * len; int tstart = (whichrow == 0 ? start : whichrow == 1 ? start1 : start2); if (tstart >= 0 && mthis[j] != MAGIC && mthis[j] != BLANK && soln[tstart+j*step] == BLANK) { int pos = tstart+j*step; #ifdef SOLVER_DIAGNOSTICS if (verbose) printf("%s %d forces %s at %d,%d\n", step==1 ? "row" : "column", step==1 ? start/w : start, mthis[j] == TENT ? "tent" : "non-tent", pos % w, pos / w); #endif soln[pos] = mthis[j]; done_something = TRUE; } } } } if (done_something) continue; if (!done_something) break; } /* * The solver has nothing further it can do. Return 1 if both * soln and sc->links are completely filled in, or 2 otherwise. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) { if (soln[y*w+x] == BLANK) return 2; if (soln[y*w+x] != NONTENT && sc->links[y*w+x] == 0) return 2; } return 1; } static char *new_game_desc(const game_params *params_in, random_state *rs, char **aux, int interactive) { game_params params_copy = *params_in; /* structure copy */ game_params *params = ¶ms_copy; int w = params->w, h = params->h; int ntrees = w * h / 5; char *grid = snewn(w*h, char); char *puzzle = snewn(w*h, char); int *numbers = snewn(w+h, int); char *soln = snewn(w*h, char); int *temp = snewn(2*w*h, int); int maxedges = ntrees*4 + w*h; int *edges = snewn(2*maxedges, int); int *capacity = snewn(maxedges, int); int *flow = snewn(maxedges, int); struct solver_scratch *sc = new_scratch(w, h); char *ret, *p; int i, j, nedges; /* * Since this puzzle has many global deductions and doesn't * permit limited clue sets, generating grids for this puzzle * is hard enough that I see no better option than to simply * generate a solution and see if it's unique and has the * required difficulty. This turns out to be computationally * plausible as well. * * We chose our tree count (hence also tent count) by dividing * the total grid area by five above. Why five? Well, w*h/4 is * the maximum number of tents you can _possibly_ fit into the * grid without violating the separation criterion, and to * achieve that you are constrained to a very small set of * possible layouts (the obvious one with a tent at every * (even,even) coordinate, and trivial variations thereon). So * if we reduce the tent count a bit more, we enable more * random-looking placement; 5 turns out to be a plausible * figure which yields sensible puzzles. Increasing the tent * count would give puzzles whose solutions were too regimented * and could be solved by the use of that knowledge (and would * also take longer to find a viable placement); decreasing it * would make the grids emptier and more boring. * * Actually generating a grid is a matter of first placing the * tents, and then placing the trees by the use of maxflow * (finding a distinct square adjacent to every tent). We do it * this way round because otherwise satisfying the tent * separation condition would become onerous: most randomly * chosen tent layouts do not satisfy this condition, so we'd * have gone to a lot of work before finding that a candidate * layout was unusable. Instead, we place the tents first and * ensure they meet the separation criterion _before_ doing * lots of computation; this works much better. * * The maxflow algorithm is not randomised, so employed naively * it would give rise to grids with clear structure and * directional bias. Hence, I assign the network nodes as seen * by maxflow to be a _random_ permutation of the squares of * the grid, so that any bias shown by maxflow towards * low-numbered nodes is turned into a random bias. * * This generation strategy can fail at many points, including * as early as tent placement (if you get a bad random order in * which to greedily try the grid squares, you won't even * manage to find enough mutually non-adjacent squares to put * the tents in). Then it can fail if maxflow doesn't manage to * find a good enough matching (i.e. the tent placements don't * admit any adequate tree placements); and finally it can fail * if the solver finds that the problem has the wrong * difficulty (including being actually non-unique). All of * these, however, are insufficiently frequent to cause * trouble. */ if (params->diff > DIFF_EASY && params->w <= 4 && params->h <= 4) params->diff = DIFF_EASY; /* downgrade to prevent tight loop */ while (1) { /* * Arrange the grid squares into a random order. */ for (i = 0; i < w*h; i++) temp[i] = i; shuffle(temp, w*h, sizeof(*temp), rs); /* * The first `ntrees' entries in temp which we can get * without making two tents adjacent will be the tent * locations. */ memset(grid, BLANK, w*h); j = ntrees; for (i = 0; i < w*h && j > 0; i++) { int x = temp[i] % w, y = temp[i] / w; int dy, dx, ok = TRUE; for (dy = -1; dy <= +1; dy++) for (dx = -1; dx <= +1; dx++) if (x+dx >= 0 && x+dx < w && y+dy >= 0 && y+dy < h && grid[(y+dy)*w+(x+dx)] == TENT) ok = FALSE; if (ok) { grid[temp[i]] = TENT; j--; } } if (j > 0) continue; /* couldn't place all the tents */ /* * Now we build up the list of graph edges. */ nedges = 0; for (i = 0; i < w*h; i++) { if (grid[temp[i]] == TENT) { for (j = 0; j < w*h; j++) { if (grid[temp[j]] != TENT) { int xi = temp[i] % w, yi = temp[i] / w; int xj = temp[j] % w, yj = temp[j] / w; if (abs(xi-xj) + abs(yi-yj) == 1) { edges[nedges*2] = i; edges[nedges*2+1] = j; capacity[nedges] = 1; nedges++; } } } } else { /* * Special node w*h is the sink node; any non-tent node * has an edge going to it. */ edges[nedges*2] = i; edges[nedges*2+1] = w*h; capacity[nedges] = 1; nedges++; } } /* * Special node w*h+1 is the source node, with an edge going to * every tent. */ for (i = 0; i < w*h; i++) { if (grid[temp[i]] == TENT) { edges[nedges*2] = w*h+1; edges[nedges*2+1] = i; capacity[nedges] = 1; nedges++; } } assert(nedges <= maxedges); /* * Now we're ready to call the maxflow algorithm to place the * trees. */ j = maxflow(w*h+2, w*h+1, w*h, nedges, edges, capacity, flow, NULL); if (j < ntrees) continue; /* couldn't place all the tents */ /* * We've placed the trees. Now we need to work out _where_ * we've placed them, which is a matter of reading back out * from the `flow' array. */ for (i = 0; i < nedges; i++) { if (edges[2*i] < w*h && edges[2*i+1] < w*h && flow[i] > 0) grid[temp[edges[2*i+1]]] = TREE; } /* * I think it looks ugly if there isn't at least one of * _something_ (tent or tree) in each row and each column * of the grid. This doesn't give any information away * since a completely empty row/column is instantly obvious * from the clues (it has no trees and a zero). */ for (i = 0; i < w; i++) { for (j = 0; j < h; j++) { if (grid[j*w+i] != BLANK) break; /* found something in this column */ } if (j == h) break; /* found empty column */ } if (i < w) continue; /* a column was empty */ for (j = 0; j < h; j++) { for (i = 0; i < w; i++) { if (grid[j*w+i] != BLANK) break; /* found something in this row */ } if (i == w) break; /* found empty row */ } if (j < h) continue; /* a row was empty */ /* * Now set up the numbers round the edge. */ for (i = 0; i < w; i++) { int n = 0; for (j = 0; j < h; j++) if (grid[j*w+i] == TENT) n++; numbers[i] = n; } for (i = 0; i < h; i++) { int n = 0; for (j = 0; j < w; j++) if (grid[i*w+j] == TENT) n++; numbers[w+i] = n; } /* * And now actually solve the puzzle, to see whether it's * unique and has the required difficulty. */ for (i = 0; i < w*h; i++) puzzle[i] = grid[i] == TREE ? TREE : BLANK; i = tents_solve(w, h, puzzle, numbers, soln, sc, params->diff-1); j = tents_solve(w, h, puzzle, numbers, soln, sc, params->diff); /* * We expect solving with difficulty params->diff to have * succeeded (otherwise the problem is too hard), and * solving with diff-1 to have failed (otherwise it's too * easy). */ if (i == 2 && j == 1) break; } /* * That's it. Encode as a game ID. */ ret = snewn((w+h)*40 + ntrees + (w*h)/26 + 1, char); p = ret; j = 0; for (i = 0; i <= w*h; i++) { int c = (i < w*h ? grid[i] == TREE : 1); if (c) { *p++ = (j == 0 ? '_' : j-1 + 'a'); j = 0; } else { j++; while (j > 25) { *p++ = 'z'; j -= 25; } } } for (i = 0; i < w+h; i++) p += sprintf(p, ",%d", numbers[i]); *p++ = '\0'; ret = sresize(ret, p - ret, char); /* * And encode the solution as an aux_info. */ *aux = snewn(ntrees * 40, char); p = *aux; *p++ = 'S'; for (i = 0; i < w*h; i++) if (grid[i] == TENT) p += sprintf(p, ";T%d,%d", i%w, i/w); *p++ = '\0'; *aux = sresize(*aux, p - *aux, char); free_scratch(sc); sfree(flow); sfree(capacity); sfree(edges); sfree(temp); sfree(soln); sfree(numbers); sfree(puzzle); sfree(grid); return ret; } static char *validate_desc(const game_params *params, const char *desc) { int w = params->w, h = params->h; int area, i; area = 0; while (*desc && *desc != ',') { if (*desc == '_') area++; else if (*desc >= 'a' && *desc < 'z') area += *desc - 'a' + 2; else if (*desc == 'z') area += 25; else if (*desc == '!' || *desc == '-') /* do nothing */; else return "Invalid character in grid specification"; desc++; } if (area < w * h + 1) return "Not enough data to fill grid"; else if (area > w * h + 1) return "Too much data to fill grid"; for (i = 0; i < w+h; i++) { if (!*desc) return "Not enough numbers given after grid specification"; else if (*desc != ',') return "Invalid character in number list"; desc++; while (*desc && isdigit((unsigned char)*desc)) desc++; } if (*desc) return "Unexpected additional data at end of game description"; 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, char); state->numbers = snew(struct numbers); state->numbers->refcount = 1; state->numbers->numbers = snewn(w+h, int); state->completed = state->used_solve = FALSE; i = 0; memset(state->grid, BLANK, w*h); while (*desc) { int run, type; type = TREE; if (*desc == '_') run = 0; else if (*desc >= 'a' && *desc < 'z') run = *desc - ('a'-1); else if (*desc == 'z') { run = 25; type = BLANK; } else { assert(*desc == '!' || *desc == '-'); run = -1; type = (*desc == '!' ? TENT : NONTENT); } desc++; i += run; assert(i >= 0 && i <= w*h); if (i == w*h) { assert(type == TREE); break; } else { if (type != BLANK) state->grid[i++] = type; } } for (i = 0; i < w+h; i++) { assert(*desc == ','); desc++; state->numbers->numbers[i] = atoi(desc); while (*desc && isdigit((unsigned char)*desc)) desc++; } assert(!*desc); 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, char); memcpy(ret->grid, state->grid, w*h); ret->numbers = state->numbers; state->numbers->refcount++; ret->completed = state->completed; ret->used_solve = state->used_solve; return ret; } static void free_game(game_state *state) { if (--state->numbers->refcount <= 0) { sfree(state->numbers->numbers); sfree(state->numbers); } sfree(state->grid); sfree(state); } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { int w = state->p.w, h = state->p.h; if (aux) { /* * If we already have the solution, save ourselves some * time. */ return dupstr(aux); } else { struct solver_scratch *sc = new_scratch(w, h); char *soln; int ret; char *move, *p; int i; soln = snewn(w*h, char); ret = tents_solve(w, h, state->grid, state->numbers->numbers, soln, sc, DIFFCOUNT-1); free_scratch(sc); if (ret != 1) { sfree(soln); if (ret == 0) *error = "This puzzle is not self-consistent"; else *error = "Unable to find a unique solution for this puzzle"; return NULL; } /* * Construct a move string which turns the current state * into the solved state. */ move = snewn(w*h * 40, char); p = move; *p++ = 'S'; for (i = 0; i < w*h; i++) if (soln[i] == TENT) p += sprintf(p, ";T%d,%d", i%w, i/w); *p++ = '\0'; move = sresize(move, p - move, char); sfree(soln); return move; } } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { int w = state->p.w, h = state->p.h; char *ret, *p; int x, y; /* * FIXME: We currently do not print the numbers round the edges * of the grid. I need to work out a sensible way of doing this * even when the column numbers exceed 9. * * In the absence of those numbers, the result size is h lines * of w+1 characters each, plus a NUL. * * This function is currently only used by the standalone * solver; until I make it look more sensible, I won't enable * it in the main game structure. */ ret = snewn(h*(w+1) + 1, char); p = ret; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { *p = (state->grid[y*w+x] == BLANK ? '.' : state->grid[y*w+x] == TREE ? 'T' : state->grid[y*w+x] == TENT ? '*' : state->grid[y*w+x] == NONTENT ? '-' : '?'); p++; } *p++ = '\n'; } *p++ = '\0'; return ret; } struct game_ui { int dsx, dsy; /* coords of drag start */ int dex, dey; /* coords of drag end */ int drag_button; /* -1 for none, or a button code */ int drag_ok; /* dragged off the window, to cancel */ int cx, cy, cdisp; /* cursor position, and ?display. */ }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->dsx = ui->dsy = -1; ui->dex = ui->dey = -1; ui->drag_button = -1; ui->drag_ok = FALSE; ui->cx = ui->cy = ui->cdisp = 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) { } struct game_drawstate { int tilesize; int started; game_params p; int *drawn, *numbersdrawn; int cx, cy; /* last-drawn cursor pos, or (-1,-1) if absent. */ }; #define PREFERRED_TILESIZE 32 #define TILESIZE (ds->tilesize) #define TLBORDER (TILESIZE/2) #define BRBORDER (TILESIZE*3/2) #define COORD(x) ( (x) * TILESIZE + TLBORDER ) #define FROMCOORD(x) ( ((x) - TLBORDER + TILESIZE) / TILESIZE - 1 ) #define FLASH_TIME 0.30F static int drag_xform(const game_ui *ui, int x, int y, int v) { int xmin, ymin, xmax, ymax; xmin = min(ui->dsx, ui->dex); xmax = max(ui->dsx, ui->dex); ymin = min(ui->dsy, ui->dey); ymax = max(ui->dsy, ui->dey); #ifndef STYLUS_BASED /* * Left-dragging has no effect, so we treat a left-drag as a * single click on dsx,dsy. */ if (ui->drag_button == LEFT_BUTTON) { xmin = xmax = ui->dsx; ymin = ymax = ui->dsy; } #endif if (x < xmin || x > xmax || y < ymin || y > ymax) return v; /* no change outside drag area */ if (v == TREE) return v; /* trees are inviolate always */ if (xmin == xmax && ymin == ymax) { /* * Results of a simple click. Left button sets blanks to * tents; right button sets blanks to non-tents; either * button clears a non-blank square. * If stylus-based however, it loops instead. */ if (ui->drag_button == LEFT_BUTTON) #ifdef STYLUS_BASED v = (v == BLANK ? TENT : (v == TENT ? NONTENT : BLANK)); else v = (v == BLANK ? NONTENT : (v == NONTENT ? TENT : BLANK)); #else v = (v == BLANK ? TENT : BLANK); else v = (v == BLANK ? NONTENT : BLANK); #endif } else { /* * Results of a drag. Left-dragging has no effect. * Right-dragging sets all blank squares to non-tents and * has no effect on anything else. */ if (ui->drag_button == RIGHT_BUTTON) v = (v == BLANK ? NONTENT : v); else #ifdef STYLUS_BASED v = (v == BLANK ? NONTENT : v); #else /* do nothing */; #endif } return v; } static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { int w = state->p.w, h = state->p.h; char tmpbuf[80]; if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { x = FROMCOORD(x); y = FROMCOORD(y); if (x < 0 || y < 0 || x >= w || y >= h) return NULL; ui->drag_button = button; ui->dsx = ui->dex = x; ui->dsy = ui->dey = y; ui->drag_ok = TRUE; ui->cdisp = 0; return ""; /* ui updated */ } if ((IS_MOUSE_DRAG(button) || IS_MOUSE_RELEASE(button)) && ui->drag_button > 0) { int xmin, ymin, xmax, ymax; char *buf, *sep; int buflen, bufsize, tmplen; x = FROMCOORD(x); y = FROMCOORD(y); if (x < 0 || y < 0 || x >= w || y >= h) { ui->drag_ok = FALSE; } else { /* * Drags are limited to one row or column. Hence, we * work out which coordinate is closer to the drag * start, and move it _to_ the drag start. */ if (abs(x - ui->dsx) < abs(y - ui->dsy)) x = ui->dsx; else y = ui->dsy; ui->dex = x; ui->dey = y; ui->drag_ok = TRUE; } if (IS_MOUSE_DRAG(button)) return ""; /* ui updated */ /* * The drag has been released. Enact it. */ if (!ui->drag_ok) { ui->drag_button = -1; return ""; /* drag was just cancelled */ } xmin = min(ui->dsx, ui->dex); xmax = max(ui->dsx, ui->dex); ymin = min(ui->dsy, ui->dey); ymax = max(ui->dsy, ui->dey); assert(0 <= xmin && xmin <= xmax && xmax < w); assert(0 <= ymin && ymin <= ymax && ymax < h); buflen = 0; bufsize = 256; buf = snewn(bufsize, char); sep = ""; for (y = ymin; y <= ymax; y++) for (x = xmin; x <= xmax; x++) { int v = drag_xform(ui, x, y, state->grid[y*w+x]); if (state->grid[y*w+x] != v) { tmplen = sprintf(tmpbuf, "%s%c%d,%d", sep, (int)(v == BLANK ? 'B' : v == TENT ? 'T' : 'N'), x, y); sep = ";"; if (buflen + tmplen >= bufsize) { bufsize = buflen + tmplen + 256; buf = sresize(buf, bufsize, char); } strcpy(buf+buflen, tmpbuf); buflen += tmplen; } } ui->drag_button = -1; /* drag is terminated */ if (buflen == 0) { sfree(buf); return ""; /* ui updated (drag was terminated) */ } else { buf[buflen] = '\0'; return buf; } } if (IS_CURSOR_MOVE(button)) { move_cursor(button, &ui->cx, &ui->cy, w, h, 0); ui->cdisp = 1; return ""; } if (ui->cdisp) { char rep = 0; int v = state->grid[ui->cy*w+ui->cx]; if (v != TREE) { #ifdef SINGLE_CURSOR_SELECT if (button == CURSOR_SELECT) /* SELECT cycles T, N, B */ rep = v == BLANK ? 'T' : v == TENT ? 'N' : 'B'; #else if (button == CURSOR_SELECT) rep = v == BLANK ? 'T' : 'B'; else if (button == CURSOR_SELECT2) rep = v == BLANK ? 'N' : 'B'; else if (button == 'T' || button == 'N' || button == 'B') rep = (char)button; #endif } if (rep) { sprintf(tmpbuf, "%c%d,%d", (int)rep, ui->cx, ui->cy); return dupstr(tmpbuf); } } else if (IS_CURSOR_SELECT(button)) { ui->cdisp = 1; return ""; } return NULL; } static game_state *execute_move(const game_state *state, const char *move) { int w = state->p.w, h = state->p.h; char c; int x, y, m, n, i, j; game_state *ret = dup_game(state); while (*move) { c = *move; if (c == 'S') { int i; ret->used_solve = TRUE; /* * Set all non-tree squares to NONTENT. The rest of the * solve move will fill the tents in over the top. */ for (i = 0; i < w*h; i++) if (ret->grid[i] != TREE) ret->grid[i] = NONTENT; move++; } else if (c == 'B' || c == 'T' || c == 'N') { move++; if (sscanf(move, "%d,%d%n", &x, &y, &n) != 2 || x < 0 || y < 0 || x >= w || y >= h) { free_game(ret); return NULL; } if (ret->grid[y*w+x] == TREE) { free_game(ret); return NULL; } ret->grid[y*w+x] = (c == 'B' ? BLANK : c == 'T' ? TENT : NONTENT); move += n; } else { free_game(ret); return NULL; } if (*move == ';') move++; else if (*move) { free_game(ret); return NULL; } } /* * Check for completion. */ for (i = n = m = 0; i < w*h; i++) { if (ret->grid[i] == TENT) n++; else if (ret->grid[i] == TREE) m++; } if (n == m) { int nedges, maxedges, *edges, *capacity, *flow; /* * We have the right number of tents, which is a * precondition for the game being complete. Now check that * the numbers add up. */ for (i = 0; i < w; i++) { n = 0; for (j = 0; j < h; j++) if (ret->grid[j*w+i] == TENT) n++; if (ret->numbers->numbers[i] != n) goto completion_check_done; } for (i = 0; i < h; i++) { n = 0; for (j = 0; j < w; j++) if (ret->grid[i*w+j] == TENT) n++; if (ret->numbers->numbers[w+i] != n) goto completion_check_done; } /* * Also, check that no two tents are adjacent. */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) { if (x+1 < w && ret->grid[y*w+x] == TENT && ret->grid[y*w+x+1] == TENT) goto completion_check_done; if (y+1 < h && ret->grid[y*w+x] == TENT && ret->grid[(y+1)*w+x] == TENT) goto completion_check_done; if (x+1 < w && y+1 < h) { if (ret->grid[y*w+x] == TENT && ret->grid[(y+1)*w+(x+1)] == TENT) goto completion_check_done; if (ret->grid[(y+1)*w+x] == TENT && ret->grid[y*w+(x+1)] == TENT) goto completion_check_done; } } /* * OK; we have the right number of tents, they match the * numeric clues, and they satisfy the non-adjacency * criterion. Finally, we need to verify that they can be * placed in a one-to-one matching with the trees such that * every tent is orthogonally adjacent to its tree. * * This bit is where the hard work comes in: we have to do * it by finding such a matching using maxflow. * * So we construct a network with one special source node, * one special sink node, one node per tent, and one node * per tree. */ maxedges = 6 * m; edges = snewn(2 * maxedges, int); capacity = snewn(maxedges, int); flow = snewn(maxedges, int); nedges = 0; /* * Node numbering: * * 0..w*h trees/tents * w*h source * w*h+1 sink */ for (y = 0; y < h; y++) for (x = 0; x < w; x++) if (ret->grid[y*w+x] == TREE) { int d; /* * Here we use the direction enum declared for * the solver. We make use of the fact that the * directions are declared in the order * U,L,R,D, meaning that we go through the four * neighbours of any square in numerically * increasing order. */ for (d = 1; d < MAXDIR; d++) { int x2 = x + dx(d), y2 = y + dy(d); if (x2 >= 0 && x2 < w && y2 >= 0 && y2 < h && ret->grid[y2*w+x2] == TENT) { assert(nedges < maxedges); edges[nedges*2] = y*w+x; edges[nedges*2+1] = y2*w+x2; capacity[nedges] = 1; nedges++; } } } else if (ret->grid[y*w+x] == TENT) { assert(nedges < maxedges); edges[nedges*2] = y*w+x; edges[nedges*2+1] = w*h+1; /* edge going to sink */ capacity[nedges] = 1; nedges++; } for (y = 0; y < h; y++) for (x = 0; x < w; x++) if (ret->grid[y*w+x] == TREE) { assert(nedges < maxedges); edges[nedges*2] = w*h; /* edge coming from source */ edges[nedges*2+1] = y*w+x; capacity[nedges] = 1; nedges++; } n = maxflow(w*h+2, w*h, w*h+1, nedges, edges, capacity, flow, NULL); sfree(flow); sfree(capacity); sfree(edges); if (n != m) goto completion_check_done; /* * We haven't managed to fault the grid on any count. Score! */ ret->completed = TRUE; } completion_check_done: 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 = TLBORDER + BRBORDER + TILESIZE * params->w; *y = TLBORDER + BRBORDER + TILESIZE * params->h; } 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_GRASS * 3 + 0] = 0.7F; ret[COL_GRASS * 3 + 1] = 1.0F; ret[COL_GRASS * 3 + 2] = 0.5F; ret[COL_TREETRUNK * 3 + 0] = 0.6F; ret[COL_TREETRUNK * 3 + 1] = 0.4F; ret[COL_TREETRUNK * 3 + 2] = 0.0F; ret[COL_TREELEAF * 3 + 0] = 0.0F; ret[COL_TREELEAF * 3 + 1] = 0.7F; ret[COL_TREELEAF * 3 + 2] = 0.0F; ret[COL_TENT * 3 + 0] = 0.8F; ret[COL_TENT * 3 + 1] = 0.7F; ret[COL_TENT * 3 + 2] = 0.0F; ret[COL_ERROR * 3 + 0] = 1.0F; ret[COL_ERROR * 3 + 1] = 0.0F; ret[COL_ERROR * 3 + 2] = 0.0F; ret[COL_ERRTEXT * 3 + 0] = 1.0F; ret[COL_ERRTEXT * 3 + 1] = 1.0F; ret[COL_ERRTEXT * 3 + 2] = 1.0F; ret[COL_ERRTRUNK * 3 + 0] = 0.6F; ret[COL_ERRTRUNK * 3 + 1] = 0.0F; ret[COL_ERRTRUNK * 3 + 2] = 0.0F; *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->started = FALSE; ds->p = state->p; /* structure copy */ ds->drawn = snewn(w*h, int); for (i = 0; i < w*h; i++) ds->drawn[i] = MAGIC; ds->numbersdrawn = snewn(w+h, int); for (i = 0; i < w+h; i++) ds->numbersdrawn[i] = 2; ds->cx = ds->cy = -1; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->drawn); sfree(ds->numbersdrawn); sfree(ds); } enum { ERR_ADJ_TOPLEFT = 4, ERR_ADJ_TOP, ERR_ADJ_TOPRIGHT, ERR_ADJ_LEFT, ERR_ADJ_RIGHT, ERR_ADJ_BOTLEFT, ERR_ADJ_BOT, ERR_ADJ_BOTRIGHT, ERR_OVERCOMMITTED }; static int *find_errors(const game_state *state, char *grid) { int w = state->p.w, h = state->p.h; int *ret = snewn(w*h + w + h, int); int *tmp = snewn(w*h*2, int), *dsf = tmp + w*h; int x, y; /* * This function goes through a grid and works out where to * highlight play errors in red. The aim is that it should * produce at least one error highlight for any complete grid * (or complete piece of grid) violating a puzzle constraint, so * that a grid containing no BLANK squares is either a win or is * marked up in some way that indicates why not. * * So it's easy enough to highlight errors in the numeric clues * - just light up any row or column number which is not * fulfilled - and it's just as easy to highlight adjacent * tents. The difficult bit is highlighting failures in the * tent/tree matching criterion. * * A natural approach would seem to be to apply the maxflow * algorithm to find the tent/tree matching; if this fails, it * must necessarily terminate with a min-cut which can be * reinterpreted as some set of trees which have too few tents * between them (or vice versa). However, it's bad for * localising errors, because it's not easy to make the * algorithm narrow down to the _smallest_ such set of trees: if * trees A and B have only one tent between them, for instance, * it might perfectly well highlight not only A and B but also * trees C and D which are correctly matched on the far side of * the grid, on the grounds that those four trees between them * have only three tents. * * Also, that approach fares badly when you introduce the * additional requirement that incomplete grids should have * errors highlighted only when they can be proved to be errors * - so that trees should not be marked as having too few tents * if there are enough BLANK squares remaining around them that * could be turned into the missing tents (to do so would be * patronising, since the overwhelming likelihood is not that * the player has forgotten to put a tree there but that they * have merely not put one there _yet_). However, tents with too * few trees can be marked immediately, since those are * definitely player error. * * So I adopt an alternative approach, which is to consider the * bipartite adjacency graph between trees and tents * ('bipartite' in the sense that for these purposes I * deliberately ignore two adjacent trees or two adjacent * tents), divide that graph up into its connected components * using a dsf, and look for components which contain different * numbers of trees and tents. This allows me to highlight * groups of tents with too few trees between them immediately, * and then in order to find groups of trees with too few tents * I redo the same process but counting BLANKs as potential * tents (so that the only trees highlighted are those * surrounded by enough NONTENTs to make it impossible to give * them enough tents). * * However, this technique is incomplete: it is not a sufficient * condition for the existence of a perfect matching that every * connected component of the graph has the same number of tents * and trees. An example of a graph which satisfies the latter * condition but still has no perfect matching is * * A B C * | / ,/| * | / ,'/ | * | / ,' / | * |/,' / | * 1 2 3 * * which can be realised in Tents as * * B * A 1 C 2 * 3 * * The matching-error highlighter described above will not mark * this construction as erroneous. However, something else will: * the three tents in the above diagram (let us suppose A,B,C * are the tents, though it doesn't matter which) contain two * diagonally adjacent pairs. So there will be _an_ error * highlighted for the above layout, even though not all types * of error will be highlighted. * * And in fact we can prove that this will always be the case: * that the shortcomings of the matching-error highlighter will * always be made up for by the easy tent adjacency highlighter. * * Lemma: Let G be a bipartite graph between n trees and n * tents, which is connected, and in which no tree has degree * more than two (but a tent may). Then G has a perfect matching. * * (Note: in the statement and proof of the Lemma I will * consistently use 'tree' to indicate a type of graph vertex as * opposed to a tent, and not to indicate a tree in the graph- * theoretic sense.) * * Proof: * * If we can find a tent of degree 1 joined to a tree of degree * 2, then any perfect matching must pair that tent with that * tree. Hence, we can remove both, leaving a smaller graph G' * which still satisfies all the conditions of the Lemma, and * which has a perfect matching iff G does. * * So, wlog, we may assume G contains no tent of degree 1 joined * to a tree of degree 2; if it does, we can reduce it as above. * * If G has no tent of degree 1 at all, then every tent has * degree at least two, so there are at least 2n edges in the * graph. But every tree has degree at most two, so there are at * most 2n edges. Hence there must be exactly 2n edges, so every * tree and every tent must have degree exactly two, which means * that the whole graph consists of a single loop (by * connectedness), and therefore certainly has a perfect * matching. * * Alternatively, if G does have a tent of degree 1 but it is * not connected to a tree of degree 2, then the tree it is * connected to must have degree 1 - and, by connectedness, that * must mean that that tent and that tree between them form the * entire graph. This trivial graph has a trivial perfect * matching. [] * * That proves the lemma. Hence, in any case where the matching- * error highlighter fails to highlight an erroneous component * (because it has the same number of tents as trees, but they * cannot be matched up), the above lemma tells us that there * must be a tree with degree more than 2, i.e. a tree * orthogonally adjacent to at least three tents. But in that * case, there must be some pair of those three tents which are * diagonally adjacent to each other, so the tent-adjacency * highlighter will necessarily show an error. So any filled * layout in Tents which is not a correct solution to the puzzle * must have _some_ error highlighted by the subroutine below. * * (Of course it would be nicer if we could highlight all * errors: in the above example layout, we would like to * highlight tents A,B as having too few trees between them, and * trees 2,3 as having too few tents, in addition to marking the * adjacency problems. But I can't immediately think of any way * to find the smallest sets of such tents and trees without an * O(2^N) loop over all subsets of a given component.) */ /* * ret[0] through to ret[w*h-1] give error markers for the grid * squares. After that, ret[w*h] to ret[w*h+w-1] give error * markers for the column numbers, and ret[w*h+w] to * ret[w*h+w+h-1] for the row numbers. */ /* * Spot tent-adjacency violations. */ for (x = 0; x < w*h; x++) ret[x] = 0; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { if (y+1 < h && x+1 < w && ((grid[y*w+x] == TENT && grid[(y+1)*w+(x+1)] == TENT) || (grid[(y+1)*w+x] == TENT && grid[y*w+(x+1)] == TENT))) { ret[y*w+x] |= 1 << ERR_ADJ_BOTRIGHT; ret[(y+1)*w+x] |= 1 << ERR_ADJ_TOPRIGHT; ret[y*w+(x+1)] |= 1 << ERR_ADJ_BOTLEFT; ret[(y+1)*w+(x+1)] |= 1 << ERR_ADJ_TOPLEFT; } if (y+1 < h && grid[y*w+x] == TENT && grid[(y+1)*w+x] == TENT) { ret[y*w+x] |= 1 << ERR_ADJ_BOT; ret[(y+1)*w+x] |= 1 << ERR_ADJ_TOP; } if (x+1 < w && grid[y*w+x] == TENT && grid[y*w+(x+1)] == TENT) { ret[y*w+x] |= 1 << ERR_ADJ_RIGHT; ret[y*w+(x+1)] |= 1 << ERR_ADJ_LEFT; } } } /* * Spot numeric clue violations. */ for (x = 0; x < w; x++) { int tents = 0, maybetents = 0; for (y = 0; y < h; y++) { if (grid[y*w+x] == TENT) tents++; else if (grid[y*w+x] == BLANK) maybetents++; } ret[w*h+x] = (tents > state->numbers->numbers[x] || tents + maybetents < state->numbers->numbers[x]); } for (y = 0; y < h; y++) { int tents = 0, maybetents = 0; for (x = 0; x < w; x++) { if (grid[y*w+x] == TENT) tents++; else if (grid[y*w+x] == BLANK) maybetents++; } ret[w*h+w+y] = (tents > state->numbers->numbers[w+y] || tents + maybetents < state->numbers->numbers[w+y]); } /* * Identify groups of tents with too few trees between them, * which we do by constructing the connected components of the * bipartite adjacency graph between tents and trees * ('bipartite' in the sense that we deliberately ignore * adjacency between tents or between trees), and highlighting * all the tents in any component which has a smaller tree * count. */ dsf_init(dsf, w*h); /* Construct the equivalence classes. */ for (y = 0; y < h; y++) { for (x = 0; x < w-1; x++) { if ((grid[y*w+x] == TREE && grid[y*w+x+1] == TENT) || (grid[y*w+x] == TENT && grid[y*w+x+1] == TREE)) dsf_merge(dsf, y*w+x, y*w+x+1); } } for (y = 0; y < h-1; y++) { for (x = 0; x < w; x++) { if ((grid[y*w+x] == TREE && grid[(y+1)*w+x] == TENT) || (grid[y*w+x] == TENT && grid[(y+1)*w+x] == TREE)) dsf_merge(dsf, y*w+x, (y+1)*w+x); } } /* Count up the tent/tree difference in each one. */ for (x = 0; x < w*h; x++) tmp[x] = 0; for (x = 0; x < w*h; x++) { y = dsf_canonify(dsf, x); if (grid[x] == TREE) tmp[y]++; else if (grid[x] == TENT) tmp[y]--; } /* And highlight any tent belonging to an equivalence class with * a score less than zero. */ for (x = 0; x < w*h; x++) { y = dsf_canonify(dsf, x); if (grid[x] == TENT && tmp[y] < 0) ret[x] |= 1 << ERR_OVERCOMMITTED; } /* * Identify groups of trees with too few tents between them. * This is done similarly, except that we now count BLANK as * equivalent to TENT, i.e. we only highlight such trees when * the user hasn't even left _room_ to provide tents for them * all. (Otherwise, we'd highlight all trees red right at the * start of the game, before the user had done anything wrong!) */ #define TENT(x) ((x)==TENT || (x)==BLANK) dsf_init(dsf, w*h); /* Construct the equivalence classes. */ for (y = 0; y < h; y++) { for (x = 0; x < w-1; x++) { if ((grid[y*w+x] == TREE && TENT(grid[y*w+x+1])) || (TENT(grid[y*w+x]) && grid[y*w+x+1] == TREE)) dsf_merge(dsf, y*w+x, y*w+x+1); } } for (y = 0; y < h-1; y++) { for (x = 0; x < w; x++) { if ((grid[y*w+x] == TREE && TENT(grid[(y+1)*w+x])) || (TENT(grid[y*w+x]) && grid[(y+1)*w+x] == TREE)) dsf_merge(dsf, y*w+x, (y+1)*w+x); } } /* Count up the tent/tree difference in each one. */ for (x = 0; x < w*h; x++) tmp[x] = 0; for (x = 0; x < w*h; x++) { y = dsf_canonify(dsf, x); if (grid[x] == TREE) tmp[y]++; else if (TENT(grid[x])) tmp[y]--; } /* And highlight any tree belonging to an equivalence class with * a score more than zero. */ for (x = 0; x < w*h; x++) { y = dsf_canonify(dsf, x); if (grid[x] == TREE && tmp[y] > 0) ret[x] |= 1 << ERR_OVERCOMMITTED; } #undef TENT sfree(tmp); return ret; } static void draw_err_adj(drawing *dr, game_drawstate *ds, int x, int y) { int coords[8]; int yext, xext; /* * Draw a diamond. */ coords[0] = x - TILESIZE*2/5; coords[1] = y; coords[2] = x; coords[3] = y - TILESIZE*2/5; coords[4] = x + TILESIZE*2/5; coords[5] = y; coords[6] = x; coords[7] = y + TILESIZE*2/5; draw_polygon(dr, coords, 4, COL_ERROR, COL_GRID); /* * Draw an exclamation mark in the diamond. This turns out to * look unpleasantly off-centre if done via draw_text, so I do * it by hand on the basis that exclamation marks aren't that * difficult to draw... */ xext = TILESIZE/16; yext = TILESIZE*2/5 - (xext*2+2); draw_rect(dr, x-xext, y-yext, xext*2+1, yext*2+1 - (xext*3), COL_ERRTEXT); draw_rect(dr, x-xext, y+yext-xext*2+1, xext*2+1, xext*2, COL_ERRTEXT); } static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, int v, int cur, int printing) { int err; int tx = COORD(x), ty = COORD(y); int cx = tx + TILESIZE/2, cy = ty + TILESIZE/2; err = v & ~15; v &= 15; clip(dr, tx, ty, TILESIZE, TILESIZE); if (!printing) { draw_rect(dr, tx, ty, TILESIZE, TILESIZE, COL_GRID); draw_rect(dr, tx+1, ty+1, TILESIZE-1, TILESIZE-1, (v == BLANK ? COL_BACKGROUND : COL_GRASS)); } if (v == TREE) { int i; (printing ? draw_rect_outline : draw_rect) (dr, cx-TILESIZE/15, ty+TILESIZE*3/10, 2*(TILESIZE/15)+1, (TILESIZE*9/10 - TILESIZE*3/10), (err & (1<p.w, h = state->p.h; int x, y, flashing; int cx = -1, cy = -1; int cmoved = 0; char *tmpgrid; int *errors; if (ui) { if (ui->cdisp) { cx = ui->cx; cy = ui->cy; } if (cx != ds->cx || cy != ds->cy) cmoved = 1; } if (printing || !ds->started) { if (!printing) { int ww, wh; game_compute_size(&state->p, TILESIZE, &ww, &wh); draw_rect(dr, 0, 0, ww, wh, COL_BACKGROUND); draw_update(dr, 0, 0, ww, wh); ds->started = TRUE; } if (printing) print_line_width(dr, TILESIZE/64); /* * Draw the grid. */ for (y = 0; y <= h; y++) draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), COL_GRID); for (x = 0; x <= w; x++) draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), COL_GRID); } if (flashtime > 0) flashing = (int)(flashtime * 3 / FLASH_TIME) != 1; else flashing = FALSE; /* * Find errors. For this we use _part_ of the information from a * currently active drag: we transform dsx,dsy but not anything * else. (This seems to strike a good compromise between having * the error highlights respond instantly to single clicks, but * not giving constant feedback during a right-drag.) */ if (ui && ui->drag_button >= 0) { tmpgrid = snewn(w*h, char); memcpy(tmpgrid, state->grid, w*h); tmpgrid[ui->dsy * w + ui->dsx] = drag_xform(ui, ui->dsx, ui->dsy, tmpgrid[ui->dsy * w + ui->dsx]); errors = find_errors(state, tmpgrid); sfree(tmpgrid); } else { errors = find_errors(state, state->grid); } /* * Draw the grid. */ for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { int v = state->grid[y*w+x]; int credraw = 0; /* * We deliberately do not take drag_ok into account * here, because user feedback suggests that it's * marginally nicer not to have the drag effects * flickering on and off disconcertingly. */ if (ui && ui->drag_button >= 0) v = drag_xform(ui, x, y, v); if (flashing && (v == TREE || v == TENT)) v = NONTENT; if (cmoved) { if ((x == cx && y == cy) || (x == ds->cx && y == ds->cy)) credraw = 1; } v |= errors[y*w+x]; if (printing || ds->drawn[y*w+x] != v || credraw) { draw_tile(dr, ds, x, y, v, (x == cx && y == cy), printing); if (!printing) ds->drawn[y*w+x] = v; } } } /* * Draw (or redraw, if their error-highlighted state has * changed) the numbers. */ for (x = 0; x < w; x++) { if (printing || ds->numbersdrawn[x] != errors[w*h+x]) { char buf[80]; draw_rect(dr, COORD(x), COORD(h)+1, TILESIZE, BRBORDER-1, COL_BACKGROUND); sprintf(buf, "%d", state->numbers->numbers[x]); draw_text(dr, COORD(x) + TILESIZE/2, COORD(h+1), FONT_VARIABLE, TILESIZE/2, ALIGN_HCENTRE|ALIGN_VNORMAL, (errors[w*h+x] ? COL_ERROR : COL_GRID), buf); draw_update(dr, COORD(x), COORD(h)+1, TILESIZE, BRBORDER-1); if (!printing) ds->numbersdrawn[x] = errors[w*h+x]; } } for (y = 0; y < h; y++) { if (printing || ds->numbersdrawn[w+y] != errors[w*h+w+y]) { char buf[80]; draw_rect(dr, COORD(w)+1, COORD(y), BRBORDER-1, TILESIZE, COL_BACKGROUND); sprintf(buf, "%d", state->numbers->numbers[w+y]); draw_text(dr, COORD(w+1), COORD(y) + TILESIZE/2, FONT_VARIABLE, TILESIZE/2, ALIGN_HRIGHT|ALIGN_VCENTRE, (errors[w*h+w+y] ? COL_ERROR : COL_GRID), buf); draw_update(dr, COORD(w)+1, COORD(y), BRBORDER-1, TILESIZE); if (!printing) ds->numbersdrawn[w+y] = errors[w*h+w+y]; } } if (cmoved) { ds->cx = cx; ds->cy = cy; } sfree(errors); } 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_redraw(dr, ds, oldstate, state, dir, ui, animtime, flashtime, FALSE); } 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->used_solve && !newstate->used_solve) return FLASH_TIME; return 0.0F; } static int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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) { int pw, ph; /* * I'll use 6mm squares by default. */ game_compute_size(params, 600, &pw, &ph); *x = pw / 100.0F; *y = ph / 100.0F; } static void game_print(drawing *dr, const game_state *state, int tilesize) { int c; /* Ick: fake up `ds->tilesize' for macro expansion purposes */ game_drawstate ads, *ds = &ads; game_set_size(dr, ds, NULL, tilesize); c = print_mono_colour(dr, 1); assert(c == COL_BACKGROUND); c = print_mono_colour(dr, 0); assert(c == COL_GRID); c = print_mono_colour(dr, 1); assert(c == COL_GRASS); c = print_mono_colour(dr, 0); assert(c == COL_TREETRUNK); c = print_mono_colour(dr, 0); assert(c == COL_TREELEAF); c = print_mono_colour(dr, 0); assert(c == COL_TENT); int_redraw(dr, ds, NULL, state, +1, NULL, 0.0F, 0.0F, TRUE); } #ifdef COMBINED #define thegame tents #endif const struct game thegame = { "Tents", "games.tents", "tents", default_params, game_fetch_preset, 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, FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, decode_ui, 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_status, TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, REQUIRE_RBUTTON, /* flags */ }; #ifdef STANDALONE_SOLVER #include int main(int argc, char **argv) { game_params *p; game_state *s, *s2; char *id = NULL, *desc, *err; int grade = FALSE; int ret, diff, really_verbose = FALSE; struct solver_scratch *sc; while (--argc > 0) { char *p = *++argv; if (!strcmp(p, "-v")) { really_verbose = 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); s2 = new_game(NULL, p, desc); sc = new_scratch(p->w, p->h); /* * When solving an Easy 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 */ for (diff = 0; diff < DIFFCOUNT; diff++) { ret = tents_solve(p->w, p->h, s->grid, s->numbers->numbers, s2->grid, sc, diff); if (ret < 2) break; } if (diff == DIFFCOUNT) { if (grade) printf("Difficulty rating: too hard to solve internally\n"); else printf("Unable to find a unique solution\n"); } else { if (grade) { if (ret == 0) printf("Difficulty rating: impossible (no solution exists)\n"); else if (ret == 1) printf("Difficulty rating: %s\n", tents_diffnames[diff]); } else { verbose = really_verbose; ret = tents_solve(p->w, p->h, s->grid, s->numbers->numbers, s2->grid, sc, diff); if (ret == 0) printf("Puzzle is inconsistent\n"); else fputs(game_text_format(s2), stdout); } } return 0; } #endif /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/towers.c0000644000175300017530000014275412132232554014130 0ustar simonsimon/* * towers.c: the puzzle also known as 'Skyscrapers'. * * Possible future work: * * - Relax the upper bound on grid size at 9? * + I'd need TOCHAR and FROMCHAR macros a bit like group's, to * be used wherever this code has +'0' or -'0' * + the pencil marks in the drawstate would need a separate * word to live in * + the clues outside the grid would have to cope with being * multi-digit, meaning in particular that the text formatting * would become more unpleasant * + most importantly, though, the solver just isn't fast * enough. Even at size 9 it can't really do the solver_hard * factorial-time enumeration at a sensible rate. Easy puzzles * higher than that would be possible, but more latin-squarey * than skyscrapery, as it were. * * - UI work? * + Allow the user to mark a clue as 'spent' in some way once * it's no longer interesting (typically because no * arrangement of the remaining possibilities _can_ violate * it)? */ #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(EASY,Easy,solver_easy,e) \ 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 towers_diffnames[] = { DIFFLIST(TITLE) }; static char const towers_diffchars[] = DIFFLIST(ENCODE); #define DIFFCONFIG DIFFLIST(CONFIG) enum { COL_BACKGROUND, COL_GRID, COL_USER, COL_HIGHLIGHT, COL_ERROR, COL_PENCIL, NCOLOURS }; struct game_params { int w, diff; }; struct clues { int refcount; int w; /* * An array of 4w integers, of which: * - the first w run across the top * - the next w across the bottom * - the third w down the left * - the last w down the right. */ int *clues; /* * An array of w*w digits. */ digit *immutable; }; /* * Macros to compute clue indices and coordinates. */ #define STARTSTEP(start, step, index, w) do { \ if (index < w) \ start = index, step = w; \ else if (index < 2*w) \ start = (w-1)*w+(index-w), step = -w; \ else if (index < 3*w) \ start = w*(index-2*w), step = 1; \ else \ start = w*(index-3*w)+(w-1), step = -1; \ } while (0) #define CSTARTSTEP(start, step, index, w) \ STARTSTEP(start, step, (((index)+2*w)%(4*w)), w) #define CLUEPOS(x, y, index, w) do { \ if (index < w) \ x = index, y = -1; \ else if (index < 2*w) \ x = index-w, y = w; \ else if (index < 3*w) \ x = -1, y = index-2*w; \ else \ x = w, y = index-3*w; \ } while (0) #ifdef STANDALONE_SOLVER static const char *const cluepos[] = { "above column", "below column", "left of row", "right of row" }; #endif struct game_state { game_params par; struct clues *clues; digit *grid; int *pencil; /* bitmaps using bits 1<<1..1<w = 5; ret->diff = DIFF_EASY; return ret; } const static struct game_params towers_presets[] = { { 4, DIFF_EASY }, { 5, DIFF_EASY }, { 5, DIFF_HARD }, { 6, DIFF_EASY }, { 6, DIFF_HARD }, { 6, DIFF_EXTREME }, { 6, DIFF_UNREASONABLE }, }; static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char buf[80]; if (i < 0 || i >= lenof(towers_presets)) return FALSE; ret = snew(game_params); *ret = towers_presets[i]; /* structure copy */ sprintf(buf, "%dx%d %s", ret->w, ret->w, towers_diffnames[ret->diff]); *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++; if (*p == 'd') { int i; p++; params->diff = DIFFCOUNT+1; /* ...which is invalid */ if (*p) { for (i = 0; i < DIFFCOUNT; i++) { if (*p == towers_diffchars[i]) params->diff = i; } p++; } } } static char *encode_params(const game_params *params, int full) { char ret[80]; sprintf(ret, "%d", params->w); if (full) sprintf(ret + strlen(ret), "d%c", towers_diffchars[params->diff]); return dupstr(ret); } static config_item *game_configure(const game_params *params) { config_item *ret; char buf[80]; ret = snewn(3, config_item); ret[0].name = "Grid size"; ret[0].type = C_STRING; sprintf(buf, "%d", params->w); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Difficulty"; ret[1].type = C_CHOICES; ret[1].sval = DIFFCONFIG; ret[1].ival = params->diff; ret[2].name = NULL; ret[2].type = C_END; ret[2].sval = NULL; ret[2].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->diff = cfg[1].ival; return ret; } static char *validate_params(const game_params *params, int full) { if (params->w < 3 || params->w > 9) return "Grid size must be between 3 and 9"; if (params->diff >= DIFFCOUNT) return "Unknown difficulty rating"; return NULL; } /* ---------------------------------------------------------------------- * Solver. */ struct solver_ctx { int w, diff; int started; int *clues; long *iscratch; int *dscratch; }; static int solver_easy(struct latin_solver *solver, void *vctx) { struct solver_ctx *ctx = (struct solver_ctx *)vctx; int w = ctx->w; int c, i, j, n, m, furthest; int start, step, cstart, cstep, clue, pos, cpos; int ret = 0; #ifdef STANDALONE_SOLVER char prefix[256]; #endif if (!ctx->started) { ctx->started = TRUE; /* * One-off loop to help get started: when a pair of facing * clues sum to w+1, it must mean that the row consists of * two increasing sequences back to back, so we can * immediately place the highest digit by knowing the * lengths of those two sequences. */ for (c = 0; c < 3*w; c = (c == w-1 ? 2*w : c+1)) { int c2 = c + w; if (ctx->clues[c] && ctx->clues[c2] && ctx->clues[c] + ctx->clues[c2] == w+1) { STARTSTEP(start, step, c, w); CSTARTSTEP(cstart, cstep, c, w); pos = start + (ctx->clues[c]-1)*step; cpos = cstart + (ctx->clues[c]-1)*cstep; if (solver->cube[cpos*w+w-1]) { #ifdef STANDALONE_SOLVER if (solver_show_working) { printf("%*sfacing clues on %s %d are maximal:\n", solver_recurse_depth*4, "", c>=2*w ? "row" : "column", c % w + 1); printf("%*s placing %d at (%d,%d)\n", solver_recurse_depth*4, "", w, pos%w+1, pos/w+1); } #endif latin_solver_place(solver, pos%w, pos/w, w); ret = 1; } else { ret = -1; } } } if (ret) return ret; } /* * Go over every clue doing reasonably simple heuristic * deductions. */ for (c = 0; c < 4*w; c++) { clue = ctx->clues[c]; if (!clue) continue; STARTSTEP(start, step, c, w); CSTARTSTEP(cstart, cstep, c, w); /* Find the location of each number in the row. */ for (i = 0; i < w; i++) ctx->dscratch[i] = w; for (i = 0; i < w; i++) if (solver->grid[start+i*step]) ctx->dscratch[solver->grid[start+i*step]-1] = i; n = m = 0; furthest = w; for (i = w; i >= 1; i--) { if (ctx->dscratch[i-1] == w) { break; } else if (ctx->dscratch[i-1] < furthest) { furthest = ctx->dscratch[i-1]; m = i; n++; } } if (clue == n+1 && furthest > 1) { #ifdef STANDALONE_SOLVER if (solver_show_working) sprintf(prefix, "%*sclue %s %d is nearly filled:\n", solver_recurse_depth*4, "", cluepos[c/w], c%w+1); else prefix[0] = '\0'; /* placate optimiser */ #endif /* * We can already see an increasing sequence of the very * highest numbers, of length one less than that * specified in the clue. All of those numbers _must_ be * part of the clue sequence, so the number right next * to the clue must be the final one - i.e. it must be * bigger than any of the numbers between it and m. This * allows us to rule out small numbers in that square. * * (This is a generalisation of the obvious deduction * that when you see a clue saying 1, it must be right * next to the largest possible number; and similarly, * when you see a clue saying 2 opposite that, it must * be right next to the second-largest.) */ j = furthest-1; /* number of small numbers we can rule out */ for (i = 1; i <= w && j > 0; i++) { if (ctx->dscratch[i-1] < w && ctx->dscratch[i-1] >= furthest) continue; /* skip this number, it's elsewhere */ j--; if (solver->cube[cstart*w+i-1]) { #ifdef STANDALONE_SOLVER if (solver_show_working) { printf("%s%*s ruling out %d at (%d,%d)\n", prefix, solver_recurse_depth*4, "", i, start%w+1, start/w+1); prefix[0] = '\0'; } #endif solver->cube[cstart*w+i-1] = 0; ret = 1; } } } if (ret) return ret; #ifdef STANDALONE_SOLVER if (solver_show_working) sprintf(prefix, "%*slower bounds for clue %s %d:\n", solver_recurse_depth*4, "", cluepos[c/w], c%w+1); else prefix[0] = '\0'; /* placate optimiser */ #endif i = 0; for (n = w; n > 0; n--) { /* * The largest number cannot occur in the first (clue-1) * squares of the row, or else there wouldn't be space * for a sufficiently long increasing sequence which it * terminated. The second-largest number (not counting * any that are known to be on the far side of a larger * number and hence excluded from this sequence) cannot * occur in the first (clue-2) squares, similarly, and * so on. */ if (ctx->dscratch[n-1] < w) { for (m = n+1; m < w; m++) if (ctx->dscratch[m] < ctx->dscratch[n-1]) break; if (m < w) continue; /* this number doesn't count */ } for (j = 0; j < clue - i - 1; j++) if (solver->cube[(cstart + j*cstep)*w+n-1]) { #ifdef STANDALONE_SOLVER if (solver_show_working) { int pos = start+j*step; printf("%s%*s ruling out %d at (%d,%d)\n", prefix, solver_recurse_depth*4, "", n, pos%w+1, pos/w+1); prefix[0] = '\0'; } #endif solver->cube[(cstart + j*cstep)*w+n-1] = 0; ret = 1; } i++; } } if (ret) return ret; return 0; } static int solver_hard(struct latin_solver *solver, void *vctx) { struct solver_ctx *ctx = (struct solver_ctx *)vctx; int w = ctx->w; int c, i, j, n, best, clue, start, step, ret; long bitmap; #ifdef STANDALONE_SOLVER char prefix[256]; #endif /* * Go over every clue analysing all possibilities. */ for (c = 0; c < 4*w; c++) { clue = ctx->clues[c]; if (!clue) continue; CSTARTSTEP(start, step, c, w); for (i = 0; i < w; i++) ctx->iscratch[i] = 0; /* * Instead of a tedious physical recursion, I iterate in the * scratch array through all possibilities. At any given * moment, i indexes the element of the box that will next * be incremented. */ i = 0; ctx->dscratch[i] = 0; best = n = 0; bitmap = 0; while (1) { if (i < w) { /* * Find the next valid value for cell i. */ int limit = (n == clue ? best : w); int pos = start + step * i; for (j = ctx->dscratch[i] + 1; j <= limit; j++) { if (bitmap & (1L << j)) continue; /* used this one already */ if (!solver->cube[pos*w+j-1]) continue; /* ruled out already */ /* Found one. */ break; } if (j > limit) { /* No valid values left; drop back. */ i--; if (i < 0) break; /* overall iteration is finished */ bitmap &= ~(1L << ctx->dscratch[i]); if (ctx->dscratch[i] == best) { n--; best = 0; for (j = 0; j < i; j++) if (best < ctx->dscratch[j]) best = ctx->dscratch[j]; } } else { /* Got a valid value; store it and move on. */ bitmap |= 1L << j; ctx->dscratch[i++] = j; if (j > best) { best = j; n++; } ctx->dscratch[i] = 0; } } else { if (n == clue) { for (j = 0; j < w; j++) ctx->iscratch[j] |= 1L << ctx->dscratch[j]; } i--; bitmap &= ~(1L << ctx->dscratch[i]); if (ctx->dscratch[i] == best) { n--; best = 0; for (j = 0; j < i; j++) if (best < ctx->dscratch[j]) best = ctx->dscratch[j]; } } } #ifdef STANDALONE_SOLVER if (solver_show_working) sprintf(prefix, "%*sexhaustive analysis of clue %s %d:\n", solver_recurse_depth*4, "", cluepos[c/w], c%w+1); else prefix[0] = '\0'; /* placate optimiser */ #endif ret = 0; for (i = 0; i < w; i++) { int pos = start + step * i; for (j = 1; j <= w; j++) { if (solver->cube[pos*w+j-1] && !(ctx->iscratch[i] & (1L << j))) { #ifdef STANDALONE_SOLVER if (solver_show_working) { printf("%s%*s ruling out %d at (%d,%d)\n", prefix, solver_recurse_depth*4, "", j, pos/w+1, pos%w+1); prefix[0] = '\0'; } #endif solver->cube[pos*w+j-1] = 0; ret = 1; } } /* * Once we find one clue we can do something with in * this way, revert to trying easier deductions, so as * not to generate solver diagnostics that make the * problem look harder than it is. */ if (ret) return ret; } } return 0; } #define SOLVER(upper,title,func,lower) func, static usersolver_t const towers_solvers[] = { DIFFLIST(SOLVER) }; static int solver(int w, int *clues, digit *soln, int maxdiff) { int ret; struct solver_ctx ctx; ctx.w = w; ctx.diff = maxdiff; ctx.clues = clues; ctx.started = FALSE; ctx.iscratch = snewn(w, long); ctx.dscratch = snewn(w+1, int); ret = latin_solver(soln, w, maxdiff, DIFF_EASY, DIFF_HARD, DIFF_EXTREME, DIFF_EXTREME, DIFF_UNREASONABLE, towers_solvers, &ctx, NULL, NULL); sfree(ctx.iscratch); sfree(ctx.dscratch); return ret; } /* ---------------------------------------------------------------------- * Grid generation. */ static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { int w = params->w, a = w*w; digit *grid, *soln, *soln2; int *clues, *order; int i, ret; int diff = params->diff; 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 e h x u; do for i in {3..9}; do echo -n "./towers --generate 1 ${i}d${d}: " perl -e 'alarm 30; exec @ARGV' ./towers --generate 1 ${i}d${d} >/dev/null \ && echo ok 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 (diff > DIFF_HARD && w <= 3) diff = DIFF_HARD; grid = NULL; clues = snewn(4*w, int); soln = snewn(a, digit); soln2 = snewn(a, digit); order = snewn(max(4*w,a), int); while (1) { /* * Construct a latin square to be the solution. */ sfree(grid); grid = latin_generate(w, rs); /* * Fill in the clues. */ for (i = 0; i < 4*w; i++) { int start, step, j, k, best; STARTSTEP(start, step, i, w); k = best = 0; for (j = 0; j < w; j++) { if (grid[start+j*step] > best) { best = grid[start+j*step]; k++; } } clues[i] = k; } /* * Remove the grid numbers and then the clues, one by one, * for as long as the game remains soluble at the given * difficulty. */ memcpy(soln, grid, a); if (diff == DIFF_EASY && w <= 5) { /* * Special case: for Easy-mode grids that are small * enough, it's nice to be able to find completely empty * grids. */ memset(soln2, 0, a); ret = solver(w, clues, soln2, diff); if (ret > diff) continue; } for (i = 0; i < a; i++) order[i] = i; shuffle(order, a, sizeof(*order), rs); for (i = 0; i < a; i++) { int j = order[i]; memcpy(soln2, grid, a); soln2[j] = 0; ret = solver(w, clues, soln2, diff); if (ret <= diff) grid[j] = 0; } if (diff > DIFF_EASY) { /* leave all clues on Easy mode */ for (i = 0; i < 4*w; i++) order[i] = i; shuffle(order, 4*w, sizeof(*order), rs); for (i = 0; i < 4*w; i++) { int j = order[i]; int clue = clues[j]; memcpy(soln2, grid, a); clues[j] = 0; ret = solver(w, clues, soln2, diff); if (ret > diff) clues[j] = clue; } } /* * See if the game can be solved at the specified difficulty * level, but not at the one below. */ memcpy(soln2, grid, a); ret = solver(w, clues, soln2, diff); if (ret != diff) continue; /* go round again */ /* * We've got a usable puzzle! */ break; } /* * Encode the puzzle description. */ desc = snewn(40*a, char); p = desc; for (i = 0; i < 4*w; i++) { p += sprintf(p, "%s%.0d", i?"/":"", clues[i]); } for (i = 0; i < a; i++) if (grid[i]) break; if (i < a) { int run = 0; *p++ = ','; for (i = 0; i <= a; i++) { int n = (i < a ? grid[i] : -1); if (!n) run++; else { if (run) { while (run > 0) { int thisrun = min(run, 26); *p++ = thisrun - 1 + 'a'; run -= thisrun; } } 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 (i > 0 && n > 0) *p++ = '_'; } if (n > 0) p += sprintf(p, "%d", n); run = 0; } } } *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] = '0' + soln[i]; (*aux)[a+1] = '\0'; sfree(grid); sfree(clues); sfree(soln); sfree(soln2); sfree(order); return desc; } /* ---------------------------------------------------------------------- * Gameplay. */ static char *validate_desc(const game_params *params, const char *desc) { int w = params->w, a = w*w; const char *p = desc; int i, clue; /* * Verify that the right number of clues are given, and that * they're in range. */ for (i = 0; i < 4*w; i++) { if (!*p) return "Too few clues for grid size"; if (i > 0) { if (*p != '/') return "Expected commas between clues"; p++; } if (isdigit((unsigned char)*p)) { clue = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; if (clue <= 0 || clue > w) return "Clue number out of range"; } } if (*p == '/') return "Too many clues for grid size"; if (*p == ',') { /* * Verify that the right amount of grid data is given, and * that any grid elements provided are in range. */ int squares = 0; p++; while (*p) { int c = *p++; if (c >= 'a' && c <= 'z') { squares += c - 'a' + 1; } else if (c == '_') { /* do nothing */; } else if (c > '0' && c <= '9') { int val = atoi(p-1); if (val < 1 || val > w) return "Out-of-range number in grid description"; squares++; while (*p && isdigit((unsigned char)*p)) p++; } else return "Invalid character in game description"; } if (squares < a) return "Not enough data to fill grid"; if (squares > a) return "Too much data to fit in grid"; } return NULL; } 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); const char *p = desc; int i; state->par = *params; /* structure copy */ state->clues = snew(struct clues); state->clues->refcount = 1; state->clues->w = w; state->clues->clues = snewn(4*w, int); state->clues->immutable = snewn(a, digit); state->grid = snewn(a, digit); state->pencil = snewn(a, int); for (i = 0; i < a; i++) { state->grid[i] = 0; state->pencil[i] = 0; } memset(state->clues->immutable, 0, a); for (i = 0; i < 4*w; i++) { if (i > 0) { assert(*p == '/'); p++; } if (*p && isdigit((unsigned char)*p)) { state->clues->clues[i] = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; } else state->clues->clues[i] = 0; } if (*p == ',') { int pos = 0; p++; while (*p) { int c = *p++; if (c >= 'a' && c <= 'z') { pos += c - 'a' + 1; } else if (c == '_') { /* do nothing */; } else if (c > '0' && c <= '9') { int val = atoi(p-1); assert(val >= 1 && val <= w); assert(pos < a); state->grid[pos] = state->clues->immutable[pos] = val; pos++; while (*p && isdigit((unsigned char)*p)) p++; } else assert(!"Corrupt game description"); } assert(pos == a); } assert(!*p); state->completed = 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->clues = state->clues; ret->clues->refcount++; ret->grid = snewn(a, digit); ret->pencil = snewn(a, int); memcpy(ret->grid, state->grid, a*sizeof(digit)); memcpy(ret->pencil, state->pencil, a*sizeof(int)); ret->completed = state->completed; ret->cheated = state->cheated; return ret; } static void free_game(game_state *state) { sfree(state->grid); sfree(state->pencil); if (--state->clues->refcount <= 0) { sfree(state->clues->immutable); sfree(state->clues->clues); sfree(state->clues); } sfree(state); } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, 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->clues->immutable, a); ret = solver(w, state->clues->clues, 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] = '0' + soln[i]; out[a+1] = '\0'; } sfree(soln); return out; } static int 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 /* , a = w*w */; char *ret; char *p; int x, y; int total; /* * We have: * - a top clue row, consisting of three spaces, then w clue * digits with spaces between (total 2*w+3 chars including * newline) * - a blank line (one newline) * - w main rows, consisting of a left clue digit, two spaces, * w grid digits with spaces between, two spaces and a right * clue digit (total 2*w+6 chars each including newline) * - a blank line (one newline) * - a bottom clue row (same as top clue row) * - terminating NUL. * * Total size is therefore 2*(2*w+3) + 2 + w*(2*w+6) + 1 * = 2w^2+10w+9. */ total = 2*w*w + 10*w + 9; ret = snewn(total, char); p = ret; /* Top clue row. */ *p++ = ' '; *p++ = ' '; for (x = 0; x < w; x++) { *p++ = ' '; *p++ = (state->clues->clues[x] ? '0' + state->clues->clues[x] : ' '); } *p++ = '\n'; /* Blank line. */ *p++ = '\n'; /* Main grid. */ for (y = 0; y < w; y++) { *p++ = (state->clues->clues[y+2*w] ? '0' + state->clues->clues[y+2*w] : ' '); *p++ = ' '; for (x = 0; x < w; x++) { *p++ = ' '; *p++ = (state->grid[y*w+x] ? '0' + state->grid[y*w+x] : ' '); } *p++ = ' '; *p++ = ' '; *p++ = (state->clues->clues[y+3*w] ? '0' + state->clues->clues[y+3*w] : ' '); *p++ = '\n'; } /* Blank line. */ *p++ = '\n'; /* Bottom clue row. */ *p++ = ' '; *p++ = ' '; for (x = 0; x < w; x++) { *p++ = ' '; *p++ = (state->clues->clues[x+w] ? '0' + state->clues->clues[x+w] : ' '); } *p++ = '\n'; *p++ = '\0'; assert(p == ret + total); return ret; } struct game_ui { /* * These are the coordinates of the currently highlighted * square on the grid, if hshow = 1. */ int hx, hy; /* * This indicates whether the current highlight is a * pencil-mark one or a real one. */ int 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. */ int 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. */ int hcursor; }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->hx = ui->hy = 0; ui->hpencil = ui->hshow = ui->hcursor = 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 = 0; } } #define PREFERRED_TILESIZE 48 #define TILESIZE (ds->tilesize) #define BORDER (TILESIZE * 9 / 8) #define COORD(x) ((x)*TILESIZE + BORDER) #define FROMCOORD(x) (((x)+(TILESIZE-BORDER)) / TILESIZE - 1) /* These always return positive values, though y offsets are actually -ve */ #define X_3D_DISP(height, w) ((height) * TILESIZE / (8 * (w))) #define Y_3D_DISP(height, w) ((height) * TILESIZE / (4 * (w))) #define FLASH_TIME 0.4F #define DF_PENCIL_SHIFT 16 #define DF_ERROR 0x8000 #define DF_HIGHLIGHT 0x4000 #define DF_HIGHLIGHT_PENCIL 0x2000 #define DF_IMMUTABLE 0x1000 #define DF_PLAYAREA 0x0800 #define DF_DIGIT_MASK 0x00FF struct game_drawstate { int tilesize; int three_d; /* default 3D graphics are user-disableable */ int started; long *tiles; /* (w+2)*(w+2) temp space */ long *drawn; /* (w+2)*(w+2)*4: current drawn data */ int *errtmp; }; static int check_errors(const game_state *state, int *errors) { int w = state->par.w /*, a = w*w */; int W = w+2, A = W*W; /* the errors array is (w+2) square */ int *clues = state->clues->clues; digit *grid = state->grid; int i, x, y, errs = FALSE; int tmp[32]; assert(w < lenof(tmp)); 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 != (1L << (w+1)) - (1L << 1)) { errs = TRUE; errmask &= ~1UL; if (errors) { for (x = 0; x < w; x++) if (errmask & (1UL << grid[y*w+x])) errors[(y+1)*W+(x+1)] = TRUE; } } } 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+1)*W+(x+1)] = TRUE; } } } for (i = 0; i < 4*w; i++) { int start, step, j, n, best; STARTSTEP(start, step, i, w); if (!clues[i]) continue; best = n = 0; for (j = 0; j < w; j++) { int number = grid[start+j*step]; if (!number) break; /* can't tell what happens next */ if (number > best) { best = number; n++; } } if (n > clues[i] || (j == w && n < clues[i])) { if (errors) { int x, y; CLUEPOS(x, y, i, w); errors[(y+1)*W+(x+1)] = TRUE; } errs = TRUE; } } return errs; } 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 (ds->three_d) { /* * In 3D mode, just locating the mouse click in the natural * square grid may not be sufficient to tell which tower the * user clicked on. Investigate the _tops_ of the nearby * towers to see if a click on one grid square was actually * a click on a tower protruding into that region from * another. */ int dx, dy; for (dy = 0; dy <= 1; dy++) for (dx = 0; dx >= -1; dx--) { int cx = tx + dx, cy = ty + dy; if (cx >= 0 && cx < w && cy >= 0 && cy < w) { int height = state->grid[cy*w+cx]; int bx = COORD(cx), by = COORD(cy); int ox = bx + X_3D_DISP(height, w); int oy = by - Y_3D_DISP(height, w); if (/* on top face? */ (x - ox >= 0 && x - ox < TILESIZE && y - oy >= 0 && y - oy < TILESIZE) || /* in triangle between top-left corners? */ (ox > bx && x >= bx && x <= ox && y <= by && (by-y) * (ox-bx) <= (by-oy) * (x-bx)) || /* in triangle between bottom-right corners? */ (ox > bx && x >= bx+TILESIZE && x <= ox+TILESIZE && y >= oy+TILESIZE && (by-y+TILESIZE)*(ox-bx) >= (by-oy)*(x-bx-TILESIZE))) { tx = cx; ty = cy; } } } } if (tx >= 0 && tx < w && ty >= 0 && ty < w) { if (button == LEFT_BUTTON) { if (tx == ui->hx && ty == ui->hy && ui->hshow && ui->hpencil == 0) { ui->hshow = 0; } else { ui->hx = tx; ui->hy = ty; ui->hshow = !state->clues->immutable[ty*w+tx]; ui->hpencil = 0; } ui->hcursor = 0; return ""; /* UI activity occurred */ } 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 = 0; } else { ui->hpencil = 1; ui->hx = tx; ui->hy = ty; ui->hshow = 1; } } else { ui->hshow = 0; } ui->hcursor = 0; return ""; /* UI activity occurred */ } } if (IS_CURSOR_MOVE(button)) { move_cursor(button, &ui->hx, &ui->hy, w, w, 0); ui->hshow = ui->hcursor = 1; return ""; } if (ui->hshow && (button == CURSOR_SELECT)) { ui->hpencil = 1 - ui->hpencil; ui->hcursor = 1; return ""; } if (ui->hshow && ((button >= '0' && button <= '9' && button - '0' <= w) || button == CURSOR_SELECT2 || button == '\b')) { int n = button - '0'; if (button == CURSOR_SELECT2 || button == '\b') n = 0; /* * 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[ui->hy*w+ui->hx]) return NULL; /* * Can't do anything to an immutable square. */ if (state->clues->immutable[ui->hy*w+ui->hx]) return NULL; sprintf(buf, "%c%d,%d,%d", (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n); if (!ui->hcursor) ui->hshow = 0; return dupstr(buf); } 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, n; if (move[0] == 'S') { ret = dup_game(from); ret->completed = ret->cheated = TRUE; for (i = 0; i < a; i++) { if (move[i+1] < '1' || move[i+1] > '0'+w) { free_game(ret); return NULL; } ret->grid[i] = move[i+1] - '0'; 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", &x, &y, &n) == 3 && x >= 0 && x < w && y >= 0 && y < w && n >= 0 && n <= w) { if (from->clues->immutable[y*w+x]) return NULL; ret = dup_game(from); if (move[0] == 'P' && n > 0) { ret->pencil[y*w+x] ^= 1L << n; } else { ret->grid[y*w+x] = n; ret->pencil[y*w+x] = 0; 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] = (1L << (w+1)) - (1L << 1); } return ret; } else return NULL; /* couldn't parse move string */ } /* ---------------------------------------------------------------------- * Drawing routines. */ #define SIZE(w) ((w) * TILESIZE + 2*BORDER) 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]; *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->tilesize = 0; ds->three_d = !getenv("TOWERS_2D"); ds->started = FALSE; ds->tiles = snewn((w+2)*(w+2), long); ds->drawn = snewn((w+2)*(w+2)*4, long); for (i = 0; i < (w+2)*(w+2)*4; i++) ds->drawn[i] = -1; ds->errtmp = snewn((w+2)*(w+2), int); return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->errtmp); sfree(ds->tiles); sfree(ds->drawn); sfree(ds); } static void draw_tile(drawing *dr, game_drawstate *ds, struct clues *clues, int x, int y, long tile) { int w = clues->w /* , a = w*w */; int tx, ty, bg; char str[64]; tx = COORD(x); ty = COORD(y); bg = (tile & DF_HIGHLIGHT) ? COL_HIGHLIGHT : COL_BACKGROUND; /* draw tower */ if (ds->three_d && (tile & DF_PLAYAREA) && (tile & DF_DIGIT_MASK)) { int coords[8]; int xoff = X_3D_DISP(tile & DF_DIGIT_MASK, w); int yoff = Y_3D_DISP(tile & DF_DIGIT_MASK, w); /* left face of tower */ coords[0] = tx; coords[1] = ty - 1; coords[2] = tx; coords[3] = ty + TILESIZE - 1; coords[4] = coords[2] + xoff; coords[5] = coords[3] - yoff; coords[6] = coords[0] + xoff; coords[7] = coords[1] - yoff; draw_polygon(dr, coords, 4, bg, COL_GRID); /* bottom face of tower */ coords[0] = tx + TILESIZE; coords[1] = ty + TILESIZE - 1; coords[2] = tx; coords[3] = ty + TILESIZE - 1; coords[4] = coords[2] + xoff; coords[5] = coords[3] - yoff; coords[6] = coords[0] + xoff; coords[7] = coords[1] - yoff; draw_polygon(dr, coords, 4, bg, COL_GRID); /* now offset all subsequent drawing to the top of the tower */ tx += xoff; ty -= yoff; } /* erase background */ draw_rect(dr, tx, ty, TILESIZE, TILESIZE, bg); /* pencil-mode highlight */ if (tile & DF_HIGHLIGHT_PENCIL) { int coords[6]; coords[0] = tx; coords[1] = ty; coords[2] = tx+TILESIZE/2; coords[3] = ty; coords[4] = tx; coords[5] = ty+TILESIZE/2; draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT); } /* draw box outline */ if (tile & DF_PLAYAREA) { int coords[8]; coords[0] = tx; coords[1] = ty - 1; coords[2] = tx + TILESIZE; coords[3] = ty - 1; coords[4] = tx + TILESIZE; coords[5] = ty + TILESIZE - 1; coords[6] = tx; coords[7] = ty + TILESIZE - 1; draw_polygon(dr, coords, 4, -1, COL_GRID); } /* new number needs drawing? */ if (tile & DF_DIGIT_MASK) { str[1] = '\0'; str[0] = (tile & DF_DIGIT_MASK) + '0'; draw_text(dr, tx + TILESIZE/2, ty + TILESIZE/2, FONT_VARIABLE, (tile & DF_PLAYAREA ? TILESIZE/2 : TILESIZE*2/5), ALIGN_VCENTRE | ALIGN_HCENTRE, (tile & DF_ERROR) ? COL_ERROR : (x < 0 || y < 0 || x >= w || y >= w) ? COL_GRID : (tile & DF_IMMUTABLE) ? COL_GRID : COL_USER, str); } 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 (tile & (1L << (i + DF_PENCIL_SHIFT))) npencil++; if (npencil) { minph = 2; /* * Determine the bounding rectangle within which we're going * to put the pencil marks. */ /* Start with the whole square, minus space for impinging towers */ pl = tx + (ds->three_d ? X_3D_DISP(w,w) : 0); pr = tx + TILESIZE; pt = ty; pb = ty + TILESIZE - (ds->three_d ? Y_3D_DISP(w,w) : 0); /* * 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 = pl + (pr - pl - fontsize * pw) / 2; pt = pt + (pb - pt - fontsize * ph) / 2; /* * Now actually draw the pencil marks. */ for (i = 1, j = 0; i <= w; i++) if (tile & (1L << (i + DF_PENCIL_SHIFT))) { int dx = j % pw, dy = j / pw; str[1] = '\0'; str[0] = i + '0'; 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++; } } } } 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 i, x, y; 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); draw_update(dr, 0, 0, SIZE(w), SIZE(w)); ds->started = TRUE; } check_errors(state, ds->errtmp); /* * Work out what data each tile should contain. */ for (i = 0; i < (w+2)*(w+2); i++) ds->tiles[i] = 0; /* completely blank square */ /* The clue squares... */ for (i = 0; i < 4*w; i++) { long tile = state->clues->clues[i]; CLUEPOS(x, y, i, w); if (ds->errtmp[(y+1)*(w+2)+(x+1)]) tile |= DF_ERROR; ds->tiles[(y+1)*(w+2)+(x+1)] = tile; } /* ... and the main grid. */ for (y = 0; y < w; y++) { for (x = 0; x < w; x++) { long tile = DF_PLAYAREA; if (state->grid[y*w+x]) tile |= state->grid[y*w+x]; else tile |= (long)state->pencil[y*w+x] << DF_PENCIL_SHIFT; if (ui->hshow && ui->hx == x && ui->hy == y) tile |= (ui->hpencil ? DF_HIGHLIGHT_PENCIL : DF_HIGHLIGHT); if (state->clues->immutable[y*w+x]) tile |= DF_IMMUTABLE; if (flashtime > 0 && (flashtime <= FLASH_TIME/3 || flashtime >= FLASH_TIME*2/3)) tile |= DF_HIGHLIGHT; /* completion flash */ if (ds->errtmp[(y+1)*(w+2)+(x+1)]) tile |= DF_ERROR; ds->tiles[(y+1)*(w+2)+(x+1)] = tile; } } /* * Now actually draw anything that needs to be changed. */ for (y = 0; y < w+2; y++) { for (x = 0; x < w+2; x++) { long tl, tr, bl, br; int i = y*(w+2)+x; tr = ds->tiles[y*(w+2)+x]; tl = (x == 0 ? 0 : ds->tiles[y*(w+2)+(x-1)]); br = (y == w+1 ? 0 : ds->tiles[(y+1)*(w+2)+x]); bl = (x == 0 || y == w+1 ? 0 : ds->tiles[(y+1)*(w+2)+(x-1)]); if (ds->drawn[i*4] != tl || ds->drawn[i*4+1] != tr || ds->drawn[i*4+2] != bl || ds->drawn[i*4+3] != br) { clip(dr, COORD(x-1), COORD(y-1), TILESIZE, TILESIZE); draw_tile(dr, ds, state->clues, x-1, y-1, tr); if (x > 0) draw_tile(dr, ds, state->clues, x-2, y-1, tl); if (y <= w) draw_tile(dr, ds, state->clues, x-1, y, br); if (x > 0 && y <= w) draw_tile(dr, ds, state->clues, x-2, y, bl); unclip(dr); draw_update(dr, COORD(x-1), COORD(y-1), TILESIZE, TILESIZE); ds->drawn[i*4] = tl; ds->drawn[i*4+1] = tr; ds->drawn[i*4+2] = bl; ds->drawn[i*4+3] = br; } } } } 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 int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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 i, 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, BORDER, w*TILESIZE, w*TILESIZE, ink); /* * Main grid. */ for (x = 1; x < w; x++) { print_line_width(dr, TILESIZE / 40); draw_line(dr, BORDER+x*TILESIZE, BORDER, BORDER+x*TILESIZE, BORDER+w*TILESIZE, ink); } for (y = 1; y < w; y++) { print_line_width(dr, TILESIZE / 40); draw_line(dr, BORDER, BORDER+y*TILESIZE, BORDER+w*TILESIZE, BORDER+y*TILESIZE, ink); } /* * Clues. */ for (i = 0; i < 4*w; i++) { char str[128]; if (!state->clues->clues[i]) continue; CLUEPOS(x, y, i, w); sprintf (str, "%d", state->clues->clues[i]); draw_text(dr, BORDER + x*TILESIZE + TILESIZE/2, BORDER + y*TILESIZE + TILESIZE/2, FONT_VARIABLE, TILESIZE/2, ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str); } /* * Numbers for the solution, if any. */ 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] = state->grid[y*w+x] + '0'; draw_text(dr, BORDER + x*TILESIZE + TILESIZE/2, BORDER + y*TILESIZE + TILESIZE/2, FONT_VARIABLE, TILESIZE/2, ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str); } } #ifdef COMBINED #define thegame towers #endif const struct game thegame = { "Towers", "games.towers", "towers", default_params, game_fetch_preset, 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, 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_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, *err; int grade = FALSE; int ret, diff, 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); /* * When solving an Easy 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 = FALSE; for (diff = 0; diff < DIFFCOUNT; diff++) { memcpy(s->grid, s->clues->immutable, p->w * p->w); ret = solver(p->w, s->clues->clues, s->grid, diff); if (ret <= diff) break; } if (diff == DIFFCOUNT) { 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", towers_diffnames[ret]); } else { solver_show_working = really_show_working; memcpy(s->grid, s->clues->immutable, p->w * p->w); ret = solver(p->w, s->clues->clues, s->grid, diff); if (ret != diff) printf("Puzzle is inconsistent\n"); else fputs(game_text_format(s), stdout); } } return 0; } #endif /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/tree234.c0000644000175300017530000017537011362620110013766 0ustar simonsimon/* * tree234.c: reasonably generic counted 2-3-4 tree routines. * * This file is copyright 1999-2001 Simon Tatham. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include "tree234.h" #include "puzzles.h" /* for smalloc/sfree */ #ifdef TEST #define LOG(x) (printf x) #define smalloc malloc #define srealloc realloc #define sfree free #else #define LOG(x) #endif typedef struct node234_Tag node234; struct tree234_Tag { node234 *root; cmpfn234 cmp; }; struct node234_Tag { node234 *parent; node234 *kids[4]; int counts[4]; void *elems[3]; }; /* * Create a 2-3-4 tree. */ tree234 *newtree234(cmpfn234 cmp) { tree234 *ret = snew(tree234); LOG(("created tree %p\n", ret)); ret->root = NULL; ret->cmp = cmp; return ret; } /* * Free a 2-3-4 tree (not including freeing the elements). */ static void freenode234(node234 *n) { if (!n) return; freenode234(n->kids[0]); freenode234(n->kids[1]); freenode234(n->kids[2]); freenode234(n->kids[3]); sfree(n); } void freetree234(tree234 *t) { freenode234(t->root); sfree(t); } /* * Internal function to count a node. */ static int countnode234(node234 *n) { int count = 0; int i; if (!n) return 0; for (i = 0; i < 4; i++) count += n->counts[i]; for (i = 0; i < 3; i++) if (n->elems[i]) count++; return count; } /* * Count the elements in a tree. */ int count234(tree234 *t) { if (t->root) return countnode234(t->root); else return 0; } /* * Propagate a node overflow up a tree until it stops. Returns 0 or * 1, depending on whether the root had to be split or not. */ static int add234_insert(node234 *left, void *e, node234 *right, node234 **root, node234 *n, int ki) { int lcount, rcount; /* * We need to insert the new left/element/right set in n at * child position ki. */ lcount = countnode234(left); rcount = countnode234(right); while (n) { LOG((" at %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); LOG((" need to insert %p/%d \"%s\" %p/%d at position %d\n", left, lcount, e, right, rcount, ki)); if (n->elems[1] == NULL) { /* * Insert in a 2-node; simple. */ if (ki == 0) { LOG((" inserting on left of 2-node\n")); n->kids[2] = n->kids[1]; n->counts[2] = n->counts[1]; n->elems[1] = n->elems[0]; n->kids[1] = right; n->counts[1] = rcount; n->elems[0] = e; n->kids[0] = left; n->counts[0] = lcount; } else { /* ki == 1 */ LOG((" inserting on right of 2-node\n")); n->kids[2] = right; n->counts[2] = rcount; n->elems[1] = e; n->kids[1] = left; n->counts[1] = lcount; } if (n->kids[0]) n->kids[0]->parent = n; if (n->kids[1]) n->kids[1]->parent = n; if (n->kids[2]) n->kids[2]->parent = n; LOG((" done\n")); break; } else if (n->elems[2] == NULL) { /* * Insert in a 3-node; simple. */ if (ki == 0) { LOG((" inserting on left of 3-node\n")); n->kids[3] = n->kids[2]; n->counts[3] = n->counts[2]; n->elems[2] = n->elems[1]; n->kids[2] = n->kids[1]; n->counts[2] = n->counts[1]; n->elems[1] = n->elems[0]; n->kids[1] = right; n->counts[1] = rcount; n->elems[0] = e; n->kids[0] = left; n->counts[0] = lcount; } else if (ki == 1) { LOG((" inserting in middle of 3-node\n")); n->kids[3] = n->kids[2]; n->counts[3] = n->counts[2]; n->elems[2] = n->elems[1]; n->kids[2] = right; n->counts[2] = rcount; n->elems[1] = e; n->kids[1] = left; n->counts[1] = lcount; } else { /* ki == 2 */ LOG((" inserting on right of 3-node\n")); n->kids[3] = right; n->counts[3] = rcount; n->elems[2] = e; n->kids[2] = left; n->counts[2] = lcount; } if (n->kids[0]) n->kids[0]->parent = n; if (n->kids[1]) n->kids[1]->parent = n; if (n->kids[2]) n->kids[2]->parent = n; if (n->kids[3]) n->kids[3]->parent = n; LOG((" done\n")); break; } else { node234 *m = snew(node234); m->parent = n->parent; LOG((" splitting a 4-node; created new node %p\n", m)); /* * Insert in a 4-node; split into a 2-node and a * 3-node, and move focus up a level. * * I don't think it matters which way round we put the * 2 and the 3. For simplicity, we'll put the 3 first * always. */ if (ki == 0) { m->kids[0] = left; m->counts[0] = lcount; m->elems[0] = e; m->kids[1] = right; m->counts[1] = rcount; m->elems[1] = n->elems[0]; m->kids[2] = n->kids[1]; m->counts[2] = n->counts[1]; e = n->elems[1]; n->kids[0] = n->kids[2]; n->counts[0] = n->counts[2]; n->elems[0] = n->elems[2]; n->kids[1] = n->kids[3]; n->counts[1] = n->counts[3]; } else if (ki == 1) { m->kids[0] = n->kids[0]; m->counts[0] = n->counts[0]; m->elems[0] = n->elems[0]; m->kids[1] = left; m->counts[1] = lcount; m->elems[1] = e; m->kids[2] = right; m->counts[2] = rcount; e = n->elems[1]; n->kids[0] = n->kids[2]; n->counts[0] = n->counts[2]; n->elems[0] = n->elems[2]; n->kids[1] = n->kids[3]; n->counts[1] = n->counts[3]; } else if (ki == 2) { m->kids[0] = n->kids[0]; m->counts[0] = n->counts[0]; m->elems[0] = n->elems[0]; m->kids[1] = n->kids[1]; m->counts[1] = n->counts[1]; m->elems[1] = n->elems[1]; m->kids[2] = left; m->counts[2] = lcount; /* e = e; */ n->kids[0] = right; n->counts[0] = rcount; n->elems[0] = n->elems[2]; n->kids[1] = n->kids[3]; n->counts[1] = n->counts[3]; } else { /* ki == 3 */ m->kids[0] = n->kids[0]; m->counts[0] = n->counts[0]; m->elems[0] = n->elems[0]; m->kids[1] = n->kids[1]; m->counts[1] = n->counts[1]; m->elems[1] = n->elems[1]; m->kids[2] = n->kids[2]; m->counts[2] = n->counts[2]; n->kids[0] = left; n->counts[0] = lcount; n->elems[0] = e; n->kids[1] = right; n->counts[1] = rcount; e = n->elems[2]; } m->kids[3] = n->kids[3] = n->kids[2] = NULL; m->counts[3] = n->counts[3] = n->counts[2] = 0; m->elems[2] = n->elems[2] = n->elems[1] = NULL; if (m->kids[0]) m->kids[0]->parent = m; if (m->kids[1]) m->kids[1]->parent = m; if (m->kids[2]) m->kids[2]->parent = m; if (n->kids[0]) n->kids[0]->parent = n; if (n->kids[1]) n->kids[1]->parent = n; LOG((" left (%p): %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", m, m->kids[0], m->counts[0], m->elems[0], m->kids[1], m->counts[1], m->elems[1], m->kids[2], m->counts[2])); LOG((" right (%p): %p/%d \"%s\" %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1])); left = m; lcount = countnode234(left); right = n; rcount = countnode234(right); } if (n->parent) ki = (n->parent->kids[0] == n ? 0 : n->parent->kids[1] == n ? 1 : n->parent->kids[2] == n ? 2 : 3); n = n->parent; } /* * If we've come out of here by `break', n will still be * non-NULL and all we need to do is go back up the tree * updating counts. If we've come here because n is NULL, we * need to create a new root for the tree because the old one * has just split into two. */ if (n) { while (n->parent) { int count = countnode234(n); int childnum; childnum = (n->parent->kids[0] == n ? 0 : n->parent->kids[1] == n ? 1 : n->parent->kids[2] == n ? 2 : 3); n->parent->counts[childnum] = count; n = n->parent; } return 0; /* root unchanged */ } else { LOG((" root is overloaded, split into two\n")); (*root) = snew(node234); (*root)->kids[0] = left; (*root)->counts[0] = lcount; (*root)->elems[0] = e; (*root)->kids[1] = right; (*root)->counts[1] = rcount; (*root)->elems[1] = NULL; (*root)->kids[2] = NULL; (*root)->counts[2] = 0; (*root)->elems[2] = NULL; (*root)->kids[3] = NULL; (*root)->counts[3] = 0; (*root)->parent = NULL; if ((*root)->kids[0]) (*root)->kids[0]->parent = (*root); if ((*root)->kids[1]) (*root)->kids[1]->parent = (*root); LOG((" new root is %p/%d \"%s\" %p/%d\n", (*root)->kids[0], (*root)->counts[0], (*root)->elems[0], (*root)->kids[1], (*root)->counts[1])); return 1; /* root moved */ } } /* * Add an element e to a 2-3-4 tree t. Returns e on success, or if * an existing element compares equal, returns that. */ static void *add234_internal(tree234 *t, void *e, int index) { node234 *n; int ki; void *orig_e = e; int c; LOG(("adding element \"%s\" to tree %p\n", e, t)); if (t->root == NULL) { t->root = snew(node234); t->root->elems[1] = t->root->elems[2] = NULL; t->root->kids[0] = t->root->kids[1] = NULL; t->root->kids[2] = t->root->kids[3] = NULL; t->root->counts[0] = t->root->counts[1] = 0; t->root->counts[2] = t->root->counts[3] = 0; t->root->parent = NULL; t->root->elems[0] = e; LOG((" created root %p\n", t->root)); return orig_e; } n = t->root; while (n) { LOG((" node %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); if (index >= 0) { if (!n->kids[0]) { /* * Leaf node. We want to insert at kid position * equal to the index: * * 0 A 1 B 2 C 3 */ ki = index; } else { /* * Internal node. We always descend through it (add * always starts at the bottom, never in the * middle). */ if (index <= n->counts[0]) { ki = 0; } else if (index -= n->counts[0] + 1, index <= n->counts[1]) { ki = 1; } else if (index -= n->counts[1] + 1, index <= n->counts[2]) { ki = 2; } else if (index -= n->counts[2] + 1, index <= n->counts[3]) { ki = 3; } else return NULL; /* error: index out of range */ } } else { if ((c = t->cmp(e, n->elems[0])) < 0) ki = 0; else if (c == 0) return n->elems[0]; /* already exists */ else if (n->elems[1] == NULL || (c = t->cmp(e, n->elems[1])) < 0) ki = 1; else if (c == 0) return n->elems[1]; /* already exists */ else if (n->elems[2] == NULL || (c = t->cmp(e, n->elems[2])) < 0) ki = 2; else if (c == 0) return n->elems[2]; /* already exists */ else ki = 3; } LOG((" moving to child %d (%p)\n", ki, n->kids[ki])); if (!n->kids[ki]) break; n = n->kids[ki]; } add234_insert(NULL, e, NULL, &t->root, n, ki); return orig_e; } void *add234(tree234 *t, void *e) { if (!t->cmp) /* tree is unsorted */ return NULL; return add234_internal(t, e, -1); } void *addpos234(tree234 *t, void *e, int index) { if (index < 0 || /* index out of range */ t->cmp) /* tree is sorted */ return NULL; /* return failure */ return add234_internal(t, e, index); /* this checks the upper bound */ } /* * Look up the element at a given numeric index in a 2-3-4 tree. * Returns NULL if the index is out of range. */ void *index234(tree234 *t, int index) { node234 *n; if (!t->root) return NULL; /* tree is empty */ if (index < 0 || index >= countnode234(t->root)) return NULL; /* out of range */ n = t->root; while (n) { if (index < n->counts[0]) n = n->kids[0]; else if (index -= n->counts[0] + 1, index < 0) return n->elems[0]; else if (index < n->counts[1]) n = n->kids[1]; else if (index -= n->counts[1] + 1, index < 0) return n->elems[1]; else if (index < n->counts[2]) n = n->kids[2]; else if (index -= n->counts[2] + 1, index < 0) return n->elems[2]; else n = n->kids[3]; } /* We shouldn't ever get here. I wonder how we did. */ return NULL; } /* * Find an element e in a sorted 2-3-4 tree t. Returns NULL if not * found. e is always passed as the first argument to cmp, so cmp * can be an asymmetric function if desired. cmp can also be passed * as NULL, in which case the compare function from the tree proper * will be used. */ void *findrelpos234(tree234 *t, void *e, cmpfn234 cmp, int relation, int *index) { node234 *n; void *ret; int c; int idx, ecount, kcount, cmpret; if (t->root == NULL) return NULL; if (cmp == NULL) cmp = t->cmp; n = t->root; /* * Attempt to find the element itself. */ idx = 0; ecount = -1; /* * Prepare a fake `cmp' result if e is NULL. */ cmpret = 0; if (e == NULL) { assert(relation == REL234_LT || relation == REL234_GT); if (relation == REL234_LT) cmpret = +1; /* e is a max: always greater */ else if (relation == REL234_GT) cmpret = -1; /* e is a min: always smaller */ } while (1) { for (kcount = 0; kcount < 4; kcount++) { if (kcount >= 3 || n->elems[kcount] == NULL || (c = cmpret ? cmpret : cmp(e, n->elems[kcount])) < 0) { break; } if (n->kids[kcount]) idx += n->counts[kcount]; if (c == 0) { ecount = kcount; break; } idx++; } if (ecount >= 0) break; if (n->kids[kcount]) n = n->kids[kcount]; else break; } if (ecount >= 0) { /* * We have found the element we're looking for. It's * n->elems[ecount], at tree index idx. If our search * relation is EQ, LE or GE we can now go home. */ if (relation != REL234_LT && relation != REL234_GT) { if (index) *index = idx; return n->elems[ecount]; } /* * Otherwise, we'll do an indexed lookup for the previous * or next element. (It would be perfectly possible to * implement these search types in a non-counted tree by * going back up from where we are, but far more fiddly.) */ if (relation == REL234_LT) idx--; else idx++; } else { /* * We've found our way to the bottom of the tree and we * know where we would insert this node if we wanted to: * we'd put it in in place of the (empty) subtree * n->kids[kcount], and it would have index idx * * But the actual element isn't there. So if our search * relation is EQ, we're doomed. */ if (relation == REL234_EQ) return NULL; /* * Otherwise, we must do an index lookup for index idx-1 * (if we're going left - LE or LT) or index idx (if we're * going right - GE or GT). */ if (relation == REL234_LT || relation == REL234_LE) { idx--; } } /* * We know the index of the element we want; just call index234 * to do the rest. This will return NULL if the index is out of * bounds, which is exactly what we want. */ ret = index234(t, idx); if (ret && index) *index = idx; return ret; } void *find234(tree234 *t, void *e, cmpfn234 cmp) { return findrelpos234(t, e, cmp, REL234_EQ, NULL); } void *findrel234(tree234 *t, void *e, cmpfn234 cmp, int relation) { return findrelpos234(t, e, cmp, relation, NULL); } void *findpos234(tree234 *t, void *e, cmpfn234 cmp, int *index) { return findrelpos234(t, e, cmp, REL234_EQ, index); } /* * Tree transformation used in delete and split: move a subtree * right, from child ki of a node to the next child. Update k and * index so that they still point to the same place in the * transformed tree. Assumes the destination child is not full, and * that the source child does have a subtree to spare. Can cope if * the destination child is undersized. * * . C . . B . * / \ -> / \ * [more] a A b B c d D e [more] a A b c C d D e * * . C . . B . * / \ -> / \ * [more] a A b B c d [more] a A b c C d */ static void trans234_subtree_right(node234 *n, int ki, int *k, int *index) { node234 *src, *dest; int i, srclen, adjust; src = n->kids[ki]; dest = n->kids[ki+1]; LOG((" trans234_subtree_right(%p, %d):\n", n, ki)); LOG((" parent %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); LOG((" src %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", src, src->kids[0], src->counts[0], src->elems[0], src->kids[1], src->counts[1], src->elems[1], src->kids[2], src->counts[2], src->elems[2], src->kids[3], src->counts[3])); LOG((" dest %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", dest, dest->kids[0], dest->counts[0], dest->elems[0], dest->kids[1], dest->counts[1], dest->elems[1], dest->kids[2], dest->counts[2], dest->elems[2], dest->kids[3], dest->counts[3])); /* * Move over the rest of the destination node to make space. */ dest->kids[3] = dest->kids[2]; dest->counts[3] = dest->counts[2]; dest->elems[2] = dest->elems[1]; dest->kids[2] = dest->kids[1]; dest->counts[2] = dest->counts[1]; dest->elems[1] = dest->elems[0]; dest->kids[1] = dest->kids[0]; dest->counts[1] = dest->counts[0]; /* which element to move over */ i = (src->elems[2] ? 2 : src->elems[1] ? 1 : 0); dest->elems[0] = n->elems[ki]; n->elems[ki] = src->elems[i]; src->elems[i] = NULL; dest->kids[0] = src->kids[i+1]; dest->counts[0] = src->counts[i+1]; src->kids[i+1] = NULL; src->counts[i+1] = 0; if (dest->kids[0]) dest->kids[0]->parent = dest; adjust = dest->counts[0] + 1; n->counts[ki] -= adjust; n->counts[ki+1] += adjust; srclen = n->counts[ki]; if (k) { LOG((" before: k,index = %d,%d\n", (*k), (*index))); if ((*k) == ki && (*index) > srclen) { (*index) -= srclen + 1; (*k)++; } else if ((*k) == ki+1) { (*index) += adjust; } LOG((" after: k,index = %d,%d\n", (*k), (*index))); } LOG((" parent %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); LOG((" src %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", src, src->kids[0], src->counts[0], src->elems[0], src->kids[1], src->counts[1], src->elems[1], src->kids[2], src->counts[2], src->elems[2], src->kids[3], src->counts[3])); LOG((" dest %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", dest, dest->kids[0], dest->counts[0], dest->elems[0], dest->kids[1], dest->counts[1], dest->elems[1], dest->kids[2], dest->counts[2], dest->elems[2], dest->kids[3], dest->counts[3])); } /* * Tree transformation used in delete and split: move a subtree * left, from child ki of a node to the previous child. Update k * and index so that they still point to the same place in the * transformed tree. Assumes the destination child is not full, and * that the source child does have a subtree to spare. Can cope if * the destination child is undersized. * * . B . . C . * / \ -> / \ * a A b c C d D e [more] a A b B c d D e [more] * * . A . . B . * / \ -> / \ * a b B c C d [more] a A b c C d [more] */ static void trans234_subtree_left(node234 *n, int ki, int *k, int *index) { node234 *src, *dest; int i, adjust; src = n->kids[ki]; dest = n->kids[ki-1]; LOG((" trans234_subtree_left(%p, %d):\n", n, ki)); LOG((" parent %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); LOG((" dest %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", dest, dest->kids[0], dest->counts[0], dest->elems[0], dest->kids[1], dest->counts[1], dest->elems[1], dest->kids[2], dest->counts[2], dest->elems[2], dest->kids[3], dest->counts[3])); LOG((" src %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", src, src->kids[0], src->counts[0], src->elems[0], src->kids[1], src->counts[1], src->elems[1], src->kids[2], src->counts[2], src->elems[2], src->kids[3], src->counts[3])); /* where in dest to put it */ i = (dest->elems[1] ? 2 : dest->elems[0] ? 1 : 0); dest->elems[i] = n->elems[ki-1]; n->elems[ki-1] = src->elems[0]; dest->kids[i+1] = src->kids[0]; dest->counts[i+1] = src->counts[0]; if (dest->kids[i+1]) dest->kids[i+1]->parent = dest; /* * Move over the rest of the source node. */ src->kids[0] = src->kids[1]; src->counts[0] = src->counts[1]; src->elems[0] = src->elems[1]; src->kids[1] = src->kids[2]; src->counts[1] = src->counts[2]; src->elems[1] = src->elems[2]; src->kids[2] = src->kids[3]; src->counts[2] = src->counts[3]; src->elems[2] = NULL; src->kids[3] = NULL; src->counts[3] = 0; adjust = dest->counts[i+1] + 1; n->counts[ki] -= adjust; n->counts[ki-1] += adjust; if (k) { LOG((" before: k,index = %d,%d\n", (*k), (*index))); if ((*k) == ki) { (*index) -= adjust; if ((*index) < 0) { (*index) += n->counts[ki-1] + 1; (*k)--; } } LOG((" after: k,index = %d,%d\n", (*k), (*index))); } LOG((" parent %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); LOG((" dest %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", dest, dest->kids[0], dest->counts[0], dest->elems[0], dest->kids[1], dest->counts[1], dest->elems[1], dest->kids[2], dest->counts[2], dest->elems[2], dest->kids[3], dest->counts[3])); LOG((" src %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", src, src->kids[0], src->counts[0], src->elems[0], src->kids[1], src->counts[1], src->elems[1], src->kids[2], src->counts[2], src->elems[2], src->kids[3], src->counts[3])); } /* * Tree transformation used in delete and split: merge child nodes * ki and ki+1 of a node. Update k and index so that they still * point to the same place in the transformed tree. Assumes both * children _are_ sufficiently small. * * . B . . * / \ -> | * a A b c C d a A b B c C d * * This routine can also cope with either child being undersized: * * . A . . * / \ -> | * a b B c a A b B c * * . A . . * / \ -> | * a b B c C d a A b B c C d */ static void trans234_subtree_merge(node234 *n, int ki, int *k, int *index) { node234 *left, *right; int i, leftlen, rightlen, lsize, rsize; left = n->kids[ki]; leftlen = n->counts[ki]; right = n->kids[ki+1]; rightlen = n->counts[ki+1]; LOG((" trans234_subtree_merge(%p, %d):\n", n, ki)); LOG((" parent %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); LOG((" left %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", left, left->kids[0], left->counts[0], left->elems[0], left->kids[1], left->counts[1], left->elems[1], left->kids[2], left->counts[2], left->elems[2], left->kids[3], left->counts[3])); LOG((" right %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", right, right->kids[0], right->counts[0], right->elems[0], right->kids[1], right->counts[1], right->elems[1], right->kids[2], right->counts[2], right->elems[2], right->kids[3], right->counts[3])); assert(!left->elems[2] && !right->elems[2]); /* neither is large! */ lsize = (left->elems[1] ? 2 : left->elems[0] ? 1 : 0); rsize = (right->elems[1] ? 2 : right->elems[0] ? 1 : 0); left->elems[lsize] = n->elems[ki]; for (i = 0; i < rsize+1; i++) { left->kids[lsize+1+i] = right->kids[i]; left->counts[lsize+1+i] = right->counts[i]; if (left->kids[lsize+1+i]) left->kids[lsize+1+i]->parent = left; if (i < rsize) left->elems[lsize+1+i] = right->elems[i]; } n->counts[ki] += rightlen + 1; sfree(right); /* * Move the rest of n up by one. */ for (i = ki+1; i < 3; i++) { n->kids[i] = n->kids[i+1]; n->counts[i] = n->counts[i+1]; } for (i = ki; i < 2; i++) { n->elems[i] = n->elems[i+1]; } n->kids[3] = NULL; n->counts[3] = 0; n->elems[2] = NULL; if (k) { LOG((" before: k,index = %d,%d\n", (*k), (*index))); if ((*k) == ki+1) { (*k)--; (*index) += leftlen + 1; } else if ((*k) > ki+1) { (*k)--; } LOG((" after: k,index = %d,%d\n", (*k), (*index))); } LOG((" parent %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); LOG((" merged %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", left, left->kids[0], left->counts[0], left->elems[0], left->kids[1], left->counts[1], left->elems[1], left->kids[2], left->counts[2], left->elems[2], left->kids[3], left->counts[3])); } /* * Delete an element e in a 2-3-4 tree. Does not free the element, * merely removes all links to it from the tree nodes. */ static void *delpos234_internal(tree234 *t, int index) { node234 *n; void *retval; int ki, i; retval = NULL; n = t->root; /* by assumption this is non-NULL */ LOG(("deleting item %d from tree %p\n", index, t)); while (1) { node234 *sub; LOG((" node %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d index=%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3], index)); if (index <= n->counts[0]) { ki = 0; } else if (index -= n->counts[0]+1, index <= n->counts[1]) { ki = 1; } else if (index -= n->counts[1]+1, index <= n->counts[2]) { ki = 2; } else if (index -= n->counts[2]+1, index <= n->counts[3]) { ki = 3; } else { assert(0); /* can't happen */ } if (!n->kids[0]) break; /* n is a leaf node; we're here! */ /* * Check to see if we've found our target element. If so, * we must choose a new target (we'll use the old target's * successor, which will be in a leaf), move it into the * place of the old one, continue down to the leaf and * delete the old copy of the new target. */ if (index == n->counts[ki]) { node234 *m; LOG((" found element in internal node, index %d\n", ki)); assert(n->elems[ki]); /* must be a kid _before_ an element */ ki++; index = 0; for (m = n->kids[ki]; m->kids[0]; m = m->kids[0]) continue; LOG((" replacing with element \"%s\" from leaf node %p\n", m->elems[0], m)); retval = n->elems[ki-1]; n->elems[ki-1] = m->elems[0]; } /* * Recurse down to subtree ki. If it has only one element, * we have to do some transformation to start with. */ LOG((" moving to subtree %d\n", ki)); sub = n->kids[ki]; if (!sub->elems[1]) { LOG((" subtree has only one element!\n")); if (ki > 0 && n->kids[ki-1]->elems[1]) { /* * Child ki has only one element, but child * ki-1 has two or more. So we need to move a * subtree from ki-1 to ki. */ trans234_subtree_right(n, ki-1, &ki, &index); } else if (ki < 3 && n->kids[ki+1] && n->kids[ki+1]->elems[1]) { /* * Child ki has only one element, but ki+1 has * two or more. Move a subtree from ki+1 to ki. */ trans234_subtree_left(n, ki+1, &ki, &index); } else { /* * ki is small with only small neighbours. Pick a * neighbour and merge with it. */ trans234_subtree_merge(n, ki>0 ? ki-1 : ki, &ki, &index); sub = n->kids[ki]; if (!n->elems[0]) { /* * The root is empty and needs to be * removed. */ LOG((" shifting root!\n")); t->root = sub; sub->parent = NULL; sfree(n); n = NULL; } } } if (n) n->counts[ki]--; n = sub; } /* * Now n is a leaf node, and ki marks the element number we * want to delete. We've already arranged for the leaf to be * bigger than minimum size, so let's just go to it. */ assert(!n->kids[0]); if (!retval) retval = n->elems[ki]; for (i = ki; i < 2 && n->elems[i+1]; i++) n->elems[i] = n->elems[i+1]; n->elems[i] = NULL; /* * It's just possible that we have reduced the leaf to zero * size. This can only happen if it was the root - so destroy * it and make the tree empty. */ if (!n->elems[0]) { LOG((" removed last element in tree, destroying empty root\n")); assert(n == t->root); sfree(n); t->root = NULL; } return retval; /* finished! */ } void *delpos234(tree234 *t, int index) { if (index < 0 || index >= countnode234(t->root)) return NULL; return delpos234_internal(t, index); } void *del234(tree234 *t, void *e) { int index; if (!findrelpos234(t, e, NULL, REL234_EQ, &index)) return NULL; /* it wasn't in there anyway */ return delpos234_internal(t, index); /* it's there; delete it. */ } /* * Join two subtrees together with a separator element between * them, given their relative height. * * (Height<0 means the left tree is shorter, >0 means the right * tree is shorter, =0 means (duh) they're equal.) * * It is assumed that any checks needed on the ordering criterion * have _already_ been done. * * The value returned in `height' is 0 or 1 depending on whether the * resulting tree is the same height as the original larger one, or * one higher. */ static node234 *join234_internal(node234 *left, void *sep, node234 *right, int *height) { node234 *root, *node; int relht = *height; int ki; LOG((" join: joining %p \"%s\" %p, relative height is %d\n", left, sep, right, relht)); if (relht == 0) { /* * The trees are the same height. Create a new one-element * root containing the separator and pointers to the two * nodes. */ node234 *newroot; newroot = snew(node234); newroot->kids[0] = left; newroot->counts[0] = countnode234(left); newroot->elems[0] = sep; newroot->kids[1] = right; newroot->counts[1] = countnode234(right); newroot->elems[1] = NULL; newroot->kids[2] = NULL; newroot->counts[2] = 0; newroot->elems[2] = NULL; newroot->kids[3] = NULL; newroot->counts[3] = 0; newroot->parent = NULL; if (left) left->parent = newroot; if (right) right->parent = newroot; *height = 1; LOG((" join: same height, brand new root\n")); return newroot; } /* * This now works like the addition algorithm on the larger * tree. We're replacing a single kid pointer with two kid * pointers separated by an element; if that causes the node to * overload, we split it in two, move a separator element up to * the next node, and repeat. */ if (relht < 0) { /* * Left tree is shorter. Search down the right tree to find * the pointer we're inserting at. */ node = root = right; while (++relht < 0) { node = node->kids[0]; } ki = 0; right = node->kids[ki]; } else { /* * Right tree is shorter; search down the left to find the * pointer we're inserting at. */ node = root = left; while (--relht > 0) { if (node->elems[2]) node = node->kids[3]; else if (node->elems[1]) node = node->kids[2]; else node = node->kids[1]; } if (node->elems[2]) ki = 3; else if (node->elems[1]) ki = 2; else ki = 1; left = node->kids[ki]; } /* * Now proceed as for addition. */ *height = add234_insert(left, sep, right, &root, node, ki); return root; } static int height234(tree234 *t) { int level = 0; node234 *n = t->root; while (n) { level++; n = n->kids[0]; } return level; } tree234 *join234(tree234 *t1, tree234 *t2) { int size2 = countnode234(t2->root); if (size2 > 0) { void *element; int relht; if (t1->cmp) { element = index234(t2, 0); element = findrelpos234(t1, element, NULL, REL234_GE, NULL); if (element) return NULL; } element = delpos234(t2, 0); relht = height234(t1) - height234(t2); t1->root = join234_internal(t1->root, element, t2->root, &relht); t2->root = NULL; } return t1; } tree234 *join234r(tree234 *t1, tree234 *t2) { int size1 = countnode234(t1->root); if (size1 > 0) { void *element; int relht; if (t2->cmp) { element = index234(t1, size1-1); element = findrelpos234(t2, element, NULL, REL234_LE, NULL); if (element) return NULL; } element = delpos234(t1, size1-1); relht = height234(t1) - height234(t2); t2->root = join234_internal(t1->root, element, t2->root, &relht); t1->root = NULL; } return t2; } /* * Split out the first elements in a tree and return a * pointer to the root node. Leave the root node of the remainder * in t. */ static node234 *split234_internal(tree234 *t, int index) { node234 *halves[2] = { NULL, NULL }, *n, *sib, *sub; node234 *lparent, *rparent; int ki, pki, i, half, lcount, rcount; n = t->root; LOG(("splitting tree %p at point %d\n", t, index)); /* * Easy special cases. After this we have also dealt completely * with the empty-tree case and we can assume the root exists. */ if (index == 0) /* return nothing */ return NULL; if (index == countnode234(t->root)) { /* return the whole tree */ node234 *ret = t->root; t->root = NULL; return ret; } /* * Search down the tree to find the split point. */ halves[0] = halves[1] = NULL; lparent = rparent = NULL; pki = -1; while (n) { LOG((" node %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d index=%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3], index)); lcount = index; rcount = countnode234(n) - lcount; if (index <= n->counts[0]) { ki = 0; } else if (index -= n->counts[0]+1, index <= n->counts[1]) { ki = 1; } else if (index -= n->counts[1]+1, index <= n->counts[2]) { ki = 2; } else { index -= n->counts[2]+1; ki = 3; } LOG((" splitting at subtree %d\n", ki)); sub = n->kids[ki]; LOG((" splitting at child index %d\n", ki)); /* * Split the node, put halves[0] on the right of the left * one and halves[1] on the left of the right one, put the * new node pointers in halves[0] and halves[1], and go up * a level. */ sib = snew(node234); for (i = 0; i < 3; i++) { if (i+ki < 3 && n->elems[i+ki]) { sib->elems[i] = n->elems[i+ki]; sib->kids[i+1] = n->kids[i+ki+1]; if (sib->kids[i+1]) sib->kids[i+1]->parent = sib; sib->counts[i+1] = n->counts[i+ki+1]; n->elems[i+ki] = NULL; n->kids[i+ki+1] = NULL; n->counts[i+ki+1] = 0; } else { sib->elems[i] = NULL; sib->kids[i+1] = NULL; sib->counts[i+1] = 0; } } if (lparent) { lparent->kids[pki] = n; lparent->counts[pki] = lcount; n->parent = lparent; rparent->kids[0] = sib; rparent->counts[0] = rcount; sib->parent = rparent; } else { halves[0] = n; n->parent = NULL; halves[1] = sib; sib->parent = NULL; } lparent = n; rparent = sib; pki = ki; LOG((" left node %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); LOG((" right node %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", sib, sib->kids[0], sib->counts[0], sib->elems[0], sib->kids[1], sib->counts[1], sib->elems[1], sib->kids[2], sib->counts[2], sib->elems[2], sib->kids[3], sib->counts[3])); n = sub; } /* * We've come off the bottom here, so we've successfully split * the tree into two equally high subtrees. The only problem is * that some of the nodes down the fault line will be smaller * than the minimum permitted size. (Since this is a 2-3-4 * tree, that means they'll be zero-element one-child nodes.) */ LOG((" fell off bottom, lroot is %p, rroot is %p\n", halves[0], halves[1])); assert(halves[0] != NULL); assert(halves[1] != NULL); lparent->counts[pki] = rparent->counts[0] = 0; lparent->kids[pki] = rparent->kids[0] = NULL; /* * So now we go back down the tree from each of the two roots, * fixing up undersize nodes. */ for (half = 0; half < 2; half++) { /* * Remove the root if it's undersize (it will contain only * one child pointer, so just throw it away and replace it * with its child). This might happen several times. */ while (halves[half] && !halves[half]->elems[0]) { LOG((" root %p is undersize, throwing away\n", halves[half])); halves[half] = halves[half]->kids[0]; sfree(halves[half]->parent); halves[half]->parent = NULL; LOG((" new root is %p\n", halves[half])); } n = halves[half]; while (n) { void (*toward)(node234 *n, int ki, int *k, int *index); int ni, merge; /* * Now we have a potentially undersize node on the * right (if half==0) or left (if half==1). Sort it * out, by merging with a neighbour or by transferring * subtrees over. At this time we must also ensure that * nodes are bigger than minimum, in case we need an * element to merge two nodes below. */ LOG((" node %p: %p/%d \"%s\" %p/%d \"%s\" %p/%d \"%s\" %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1], n->elems[1], n->kids[2], n->counts[2], n->elems[2], n->kids[3], n->counts[3])); if (half == 1) { ki = 0; /* the kid we're interested in */ ni = 1; /* the neighbour */ merge = 0; /* for merge: leftmost of the two */ toward = trans234_subtree_left; } else { ki = (n->kids[3] ? 3 : n->kids[2] ? 2 : 1); ni = ki-1; merge = ni; toward = trans234_subtree_right; } sub = n->kids[ki]; if (sub && !sub->elems[1]) { /* * This node is undersized or minimum-size. If we * can merge it with its neighbour, we do so; * otherwise we must be able to transfer subtrees * over to it until it is greater than minimum * size. */ int undersized = (!sub->elems[0]); LOG((" child %d is %ssize\n", ki, undersized ? "under" : "minimum-")); LOG((" neighbour is %s\n", n->kids[ni]->elems[2] ? "large" : n->kids[ni]->elems[1] ? "medium" : "small")); if (!n->kids[ni]->elems[1] || (undersized && !n->kids[ni]->elems[2])) { /* * Neighbour is small, or possibly neighbour is * medium and we are undersize. */ trans234_subtree_merge(n, merge, NULL, NULL); sub = n->kids[merge]; if (!n->elems[0]) { /* * n is empty, and hence must have been the * root and needs to be removed. */ assert(!n->parent); LOG((" shifting root!\n")); halves[half] = sub; halves[half]->parent = NULL; sfree(n); } } else { /* Neighbour is big enough to move trees over. */ toward(n, ni, NULL, NULL); if (undersized) toward(n, ni, NULL, NULL); } } n = sub; } } t->root = halves[1]; return halves[0]; } tree234 *splitpos234(tree234 *t, int index, int before) { tree234 *ret; node234 *n; int count; count = countnode234(t->root); if (index < 0 || index > count) return NULL; /* error */ ret = newtree234(t->cmp); n = split234_internal(t, index); if (before) { /* We want to return the ones before the index. */ ret->root = n; } else { /* * We want to keep the ones before the index and return the * ones after. */ ret->root = t->root; t->root = n; } return ret; } tree234 *split234(tree234 *t, void *e, cmpfn234 cmp, int rel) { int before; int index; assert(rel != REL234_EQ); if (rel == REL234_GT || rel == REL234_GE) { before = 1; rel = (rel == REL234_GT ? REL234_LE : REL234_LT); } else { before = 0; } if (!findrelpos234(t, e, cmp, rel, &index)) index = 0; return splitpos234(t, index+1, before); } static node234 *copynode234(node234 *n, copyfn234 copyfn, void *copyfnstate) { int i; node234 *n2 = snew(node234); for (i = 0; i < 3; i++) { if (n->elems[i] && copyfn) n2->elems[i] = copyfn(copyfnstate, n->elems[i]); else n2->elems[i] = n->elems[i]; } for (i = 0; i < 4; i++) { if (n->kids[i]) { n2->kids[i] = copynode234(n->kids[i], copyfn, copyfnstate); n2->kids[i]->parent = n2; } else { n2->kids[i] = NULL; } n2->counts[i] = n->counts[i]; } return n2; } tree234 *copytree234(tree234 *t, copyfn234 copyfn, void *copyfnstate) { tree234 *t2; t2 = newtree234(t->cmp); if (t->root) { t2->root = copynode234(t->root, copyfn, copyfnstate); t2->root->parent = NULL; } else t2->root = NULL; return t2; } #ifdef TEST /* * Test code for the 2-3-4 tree. This code maintains an alternative * representation of the data in the tree, in an array (using the * obvious and slow insert and delete functions). After each tree * operation, the verify() function is called, which ensures all * the tree properties are preserved: * - node->child->parent always equals node * - tree->root->parent always equals NULL * - number of kids == 0 or number of elements + 1; * - tree has the same depth everywhere * - every node has at least one element * - subtree element counts are accurate * - any NULL kid pointer is accompanied by a zero count * - in a sorted tree: ordering property between elements of a * node and elements of its children is preserved * and also ensures the list represented by the tree is the same * list it should be. (This last check also doubly verifies the * ordering properties, because the `same list it should be' is by * definition correctly ordered. It also ensures all nodes are * distinct, because the enum functions would get caught in a loop * if not.) */ #include #include #define srealloc realloc /* * Error reporting function. */ void error(char *fmt, ...) { va_list ap; printf("ERROR: "); va_start(ap, fmt); vfprintf(stdout, fmt, ap); va_end(ap); printf("\n"); } /* The array representation of the data. */ void **array; int arraylen, arraysize; cmpfn234 cmp; /* The tree representation of the same data. */ tree234 *tree; /* * Routines to provide a diagnostic printout of a tree. Currently * relies on every element in the tree being a one-character string * :-) */ typedef struct { char **levels; } dispctx; int dispnode(node234 *n, int level, dispctx *ctx) { if (level == 0) { int xpos = strlen(ctx->levels[0]); int len; if (n->elems[2]) len = sprintf(ctx->levels[0]+xpos, " %s%s%s", n->elems[0], n->elems[1], n->elems[2]); else if (n->elems[1]) len = sprintf(ctx->levels[0]+xpos, " %s%s", n->elems[0], n->elems[1]); else len = sprintf(ctx->levels[0]+xpos, " %s", n->elems[0]); return xpos + 1 + (len-1) / 2; } else { int xpos[4], nkids; int nodelen, mypos, myleft, x, i; xpos[0] = dispnode(n->kids[0], level-3, ctx); xpos[1] = dispnode(n->kids[1], level-3, ctx); nkids = 2; if (n->kids[2]) { xpos[2] = dispnode(n->kids[2], level-3, ctx); nkids = 3; } if (n->kids[3]) { xpos[3] = dispnode(n->kids[3], level-3, ctx); nkids = 4; } if (nkids == 4) mypos = (xpos[1] + xpos[2]) / 2; else if (nkids == 3) mypos = xpos[1]; else mypos = (xpos[0] + xpos[1]) / 2; nodelen = nkids * 2 - 1; myleft = mypos - ((nodelen-1)/2); assert(myleft >= xpos[0]); assert(myleft + nodelen-1 <= xpos[nkids-1]); x = strlen(ctx->levels[level]); while (x <= xpos[0] && x < myleft) ctx->levels[level][x++] = ' '; while (x < myleft) ctx->levels[level][x++] = '_'; if (nkids==4) x += sprintf(ctx->levels[level]+x, ".%s.%s.%s.", n->elems[0], n->elems[1], n->elems[2]); else if (nkids==3) x += sprintf(ctx->levels[level]+x, ".%s.%s.", n->elems[0], n->elems[1]); else x += sprintf(ctx->levels[level]+x, ".%s.", n->elems[0]); while (x < xpos[nkids-1]) ctx->levels[level][x++] = '_'; ctx->levels[level][x] = '\0'; x = strlen(ctx->levels[level-1]); for (i = 0; i < nkids; i++) { int rpos, pos; rpos = xpos[i]; if (i > 0 && i < nkids-1) pos = myleft + 2*i; else pos = rpos; if (rpos < pos) rpos++; while (x < pos && x < rpos) ctx->levels[level-1][x++] = ' '; if (x == pos) ctx->levels[level-1][x++] = '|'; while (x < pos || x < rpos) ctx->levels[level-1][x++] = '_'; if (x == pos) ctx->levels[level-1][x++] = '|'; } ctx->levels[level-1][x] = '\0'; x = strlen(ctx->levels[level-2]); for (i = 0; i < nkids; i++) { int rpos = xpos[i]; while (x < rpos) ctx->levels[level-2][x++] = ' '; ctx->levels[level-2][x++] = '|'; } ctx->levels[level-2][x] = '\0'; return mypos; } } void disptree(tree234 *t) { dispctx ctx; char *leveldata; int width = count234(t); int ht = height234(t) * 3 - 2; int i; if (!t->root) { printf("[empty tree]\n"); } leveldata = smalloc(ht * (width+2)); ctx.levels = smalloc(ht * sizeof(char *)); for (i = 0; i < ht; i++) { ctx.levels[i] = leveldata + i * (width+2); ctx.levels[i][0] = '\0'; } (void) dispnode(t->root, ht-1, &ctx); for (i = ht; i-- ;) printf("%s\n", ctx.levels[i]); sfree(ctx.levels); sfree(leveldata); } typedef struct { int treedepth; int elemcount; } chkctx; int chknode(chkctx *ctx, int level, node234 *node, void *lowbound, void *highbound) { int nkids, nelems; int i; int count; /* Count the non-NULL kids. */ for (nkids = 0; nkids < 4 && node->kids[nkids]; nkids++); /* Ensure no kids beyond the first NULL are non-NULL. */ for (i = nkids; i < 4; i++) if (node->kids[i]) { error("node %p: nkids=%d but kids[%d] non-NULL", node, nkids, i); } else if (node->counts[i]) { error("node %p: kids[%d] NULL but count[%d]=%d nonzero", node, i, i, node->counts[i]); } /* Count the non-NULL elements. */ for (nelems = 0; nelems < 3 && node->elems[nelems]; nelems++); /* Ensure no elements beyond the first NULL are non-NULL. */ for (i = nelems; i < 3; i++) if (node->elems[i]) { error("node %p: nelems=%d but elems[%d] non-NULL", node, nelems, i); } if (nkids == 0) { /* * If nkids==0, this is a leaf node; verify that the tree * depth is the same everywhere. */ if (ctx->treedepth < 0) ctx->treedepth = level; /* we didn't know the depth yet */ else if (ctx->treedepth != level) error("node %p: leaf at depth %d, previously seen depth %d", node, level, ctx->treedepth); } else { /* * If nkids != 0, then it should be nelems+1, unless nelems * is 0 in which case nkids should also be 0 (and so we * shouldn't be in this condition at all). */ int shouldkids = (nelems ? nelems+1 : 0); if (nkids != shouldkids) { error("node %p: %d elems should mean %d kids but has %d", node, nelems, shouldkids, nkids); } } /* * nelems should be at least 1. */ if (nelems == 0) { error("node %p: no elems", node, nkids); } /* * Add nelems to the running element count of the whole tree. */ ctx->elemcount += nelems; /* * Check ordering property: all elements should be strictly > * lowbound, strictly < highbound, and strictly < each other in * sequence. (lowbound and highbound are NULL at edges of tree * - both NULL at root node - and NULL is considered to be < * everything and > everything. IYSWIM.) */ if (cmp) { for (i = -1; i < nelems; i++) { void *lower = (i == -1 ? lowbound : node->elems[i]); void *higher = (i+1 == nelems ? highbound : node->elems[i+1]); if (lower && higher && cmp(lower, higher) >= 0) { error("node %p: kid comparison [%d=%s,%d=%s] failed", node, i, lower, i+1, higher); } } } /* * Check parent pointers: all non-NULL kids should have a * parent pointer coming back to this node. */ for (i = 0; i < nkids; i++) if (node->kids[i]->parent != node) { error("node %p kid %d: parent ptr is %p not %p", node, i, node->kids[i]->parent, node); } /* * Now (finally!) recurse into subtrees. */ count = nelems; for (i = 0; i < nkids; i++) { void *lower = (i == 0 ? lowbound : node->elems[i-1]); void *higher = (i >= nelems ? highbound : node->elems[i]); int subcount = chknode(ctx, level+1, node->kids[i], lower, higher); if (node->counts[i] != subcount) { error("node %p kid %d: count says %d, subtree really has %d", node, i, node->counts[i], subcount); } count += subcount; } return count; } void verifytree(tree234 *tree, void **array, int arraylen) { chkctx ctx; int i; void *p; ctx.treedepth = -1; /* depth unknown yet */ ctx.elemcount = 0; /* no elements seen yet */ /* * Verify validity of tree properties. */ if (tree->root) { if (tree->root->parent != NULL) error("root->parent is %p should be null", tree->root->parent); chknode(&ctx, 0, tree->root, NULL, NULL); } printf("tree depth: %d\n", ctx.treedepth); /* * Enumerate the tree and ensure it matches up to the array. */ for (i = 0; NULL != (p = index234(tree, i)); i++) { if (i >= arraylen) error("tree contains more than %d elements", arraylen); if (array[i] != p) error("enum at position %d: array says %s, tree says %s", i, array[i], p); } if (ctx.elemcount != i) { error("tree really contains %d elements, enum gave %d", ctx.elemcount, i); } if (i < arraylen) { error("enum gave only %d elements, array has %d", i, arraylen); } i = count234(tree); if (ctx.elemcount != i) { error("tree really contains %d elements, count234 gave %d", ctx.elemcount, i); } } void verify(void) { verifytree(tree, array, arraylen); } void internal_addtest(void *elem, int index, void *realret) { int i, j; void *retval; if (arraysize < arraylen+1) { arraysize = arraylen+1+256; array = (array == NULL ? smalloc(arraysize*sizeof(*array)) : srealloc(array, arraysize*sizeof(*array))); } i = index; /* now i points to the first element >= elem */ retval = elem; /* expect elem returned (success) */ for (j = arraylen; j > i; j--) array[j] = array[j-1]; array[i] = elem; /* add elem to array */ arraylen++; if (realret != retval) { error("add: retval was %p expected %p", realret, retval); } verify(); } void addtest(void *elem) { int i; void *realret; realret = add234(tree, elem); i = 0; while (i < arraylen && cmp(elem, array[i]) > 0) i++; if (i < arraylen && !cmp(elem, array[i])) { void *retval = array[i]; /* expect that returned not elem */ if (realret != retval) { error("add: retval was %p expected %p", realret, retval); } } else internal_addtest(elem, i, realret); } void addpostest(void *elem, int i) { void *realret; realret = addpos234(tree, elem, i); internal_addtest(elem, i, realret); } void delpostest(int i) { int index = i; void *elem = array[i], *ret; /* i points to the right element */ while (i < arraylen-1) { array[i] = array[i+1]; i++; } arraylen--; /* delete elem from array */ if (tree->cmp) ret = del234(tree, elem); else ret = delpos234(tree, index); if (ret != elem) { error("del returned %p, expected %p", ret, elem); } verify(); } void deltest(void *elem) { int i; i = 0; while (i < arraylen && cmp(elem, array[i]) > 0) i++; if (i >= arraylen || cmp(elem, array[i]) != 0) return; /* don't do it! */ delpostest(i); } /* A sample data set and test utility. Designed for pseudo-randomness, * and yet repeatability. */ /* * This random number generator uses the `portable implementation' * given in ANSI C99 draft N869. It assumes `unsigned' is 32 bits; * change it if not. */ int randomnumber(unsigned *seed) { *seed *= 1103515245; *seed += 12345; return ((*seed) / 65536) % 32768; } int mycmp(void *av, void *bv) { char const *a = (char const *)av; char const *b = (char const *)bv; return strcmp(a, b); } char *strings[] = { "0", "2", "3", "I", "K", "d", "H", "J", "Q", "N", "n", "q", "j", "i", "7", "G", "F", "D", "b", "x", "g", "B", "e", "v", "V", "T", "f", "E", "S", "8", "A", "k", "X", "p", "C", "R", "a", "o", "r", "O", "Z", "u", "6", "1", "w", "L", "P", "M", "c", "U", "h", "9", "t", "5", "W", "Y", "m", "s", "l", "4", #if 0 "a", "ab", "absque", "coram", "de", "palam", "clam", "cum", "ex", "e", "sine", "tenus", "pro", "prae", "banana", "carrot", "cabbage", "broccoli", "onion", "zebra", "penguin", "blancmange", "pangolin", "whale", "hedgehog", "giraffe", "peanut", "bungee", "foo", "bar", "baz", "quux", "murfl", "spoo", "breen", "flarn", "octothorpe", "snail", "tiger", "elephant", "octopus", "warthog", "armadillo", "aardvark", "wyvern", "dragon", "elf", "dwarf", "orc", "goblin", "pixie", "basilisk", "warg", "ape", "lizard", "newt", "shopkeeper", "wand", "ring", "amulet" #endif }; #define NSTR lenof(strings) void findtest(void) { static const int rels[] = { REL234_EQ, REL234_GE, REL234_LE, REL234_LT, REL234_GT }; static const char *const relnames[] = { "EQ", "GE", "LE", "LT", "GT" }; int i, j, rel, index; char *p, *ret, *realret, *realret2; int lo, hi, mid, c; for (i = 0; i < (int)NSTR; i++) { p = strings[i]; for (j = 0; j < (int)(sizeof(rels)/sizeof(*rels)); j++) { rel = rels[j]; lo = 0; hi = arraylen-1; while (lo <= hi) { mid = (lo + hi) / 2; c = strcmp(p, array[mid]); if (c < 0) hi = mid-1; else if (c > 0) lo = mid+1; else break; } if (c == 0) { if (rel == REL234_LT) ret = (mid > 0 ? array[--mid] : NULL); else if (rel == REL234_GT) ret = (mid < arraylen-1 ? array[++mid] : NULL); else ret = array[mid]; } else { assert(lo == hi+1); if (rel == REL234_LT || rel == REL234_LE) { mid = hi; ret = (hi >= 0 ? array[hi] : NULL); } else if (rel == REL234_GT || rel == REL234_GE) { mid = lo; ret = (lo < arraylen ? array[lo] : NULL); } else ret = NULL; } realret = findrelpos234(tree, p, NULL, rel, &index); if (realret != ret) { error("find(\"%s\",%s) gave %s should be %s", p, relnames[j], realret, ret); } if (realret && index != mid) { error("find(\"%s\",%s) gave %d should be %d", p, relnames[j], index, mid); } if (realret && rel == REL234_EQ) { realret2 = index234(tree, index); if (realret2 != realret) { error("find(\"%s\",%s) gave %s(%d) but %d -> %s", p, relnames[j], realret, index, index, realret2); } } #if 0 printf("find(\"%s\",%s) gave %s(%d)\n", p, relnames[j], realret, index); #endif } } realret = findrelpos234(tree, NULL, NULL, REL234_GT, &index); if (arraylen && (realret != array[0] || index != 0)) { error("find(NULL,GT) gave %s(%d) should be %s(0)", realret, index, array[0]); } else if (!arraylen && (realret != NULL)) { error("find(NULL,GT) gave %s(%d) should be NULL", realret, index); } realret = findrelpos234(tree, NULL, NULL, REL234_LT, &index); if (arraylen && (realret != array[arraylen-1] || index != arraylen-1)) { error("find(NULL,LT) gave %s(%d) should be %s(0)", realret, index, array[arraylen-1]); } else if (!arraylen && (realret != NULL)) { error("find(NULL,LT) gave %s(%d) should be NULL", realret, index); } } void splittest(tree234 *tree, void **array, int arraylen) { int i; tree234 *tree3, *tree4; for (i = 0; i <= arraylen; i++) { tree3 = copytree234(tree, NULL, NULL); tree4 = splitpos234(tree3, i, 0); verifytree(tree3, array, i); verifytree(tree4, array+i, arraylen-i); join234(tree3, tree4); freetree234(tree4); /* left empty by join */ verifytree(tree3, array, arraylen); freetree234(tree3); } } int main(void) { int in[NSTR]; int i, j, k; int tworoot, tmplen; unsigned seed = 0; tree234 *tree2, *tree3, *tree4; int c; setvbuf(stdout, NULL, _IOLBF, 0); for (i = 0; i < (int)NSTR; i++) in[i] = 0; array = NULL; arraylen = arraysize = 0; tree = newtree234(mycmp); cmp = mycmp; verify(); for (i = 0; i < 10000; i++) { j = randomnumber(&seed); j %= NSTR; printf("trial: %d\n", i); if (in[j]) { printf("deleting %s (%d)\n", strings[j], j); deltest(strings[j]); in[j] = 0; } else { printf("adding %s (%d)\n", strings[j], j); addtest(strings[j]); in[j] = 1; } disptree(tree); findtest(); } while (arraylen > 0) { j = randomnumber(&seed); j %= arraylen; deltest(array[j]); } freetree234(tree); /* * Now try an unsorted tree. We don't really need to test * delpos234 because we know del234 is based on it, so it's * already been tested in the above sorted-tree code; but for * completeness we'll use it to tear down our unsorted tree * once we've built it. */ tree = newtree234(NULL); cmp = NULL; verify(); for (i = 0; i < 1000; i++) { printf("trial: %d\n", i); j = randomnumber(&seed); j %= NSTR; k = randomnumber(&seed); k %= count234(tree)+1; printf("adding string %s at index %d\n", strings[j], k); addpostest(strings[j], k); } /* * While we have this tree in its full form, we'll take a copy * of it to use in split and join testing. */ tree2 = copytree234(tree, NULL, NULL); verifytree(tree2, array, arraylen);/* check the copy is accurate */ /* * Split tests. Split the tree at every possible point and * check the resulting subtrees. */ tworoot = (!tree2->root->elems[1]);/* see if it has a 2-root */ splittest(tree2, array, arraylen); /* * Now do the split test again, but on a tree that has a 2-root * (if the previous one didn't) or doesn't (if the previous one * did). */ tmplen = arraylen; while ((!tree2->root->elems[1]) == tworoot) { delpos234(tree2, --tmplen); } printf("now trying splits on second tree\n"); splittest(tree2, array, tmplen); freetree234(tree2); /* * Back to the main testing of uncounted trees. */ while (count234(tree) > 0) { printf("cleanup: tree size %d\n", count234(tree)); j = randomnumber(&seed); j %= count234(tree); printf("deleting string %s from index %d\n", (char *)array[j], j); delpostest(j); } freetree234(tree); /* * Finally, do some testing on split/join on _sorted_ trees. At * the same time, we'll be testing split on very small trees. */ tree = newtree234(mycmp); cmp = mycmp; arraylen = 0; for (i = 0; i < 17; i++) { tree2 = copytree234(tree, NULL, NULL); splittest(tree2, array, arraylen); freetree234(tree2); if (i < 16) addtest(strings[i]); } freetree234(tree); /* * Test silly cases of join: join(emptytree, emptytree), and * also ensure join correctly spots when sorted trees fail the * ordering constraint. */ tree = newtree234(mycmp); tree2 = newtree234(mycmp); tree3 = newtree234(mycmp); tree4 = newtree234(mycmp); assert(mycmp(strings[0], strings[1]) < 0); /* just in case :-) */ add234(tree2, strings[1]); add234(tree4, strings[0]); array[0] = strings[0]; array[1] = strings[1]; verifytree(tree, array, 0); verifytree(tree2, array+1, 1); verifytree(tree3, array, 0); verifytree(tree4, array, 1); /* * So: * - join(tree,tree3) should leave both tree and tree3 unchanged. * - joinr(tree,tree2) should leave both tree and tree2 unchanged. * - join(tree4,tree3) should leave both tree3 and tree4 unchanged. * - join(tree, tree2) should move the element from tree2 to tree. * - joinr(tree4, tree3) should move the element from tree4 to tree3. * - join(tree,tree3) should return NULL and leave both unchanged. * - join(tree3,tree) should work and create a bigger tree in tree3. */ assert(tree == join234(tree, tree3)); verifytree(tree, array, 0); verifytree(tree3, array, 0); assert(tree2 == join234r(tree, tree2)); verifytree(tree, array, 0); verifytree(tree2, array+1, 1); assert(tree4 == join234(tree4, tree3)); verifytree(tree3, array, 0); verifytree(tree4, array, 1); assert(tree == join234(tree, tree2)); verifytree(tree, array+1, 1); verifytree(tree2, array, 0); assert(tree3 == join234r(tree4, tree3)); verifytree(tree3, array, 1); verifytree(tree4, array, 0); assert(NULL == join234(tree, tree3)); verifytree(tree, array+1, 1); verifytree(tree3, array, 1); assert(tree3 == join234(tree3, tree)); verifytree(tree3, array, 2); verifytree(tree, array, 0); return 0; } #endif #if 0 /* sorted list of strings might be useful */ { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", } #endif puzzles-r9872/twiddle.c0000644000175300017530000010715112132232554014231 0ustar simonsimon/* * twiddle.c: Puzzle involving rearranging a grid of squares by * rotating subsquares. Adapted and generalised from a * door-unlocking puzzle in Metroid Prime 2 (the one in the Main * Gyro Chamber). */ #include #include #include #include #include #include #include "puzzles.h" #define PREFERRED_TILE_SIZE 48 #define TILE_SIZE (ds->tilesize) #define BORDER (TILE_SIZE / 2) #define HIGHLIGHT_WIDTH (TILE_SIZE / 20) #define COORD(x) ( (x) * TILE_SIZE + BORDER ) #define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 ) #define ANIM_PER_BLKSIZE_UNIT 0.13F #define FLASH_FRAME 0.13F enum { COL_BACKGROUND, COL_TEXT, COL_HIGHLIGHT, COL_HIGHLIGHT_GENTLE, COL_LOWLIGHT, COL_LOWLIGHT_GENTLE, COL_HIGHCURSOR, COL_LOWCURSOR, NCOLOURS }; struct game_params { int w, h, n; int rowsonly; int orientable; int movetarget; }; struct game_state { int w, h, n; int orientable; int *grid; int completed; int used_solve; /* used to suppress completion flash */ int movecount, movetarget; int lastx, lasty, lastr; /* coordinates of last rotation */ }; static game_params *default_params(void) { game_params *ret = snew(game_params); ret->w = ret->h = 3; ret->n = 2; ret->rowsonly = ret->orientable = FALSE; ret->movetarget = 0; 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 int game_fetch_preset(int i, char **name, game_params **params) { static struct { char *title; game_params params; } presets[] = { { "3x3 rows only", { 3, 3, 2, TRUE, FALSE } }, { "3x3 normal", { 3, 3, 2, FALSE, FALSE } }, { "3x3 orientable", { 3, 3, 2, FALSE, TRUE } }, { "4x4 normal", { 4, 4, 2, FALSE } }, { "4x4 orientable", { 4, 4, 2, FALSE, TRUE } }, { "4x4, rotating 3x3 blocks", { 4, 4, 3, FALSE } }, { "5x5, rotating 3x3 blocks", { 5, 5, 3, FALSE } }, { "6x6, rotating 4x4 blocks", { 6, 6, 4, FALSE } }, }; if (i < 0 || i >= lenof(presets)) return FALSE; *name = dupstr(presets[i].title); *params = dup_params(&presets[i].params); return TRUE; } static void decode_params(game_params *ret, char const *string) { ret->w = ret->h = atoi(string); ret->n = 2; ret->rowsonly = ret->orientable = FALSE; ret->movetarget = 0; while (*string && isdigit((unsigned char)*string)) string++; if (*string == 'x') { string++; ret->h = atoi(string); while (*string && isdigit((unsigned char)*string)) string++; } if (*string == 'n') { string++; ret->n = atoi(string); while (*string && isdigit((unsigned char)*string)) string++; } while (*string) { if (*string == 'r') { ret->rowsonly = TRUE; } else if (*string == 'o') { ret->orientable = TRUE; } else if (*string == 'm') { string++; ret->movetarget = atoi(string); while (string[1] && isdigit((unsigned char)string[1])) string++; } string++; } } static char *encode_params(const game_params *params, int full) { char buf[256]; sprintf(buf, "%dx%dn%d%s%s", params->w, params->h, params->n, params->rowsonly ? "r" : "", params->orientable ? "o" : ""); /* Shuffle limit is part of the limited parameters, because we have to * supply the target move count. */ if (params->movetarget) sprintf(buf + strlen(buf), "m%d", params->movetarget); return dupstr(buf); } static config_item *game_configure(const game_params *params) { config_item *ret; char buf[80]; ret = snewn(7, config_item); ret[0].name = "Width"; ret[0].type = C_STRING; sprintf(buf, "%d", params->w); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Rotating block size"; ret[2].type = C_STRING; sprintf(buf, "%d", params->n); ret[2].sval = dupstr(buf); ret[2].ival = 0; ret[3].name = "One number per row"; ret[3].type = C_BOOLEAN; ret[3].sval = NULL; ret[3].ival = params->rowsonly; ret[4].name = "Orientation matters"; ret[4].type = C_BOOLEAN; ret[4].sval = NULL; ret[4].ival = params->orientable; ret[5].name = "Number of shuffling moves"; ret[5].type = C_STRING; sprintf(buf, "%d", params->movetarget); ret[5].sval = dupstr(buf); ret[5].ival = 0; ret[6].name = NULL; ret[6].type = C_END; ret[6].sval = NULL; ret[6].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); ret->n = atoi(cfg[2].sval); ret->rowsonly = cfg[3].ival; ret->orientable = cfg[4].ival; ret->movetarget = atoi(cfg[5].sval); return ret; } static char *validate_params(const game_params *params, int full) { if (params->n < 2) return "Rotating block size must be at least two"; if (params->w < params->n) return "Width must be at least the rotating block size"; if (params->h < params->n) return "Height must be at least the rotating block size"; return NULL; } /* * This function actually performs a rotation on a grid. The `x' * and `y' coordinates passed in are the coordinates of the _top * left corner_ of the rotated region. (Using the centre would have * involved half-integers and been annoyingly fiddly. Clicking in * the centre is good for a user interface, but too inconvenient to * use internally.) */ static void do_rotate(int *grid, int w, int h, int n, int orientable, int x, int y, int dir) { int i, j; assert(x >= 0 && x+n <= w); assert(y >= 0 && y+n <= h); dir &= 3; if (dir == 0) return; /* nothing to do */ grid += y*w+x; /* translate region to top corner */ /* * If we were leaving the result of the rotation in a separate * grid, the simple thing to do would be to loop over each * square within the rotated region and assign it from its * source square. However, to do it in place without taking * O(n^2) memory, we need to be marginally more clever. What * I'm going to do is loop over about one _quarter_ of the * rotated region and permute each element within that quarter * with its rotational coset. * * The size of the region I need to loop over is (n+1)/2 by * n/2, which is an obvious exact quarter for even n and is a * rectangle for odd n. (For odd n, this technique leaves out * one element of the square, which is of course the central * one that never moves anyway.) */ for (i = 0; i < (n+1)/2; i++) { for (j = 0; j < n/2; j++) { int k; int g[4]; int p[4]; p[0] = j*w+i; p[1] = i*w+(n-j-1); p[2] = (n-j-1)*w+(n-i-1); p[3] = (n-i-1)*w+j; for (k = 0; k < 4; k++) g[k] = grid[p[k]]; for (k = 0; k < 4; k++) { int v = g[(k+dir) & 3]; if (orientable) v ^= ((v+dir) ^ v) & 3; /* alter orientation */ grid[p[k]] = v; } } } /* * Don't forget the orientation on the centre square, if n is * odd. */ if (orientable && (n & 1)) { int v = grid[n/2*(w+1)]; v ^= ((v+dir) ^ v) & 3; /* alter orientation */ grid[n/2*(w+1)] = v; } } static int grid_complete(int *grid, int wh, int orientable) { int ok = TRUE; int i; for (i = 1; i < wh; i++) if (grid[i] < grid[i-1]) ok = FALSE; if (orientable) { for (i = 0; i < wh; i++) if (grid[i] & 3) ok = FALSE; } return ok; } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { int *grid; int w = params->w, h = params->h, n = params->n, wh = w*h; int i; char *ret; int retlen; int total_moves; /* * Set up a solved grid. */ grid = snewn(wh, int); for (i = 0; i < wh; i++) grid[i] = ((params->rowsonly ? i/w : i) + 1) * 4; /* * Shuffle it. This game is complex enough that I don't feel up * to analysing its full symmetry properties (particularly at * n=4 and above!), so I'm going to do it the pedestrian way * and simply shuffle the grid by making a long sequence of * randomly chosen moves. */ total_moves = params->movetarget; if (!total_moves) /* Add a random move to avoid parity issues. */ total_moves = w*h*n*n*2 + random_upto(rs, 2); do { int *prevmoves; int rw, rh; /* w/h of rotation centre space */ rw = w - n + 1; rh = h - n + 1; prevmoves = snewn(rw * rh, int); for (i = 0; i < rw * rh; i++) prevmoves[i] = 0; for (i = 0; i < total_moves; i++) { int x, y, r, oldtotal, newtotal, dx, dy; do { x = random_upto(rs, w - n + 1); y = random_upto(rs, h - n + 1); r = 2 * random_upto(rs, 2) - 1; /* * See if any previous rotations has happened at * this point which nothing has overlapped since. * If so, ensure we haven't either undone a * previous move or repeated one so many times that * it turns into fewer moves in the inverse * direction (i.e. three identical rotations). */ oldtotal = prevmoves[y*rw+x]; newtotal = oldtotal + r; /* * Special case here for w==h==n, in which case * there is actually no way to _avoid_ all moves * repeating or undoing previous ones. */ } while ((w != n || h != n) && (abs(newtotal) < abs(oldtotal) || abs(newtotal) > 2)); do_rotate(grid, w, h, n, params->orientable, x, y, r); /* * Log the rotation we've just performed at this point, * for inversion detection in the next move. * * Also zero a section of the prevmoves array, because * any rotation area which _overlaps_ this one is now * entirely safe to perform further moves in. * * Two rotation areas overlap if their top left * coordinates differ by strictly less than n in both * directions */ prevmoves[y*rw+x] += r; for (dy = -n+1; dy <= n-1; dy++) { if (y + dy < 0 || y + dy >= rh) continue; for (dx = -n+1; dx <= n-1; dx++) { if (x + dx < 0 || x + dx >= rw) continue; if (dx == 0 && dy == 0) continue; prevmoves[(y+dy)*rw+(x+dx)] = 0; } } } sfree(prevmoves); } while (grid_complete(grid, wh, params->orientable)); /* * Now construct the game description, by describing the grid * as a simple sequence of integers. They're comma-separated, * unless the puzzle is orientable in which case they're * separated by orientation letters `u', `d', `l' and `r'. */ ret = NULL; retlen = 0; for (i = 0; i < wh; i++) { char buf[80]; int k; k = sprintf(buf, "%d%c", grid[i] / 4, (char)(params->orientable ? "uldr"[grid[i] & 3] : ',')); ret = sresize(ret, retlen + k + 1, char); strcpy(ret + retlen, buf); retlen += k; } if (!params->orientable) ret[retlen-1] = '\0'; /* delete last comma */ sfree(grid); return ret; } static char *validate_desc(const game_params *params, const char *desc) { const char *p; int w = params->w, h = params->h, wh = w*h; int i; p = desc; for (i = 0; i < wh; i++) { if (*p < '0' || *p > '9') return "Not enough numbers in string"; while (*p >= '0' && *p <= '9') p++; if (!params->orientable && i < wh-1) { if (*p != ',') return "Expected comma after number"; } else if (params->orientable && i < wh) { if (*p != 'l' && *p != 'r' && *p != 'u' && *p != 'd') return "Expected orientation letter after number"; } else if (i == wh-1 && *p) { return "Excess junk at end of string"; } if (*p) p++; /* eat comma */ } return NULL; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { game_state *state = snew(game_state); int w = params->w, h = params->h, n = params->n, wh = w*h; int i; const char *p; state->w = w; state->h = h; state->n = n; state->orientable = params->orientable; state->completed = 0; state->used_solve = FALSE; state->movecount = 0; state->movetarget = params->movetarget; state->lastx = state->lasty = state->lastr = -1; state->grid = snewn(wh, int); p = desc; for (i = 0; i < wh; i++) { state->grid[i] = 4 * atoi(p); while (*p >= '0' && *p <= '9') p++; if (*p) { if (params->orientable) { switch (*p) { case 'l': state->grid[i] |= 1; break; case 'd': state->grid[i] |= 2; break; case 'r': state->grid[i] |= 3; break; } } p++; } } return state; } static game_state *dup_game(const game_state *state) { game_state *ret = snew(game_state); ret->w = state->w; ret->h = state->h; ret->n = state->n; ret->orientable = state->orientable; ret->completed = state->completed; ret->movecount = state->movecount; ret->movetarget = state->movetarget; ret->lastx = state->lastx; ret->lasty = state->lasty; ret->lastr = state->lastr; ret->used_solve = state->used_solve; ret->grid = snewn(ret->w * ret->h, int); memcpy(ret->grid, state->grid, ret->w * ret->h * sizeof(int)); return ret; } static void free_game(game_state *state) { sfree(state->grid); sfree(state); } static int compare_int(const void *av, const void *bv) { const int *a = (const int *)av; const int *b = (const int *)bv; if (*a < *b) return -1; else if (*a > *b) return +1; else return 0; } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { return dupstr("S"); } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { char *ret, *p, buf[80]; int i, x, y, col, o, maxlen; /* * First work out how many characters we need to display each * number. We're pretty flexible on grid contents here, so we * have to scan the entire grid. */ col = 0; for (i = 0; i < state->w * state->h; i++) { x = sprintf(buf, "%d", state->grid[i] / 4); if (col < x) col = x; } o = (state->orientable ? 1 : 0); /* * Now we know the exact total size of the grid we're going to * produce: it's got h rows, each containing w lots of col+o, * w-1 spaces and a trailing newline. */ maxlen = state->h * state->w * (col+o+1); ret = snewn(maxlen+1, char); p = ret; for (y = 0; y < state->h; y++) { for (x = 0; x < state->w; x++) { int v = state->grid[state->w*y+x]; sprintf(buf, "%*d", col, v/4); memcpy(p, buf, col); p += col; if (o) *p++ = "^"[v & 3]; if (x+1 == state->w) *p++ = '\n'; else *p++ = ' '; } } assert(p - ret == maxlen); *p = '\0'; return ret; } struct game_ui { int cur_x, cur_y; int cur_visible; }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->cur_x = 0; ui->cur_y = 0; ui->cur_visible = FALSE; 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) { } struct game_drawstate { int started; int w, h, bgcolour; int *grid; int tilesize; int cur_x, cur_y; }; 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, n = state->n /* , wh = w*h */; char buf[80]; int dir; button = button & (~MOD_MASK | MOD_NUM_KEYPAD); if (IS_CURSOR_MOVE(button)) { if (button == CURSOR_LEFT && ui->cur_x > 0) ui->cur_x--; if (button == CURSOR_RIGHT && (ui->cur_x+n) < (w)) ui->cur_x++; if (button == CURSOR_UP && ui->cur_y > 0) ui->cur_y--; if (button == CURSOR_DOWN && (ui->cur_y+n) < (h)) ui->cur_y++; ui->cur_visible = 1; return ""; } if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { /* * Determine the coordinates of the click. We offset by n-1 * half-blocks so that the user must click at the centre of * a rotation region rather than at the corner. */ x -= (n-1) * TILE_SIZE / 2; y -= (n-1) * TILE_SIZE / 2; x = FROMCOORD(x); y = FROMCOORD(y); dir = (button == LEFT_BUTTON ? 1 : -1); if (x < 0 || x > w-n || y < 0 || y > h-n) return NULL; ui->cur_visible = 0; } else if (IS_CURSOR_SELECT(button)) { if (ui->cur_visible) { x = ui->cur_x; y = ui->cur_y; dir = (button == CURSOR_SELECT2) ? -1 : +1; } else { ui->cur_visible = 1; return ""; } } else if (button == 'a' || button == 'A' || button==MOD_NUM_KEYPAD+'7') { x = y = 0; dir = (button == 'A' ? -1 : +1); } else if (button == 'b' || button == 'B' || button==MOD_NUM_KEYPAD+'9') { x = w-n; y = 0; dir = (button == 'B' ? -1 : +1); } else if (button == 'c' || button == 'C' || button==MOD_NUM_KEYPAD+'1') { x = 0; y = h-n; dir = (button == 'C' ? -1 : +1); } else if (button == 'd' || button == 'D' || button==MOD_NUM_KEYPAD+'3') { x = w-n; y = h-n; dir = (button == 'D' ? -1 : +1); } else if (button==MOD_NUM_KEYPAD+'8' && (w-n) % 2 == 0) { x = (w-n) / 2; y = 0; dir = +1; } else if (button==MOD_NUM_KEYPAD+'2' && (w-n) % 2 == 0) { x = (w-n) / 2; y = h-n; dir = +1; } else if (button==MOD_NUM_KEYPAD+'4' && (h-n) % 2 == 0) { x = 0; y = (h-n) / 2; dir = +1; } else if (button==MOD_NUM_KEYPAD+'6' && (h-n) % 2 == 0) { x = w-n; y = (h-n) / 2; dir = +1; } else if (button==MOD_NUM_KEYPAD+'5' && (w-n) % 2 == 0 && (h-n) % 2 == 0){ x = (w-n) / 2; y = (h-n) / 2; dir = +1; } else { return NULL; /* no move to be made */ } /* * If we reach here, we have a valid move. */ sprintf(buf, "M%d,%d,%d", x, y, dir); return dupstr(buf); } static game_state *execute_move(const game_state *from, const char *move) { game_state *ret; int w = from->w, h = from->h, n = from->n, wh = w*h; int x, y, dir; if (!strcmp(move, "S")) { int i; ret = dup_game(from); /* * Simply replace the grid with a solved one. For this game, * this isn't a useful operation for actually telling the user * what they should have done, but it is useful for * conveniently being able to get hold of a clean state from * which to practise manoeuvres. */ qsort(ret->grid, ret->w*ret->h, sizeof(int), compare_int); for (i = 0; i < ret->w*ret->h; i++) ret->grid[i] &= ~3; ret->used_solve = TRUE; ret->completed = ret->movecount = 1; return ret; } if (move[0] != 'M' || sscanf(move+1, "%d,%d,%d", &x, &y, &dir) != 3 || x < 0 || y < 0 || x > from->w - n || y > from->h - n) return NULL; /* can't parse this move string */ ret = dup_game(from); ret->movecount++; do_rotate(ret->grid, w, h, n, ret->orientable, x, y, dir); ret->lastx = x; ret->lasty = y; ret->lastr = dir; /* * See if the game has been completed. To do this we simply * test that the grid contents are in increasing order. */ if (!ret->completed && grid_complete(ret->grid, wh, ret->orientable)) ret->completed = ret->movecount; 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 = TILE_SIZE * params->w + 2 * BORDER; *y = TILE_SIZE * params->h + 2 * BORDER; } 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); /* cursor is light-background with a red tinge. */ ret[COL_HIGHCURSOR * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 1.0F; ret[COL_HIGHCURSOR * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.5F; ret[COL_HIGHCURSOR * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 0.5F; for (i = 0; i < 3; i++) { ret[COL_HIGHLIGHT_GENTLE * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 1.1F; ret[COL_LOWLIGHT_GENTLE * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.9F; ret[COL_TEXT * 3 + i] = 0.0; ret[COL_LOWCURSOR * 3 + i] = ret[COL_HIGHCURSOR * 3 + i] * 0.6F; } *ncolours = NCOLOURS; return ret; } static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { struct game_drawstate *ds = snew(struct game_drawstate); int i; ds->started = FALSE; ds->w = state->w; ds->h = state->h; ds->bgcolour = COL_BACKGROUND; ds->grid = snewn(ds->w*ds->h, int); ds->tilesize = 0; /* haven't decided yet */ for (i = 0; i < ds->w*ds->h; i++) ds->grid[i] = -1; ds->cur_x = ds->cur_y = -state->n; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->grid); sfree(ds); } struct rotation { int cx, cy, cw, ch; /* clip region */ int ox, oy; /* rotation origin */ float c, s; /* cos and sin of rotation angle */ int lc, rc, tc, bc; /* colours of tile edges */ }; static void rotate(int *xy, struct rotation *rot) { if (rot) { float xf = (float)xy[0] - rot->ox, yf = (float)xy[1] - rot->oy; float xf2, yf2; xf2 = rot->c * xf + rot->s * yf; yf2 = - rot->s * xf + rot->c * yf; xy[0] = (int)(xf2 + rot->ox + 0.5); /* round to nearest */ xy[1] = (int)(yf2 + rot->oy + 0.5); /* round to nearest */ } } #define CUR_TOP 1 #define CUR_RIGHT 2 #define CUR_BOTTOM 4 #define CUR_LEFT 8 static void draw_tile(drawing *dr, game_drawstate *ds, const game_state *state, int x, int y, int tile, int flash_colour, struct rotation *rot, unsigned cedges) { int coords[8]; char str[40]; /* * If we've been passed a rotation region but we're drawing a * tile which is outside it, we must draw it normally. This can * occur if we're cleaning up after a completion flash while a * new move is also being made. */ if (rot && (x < rot->cx || y < rot->cy || x >= rot->cx+rot->cw || y >= rot->cy+rot->ch)) rot = NULL; if (rot) clip(dr, rot->cx, rot->cy, rot->cw, rot->ch); /* * We must draw each side of the tile's highlight separately, * because in some cases (during rotation) they will all need * to be different colours. */ /* The centre point is common to all sides. */ coords[4] = x + TILE_SIZE / 2; coords[5] = y + TILE_SIZE / 2; rotate(coords+4, rot); /* Right side. */ coords[0] = x + TILE_SIZE - 1; coords[1] = y + TILE_SIZE - 1; rotate(coords+0, rot); coords[2] = x + TILE_SIZE - 1; coords[3] = y; rotate(coords+2, rot); draw_polygon(dr, coords, 3, rot ? rot->rc : COL_LOWLIGHT, rot ? rot->rc : (cedges & CUR_RIGHT) ? COL_LOWCURSOR : COL_LOWLIGHT); /* Bottom side. */ coords[2] = x; coords[3] = y + TILE_SIZE - 1; rotate(coords+2, rot); draw_polygon(dr, coords, 3, rot ? rot->bc : COL_LOWLIGHT, rot ? rot->bc : (cedges & CUR_BOTTOM) ? COL_LOWCURSOR : COL_LOWLIGHT); /* Left side. */ coords[0] = x; coords[1] = y; rotate(coords+0, rot); draw_polygon(dr, coords, 3, rot ? rot->lc : COL_HIGHLIGHT, rot ? rot->lc : (cedges & CUR_LEFT) ? COL_HIGHCURSOR : COL_HIGHLIGHT); /* Top side. */ coords[2] = x + TILE_SIZE - 1; coords[3] = y; rotate(coords+2, rot); draw_polygon(dr, coords, 3, rot ? rot->tc : COL_HIGHLIGHT, rot ? rot->tc : (cedges & CUR_TOP) ? COL_HIGHCURSOR : COL_HIGHLIGHT); /* * Now the main blank area in the centre of the tile. */ if (rot) { coords[0] = x + HIGHLIGHT_WIDTH; coords[1] = y + HIGHLIGHT_WIDTH; rotate(coords+0, rot); coords[2] = x + HIGHLIGHT_WIDTH; coords[3] = y + TILE_SIZE - 1 - HIGHLIGHT_WIDTH; rotate(coords+2, rot); coords[4] = x + TILE_SIZE - 1 - HIGHLIGHT_WIDTH; coords[5] = y + TILE_SIZE - 1 - HIGHLIGHT_WIDTH; rotate(coords+4, rot); coords[6] = x + TILE_SIZE - 1 - HIGHLIGHT_WIDTH; coords[7] = y + HIGHLIGHT_WIDTH; rotate(coords+6, rot); draw_polygon(dr, coords, 4, flash_colour, flash_colour); } else { draw_rect(dr, x + HIGHLIGHT_WIDTH, y + HIGHLIGHT_WIDTH, TILE_SIZE - 2*HIGHLIGHT_WIDTH, TILE_SIZE - 2*HIGHLIGHT_WIDTH, flash_colour); } /* * Next, the triangles for orientation. */ if (state->orientable) { int xdx, xdy, ydx, ydy; int cx, cy, displ, displ2; switch (tile & 3) { case 0: xdx = 1, xdy = 0; ydx = 0, ydy = 1; break; case 1: xdx = 0, xdy = -1; ydx = 1, ydy = 0; break; case 2: xdx = -1, xdy = 0; ydx = 0, ydy = -1; break; default /* case 3 */: xdx = 0, xdy = 1; ydx = -1, ydy = 0; break; } cx = x + TILE_SIZE / 2; cy = y + TILE_SIZE / 2; displ = TILE_SIZE / 2 - HIGHLIGHT_WIDTH - 2; displ2 = TILE_SIZE / 3 - HIGHLIGHT_WIDTH; coords[0] = cx - displ * xdx + displ2 * ydx; coords[1] = cy - displ * xdy + displ2 * ydy; rotate(coords+0, rot); coords[2] = cx + displ * xdx + displ2 * ydx; coords[3] = cy + displ * xdy + displ2 * ydy; rotate(coords+2, rot); coords[4] = cx - displ * ydx; coords[5] = cy - displ * ydy; rotate(coords+4, rot); draw_polygon(dr, coords, 3, COL_LOWLIGHT_GENTLE, COL_LOWLIGHT_GENTLE); } coords[0] = x + TILE_SIZE/2; coords[1] = y + TILE_SIZE/2; rotate(coords+0, rot); sprintf(str, "%d", tile / 4); draw_text(dr, coords[0], coords[1], FONT_VARIABLE, TILE_SIZE/3, ALIGN_VCENTRE | ALIGN_HCENTRE, COL_TEXT, str); if (rot) unclip(dr); draw_update(dr, x, y, TILE_SIZE, TILE_SIZE); } static int highlight_colour(float angle) { int colours[32] = { COL_LOWLIGHT, COL_LOWLIGHT_GENTLE, COL_LOWLIGHT_GENTLE, COL_LOWLIGHT_GENTLE, COL_HIGHLIGHT_GENTLE, COL_HIGHLIGHT_GENTLE, COL_HIGHLIGHT_GENTLE, COL_HIGHLIGHT, COL_HIGHLIGHT, COL_HIGHLIGHT, COL_HIGHLIGHT, COL_HIGHLIGHT, COL_HIGHLIGHT, COL_HIGHLIGHT, COL_HIGHLIGHT, COL_HIGHLIGHT, COL_HIGHLIGHT, COL_HIGHLIGHT_GENTLE, COL_HIGHLIGHT_GENTLE, COL_HIGHLIGHT_GENTLE, COL_LOWLIGHT_GENTLE, COL_LOWLIGHT_GENTLE, COL_LOWLIGHT_GENTLE, COL_LOWLIGHT, COL_LOWLIGHT, COL_LOWLIGHT, COL_LOWLIGHT, COL_LOWLIGHT, COL_LOWLIGHT, COL_LOWLIGHT, COL_LOWLIGHT, COL_LOWLIGHT, }; return colours[(int)((angle + 2*PI) / (PI/16)) & 31]; } static float game_anim_length_real(const game_state *oldstate, const game_state *newstate, int dir, const game_ui *ui) { /* * Our game_anim_length doesn't need to modify its game_ui, so * this is the real function which declares ui as const. We must * wrap this for the backend structure with a version that has ui * non-const, but we still need this version to call from within * game_redraw which only has a const ui available. */ return (float)(ANIM_PER_BLKSIZE_UNIT * sqrt(newstate->n-1)); } static float game_anim_length(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui) { return game_anim_length_real(oldstate, newstate, dir, ui); } static float game_flash_length(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui) { if (!oldstate->completed && newstate->completed && !oldstate->used_solve && !newstate->used_solve) return 2 * FLASH_FRAME; else return 0.0F; } static int game_status(const game_state *state) { return state->completed ? +1 : 0; } 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 i, bgcolour; struct rotation srot, *rot; int lastx = -1, lasty = -1, lastr = -1; int cx, cy, cmoved = 0, n = state->n; cx = ui->cur_visible ? ui->cur_x : -state->n; cy = ui->cur_visible ? ui->cur_y : -state->n; if (cx != ds->cur_x || cy != ds->cur_y) cmoved = 1; if (flashtime > 0) { int frame = (int)(flashtime / FLASH_FRAME); bgcolour = (frame % 2 ? COL_LOWLIGHT : COL_HIGHLIGHT); } else bgcolour = COL_BACKGROUND; if (!ds->started) { int coords[10]; draw_rect(dr, 0, 0, TILE_SIZE * state->w + 2 * BORDER, TILE_SIZE * state->h + 2 * BORDER, COL_BACKGROUND); draw_update(dr, 0, 0, TILE_SIZE * state->w + 2 * BORDER, TILE_SIZE * state->h + 2 * BORDER); /* * Recessed area containing the whole puzzle. */ coords[0] = COORD(state->w) + HIGHLIGHT_WIDTH - 1; coords[1] = COORD(state->h) + HIGHLIGHT_WIDTH - 1; coords[2] = COORD(state->w) + HIGHLIGHT_WIDTH - 1; coords[3] = COORD(0) - HIGHLIGHT_WIDTH; coords[4] = coords[2] - TILE_SIZE; coords[5] = coords[3] + TILE_SIZE; coords[8] = COORD(0) - HIGHLIGHT_WIDTH; coords[9] = COORD(state->h) + HIGHLIGHT_WIDTH - 1; coords[6] = coords[8] + TILE_SIZE; coords[7] = coords[9] - TILE_SIZE; draw_polygon(dr, coords, 5, COL_HIGHLIGHT, COL_HIGHLIGHT); coords[1] = COORD(0) - HIGHLIGHT_WIDTH; coords[0] = COORD(0) - HIGHLIGHT_WIDTH; draw_polygon(dr, coords, 5, COL_LOWLIGHT, COL_LOWLIGHT); ds->started = TRUE; } /* * If we're drawing any rotated tiles, sort out the rotation * parameters, and also zap the rotation region to the * background colour before doing anything else. */ if (oldstate) { float angle; float anim_max = game_anim_length_real(oldstate, state, dir, ui); if (dir > 0) { lastx = state->lastx; lasty = state->lasty; lastr = state->lastr; } else { lastx = oldstate->lastx; lasty = oldstate->lasty; lastr = -oldstate->lastr; } rot = &srot; rot->cx = COORD(lastx); rot->cy = COORD(lasty); rot->cw = rot->ch = TILE_SIZE * state->n; rot->ox = rot->cx + rot->cw/2; rot->oy = rot->cy + rot->ch/2; angle = (float)((-PI/2 * lastr) * (1.0 - animtime / anim_max)); rot->c = (float)cos(angle); rot->s = (float)sin(angle); /* * Sort out the colours of the various sides of the tile. */ rot->lc = highlight_colour((float)PI + angle); rot->rc = highlight_colour(angle); rot->tc = highlight_colour((float)(PI/2.0) + angle); rot->bc = highlight_colour((float)(-PI/2.0) + angle); draw_rect(dr, rot->cx, rot->cy, rot->cw, rot->ch, bgcolour); } else rot = NULL; /* * Now draw each tile. */ for (i = 0; i < state->w * state->h; i++) { int t, cc = 0; int tx = i % state->w, ty = i / state->w; /* * Figure out what should be displayed at this location. * Usually it will be state->grid[i], unless we're in the * middle of animating an actual rotation and this cell is * within the rotation region, in which case we set -1 * (always display). */ if (oldstate && lastx >= 0 && lasty >= 0 && tx >= lastx && tx < lastx + state->n && ty >= lasty && ty < lasty + state->n) t = -1; else t = state->grid[i]; if (cmoved) { /* cursor has moved (or changed visibility)... */ if (tx == cx || tx == cx+n-1 || ty == cy || ty == cy+n-1) cc = 1; /* ...we're on new cursor, redraw */ if (tx == ds->cur_x || tx == ds->cur_x+n-1 || ty == ds->cur_y || ty == ds->cur_y+n-1) cc = 1; /* ...we were on old cursor, redraw */ } if (ds->bgcolour != bgcolour || /* always redraw when flashing */ ds->grid[i] != t || ds->grid[i] == -1 || t == -1 || cc) { int x = COORD(tx), y = COORD(ty); unsigned cedges = 0; if (tx == cx && ty >= cy && ty <= cy+n-1) cedges |= CUR_LEFT; if (ty == cy && tx >= cx && tx <= cx+n-1) cedges |= CUR_TOP; if (tx == cx+n-1 && ty >= cy && ty <= cy+n-1) cedges |= CUR_RIGHT; if (ty == cy+n-1 && tx >= cx && tx <= cx+n-1) cedges |= CUR_BOTTOM; draw_tile(dr, ds, state, x, y, state->grid[i], bgcolour, rot, cedges); ds->grid[i] = t; } } ds->bgcolour = bgcolour; ds->cur_x = cx; ds->cur_y = cy; /* * Update the status bar. */ { char statusbuf[256]; /* * Don't show the new status until we're also showing the * new _state_ - after the game animation is complete. */ if (oldstate) state = oldstate; if (state->used_solve) sprintf(statusbuf, "Moves since auto-solve: %d", state->movecount - state->completed); else { sprintf(statusbuf, "%sMoves: %d", (state->completed ? "COMPLETED! " : ""), (state->completed ? state->completed : state->movecount)); if (state->movetarget) sprintf(statusbuf+strlen(statusbuf), " (target %d)", state->movetarget); } status_bar(dr, statusbuf); } } static int 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 twiddle #endif const struct game thegame = { "Twiddle", "games.twiddle", "twiddle", default_params, game_fetch_preset, 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, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, FALSE, FALSE, game_print_size, game_print, TRUE, /* wants_statusbar */ FALSE, game_timing_state, 0, /* flags */ }; /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/undead.c0000644000175300017530000025554112132232554014044 0ustar simonsimon/* * undead: Implementation of Haunted Mirror Mazes * * http://www.janko.at/Raetsel/Spukschloss/index.htm * * Puzzle definition is the total number of each monster type, the * grid definition, and the list of sightings (clockwise, starting * from top left corner) * * Example: (Janko puzzle No. 1, * http://www.janko.at/Raetsel/Spukschloss/001.a.htm ) * * Ghosts: 0 Vampires: 2 Zombies: 6 * * 2 1 1 1 * 1 \ \ . / 2 * 0 \ . / . 2 * 0 / . / . 2 * 3 . . . \ 2 * 3 3 2 2 * * would be encoded into: * 4x4:0,2,6,LLaRLaRaRaRdL,2,1,1,1,2,2,2,2,2,2,3,3,3,0,0,1 * * Additionally, the game description can contain monsters fixed at a * certain grid position. The internal generator does not (yet) use * this feature, but this is needed to enter puzzles like Janko No. * 14, which is encoded as: * 8x5:12,12,0,LaRbLaRaLaRLbRaVaVaGRaRaRaLbLaRbRLb,0,2,0,2,2,1,2,1,3,1,0,1,8,4,3,0,0,2,3,2,7,2,1,6,2,1 * */ #include #include #include #include #include #include #include "puzzles.h" enum { COL_BACKGROUND, COL_GRID, COL_TEXT, COL_ERROR, COL_HIGHLIGHT, COL_FLASH, COL_GHOST, COL_ZOMBIE, COL_VAMPIRE, NCOLOURS }; #define DIFFLIST(A) \ A(EASY,Easy,e) \ A(NORMAL,Normal,n) \ A(TRICKY,Tricky,t) #define ENUM(upper,title,lower) DIFF_ ## upper, #define TITLE(upper,title,lower) #title, #define ENCODE(upper,title,lower) #lower #define CONFIG(upper,title,lower) ":" #title enum { DIFFLIST(ENUM) DIFFCOUNT }; static char const *const undead_diffnames[] = { DIFFLIST(TITLE) "(count)" }; static char const undead_diffchars[] = DIFFLIST(ENCODE); #define DIFFCONFIG DIFFLIST(CONFIG) struct game_params { int w; /* Grid width */ int h; /* Grid height */ int diff; /* Puzzle difficulty */ }; static const struct game_params undead_presets[] = { { 4, 4, DIFF_EASY }, { 4, 4, DIFF_NORMAL }, { 4, 4, DIFF_TRICKY }, { 5, 5, DIFF_EASY }, { 5, 5, DIFF_NORMAL }, { 5, 5, DIFF_TRICKY }, { 7, 7, DIFF_EASY }, { 7, 7, DIFF_NORMAL } }; #define DEFAULT_PRESET 1 static game_params *default_params(void) { game_params *ret = snew(game_params); *ret = undead_presets[DEFAULT_PRESET]; return ret; } static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char buf[64]; if (i < 0 || i >= lenof(undead_presets)) return FALSE; ret = default_params(); *ret = undead_presets[i]; /* struct copy */ *params = ret; sprintf(buf, "%dx%d %s", undead_presets[i].w, undead_presets[i].h, undead_diffnames[undead_presets[i].diff]); *name = dupstr(buf); 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++; } params->diff = DIFF_NORMAL; if (*string == 'd') { int i; string++; for (i = 0; i < DIFFCOUNT; i++) if (*string == undead_diffchars[i]) params->diff = i; if (*string) string++; } return; } static char *encode_params(const game_params *params, int full) { char buf[256]; sprintf(buf, "%dx%d", params->w, params->h); if (full) sprintf(buf + strlen(buf), "d%c", undead_diffchars[params->diff]); return dupstr(buf); } static config_item *game_configure(const game_params *params) { config_item *ret; char buf[64]; ret = snewn(4, config_item); ret[0].name = "Width"; ret[0].type = C_STRING; sprintf(buf, "%d", params->w); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Difficulty"; ret[2].type = C_CHOICES; ret[2].sval = DIFFCONFIG; ret[2].ival = params->diff; ret[3].name = NULL; ret[3].type = C_END; ret[3].sval = NULL; ret[3].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w = atoi(cfg[0].sval); ret->h = atoi(cfg[1].sval); ret->diff = cfg[2].ival; return ret; } static char *validate_params(const game_params *params, int full) { if ((params->w * params->h ) > 54) return "Grid is too big"; if (params->w < 3) return "Width must be at least 3"; if (params->h < 3) return "Height must be at least 3"; if (params->diff >= DIFFCOUNT) return "Unknown difficulty rating"; return NULL; } /* --------------------------------------------------------------- */ /* Game state allocation, deallocation. */ struct path { int length; int *p; int grid_start; int grid_end; int num_monsters; int *mapping; int sightings_start; int sightings_end; int *xy; }; struct game_common { int refcount; struct game_params params; int wh; int num_ghosts,num_vampires,num_zombies,num_total; int num_paths; struct path *paths; int *grid; int *xinfo; int *fixed; int solved; }; struct game_state { struct game_common *common; int *guess; unsigned char *pencils; unsigned char *cell_errors; unsigned char *hint_errors; unsigned char count_errors[3]; int solved; int cheated; }; static game_state *new_state(const game_params *params) { int i; game_state *state = snew(game_state); state->common = snew(struct game_common); state->common->refcount = 1; state->common->params.w = params->w; state->common->params.h = params->h; state->common->params.diff = params->diff; state->common->wh = (state->common->params.w +2) * (state->common->params.h +2); state->common->num_ghosts = 0; state->common->num_vampires = 0; state->common->num_zombies = 0; state->common->num_total = 0; state->common->grid = snewn(state->common->wh, int); state->common->xinfo = snewn(state->common->wh, int); state->common->fixed = NULL; state->common->solved = FALSE; state->common->num_paths = state->common->params.w + state->common->params.h; state->common->paths = snewn(state->common->num_paths, struct path); for (i=0;icommon->num_paths;i++) { state->common->paths[i].length = 0; state->common->paths[i].grid_start = -1; state->common->paths[i].grid_end = -1; state->common->paths[i].num_monsters = 0; state->common->paths[i].sightings_start = 0; state->common->paths[i].sightings_end = 0; state->common->paths[i].p = snewn(state->common->wh,int); state->common->paths[i].xy = snewn(state->common->wh,int); state->common->paths[i].mapping = snewn(state->common->wh,int); } state->guess = NULL; state->pencils = NULL; state->cell_errors = snewn(state->common->wh, unsigned char); for (i=0;icommon->wh;i++) state->cell_errors[i] = FALSE; state->hint_errors = snewn(2*state->common->num_paths, unsigned char); for (i=0;i<2*state->common->num_paths;i++) state->hint_errors[i] = FALSE; for (i=0;i<3;i++) state->count_errors[i] = FALSE; state->solved = FALSE; state->cheated = FALSE; return state; } static game_state *dup_game(const game_state *state) { game_state *ret = snew(game_state); ret->common = state->common; ret->common->refcount++; if (state->guess != NULL) { ret->guess = snewn(ret->common->num_total,int); memcpy(ret->guess, state->guess, ret->common->num_total*sizeof(int)); } else ret->guess = NULL; if (state->pencils != NULL) { ret->pencils = snewn(ret->common->num_total,unsigned char); memcpy(ret->pencils, state->pencils, ret->common->num_total*sizeof(unsigned char)); } else ret->pencils = NULL; if (state->cell_errors != NULL) { ret->cell_errors = snewn(ret->common->wh,unsigned char); memcpy(ret->cell_errors, state->cell_errors, ret->common->wh*sizeof(unsigned char)); } else ret->cell_errors = NULL; if (state->hint_errors != NULL) { ret->hint_errors = snewn(2*ret->common->num_paths,unsigned char); memcpy(ret->hint_errors, state->hint_errors, 2*ret->common->num_paths*sizeof(unsigned char)); } else ret->hint_errors = NULL; ret->count_errors[0] = state->count_errors[0]; ret->count_errors[1] = state->count_errors[1]; ret->count_errors[2] = state->count_errors[2]; ret->solved = state->solved; ret->cheated = state->cheated; return ret; } static void free_game(game_state *state) { int i; state->common->refcount--; if (state->common->refcount == 0) { for (i=0;icommon->num_paths;i++) { sfree(state->common->paths[i].mapping); sfree(state->common->paths[i].xy); sfree(state->common->paths[i].p); } sfree(state->common->paths); sfree(state->common->xinfo); sfree(state->common->grid); if (state->common->fixed != NULL) sfree(state->common->fixed); sfree(state->common); } if (state->hint_errors != NULL) sfree(state->hint_errors); if (state->cell_errors != NULL) sfree(state->cell_errors); if (state->pencils != NULL) sfree(state->pencils); if (state->guess != NULL) sfree(state->guess); sfree(state); return; } /* --------------------------------------------------------------- */ /* Puzzle generator */ /* cell states */ enum { CELL_EMPTY, CELL_MIRROR_L, CELL_MIRROR_R, CELL_GHOST, CELL_VAMPIRE, CELL_ZOMBIE, CELL_UNDEF }; /* grid walk directions */ enum { DIRECTION_NONE, DIRECTION_UP, DIRECTION_RIGHT, DIRECTION_LEFT, DIRECTION_DOWN }; int range2grid(int rangeno, int width, int height, int *x, int *y) { if (rangeno < 0) { *x = 0; *y = 0; return DIRECTION_NONE; } if (rangeno < width) { *x = rangeno+1; *y = 0; return DIRECTION_DOWN; } rangeno = rangeno - width; if (rangeno < height) { *x = width+1; *y = rangeno+1; return DIRECTION_LEFT; } rangeno = rangeno - height; if (rangeno < width) { *x = width-rangeno; *y = height+1; return DIRECTION_UP; } rangeno = rangeno - width; if (rangeno < height) { *x = 0; *y = height-rangeno; return DIRECTION_RIGHT; } *x = 0; *y = 0; return DIRECTION_NONE; } int grid2range(int x, int y, int w, int h) { if (x>0 && x0 && yw+1 || y<0 || y>h+1) return -1; if ((x == 0 || x==w+1) && (y==0 || y==h+1)) return -1; if (y==0) return x-1; if (x==(w+1)) return y-1+w; if (y==(h+1)) return 2*w + h - x; return 2*(w+h) - y; } void make_paths(game_state *state) { int i; int count = 0; for (i=0;i<2*(state->common->params.w + state->common->params.h);i++) { int x,y,dir; int j,k,num_monsters; int found; int c,p; found = FALSE; /* Check whether inverse path is already in list */ for (j=0;jcommon->paths[j].grid_end) { found = TRUE; break; } } if (found) continue; /* We found a new path through the mirror maze */ state->common->paths[count].grid_start = i; dir = range2grid(i, state->common->params.w, state->common->params.h,&x,&y); state->common->paths[count].sightings_start = state->common->grid[x+y*(state->common->params.w +2)]; while (TRUE) { int c,r; if (dir == DIRECTION_DOWN) y++; else if (dir == DIRECTION_LEFT) x--; else if (dir == DIRECTION_UP) y--; else if (dir == DIRECTION_RIGHT) x++; r = grid2range(x, y, state->common->params.w, state->common->params.h); if (r != -1) { state->common->paths[count].grid_end = r; state->common->paths[count].sightings_end = state->common->grid[x+y*(state->common->params.w +2)]; break; } c = state->common->grid[x+y*(state->common->params.w+2)]; state->common->paths[count].xy[state->common->paths[count].length] = x+y*(state->common->params.w+2); if (c == CELL_MIRROR_L) { state->common->paths[count].p[state->common->paths[count].length] = -1; if (dir == DIRECTION_DOWN) dir = DIRECTION_RIGHT; else if (dir == DIRECTION_LEFT) dir = DIRECTION_UP; else if (dir == DIRECTION_UP) dir = DIRECTION_LEFT; else if (dir == DIRECTION_RIGHT) dir = DIRECTION_DOWN; } else if (c == CELL_MIRROR_R) { state->common->paths[count].p[state->common->paths[count].length] = -1; if (dir == DIRECTION_DOWN) dir = DIRECTION_LEFT; else if (dir == DIRECTION_LEFT) dir = DIRECTION_DOWN; else if (dir == DIRECTION_UP) dir = DIRECTION_RIGHT; else if (dir == DIRECTION_RIGHT) dir = DIRECTION_UP; } else { state->common->paths[count].p[state->common->paths[count].length] = state->common->xinfo[x+y*(state->common->params.w+2)]; } state->common->paths[count].length++; } /* Count unique monster entries in each path */ state->common->paths[count].num_monsters = 0; for (j=0;jcommon->num_total;j++) { num_monsters = 0; for (k=0;kcommon->paths[count].length;k++) if (state->common->paths[count].p[k] == j) num_monsters++; if (num_monsters > 0) state->common->paths[count].num_monsters++; } /* Generate mapping vector */ c = 0; for (p=0;pcommon->paths[count].length;p++) { int m; m = state->common->paths[count].p[p]; if (m == -1) continue; found = FALSE; for (j=0; jcommon->paths[count].mapping[j] == m) found = TRUE; if (!found) state->common->paths[count].mapping[c++] = m; } count++; } return; } struct guess { int length; int *guess; int *possible; }; int next_list(struct guess *g, int pos) { if (pos == 0) { if ((g->guess[pos] == 1 && g->possible[pos] == 1) || (g->guess[pos] == 2 && (g->possible[pos] == 3 || g->possible[pos] == 2)) || g->guess[pos] == 4) return FALSE; if (g->guess[pos] == 1 && (g->possible[pos] == 3 || g->possible[pos] == 7)) { g->guess[pos] = 2; return TRUE; } if (g->guess[pos] == 1 && g->possible[pos] == 5) { g->guess[pos] = 4; return TRUE; } if (g->guess[pos] == 2 && (g->possible[pos] == 6 || g->possible[pos] == 7)) { g->guess[pos] = 4; return TRUE; } } if (g->guess[pos] == 1) { if (g->possible[pos] == 1) { return next_list(g,pos-1); } if (g->possible[pos] == 3 || g->possible[pos] == 7) { g->guess[pos] = 2; return TRUE; } if (g->possible[pos] == 5) { g->guess[pos] = 4; return TRUE; } } if (g->guess[pos] == 2) { if (g->possible[pos] == 2) { return next_list(g,pos-1); } if (g->possible[pos] == 3) { g->guess[pos] = 1; return next_list(g,pos-1); } if (g->possible[pos] == 6 || g->possible[pos] == 7) { g->guess[pos] = 4; return TRUE; } } if (g->guess[pos] == 4) { if (g->possible[pos] == 5 || g->possible[pos] == 7) { g->guess[pos] = 1; return next_list(g,pos-1); } if (g->possible[pos] == 6) { g->guess[pos] = 2; return next_list(g,pos-1); } if (g->possible[pos] == 4) { return next_list(g,pos-1); } } return FALSE; } void get_unique(game_state *state, int counter, random_state *rs) { int p,i,c,pathlimit,count_uniques; struct guess path_guess; int *view_count; struct entry { struct entry *link; int *guess; int start_view; int end_view; }; struct { struct entry *head; struct entry *node; } views, single_views, test_views; struct entry test_entry; path_guess.length = state->common->paths[counter].num_monsters; path_guess.guess = snewn(path_guess.length,int); path_guess.possible = snewn(path_guess.length,int); for (i=0;iguess[state->common->paths[counter].mapping[p]]; switch (path_guess.possible[p]) { case 1: path_guess.guess[p] = 1; break; case 2: path_guess.guess[p] = 2; break; case 3: path_guess.guess[p] = 1; break; case 4: path_guess.guess[p] = 4; break; case 5: path_guess.guess[p] = 1; break; case 6: path_guess.guess[p] = 2; break; case 7: path_guess.guess[p] = 1; break; } } views.head = NULL; views.node = NULL; pathlimit = state->common->paths[counter].length + 1; view_count = snewn(pathlimit*pathlimit, int); for (i = 0; i < pathlimit*pathlimit; i++) view_count[i] = 0; do { int mirror, start_view, end_view; mirror = FALSE; start_view = 0; for (p=0;pcommon->paths[counter].length;p++) { if (state->common->paths[counter].p[p] == -1) mirror = TRUE; else { for (i=0;icommon->paths[counter].p[p] == state->common->paths[counter].mapping[i]) { if (path_guess.guess[i] == 1 && mirror == TRUE) start_view++; if (path_guess.guess[i] == 2 && mirror == FALSE) start_view++; if (path_guess.guess[i] == 4) start_view++; break; } } } } mirror = FALSE; end_view = 0; for (p=state->common->paths[counter].length-1;p>=0;p--) { if (state->common->paths[counter].p[p] == -1) mirror = TRUE; else { for (i=0;icommon->paths[counter].p[p] == state->common->paths[counter].mapping[i]) { if (path_guess.guess[i] == 1 && mirror == TRUE) end_view++; if (path_guess.guess[i] == 2 && mirror == FALSE) end_view++; if (path_guess.guess[i] == 4) end_view++; break; } } } } assert(start_view >= 0 && start_view < pathlimit); assert(end_view >= 0 && end_view < pathlimit); i = start_view * pathlimit + end_view; view_count[i]++; if (view_count[i] == 1) { views.node = snewn(1,struct entry); views.node->link = views.head; views.node->guess = snewn(path_guess.length,int); views.head = views.node; views.node->start_view = start_view; views.node->end_view = end_view; memcpy(views.node->guess, path_guess.guess, path_guess.length*sizeof(int)); } } while (next_list(&path_guess, path_guess.length-1)); /* extract single entries from view list */ test_views.head = views.head; test_views.node = views.node; test_entry.guess = snewn(path_guess.length,int); single_views.head = NULL; single_views.node = NULL; count_uniques = 0; while (test_views.head != NULL) { test_views.node = test_views.head; test_views.head = test_views.head->link; i = test_views.node->start_view * pathlimit + test_views.node->end_view; if (view_count[i] == 1) { single_views.node = snewn(1,struct entry); single_views.node->link = single_views.head; single_views.node->guess = snewn(path_guess.length,int); single_views.head = single_views.node; single_views.node->start_view = test_views.node->start_view; single_views.node->end_view = test_views.node->end_view; memcpy(single_views.node->guess, test_views.node->guess, path_guess.length*sizeof(int)); count_uniques++; } } sfree(view_count); if (count_uniques > 0) { test_entry.start_view = 0; test_entry.end_view = 0; /* Choose one unique guess per random */ /* While we are busy with looping through single_views, we * conveniently free the linked list single_view */ c = random_upto(rs,count_uniques); while(single_views.head != NULL) { single_views.node = single_views.head; single_views.head = single_views.head->link; if (c-- == 0) { memcpy(test_entry.guess, single_views.node->guess, path_guess.length*sizeof(int)); test_entry.start_view = single_views.node->start_view; test_entry.end_view = single_views.node->end_view; } sfree(single_views.node->guess); sfree(single_views.node); } /* Modify state_guess according to path_guess.mapping */ for (i=0;iguess[state->common->paths[counter].mapping[i]] = test_entry.guess[i]; } sfree(test_entry.guess); while (views.head != NULL) { views.node = views.head; views.head = views.head->link; sfree(views.node->guess); sfree(views.node); } sfree(path_guess.possible); sfree(path_guess.guess); return; } int count_monsters(game_state *state, int *cGhost, int *cVampire, int *cZombie) { int cNone; int i; *cGhost = *cVampire = *cZombie = cNone = 0; for (i=0;icommon->num_total;i++) { if (state->guess[i] == 1) (*cGhost)++; else if (state->guess[i] == 2) (*cVampire)++; else if (state->guess[i] == 4) (*cZombie)++; else cNone++; } return cNone; } int check_numbers(game_state *state, int *guess) { int valid; int i; int count_ghosts, count_vampires, count_zombies; count_ghosts = count_vampires = count_zombies = 0; for (i=0;icommon->num_total;i++) { if (guess[i] == 1) count_ghosts++; if (guess[i] == 2) count_vampires++; if (guess[i] == 4) count_zombies++; } valid = TRUE; if (count_ghosts > state->common->num_ghosts) valid = FALSE; if (count_vampires > state->common->num_vampires) valid = FALSE; if (count_zombies > state->common->num_zombies) valid = FALSE; return valid; } int check_solution(int *g, struct path path) { int i; int mirror; int count; count = 0; mirror = FALSE; for (i=0;i=0;i--) { if (path.p[i] == -1) mirror = TRUE; else { if (g[path.p[i]] == 1 && mirror) count++; else if (g[path.p[i]] == 2 && !mirror) count++; else if (g[path.p[i]] == 4) count++; } } if (count != path.sightings_end) return FALSE; return TRUE; } int solve_iterative(game_state *state, struct path *paths) { int solved; int p,i,j,count; int *guess; int *possible; struct guess loop; solved = TRUE; loop.length = state->common->num_total; guess = snewn(state->common->num_total,int); possible = snewn(state->common->num_total,int); for (i=0;icommon->num_total;i++) { guess[i] = state->guess[i]; possible[i] = 0; } for (p=0;pcommon->num_paths;p++) { if (paths[p].num_monsters > 0) { loop.length = paths[p].num_monsters; loop.guess = snewn(paths[p].num_monsters,int); loop.possible = snewn(paths[p].num_monsters,int); for (i=0;iguess[paths[p].mapping[i]]) { case 1: loop.guess[i] = 1; break; case 2: loop.guess[i] = 2; break; case 3: loop.guess[i] = 1; break; case 4: loop.guess[i] = 4; break; case 5: loop.guess[i] = 1; break; case 6: loop.guess[i] = 2; break; case 7: loop.guess[i] = 1; break; } loop.possible[i] = state->guess[paths[p].mapping[i]]; possible[paths[p].mapping[i]] = 0; } while(TRUE) { for (i=0;icommon->num_total;i++) { guess[i] = state->guess[i]; } count = 0; for (i=0;iguess[paths[p].mapping[i]] &= possible[paths[p].mapping[i]]; sfree(loop.possible); sfree(loop.guess); } } for (i=0;icommon->num_total;i++) { if (state->guess[i] == 3 || state->guess[i] == 5 || state->guess[i] == 6 || state->guess[i] == 7) { solved = FALSE; break; } } sfree(possible); sfree(guess); return solved; } int solve_bruteforce(game_state *state, struct path *paths) { int solved, correct; int number_solutions; int p,i; struct guess loop; loop.guess = snewn(state->common->num_total,int); loop.possible = snewn(state->common->num_total,int); for (i=0;icommon->num_total;i++) { loop.possible[i] = state->guess[i]; switch (state->guess[i]) { case 1: loop.guess[i] = 1; break; case 2: loop.guess[i] = 2; break; case 3: loop.guess[i] = 1; break; case 4: loop.guess[i] = 4; break; case 5: loop.guess[i] = 1; break; case 6: loop.guess[i] = 2; break; case 7: loop.guess[i] = 1; break; } } solved = FALSE; number_solutions = 0; while (TRUE) { correct = TRUE; if (!check_numbers(state,loop.guess)) correct = FALSE; else for (p=0;pcommon->num_paths;p++) if (!check_solution(loop.guess,paths[p])) { correct = FALSE; break; } if (correct) { number_solutions++; solved = TRUE; if(number_solutions > 1) { solved = FALSE; break; } for (i=0;icommon->num_total;i++) state->guess[i] = loop.guess[i]; } if (!next_list(&loop,state->common->num_total -1)) { break; } } sfree(loop.possible); sfree(loop.guess); return solved; } int path_cmp(const void *a, const void *b) { const struct path *pa = (const struct path *)a; const struct path *pb = (const struct path *)b; return pa->num_monsters - pb->num_monsters; } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { int i,count,c,w,h,r,p,g; game_state *new; /* Variables for puzzle generation algorithm */ int filling; int max_length; int count_ghosts, count_vampires, count_zombies; int abort; float ratio; /* Variables for solver algorithm */ int solved_iterative, solved_bruteforce, contains_inconsistency, count_ambiguous; int iterative_depth; int *old_guess; /* Variables for game description generation */ int x,y; char *e; char *desc; i = 0; while (TRUE) { new = new_state(params); abort = FALSE; /* Fill grid with random mirrors and (later to be populated) * empty monster cells */ count = 0; for (h=1;hcommon->params.h+1;h++) for (w=1;wcommon->params.w+1;w++) { c = random_upto(rs,5); if (c >= 2) { new->common->grid[w+h*(new->common->params.w+2)] = CELL_EMPTY; new->common->xinfo[w+h*(new->common->params.w+2)] = count++; } else if (c == 0) { new->common->grid[w+h*(new->common->params.w+2)] = CELL_MIRROR_L; new->common->xinfo[w+h*(new->common->params.w+2)] = -1; } else { new->common->grid[w+h*(new->common->params.w+2)] = CELL_MIRROR_R; new->common->xinfo[w+h*(new->common->params.w+2)] = -1; } } new->common->num_total = count; /* Total number of monsters in maze */ /* Puzzle is boring if it has too few monster cells. Discard * grid, make new grid */ if (new->common->num_total <= 4) { free_game(new); continue; } /* Monsters / Mirrors ratio should be balanced */ ratio = (float)new->common->num_total / (float)(new->common->params.w * new->common->params.h); if (ratio < 0.48 || ratio > 0.78) { free_game(new); continue; } /* Assign clue identifiers */ for (r=0;r<2*(new->common->params.w+new->common->params.h);r++) { int x,y,gridno; gridno = range2grid(r,new->common->params.w,new->common->params.h, &x,&y); new->common->grid[x+y*(new->common->params.w +2)] = gridno; new->common->xinfo[x+y*(new->common->params.w +2)] = 0; } /* The four corners don't matter at all for the game. Set them * all to zero, just to have a nice data structure */ new->common->grid[0] = 0; new->common->xinfo[0] = 0; new->common->grid[new->common->params.w+1] = 0; new->common->xinfo[new->common->params.w+1] = 0; new->common->grid[new->common->params.w+1 + (new->common->params.h+1)*(new->common->params.w+2)] = 0; new->common->xinfo[new->common->params.w+1 + (new->common->params.h+1)*(new->common->params.w+2)] = 0; new->common->grid[(new->common->params.h+1)*(new->common->params.w+2)] = 0; new->common->xinfo[(new->common->params.h+1)*(new->common->params.w+2)] = 0; /* Initialize solution vector */ new->guess = snewn(new->common->num_total,int); for (g=0;gcommon->num_total;g++) new->guess[g] = 7; /* Initialize fixed flag from common. Not needed for the * puzzle generator; initialize it for having clean code */ new->common->fixed = snewn(new->common->num_total,int); for (g=0;gcommon->num_total;g++) new->common->fixed[g] = FALSE; /* paths generation */ make_paths(new); /* Grid is invalid if max. path length > threshold. Discard * grid, make new one */ switch (new->common->params.diff) { case DIFF_EASY: max_length = min(new->common->params.w,new->common->params.h) + 1; break; case DIFF_NORMAL: max_length = (max(new->common->params.w,new->common->params.h) * 3) / 2; break; case DIFF_TRICKY: max_length = 9; break; default: max_length = 9; break; } for (p=0;pcommon->num_paths;p++) { if (new->common->paths[p].num_monsters > max_length) { abort = TRUE; } } if (abort) { free_game(new); continue; } qsort(new->common->paths, new->common->num_paths, sizeof(struct path), path_cmp); /* Grid monster initialization */ /* For easy puzzles, we try to fill nearly the whole grid with unique solution paths (up to 2) For more difficult puzzles, we fill only roughly half the grid, and choose random monsters for the rest For hard puzzles, we fill even less paths with unique solutions */ switch (new->common->params.diff) { case DIFF_EASY: filling = 2; break; case DIFF_NORMAL: filling = min( (new->common->params.w+new->common->params.h) , (new->common->num_total)/2 ); break; case DIFF_TRICKY: filling = max( (new->common->params.w+new->common->params.h) , (new->common->num_total)/2 ); break; default: filling = 0; break; } count = 0; while ( (count_monsters(new, &count_ghosts, &count_vampires, &count_zombies)) > filling) { if ((count) >= new->common->num_paths) break; if (new->common->paths[count].num_monsters == 0) { count++; continue; } get_unique(new,count,rs); count++; } /* Fill any remaining ambiguous entries with random monsters */ for(g=0;gcommon->num_total;g++) { if (new->guess[g] == 7) { r = random_upto(rs,3); new->guess[g] = (r == 0) ? 1 : ( (r == 1) ? 2 : 4 ); } } /* Determine all hints */ count_monsters(new, &new->common->num_ghosts, &new->common->num_vampires, &new->common->num_zombies); /* Puzzle is trivial if it has only one type of monster. Discard. */ if ((new->common->num_ghosts == 0 && new->common->num_vampires == 0) || (new->common->num_ghosts == 0 && new->common->num_zombies == 0) || (new->common->num_vampires == 0 && new->common->num_zombies == 0)) { free_game(new); continue; } /* Discard puzzle if difficulty Tricky, and it has only 1 * member of any monster type */ if (new->common->params.diff == DIFF_TRICKY && (new->common->num_ghosts <= 1 || new->common->num_vampires <= 1 || new->common->num_zombies <= 1)) { free_game(new); continue; } for (w=1;wcommon->params.w+1;w++) for (h=1;hcommon->params.h+1;h++) { c = new->common->xinfo[w+h*(new->common->params.w+2)]; if (c >= 0) { if (new->guess[c] == 1) new->common->grid[w+h*(new->common->params.w+2)] = CELL_GHOST; if (new->guess[c] == 2) new->common->grid[w+h*(new->common->params.w+2)] = CELL_VAMPIRE; if (new->guess[c] == 4) new->common->grid[w+h*(new->common->params.w+2)] = CELL_ZOMBIE; } } /* Prepare path information needed by the solver (containing all hints) */ for (p=0;pcommon->num_paths;p++) { int mirror,x,y; new->common->paths[p].sightings_start = 0; new->common->paths[p].sightings_end = 0; mirror = FALSE; for (g=0;gcommon->paths[p].length;g++) { if (new->common->paths[p].p[g] == -1) mirror = TRUE; else { if (new->guess[new->common->paths[p].p[g]] == 1 && mirror == TRUE) (new->common->paths[p].sightings_start)++; else if (new->guess[new->common->paths[p].p[g]] == 2 && mirror == FALSE) (new->common->paths[p].sightings_start)++; else if (new->guess[new->common->paths[p].p[g]] == 4) (new->common->paths[p].sightings_start)++; } } mirror = FALSE; for (g=new->common->paths[p].length-1;g>=0;g--) { if (new->common->paths[p].p[g] == -1) mirror = TRUE; else { if (new->guess[new->common->paths[p].p[g]] == 1 && mirror == TRUE) (new->common->paths[p].sightings_end)++; else if (new->guess[new->common->paths[p].p[g]] == 2 && mirror == FALSE) (new->common->paths[p].sightings_end)++; else if (new->guess[new->common->paths[p].p[g]] == 4) (new->common->paths[p].sightings_end)++; } } range2grid(new->common->paths[p].grid_start, new->common->params.w,new->common->params.h,&x,&y); new->common->grid[x+y*(new->common->params.w +2)] = new->common->paths[p].sightings_start; range2grid(new->common->paths[p].grid_end, new->common->params.w,new->common->params.h,&x,&y); new->common->grid[x+y*(new->common->params.w +2)] = new->common->paths[p].sightings_end; } /* Try to solve the puzzle with the iterative solver */ old_guess = snewn(new->common->num_total,int); for (p=0;pcommon->num_total;p++) { new->guess[p] = 7; old_guess[p] = 7; } iterative_depth = 0; solved_iterative = FALSE; contains_inconsistency = FALSE; count_ambiguous = 0; while (TRUE) { int no_change; no_change = TRUE; solved_iterative = solve_iterative(new,new->common->paths); iterative_depth++; for (p=0;pcommon->num_total;p++) { if (new->guess[p] != old_guess[p]) no_change = FALSE; old_guess[p] = new->guess[p]; if (new->guess[p] == 0) contains_inconsistency = TRUE; } if (solved_iterative || no_change) break; } /* If necessary, try to solve the puzzle with the brute-force solver */ solved_bruteforce = FALSE; if (new->common->params.diff != DIFF_EASY && !solved_iterative && !contains_inconsistency) { for (p=0;pcommon->num_total;p++) if (new->guess[p] != 1 && new->guess[p] != 2 && new->guess[p] != 4) count_ambiguous++; solved_bruteforce = solve_bruteforce(new, new->common->paths); } /* Determine puzzle difficulty level */ if (new->common->params.diff == DIFF_EASY && solved_iterative && iterative_depth <= 3 && !contains_inconsistency) { /* printf("Puzzle level: EASY Level %d Ratio %f Ambiguous %d (Found after %i tries)\n",iterative_depth, ratio, count_ambiguous, i); */ break; } if (new->common->params.diff == DIFF_NORMAL && ((solved_iterative && iterative_depth > 3) || (solved_bruteforce && count_ambiguous < 4)) && !contains_inconsistency) { /* printf("Puzzle level: NORMAL Level %d Ratio %f Ambiguous %d (Found after %d tries)\n", iterative_depth, ratio, count_ambiguous, i); */ break; } if (new->common->params.diff == DIFF_TRICKY && solved_bruteforce && iterative_depth > 0 && count_ambiguous >= 4 && !contains_inconsistency) { /* printf("Puzzle level: TRICKY Level %d Ratio %f Ambiguous %d (Found after %d tries)\n", iterative_depth, ratio, count_ambiguous, i); */ break; } /* If puzzle is not solvable or does not satisfy the desired * difficulty level, free memory and start from scratch */ sfree(old_guess); free_game(new); i++; } /* We have a valid puzzle! */ desc = snewn(10 + new->common->wh + 6*(new->common->params.w + new->common->params.h), char); e = desc; /* Encode monster counts */ e += sprintf(e, "%d,", new->common->num_ghosts); e += sprintf(e, "%d,", new->common->num_vampires); e += sprintf(e, "%d,", new->common->num_zombies); /* Encode grid */ count = 0; for (y=1;ycommon->params.h+1;y++) for (x=1;xcommon->params.w+1;x++) { c = new->common->grid[x+y*(new->common->params.w+2)]; if (count > 25) { *e++ = 'z'; count -= 26; } if (c != CELL_MIRROR_L && c != CELL_MIRROR_R) { count++; } else if (c == CELL_MIRROR_L) { if (count > 0) *e++ = (count-1 + 'a'); *e++ = 'L'; count = 0; } else { if (count > 0) *e++ = (count-1 + 'a'); *e++ = 'R'; count = 0; } } if (count > 0) *e++ = (count-1 + 'a'); /* Encode hints */ for (p=0;p<2*(new->common->params.w + new->common->params.h);p++) { range2grid(p,new->common->params.w,new->common->params.h,&x,&y); e += sprintf(e, ",%d", new->common->grid[x+y*(new->common->params.w+2)]); } *e++ = '\0'; desc = sresize(desc, e - desc, char); sfree(old_guess); free_game(new); return desc; } void num2grid(int num, int width, int height, int *x, int *y) { *x = 1+(num%width); *y = 1+(num/width); return; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { int i; int n; int count; game_state *state = new_state(params); state->common->num_ghosts = atoi(desc); while (*desc && isdigit((unsigned char)*desc)) desc++; desc++; state->common->num_vampires = atoi(desc); while (*desc && isdigit((unsigned char)*desc)) desc++; desc++; state->common->num_zombies = atoi(desc); while (*desc && isdigit((unsigned char)*desc)) desc++; desc++; state->common->num_total = state->common->num_ghosts + state->common->num_vampires + state->common->num_zombies; state->guess = snewn(state->common->num_total,int); state->pencils = snewn(state->common->num_total,unsigned char); state->common->fixed = snewn(state->common->num_total,int); for (i=0;icommon->num_total;i++) { state->guess[i] = 7; state->pencils[i] = 0; state->common->fixed[i] = FALSE; } for (i=0;icommon->wh;i++) state->cell_errors[i] = FALSE; for (i=0;i<2*state->common->num_paths;i++) state->hint_errors[i] = FALSE; for (i=0;i<3;i++) state->count_errors[i] = FALSE; count = 0; n = 0; while (*desc != ',') { int c; int x,y; if (*desc == 'L') { num2grid(n,state->common->params.w,state->common->params.h,&x,&y); state->common->grid[x+y*(state->common->params.w +2)] = CELL_MIRROR_L; state->common->xinfo[x+y*(state->common->params.w+2)] = -1; n++; } else if (*desc == 'R') { num2grid(n,state->common->params.w,state->common->params.h,&x,&y); state->common->grid[x+y*(state->common->params.w +2)] = CELL_MIRROR_R; state->common->xinfo[x+y*(state->common->params.w+2)] = -1; n++; } else if (*desc == 'G') { num2grid(n,state->common->params.w,state->common->params.h,&x,&y); state->common->grid[x+y*(state->common->params.w +2)] = CELL_GHOST; state->common->xinfo[x+y*(state->common->params.w+2)] = count; state->guess[count] = 1; state->common->fixed[count++] = TRUE; n++; } else if (*desc == 'V') { num2grid(n,state->common->params.w,state->common->params.h,&x,&y); state->common->grid[x+y*(state->common->params.w +2)] = CELL_VAMPIRE; state->common->xinfo[x+y*(state->common->params.w+2)] = count; state->guess[count] = 2; state->common->fixed[count++] = TRUE; n++; } else if (*desc == 'Z') { num2grid(n,state->common->params.w,state->common->params.h,&x,&y); state->common->grid[x+y*(state->common->params.w +2)] = CELL_ZOMBIE; state->common->xinfo[x+y*(state->common->params.w+2)] = count; state->guess[count] = 4; state->common->fixed[count++] = TRUE; n++; } else { c = *desc - ('a' -1); while (c-- > 0) { num2grid(n,state->common->params.w,state->common->params.h,&x,&y); state->common->grid[x+y*(state->common->params.w +2)] = CELL_EMPTY; state->common->xinfo[x+y*(state->common->params.w+2)] = count; state->guess[count] = 7; state->common->fixed[count++] = FALSE; n++; } } desc++; } desc++; for (i=0;i<2*(state->common->params.w + state->common->params.h);i++) { int x,y; int sights; sights = atoi(desc); while (*desc && isdigit((unsigned char)*desc)) desc++; desc++; range2grid(i,state->common->params.w,state->common->params.h,&x,&y); state->common->grid[x+y*(state->common->params.w +2)] = sights; state->common->xinfo[x+y*(state->common->params.w +2)] = -2; } state->common->grid[0] = 0; state->common->xinfo[0] = -2; state->common->grid[state->common->params.w+1] = 0; state->common->xinfo[state->common->params.w+1] = -2; state->common->grid[state->common->params.w+1 + (state->common->params.h+1)*(state->common->params.w+2)] = 0; state->common->xinfo[state->common->params.w+1 + (state->common->params.h+1)*(state->common->params.w+2)] = -2; state->common->grid[(state->common->params.h+1)*(state->common->params.w+2)] = 0; state->common->xinfo[(state->common->params.h+1)*(state->common->params.w+2)] = -2; make_paths(state); qsort(state->common->paths, state->common->num_paths, sizeof(struct path), path_cmp); return state; } static char *validate_desc(const game_params *params, const char *desc) { int i; int w = params->w, h = params->h; int wh = w*h; int area; int monsters; int monster_count; const char *desc_s = desc; for (i=0;i<3;i++) { if (!*desc) return "Faulty game description"; while (*desc && isdigit((unsigned char)*desc)) { desc++; } if (*desc != ',') return "Invalid character in number list"; desc++; } desc = desc_s; area = monsters = monster_count = 0; for (i=0;i<3;i++) { monster_count += atoi(desc); while (*desc && isdigit((unsigned char)*desc)) desc++; desc++; } while (*desc && *desc != ',') { if (*desc >= 'a' && *desc <= 'z') { area += *desc - 'a' +1; monsters += *desc - 'a' +1; } else if (*desc == 'G' || *desc == 'V' || *desc == 'Z') { area++; monsters++; } else if (*desc == 'L' || *desc == 'R') { area++; } else return "Invalid character in grid specification"; desc++; } if (area < wh) return "Not enough data to fill grid"; else if (area > wh) return "Too much data to fill grid"; if (monsters != monster_count) return "Monster numbers do not match grid spaces"; for (i = 0; i < 2*(w+h); i++) { if (!*desc) return "Not enough numbers given after grid specification"; else if (*desc != ',') return "Invalid character in number list"; desc++; while (*desc && isdigit((unsigned char)*desc)) { desc++; } } if (*desc) return "Unexpected additional data at end of game description"; return NULL; } static char *solve_game(const game_state *state_start, const game_state *currstate, const char *aux, char **error) { int p; int *old_guess; int iterative_depth; int solved_iterative, solved_bruteforce, contains_inconsistency, count_ambiguous; int i; char *move, *c; game_state *solve_state = dup_game(currstate); old_guess = snewn(solve_state->common->num_total,int); for (p=0;pcommon->num_total;p++) { if (solve_state->common->fixed[p]) { old_guess[p] = solve_state->guess[p] = state_start->guess[p]; } else { old_guess[p] = solve_state->guess[p] = 7; } } iterative_depth = 0; solved_iterative = FALSE; contains_inconsistency = FALSE; count_ambiguous = 0; /* Try to solve the puzzle with the iterative solver */ while (TRUE) { int no_change; no_change = TRUE; solved_iterative = solve_iterative(solve_state,solve_state->common->paths); iterative_depth++; for (p=0;pcommon->num_total;p++) { if (solve_state->guess[p] != old_guess[p]) no_change = FALSE; old_guess[p] = solve_state->guess[p]; if (solve_state->guess[p] == 0) contains_inconsistency = TRUE; } if (solved_iterative || no_change || contains_inconsistency) break; } if (contains_inconsistency) { *error = "Puzzle is inconsistent"; sfree(old_guess); free_game(solve_state); return NULL; } /* If necessary, try to solve the puzzle with the brute-force solver */ solved_bruteforce = FALSE; if (!solved_iterative) { for (p=0;pcommon->num_total;p++) if (solve_state->guess[p] != 1 && solve_state->guess[p] != 2 && solve_state->guess[p] != 4) count_ambiguous++; solved_bruteforce = solve_bruteforce(solve_state, solve_state->common->paths); } if (!solved_iterative && !solved_bruteforce) { *error = "Puzzle is unsolvable"; sfree(old_guess); free_game(solve_state); return NULL; } /* printf("Puzzle solved at level %s, iterations %d, ambiguous %d\n", (solved_bruteforce ? "TRICKY" : "NORMAL"), iterative_depth, count_ambiguous); */ move = snewn(solve_state->common->num_total * 4 +2, char); c = move; *c++='S'; for (i = 0; i < solve_state->common->num_total; i++) { if (solve_state->guess[i] == 1) c += sprintf(c, ";G%d", i); if (solve_state->guess[i] == 2) c += sprintf(c, ";V%d", i); if (solve_state->guess[i] == 4) c += sprintf(c, ";Z%d", i); } *c++ = '\0'; move = sresize(move, c - move, char); sfree(old_guess); free_game(solve_state); return move; } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { int w,h,c,r,xi,g; char *ret; char buf[120]; ret = snewn(50 + 6*(state->common->params.w +2) + 6*(state->common->params.h+2) + 3*(state->common->params.w * state->common->params.h), char); sprintf(ret,"G: %d V: %d Z: %d\n\n",state->common->num_ghosts, state->common->num_vampires, state->common->num_zombies); for (h=0;hcommon->params.h+2;h++) { for (w=0;wcommon->params.w+2;w++) { c = state->common->grid[w+h*(state->common->params.w+2)]; xi = state->common->xinfo[w+h*(state->common->params.w+2)]; r = grid2range(w,h,state->common->params.w,state->common->params.h); if (r != -1) { sprintf(buf,"%2d", c); strcat(ret,buf); } else if (c == CELL_MIRROR_L) { sprintf(buf," \\"); strcat(ret,buf); } else if (c == CELL_MIRROR_R) { sprintf(buf," /"); strcat(ret,buf); } else if (xi >= 0) { g = state->guess[xi]; if (g == 1) { sprintf(buf," G"); strcat(ret,buf); } else if (g == 2) { sprintf(buf," V"); strcat(ret,buf); } else if (g == 4) { sprintf(buf," Z"); strcat(ret,buf); } else { sprintf(buf," ."); strcat(ret,buf); } } else { sprintf(buf," "); strcat(ret,buf); } } sprintf(buf,"\n"); strcat(ret,buf); } return ret; } struct game_ui { int hx, hy; /* as for solo.c, highlight pos */ int hshow, hpencil, hcursor; /* show state, type, and ?cursor. */ int ascii; }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->hx = ui->hy = 0; ui->hpencil = ui->hshow = ui->hcursor = 0; ui->ascii = FALSE; return ui; } static void free_ui(game_ui *ui) { sfree(ui); return; } static char *encode_ui(const game_ui *ui) { return NULL; } static void decode_ui(game_ui *ui, const char *encoding) { return; } static void game_changed_state(game_ui *ui, const game_state *oldstate, const game_state *newstate) { /* See solo.c; if we were pencil-mode highlighting and * somehow a square has just been properly filled, cancel * pencil mode. */ if (ui->hshow && ui->hpencil && !ui->hcursor) { int g = newstate->guess[newstate->common->xinfo[ui->hx + ui->hy*(newstate->common->params.w+2)]]; if (g == 1 || g == 2 || g == 4) ui->hshow = 0; } } struct game_drawstate { int tilesize, started, solved; int w, h; int *monsters; unsigned char *pencils; unsigned char count_errors[3]; unsigned char *cell_errors; unsigned char *hint_errors; int hx, hy, hshow, hpencil; /* as for game_ui. */ int hflash; int ascii; }; #define TILESIZE (ds->tilesize) #define BORDER (TILESIZE/4) static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { int gx,gy; int g,xi; char buf[80]; gx = ((x-BORDER-1) / TILESIZE ); gy = ((y-BORDER-2) / TILESIZE ) - 1; if (button == 'a' || button == 'A') { ui->ascii = !ui->ascii; return ""; } if (ui->hshow == 1 && ui->hpencil == 0) { xi = state->common->xinfo[ui->hx + ui->hy*(state->common->params.w+2)]; if (xi >= 0 && !state->common->fixed[xi]) { if (button == 'g' || button == 'G' || button == '1') { if (!ui->hcursor) ui->hshow = 0; sprintf(buf,"G%d",xi); return dupstr(buf); } if (button == 'v' || button == 'V' || button == '2') { if (!ui->hcursor) ui->hshow = 0; sprintf(buf,"V%d",xi); return dupstr(buf); } if (button == 'z' || button == 'Z' || button == '3') { if (!ui->hcursor) ui->hshow = 0; sprintf(buf,"Z%d",xi); return dupstr(buf); } if (button == 'e' || button == 'E' || button == CURSOR_SELECT2 || button == '0' || button == '\b' ) { if (!ui->hcursor) ui->hshow = 0; sprintf(buf,"E%d",xi); return dupstr(buf); } } } if (IS_CURSOR_MOVE(button)) { if (ui->hx == 0 && ui->hy == 0) { ui->hx = 1; ui->hy = 1; } else switch (button) { case CURSOR_UP: ui->hy -= (ui->hy > 1) ? 1 : 0; break; case CURSOR_DOWN: ui->hy += (ui->hy < ds->h) ? 1 : 0; break; case CURSOR_RIGHT: ui->hx += (ui->hx < ds->w) ? 1 : 0; break; case CURSOR_LEFT: ui->hx -= (ui->hx > 1) ? 1 : 0; break; } ui->hshow = ui->hcursor = 1; return ""; } if (ui->hshow && button == CURSOR_SELECT) { ui->hpencil = 1 - ui->hpencil; ui->hcursor = 1; return ""; } if (ui->hshow == 1 && ui->hpencil == 1) { xi = state->common->xinfo[ui->hx + ui->hy*(state->common->params.w+2)]; if (xi >= 0 && !state->common->fixed[xi]) { if (button == 'g' || button == 'G' || button == '1') { sprintf(buf,"g%d",xi); if (!ui->hcursor) ui->hpencil = ui->hshow = 0; return dupstr(buf); } if (button == 'v' || button == 'V' || button == '2') { sprintf(buf,"v%d",xi); if (!ui->hcursor) ui->hpencil = ui->hshow = 0; return dupstr(buf); } if (button == 'z' || button == 'Z' || button == '3') { sprintf(buf,"z%d",xi); if (!ui->hcursor) ui->hpencil = ui->hshow = 0; return dupstr(buf); } if (button == 'e' || button == 'E' || button == CURSOR_SELECT2 || button == '0' || button == '\b') { sprintf(buf,"E%d",xi); if (!ui->hcursor) ui->hpencil = ui->hshow = 0; return dupstr(buf); } } } if (gx > 0 && gx < ds->w+1 && gy > 0 && gy < ds->h+1) { xi = state->common->xinfo[gx+gy*(state->common->params.w+2)]; if (xi >= 0 && !state->common->fixed[xi]) { g = state->guess[xi]; if (ui->hshow == 0) { if (button == LEFT_BUTTON) { ui->hshow = 1; ui->hpencil = 0; ui->hcursor = 0; ui->hx = gx; ui->hy = gy; return ""; } else if (button == RIGHT_BUTTON && g == 7) { ui->hshow = 1; ui->hpencil = 1; ui->hcursor = 0; ui->hx = gx; ui->hy = gy; return ""; } } else if (ui->hshow == 1) { if (button == LEFT_BUTTON) { if (ui->hpencil == 0) { if (gx == ui->hx && gy == ui->hy) { ui->hshow = 0; ui->hpencil = 0; ui->hcursor = 0; ui->hx = 0; ui->hy = 0; return ""; } else { ui->hshow = 1; ui->hpencil = 0; ui->hcursor = 0; ui->hx = gx; ui->hy = gy; return ""; } } else { ui->hshow = 1; ui->hpencil = 0; ui->hcursor = 0; ui->hx = gx; ui->hy = gy; return ""; } } else if (button == RIGHT_BUTTON) { if (ui->hpencil == 0 && g == 7) { ui->hshow = 1; ui->hpencil = 1; ui->hcursor = 0; ui->hx = gx; ui->hy = gy; return ""; } else { if (gx == ui->hx && gy == ui->hy) { ui->hshow = 0; ui->hpencil = 0; ui->hcursor = 0; ui->hx = 0; ui->hy = 0; return ""; } else if (g == 7) { ui->hshow = 1; ui->hpencil = 1; ui->hcursor = 0; ui->hx = gx; ui->hy = gy; return ""; } } } } } } return NULL; } int check_numbers_draw(game_state *state, int *guess) { int valid, filled; int i,x,y,xy; int count_ghosts, count_vampires, count_zombies; count_ghosts = count_vampires = count_zombies = 0; for (i=0;icommon->num_total;i++) { if (guess[i] == 1) count_ghosts++; if (guess[i] == 2) count_vampires++; if (guess[i] == 4) count_zombies++; } valid = TRUE; filled = (count_ghosts + count_vampires + count_zombies >= state->common->num_total); if (count_ghosts > state->common->num_ghosts || (filled && count_ghosts != state->common->num_ghosts) ) { valid = FALSE; state->count_errors[0] = TRUE; for (x=1;xcommon->params.w+1;x++) for (y=1;ycommon->params.h+1;y++) { xy = x+y*(state->common->params.w+2); if (state->common->xinfo[xy] >= 0 && guess[state->common->xinfo[xy]] == 1) state->cell_errors[xy] = TRUE; } } if (count_vampires > state->common->num_vampires || (filled && count_vampires != state->common->num_vampires) ) { valid = FALSE; state->count_errors[1] = TRUE; for (x=1;xcommon->params.w+1;x++) for (y=1;ycommon->params.h+1;y++) { xy = x+y*(state->common->params.w+2); if (state->common->xinfo[xy] >= 0 && guess[state->common->xinfo[xy]] == 2) state->cell_errors[xy] = TRUE; } } if (count_zombies > state->common->num_zombies || (filled && count_zombies != state->common->num_zombies) ) { valid = FALSE; state->count_errors[2] = TRUE; for (x=1;xcommon->params.w+1;x++) for (y=1;ycommon->params.h+1;y++) { xy = x+y*(state->common->params.w+2); if (state->common->xinfo[xy] >= 0 && guess[state->common->xinfo[xy]] == 4) state->cell_errors[xy] = TRUE; } } return valid; } int check_path_solution(game_state *state, int p) { int i; int mirror; int count; int correct; int unfilled; count = 0; mirror = FALSE; correct = TRUE; unfilled = 0; for (i=0;icommon->paths[p].length;i++) { if (state->common->paths[p].p[i] == -1) mirror = TRUE; else { if (state->guess[state->common->paths[p].p[i]] == 1 && mirror) count++; else if (state->guess[state->common->paths[p].p[i]] == 2 && !mirror) count++; else if (state->guess[state->common->paths[p].p[i]] == 4) count++; else if (state->guess[state->common->paths[p].p[i]] == 7) unfilled++; } } if (unfilled == 0 && count != state->common->paths[p].sightings_start) { correct = FALSE; state->hint_errors[state->common->paths[p].grid_start] = TRUE; } count = 0; mirror = FALSE; unfilled = 0; for (i=state->common->paths[p].length-1;i>=0;i--) { if (state->common->paths[p].p[i] == -1) mirror = TRUE; else { if (state->guess[state->common->paths[p].p[i]] == 1 && mirror) count++; else if (state->guess[state->common->paths[p].p[i]] == 2 && !mirror) count++; else if (state->guess[state->common->paths[p].p[i]] == 4) count++; else if (state->guess[state->common->paths[p].p[i]] == 7) unfilled++; } } if (unfilled == 0 && count != state->common->paths[p].sightings_end) { correct = FALSE; state->hint_errors[state->common->paths[p].grid_end] = TRUE; } if (!correct) { for (i=0;icommon->paths[p].length;i++) state->cell_errors[state->common->paths[p].xy[i]] = TRUE; } return correct; } static game_state *execute_move(const game_state *state, const char *move) { int x,n,p,i; char c; int correct; int solver; game_state *ret = dup_game(state); solver = FALSE; while (*move) { c = *move; if (c == 'S') { move++; solver = TRUE; } if (c == 'G' || c == 'V' || c == 'Z' || c == 'E' || c == 'g' || c == 'v' || c == 'z') { move++; sscanf(move, "%d%n", &x, &n); if (c == 'G') ret->guess[x] = 1; if (c == 'V') ret->guess[x] = 2; if (c == 'Z') ret->guess[x] = 4; if (c == 'E') { ret->guess[x] = 7; ret->pencils[x] = 0; } if (c == 'g') ret->pencils[x] ^= 1; if (c == 'v') ret->pencils[x] ^= 2; if (c == 'z') ret->pencils[x] ^= 4; move += n; } if (*move == ';') move++; } correct = TRUE; for (i=0;icommon->wh;i++) ret->cell_errors[i] = FALSE; for (i=0;i<2*ret->common->num_paths;i++) ret->hint_errors[i] = FALSE; for (i=0;i<3;i++) ret->count_errors[i] = FALSE; if (!check_numbers_draw(ret,ret->guess)) correct = FALSE; for (p=0;pcommon->num_paths;p++) if (!check_path_solution(ret,p)) correct = FALSE; for (i=0;icommon->num_total;i++) if (!(ret->guess[i] == 1 || ret->guess[i] == 2 || ret->guess[i] == 4)) correct = FALSE; if (correct && !solver) ret->solved = TRUE; if (solver) ret->cheated = TRUE; return ret; } /* ---------------------------------------------------------------------- * Drawing routines. */ #define PREFERRED_TILE_SIZE 64 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+(params->w+2)*TILESIZE; *y = 2*BORDER+(params->h+3)*TILESIZE; return; } static void game_set_size(drawing *dr, game_drawstate *ds, const game_params *params, int tilesize) { ds->tilesize = tilesize; return; } #define COLOUR(ret, i, r, g, b) ((ret[3*(i)+0] = (r)), (ret[3*(i)+1] = (g)), (ret[3*(i)+2] = (b))) 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_TEXT * 3 + 0] = 0.0F; ret[COL_TEXT * 3 + 1] = 0.0F; ret[COL_TEXT * 3 + 2] = 0.0F; ret[COL_ERROR * 3 + 0] = 1.0F; ret[COL_ERROR * 3 + 1] = 0.0F; ret[COL_ERROR * 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_FLASH * 3 + 0] = 1.0F; ret[COL_FLASH * 3 + 1] = 1.0F; ret[COL_FLASH * 3 + 2] = 1.0F; ret[COL_GHOST * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.5F; ret[COL_GHOST * 3 + 1] = ret[COL_BACKGROUND * 3 + 0]; ret[COL_GHOST * 3 + 2] = ret[COL_BACKGROUND * 3 + 0]; ret[COL_ZOMBIE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.5F; ret[COL_ZOMBIE * 3 + 1] = ret[COL_BACKGROUND * 3 + 0]; ret[COL_ZOMBIE * 3 + 2] = ret[COL_BACKGROUND * 3 + 0] * 0.5F; ret[COL_VAMPIRE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0]; ret[COL_VAMPIRE * 3 + 1] = ret[COL_BACKGROUND * 3 + 0] * 0.9F; ret[COL_VAMPIRE * 3 + 2] = ret[COL_BACKGROUND * 3 + 0] * 0.9F; *ncolours = NCOLOURS; return ret; } static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { int i; struct game_drawstate *ds = snew(struct game_drawstate); ds->tilesize = 0; ds->started = ds->solved = FALSE; ds->w = state->common->params.w; ds->h = state->common->params.h; ds->ascii = FALSE; ds->count_errors[0] = FALSE; ds->count_errors[1] = FALSE; ds->count_errors[2] = FALSE; ds->monsters = snewn(state->common->num_total,int); for (i=0;i<(state->common->num_total);i++) ds->monsters[i] = 7; ds->pencils = snewn(state->common->num_total,unsigned char); for (i=0;icommon->num_total;i++) ds->pencils[i] = 0; ds->cell_errors = snewn(state->common->wh,unsigned char); for (i=0;icommon->wh;i++) ds->cell_errors[i] = FALSE; ds->hint_errors = snewn(2*state->common->num_paths,unsigned char); for (i=0;i<2*state->common->num_paths;i++) ds->hint_errors[i] = FALSE; ds->hshow = ds->hpencil = ds->hflash = 0; ds->hx = ds->hy = 0; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->hint_errors); sfree(ds->cell_errors); sfree(ds->pencils); sfree(ds->monsters); sfree(ds); return; } static void draw_cell_background(drawing *dr, game_drawstate *ds, const game_state *state, const game_ui *ui, int x, int y) { int hon; int dx,dy; dx = BORDER+(x* ds->tilesize)+(TILESIZE/2); dy = BORDER+(y* ds->tilesize)+(TILESIZE/2)+TILESIZE; hon = (ui->hshow && x == ui->hx && y == ui->hy); draw_rect(dr,dx-(TILESIZE/2)+1,dy-(TILESIZE/2)+1,TILESIZE-1,TILESIZE-1,(hon && !ui->hpencil) ? COL_HIGHLIGHT : COL_BACKGROUND); if (hon && ui->hpencil) { int coords[6]; coords[0] = dx-(TILESIZE/2)+1; coords[1] = dy-(TILESIZE/2)+1; coords[2] = coords[0] + TILESIZE/2; coords[3] = coords[1]; coords[4] = coords[0]; coords[5] = coords[1] + TILESIZE/2; draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT); } draw_update(dr,dx-(TILESIZE/2)+1,dy-(TILESIZE/2)+1,TILESIZE-1,TILESIZE-1); return; } static void draw_circle_or_point(drawing *dr, int cx, int cy, int radius, int colour) { if (radius > 0) draw_circle(dr, cx, cy, radius, colour, colour); else draw_rect(dr, cx, cy, 1, 1, colour); } static void draw_monster(drawing *dr, game_drawstate *ds, int x, int y, int tilesize, int hflash, int monster) { int black = (hflash ? COL_FLASH : COL_TEXT); if (monster == 1) { /* ghost */ int poly[80], i, j; clip(dr,x-(tilesize/2)+2,y-(tilesize/2)+2,tilesize-3,tilesize/2+1); draw_circle(dr,x,y,2*tilesize/5, COL_GHOST,black); unclip(dr); i = 0; poly[i++] = x - 2*tilesize/5; poly[i++] = y-2; poly[i++] = x - 2*tilesize/5; poly[i++] = y + 2*tilesize/5; for (j = 0; j < 3; j++) { int total = (2*tilesize/5) * 2; int before = total * j / 3; int after = total * (j+1) / 3; int mid = (before + after) / 2; poly[i++] = x - 2*tilesize/5 + mid; poly[i++] = y + 2*tilesize/5 - (total / 6); poly[i++] = x - 2*tilesize/5 + after; poly[i++] = y + 2*tilesize/5; } poly[i++] = x + 2*tilesize/5; poly[i++] = y-2; clip(dr,x-(tilesize/2)+2,y,tilesize-3,tilesize-(tilesize/2)-1); draw_polygon(dr, poly, i/2, COL_GHOST, black); unclip(dr); draw_circle(dr,x-tilesize/6,y-tilesize/12,tilesize/10, COL_BACKGROUND,black); draw_circle(dr,x+tilesize/6,y-tilesize/12,tilesize/10, COL_BACKGROUND,black); draw_circle_or_point(dr,x-tilesize/6+1+tilesize/48,y-tilesize/12, tilesize/48,black); draw_circle_or_point(dr,x+tilesize/6+1+tilesize/48,y-tilesize/12, tilesize/48,black); } else if (monster == 2) { /* vampire */ int poly[80], i; clip(dr,x-(tilesize/2)+2,y-(tilesize/2)+2,tilesize-3,tilesize/2); draw_circle(dr,x,y,2*tilesize/5,black,black); unclip(dr); clip(dr,x-(tilesize/2)+2,y-(tilesize/2)+2,tilesize/2+1,tilesize/2); draw_circle(dr,x-tilesize/7,y,2*tilesize/5-tilesize/7, COL_VAMPIRE,black); unclip(dr); clip(dr,x,y-(tilesize/2)+2,tilesize/2+1,tilesize/2); draw_circle(dr,x+tilesize/7,y,2*tilesize/5-tilesize/7, COL_VAMPIRE,black); unclip(dr); clip(dr,x-(tilesize/2)+2,y,tilesize-3,tilesize/2); draw_circle(dr,x,y,2*tilesize/5, COL_VAMPIRE,black); unclip(dr); draw_circle(dr, x-tilesize/7, y-tilesize/16, tilesize/16, COL_BACKGROUND, black); draw_circle(dr, x+tilesize/7, y-tilesize/16, tilesize/16, COL_BACKGROUND, black); draw_circle_or_point(dr, x-tilesize/7, y-tilesize/16, tilesize/48, black); draw_circle_or_point(dr, x+tilesize/7, y-tilesize/16, tilesize/48, black); clip(dr, x-(tilesize/2)+2, y+tilesize/8, tilesize-3, tilesize/4); i = 0; poly[i++] = x-3*tilesize/16; poly[i++] = y+1*tilesize/8; poly[i++] = x-2*tilesize/16; poly[i++] = y+7*tilesize/24; poly[i++] = x-1*tilesize/16; poly[i++] = y+1*tilesize/8; draw_polygon(dr, poly, i/2, COL_BACKGROUND, black); i = 0; poly[i++] = x+3*tilesize/16; poly[i++] = y+1*tilesize/8; poly[i++] = x+2*tilesize/16; poly[i++] = y+7*tilesize/24; poly[i++] = x+1*tilesize/16; poly[i++] = y+1*tilesize/8; draw_polygon(dr, poly, i/2, COL_BACKGROUND, black); draw_circle(dr, x, y-tilesize/5, 2*tilesize/5, COL_VAMPIRE, black); unclip(dr); } else if (monster == 4) { /* zombie */ draw_circle(dr,x,y,2*tilesize/5, COL_ZOMBIE,black); draw_line(dr, x-tilesize/7-tilesize/16, y-tilesize/12-tilesize/16, x-tilesize/7+tilesize/16, y-tilesize/12+tilesize/16, black); draw_line(dr, x-tilesize/7+tilesize/16, y-tilesize/12-tilesize/16, x-tilesize/7-tilesize/16, y-tilesize/12+tilesize/16, black); draw_line(dr, x+tilesize/7-tilesize/16, y-tilesize/12-tilesize/16, x+tilesize/7+tilesize/16, y-tilesize/12+tilesize/16, black); draw_line(dr, x+tilesize/7+tilesize/16, y-tilesize/12-tilesize/16, x+tilesize/7-tilesize/16, y-tilesize/12+tilesize/16, black); clip(dr, x-tilesize/5, y+tilesize/6, 2*tilesize/5+1, tilesize/2); draw_circle(dr, x-tilesize/15, y+tilesize/6, tilesize/12, COL_BACKGROUND, black); unclip(dr); draw_line(dr, x-tilesize/5, y+tilesize/6, x+tilesize/5, y+tilesize/6, black); } draw_update(dr,x-(tilesize/2)+2,y-(tilesize/2)+2,tilesize-3,tilesize-3); } static void draw_monster_count(drawing *dr, game_drawstate *ds, const game_state *state, int c, int hflash) { int dx,dy,dh; char buf[8]; char bufm[8]; dy = TILESIZE/2; dh = TILESIZE; dx = BORDER+(ds->w+2)*TILESIZE/2+TILESIZE/4; switch (c) { case 0: sprintf(buf,"%d",state->common->num_ghosts); sprintf(bufm,"G"); dx -= 3*TILESIZE/2; break; case 1: sprintf(buf,"%d",state->common->num_vampires); sprintf(bufm,"V"); break; case 2: sprintf(buf,"%d",state->common->num_zombies); sprintf(bufm,"Z"); dx += 3*TILESIZE/2; break; } if (!ds->ascii) { draw_rect(dr,dx-2*TILESIZE/3,dy,3*TILESIZE/2,dh,COL_BACKGROUND); draw_monster(dr,ds,dx-TILESIZE/3,dh,2*TILESIZE/3,hflash,1<count_errors[c] ? COL_ERROR : hflash ? COL_FLASH : COL_TEXT), buf); draw_update(dr,dx-2*TILESIZE/3,dy,3*TILESIZE/2,dh); } else { draw_rect(dr,dx-2*TILESIZE/3,dy,3*TILESIZE/2,dh,COL_BACKGROUND); draw_text(dr,dx-TILESIZE/3,dh,FONT_VARIABLE,dy-1, ALIGN_HCENTRE|ALIGN_VCENTRE, hflash ? COL_FLASH : COL_TEXT, bufm); draw_text(dr,dx,dh,FONT_VARIABLE,dy-1,ALIGN_HLEFT|ALIGN_VCENTRE, (state->count_errors[c] ? COL_ERROR : hflash ? COL_FLASH : COL_TEXT), buf); draw_update(dr,dx-2*TILESIZE/3,dy,3*TILESIZE/2,dh); } return; } static void draw_path_hint(drawing *dr, game_drawstate *ds, const game_state *state, int i, int hflash, int start) { int dx,dy,x,y; int p,error; char buf[80]; p = start ? state->common->paths[i].grid_start : state->common->paths[i].grid_end; range2grid(p,state->common->params.w,state->common->params.h,&x,&y); error = ds->hint_errors[p]; dx = BORDER+(x* ds->tilesize)+(TILESIZE/2); dy = BORDER+(y* ds->tilesize)+(TILESIZE/2)+TILESIZE; sprintf(buf,"%d", start ? state->common->paths[i].sightings_start : state->common->paths[i].sightings_end); draw_rect(dr,dx-(TILESIZE/2)+2,dy-(TILESIZE/2)+2,TILESIZE-3,TILESIZE-3,COL_BACKGROUND); draw_text(dr,dx,dy,FONT_FIXED,TILESIZE/2,ALIGN_HCENTRE|ALIGN_VCENTRE, error ? COL_ERROR : hflash ? COL_FLASH : COL_TEXT,buf); draw_update(dr,dx-(TILESIZE/2)+2,dy-(TILESIZE/2)+2,TILESIZE-3,TILESIZE-3); return; } static void draw_mirror(drawing *dr, game_drawstate *ds, const game_state *state, int x, int y, int hflash, int mirror) { int dx,dy,mx1,my1,mx2,my2; dx = BORDER+(x* ds->tilesize)+(TILESIZE/2); dy = BORDER+(y* ds->tilesize)+(TILESIZE/2)+TILESIZE; if (mirror == CELL_MIRROR_L) { mx1 = dx-(TILESIZE/4); my1 = dy-(TILESIZE/4); mx2 = dx+(TILESIZE/4); my2 = dy+(TILESIZE/4); } else { mx1 = dx-(TILESIZE/4); my1 = dy+(TILESIZE/4); mx2 = dx+(TILESIZE/4); my2 = dy-(TILESIZE/4); } draw_thick_line(dr,(float)(TILESIZE/16),mx1,my1,mx2,my2, hflash ? COL_FLASH : COL_TEXT); draw_update(dr,dx-(TILESIZE/2)+1,dy-(TILESIZE/2)+1,TILESIZE-1,TILESIZE-1); return; } static void draw_big_monster(drawing *dr, game_drawstate *ds, const game_state *state, int x, int y, int hflash, int monster) { int dx,dy; char buf[10]; dx = BORDER+(x* ds->tilesize)+(TILESIZE/2); dy = BORDER+(y* ds->tilesize)+(TILESIZE/2)+TILESIZE; if (ds->ascii) { if (monster == 1) sprintf(buf,"G"); else if (monster == 2) sprintf(buf,"V"); else if (monster == 4) sprintf(buf,"Z"); else sprintf(buf," "); draw_text(dr,dx,dy,FONT_FIXED,TILESIZE/2,ALIGN_HCENTRE|ALIGN_VCENTRE, hflash ? COL_FLASH : COL_TEXT,buf); draw_update(dr,dx-(TILESIZE/2)+2,dy-(TILESIZE/2)+2,TILESIZE-3, TILESIZE-3); } else { draw_monster(dr, ds, dx, dy, 3*TILESIZE/4, hflash, monster); } return; } static void draw_pencils(drawing *dr, game_drawstate *ds, const game_state *state, int x, int y, int pencil) { int dx, dy; int monsters[4]; int i, j, px, py; char buf[10]; dx = BORDER+(x* ds->tilesize)+(TILESIZE/4); dy = BORDER+(y* ds->tilesize)+(TILESIZE/4)+TILESIZE; for (i = 0, j = 1; j < 8; j *= 2) if (pencil & j) monsters[i++] = j; while (i < 4) monsters[i++] = 0; for (py = 0; py < 2; py++) for (px = 0; px < 2; px++) if (monsters[py*2+px]) { if (!ds->ascii) { draw_monster(dr, ds, dx + TILESIZE/2 * px, dy + TILESIZE/2 * py, TILESIZE/2, 0, monsters[py*2+px]); } else { switch (monsters[py*2+px]) { case 1: sprintf(buf,"G"); break; case 2: sprintf(buf,"V"); break; case 4: sprintf(buf,"Z"); break; } draw_text(dr,dx + TILESIZE/2 * px,dy + TILESIZE/2 * py, FONT_FIXED,TILESIZE/4,ALIGN_HCENTRE|ALIGN_VCENTRE, COL_TEXT,buf); } } draw_update(dr,dx-(TILESIZE/4)+2,dy-(TILESIZE/4)+2, (TILESIZE/2)-3,(TILESIZE/2)-3); return; } #define FLASH_TIME 0.7F 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 i,j,x,y,xy; int stale, xi, c, hflash, hchanged, changed_ascii; hflash = (int)(flashtime * 5 / FLASH_TIME) % 2; /* Draw static grid components at startup */ if (!ds->started) { draw_rect(dr, 0, 0, 2*BORDER+(ds->w+2)*TILESIZE, 2*BORDER+(ds->h+3)*TILESIZE, COL_BACKGROUND); draw_rect(dr, BORDER+TILESIZE-1, BORDER+2*TILESIZE-1, (ds->w)*TILESIZE +3, (ds->h)*TILESIZE +3, COL_GRID); for (i=0;iw;i++) for (j=0;jh;j++) draw_rect(dr, BORDER+(ds->tilesize*(i+1))+1, BORDER+(ds->tilesize*(j+2))+1, ds->tilesize-1, ds->tilesize-1, COL_BACKGROUND); draw_update(dr, 0, 0, 2*BORDER+(ds->w+2)*TILESIZE, 2*BORDER+(ds->h+3)*TILESIZE); } hchanged = FALSE; if (ds->hx != ui->hx || ds->hy != ui->hy || ds->hshow != ui->hshow || ds->hpencil != ui->hpencil) hchanged = TRUE; if (ds->ascii != ui->ascii) { ds->ascii = ui->ascii; changed_ascii = TRUE; } else changed_ascii = FALSE; /* Draw monster count hints */ for (i=0;i<3;i++) { stale = FALSE; if (!ds->started) stale = TRUE; if (ds->hflash != hflash) stale = TRUE; if (changed_ascii) stale = TRUE; if (ds->count_errors[i] != state->count_errors[i]) { stale = TRUE; ds->count_errors[i] = state->count_errors[i]; } if (stale) { draw_monster_count(dr, ds, state, i, hflash); } } /* Draw path count hints */ for (i=0;icommon->num_paths;i++) { int p; stale = FALSE; if (!ds->started) stale = TRUE; if (ds->hflash != hflash) stale = TRUE; p = state->common->paths[i].grid_start; if (ds->hint_errors[p] != state->hint_errors[p]) { stale = TRUE; ds->hint_errors[p] = state->hint_errors[p]; } if (stale) { draw_path_hint(dr, ds, state, i, hflash, TRUE); } stale = FALSE; if (!ds->started) stale = TRUE; if (ds->hflash != hflash) stale = TRUE; p = state->common->paths[i].grid_end; if (ds->hint_errors[p] != state->hint_errors[p]) { stale = TRUE; ds->hint_errors[p] = state->hint_errors[p]; } if (stale) { draw_path_hint(dr, ds, state, i, hflash, FALSE); } } /* Draw puzzle grid contents */ for (x = 1; x < ds->w+1; x++) for (y = 1; y < ds->h+1; y++) { stale = FALSE; xy = x+y*(state->common->params.w+2); xi = state->common->xinfo[xy]; c = state->common->grid[xy]; if (!ds->started) stale = TRUE; if (ds->hflash != hflash) stale = TRUE; if (changed_ascii) stale = TRUE; if (hchanged) { if ((x == ui->hx && y == ui->hy) || (x == ds->hx && y == ds->hy)) stale = TRUE; } if (xi >= 0 && (state->guess[xi] != ds->monsters[xi]) ) { stale = TRUE; ds->monsters[xi] = state->guess[xi]; } if (xi >= 0 && (state->pencils[xi] != ds->pencils[xi]) ) { stale = TRUE; ds->pencils[xi] = state->pencils[xi]; } if (state->cell_errors[xy] != ds->cell_errors[xy]) { stale = TRUE; ds->cell_errors[xy] = state->cell_errors[xy]; } if (stale) { draw_cell_background(dr, ds, state, ui, x, y); if (xi < 0) draw_mirror(dr, ds, state, x, y, hflash, c); else if (state->guess[xi] == 1 || state->guess[xi] == 2 || state->guess[xi] == 4) draw_big_monster(dr, ds, state, x, y, hflash, state->guess[xi]); else draw_pencils(dr, ds, state, x, y, state->pencils[xi]); } } ds->hx = ui->hx; ds->hy = ui->hy; ds->hshow = ui->hshow; ds->hpencil = ui->hpencil; ds->hflash = hflash; ds->started = TRUE; return; } 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 (!oldstate->solved && newstate->solved && !oldstate->cheated && !newstate->cheated) ? FLASH_TIME : 0.0F; } static int game_status(const game_state *state) { return state->solved; } static int 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 undead #endif const struct game thegame = { "Undead", "games.undead", "undead", default_params, game_fetch_preset, 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, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, FALSE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, 0, /* flags */ }; puzzles-r9872/unequal.c0000644000175300017530000017760512132232554014262 0ustar simonsimon/* * unequal.c * * Implementation of 'Futoshiki', a puzzle featured in the Guardian. * * TTD: * add multiple-links-on-same-col/row solver nous * Optimise set solver to use bit operations instead * * Guardian puzzles of note: * #1: 5:0,0L,0L,0,0,0R,0,0L,0D,0L,0R,0,2,0D,0,0,0,0,0,0,0U,0,0,0,0U, * #2: 5:0,0,0,4L,0L,0,2LU,0L,0U,0,0,0U,0,0,0,0,0D,0,3LRUD,0,0R,3,0L,0,0, * #3: (reprint of #2) * #4: * #5: 5:0,0,0,0,0,0,2,0U,3U,0U,0,0,3,0,0,0,3,0D,4,0,0,0L,0R,0,0, * #6: 5:0D,0L,0,0R,0,0,0D,0,3,0D,0,0R,0,0R,0D,0U,0L,0,1,2,0,0,0U,0,0L, */ #include #include #include #include #include #include #include "puzzles.h" #include "latin.h" /* contains typedef for digit */ /* ---------------------------------------------------------- * Constant and structure definitions */ #define FLASH_TIME 0.4F #define PREFERRED_TILE_SIZE 32 #define TILE_SIZE (ds->tilesize) #define GAP_SIZE (TILE_SIZE/2) #define SQUARE_SIZE (TILE_SIZE + GAP_SIZE) #define BORDER (TILE_SIZE / 2) #define COORD(x) ( (x) * SQUARE_SIZE + BORDER ) #define FROMCOORD(x) ( ((x) - BORDER + SQUARE_SIZE) / SQUARE_SIZE - 1 ) #define GRID(p,w,x,y) ((p)->w[((y)*(p)->order)+(x)]) #define GRID3(p,w,x,y,z) ((p)->w[ (((x)*(p)->order+(y))*(p)->order+(z)) ]) #define HINT(p,x,y,n) GRID3(p, hints, x, y, n) enum { COL_BACKGROUND, COL_GRID, COL_TEXT, COL_GUESS, COL_ERROR, COL_PENCIL, COL_HIGHLIGHT, COL_LOWLIGHT, NCOLOURS }; struct game_params { int order; /* Size of latin square */ int diff; /* Difficulty */ int adjacent; /* Puzzle indicators are 'adjacent number' not 'greater-than'. */ }; #define F_IMMUTABLE 1 /* passed in as game description */ #define F_ADJ_UP 2 #define F_ADJ_RIGHT 4 #define F_ADJ_DOWN 8 #define F_ADJ_LEFT 16 #define F_ERROR 32 #define F_ERROR_UP 64 #define F_ERROR_RIGHT 128 #define F_ERROR_DOWN 256 #define F_ERROR_LEFT 512 #define F_ERROR_MASK (F_ERROR|F_ERROR_UP|F_ERROR_RIGHT|F_ERROR_DOWN|F_ERROR_LEFT) struct game_state { int order, completed, cheated, adjacent; digit *nums; /* actual numbers (size order^2) */ unsigned char *hints; /* remaining possiblities (size order^3) */ unsigned int *flags; /* flags (size order^2) */ }; /* ---------------------------------------------------------- * Game parameters and presets */ /* Steal the method from map.c for difficulty levels. */ #define DIFFLIST(A) \ A(LATIN,Trivial,NULL,t) \ A(EASY,Easy,solver_easy, e) \ A(SET,Tricky,solver_set, k) \ A(EXTREME,Extreme,NULL,x) \ A(RECURSIVE,Recursive,NULL,r) #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, DIFF_IMPOSSIBLE = diff_impossible, DIFF_AMBIGUOUS = diff_ambiguous, DIFF_UNFINISHED = diff_unfinished }; static char const *const unequal_diffnames[] = { DIFFLIST(TITLE) }; static char const unequal_diffchars[] = DIFFLIST(ENCODE); #define DIFFCONFIG DIFFLIST(CONFIG) #define DEFAULT_PRESET 0 const static struct game_params unequal_presets[] = { { 4, DIFF_EASY, 0 }, { 5, DIFF_EASY, 0 }, { 5, DIFF_SET, 0 }, { 5, DIFF_SET, 1 }, { 5, DIFF_EXTREME, 0 }, { 6, DIFF_EASY, 0 }, { 6, DIFF_SET, 0 }, { 6, DIFF_SET, 1 }, { 6, DIFF_EXTREME, 0 }, { 7, DIFF_SET, 0 }, { 7, DIFF_SET, 1 }, { 7, DIFF_EXTREME, 0 } }; static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char buf[80]; if (i < 0 || i >= lenof(unequal_presets)) return FALSE; ret = snew(game_params); *ret = unequal_presets[i]; /* structure copy */ sprintf(buf, "%s: %dx%d %s", ret->adjacent ? "Adjacent" : "Unequal", ret->order, ret->order, unequal_diffnames[ret->diff]); *name = dupstr(buf); *params = ret; return TRUE; } static game_params *default_params(void) { game_params *ret; char *name; if (!game_fetch_preset(DEFAULT_PRESET, &name, &ret)) return NULL; sfree(name); 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 void decode_params(game_params *ret, char const *string) { char const *p = string; ret->order = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; if (*p == 'a') { p++; ret->adjacent = 1; } else ret->adjacent = 0; if (*p == 'd') { int i; p++; ret->diff = DIFFCOUNT+1; /* ...which is invalid */ if (*p) { for (i = 0; i < DIFFCOUNT; i++) { if (*p == unequal_diffchars[i]) ret->diff = i; } p++; } } } static char *encode_params(const game_params *params, int full) { char ret[80]; sprintf(ret, "%d", params->order); if (params->adjacent) sprintf(ret + strlen(ret), "a"); if (full) sprintf(ret + strlen(ret), "d%c", unequal_diffchars[params->diff]); 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 = "Mode"; ret[0].type = C_CHOICES; ret[0].sval = ":Unequal:Adjacent"; ret[0].ival = params->adjacent; ret[1].name = "Size (s*s)"; ret[1].type = C_STRING; sprintf(buf, "%d", params->order); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Difficulty"; ret[2].type = C_CHOICES; ret[2].sval = DIFFCONFIG; ret[2].ival = params->diff; ret[3].name = NULL; ret[3].type = C_END; ret[3].sval = NULL; ret[3].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->adjacent = cfg[0].ival; ret->order = atoi(cfg[1].sval); ret->diff = cfg[2].ival; return ret; } static char *validate_params(const game_params *params, int full) { if (params->order < 3 || params->order > 32) return "Order must be between 3 and 32"; if (params->diff >= DIFFCOUNT) return "Unknown difficulty rating"; if (params->order < 5 && params->adjacent && params->diff >= DIFF_SET) return "Order must be at least 5 for Adjacent puzzles of this difficulty."; return NULL; } /* ---------------------------------------------------------- * Various utility functions */ static const struct { unsigned int f, fo, fe; int dx, dy; char c, ac; } adjthan[] = { { F_ADJ_UP, F_ADJ_DOWN, F_ERROR_UP, 0, -1, '^', '-' }, { F_ADJ_RIGHT, F_ADJ_LEFT, F_ERROR_RIGHT, 1, 0, '>', '|' }, { F_ADJ_DOWN, F_ADJ_UP, F_ERROR_DOWN, 0, 1, 'v', '-' }, { F_ADJ_LEFT, F_ADJ_RIGHT, F_ERROR_LEFT, -1, 0, '<', '|' } }; static game_state *blank_game(int order, int adjacent) { game_state *state = snew(game_state); int o2 = order*order, o3 = o2*order; state->order = order; state->adjacent = adjacent; state->completed = state->cheated = 0; state->nums = snewn(o2, digit); state->hints = snewn(o3, unsigned char); state->flags = snewn(o2, unsigned int); memset(state->nums, 0, o2 * sizeof(digit)); memset(state->hints, 0, o3); memset(state->flags, 0, o2 * sizeof(unsigned int)); return state; } static game_state *dup_game(const game_state *state) { game_state *ret = blank_game(state->order, state->adjacent); int o2 = state->order*state->order, o3 = o2*state->order; memcpy(ret->nums, state->nums, o2 * sizeof(digit)); memcpy(ret->hints, state->hints, o3); memcpy(ret->flags, state->flags, o2 * sizeof(unsigned int)); return ret; } static void free_game(game_state *state) { sfree(state->nums); sfree(state->hints); sfree(state->flags); sfree(state); } #define CHECKG(x,y) grid[(y)*o+(x)] /* Returns 0 if it finds an error, 1 otherwise. */ static int check_num_adj(digit *grid, game_state *state, int x, int y, int me) { unsigned int f = GRID(state, flags, x, y); int ret = 1, i, o = state->order; for (i = 0; i < 4; i++) { int dx = adjthan[i].dx, dy = adjthan[i].dy, n, dn; if (x+dx < 0 || x+dx >= o || y+dy < 0 || y+dy >= o) continue; n = CHECKG(x, y); dn = CHECKG(x+dx, y+dy); assert (n != 0); if (dn == 0) continue; if (state->adjacent) { int gd = abs(n-dn); if ((f & adjthan[i].f) && (gd != 1)) { debug(("check_adj error (%d,%d):%d should be | (%d,%d):%d", x, y, n, x+dx, y+dy, dn)); if (me) GRID(state, flags, x, y) |= adjthan[i].fe; ret = 0; } if (!(f & adjthan[i].f) && (gd == 1)) { debug(("check_adj error (%d,%d):%d should not be | (%d,%d):%d", x, y, n, x+dx, y+dy, dn)); if (me) GRID(state, flags, x, y) |= adjthan[i].fe; ret = 0; } } else { if ((f & adjthan[i].f) && (n <= dn)) { debug(("check_adj error (%d,%d):%d not > (%d,%d):%d", x, y, n, x+dx, y+dy, dn)); if (me) GRID(state, flags, x, y) |= adjthan[i].fe; ret = 0; } } } return ret; } /* Returns 0 if it finds an error, 1 otherwise. */ static int check_num_error(digit *grid, game_state *state, int x, int y, int mark_errors) { int o = state->order; int xx, yy, val = CHECKG(x,y), ret = 1; assert(val != 0); /* check for dups in same column. */ for (yy = 0; yy < state->order; yy++) { if (yy == y) continue; if (CHECKG(x,yy) == val) ret = 0; } /* check for dups in same row. */ for (xx = 0; xx < state->order; xx++) { if (xx == x) continue; if (CHECKG(xx,y) == val) ret = 0; } if (!ret) { debug(("check_num_error (%d,%d) duplicate %d", x, y, val)); if (mark_errors) GRID(state, flags, x, y) |= F_ERROR; } return ret; } /* Returns: -1 for 'wrong' * 0 for 'incomplete' * 1 for 'complete and correct' */ static int check_complete(digit *grid, game_state *state, int mark_errors) { int x, y, ret = 1, o = state->order; if (mark_errors) assert(grid == state->nums); for (x = 0; x < state->order; x++) { for (y = 0; y < state->order; y++) { if (mark_errors) GRID(state, flags, x, y) &= ~F_ERROR_MASK; if (grid[y*o+x] == 0) { ret = 0; } else { if (!check_num_error(grid, state, x, y, mark_errors)) ret = -1; if (!check_num_adj(grid, state, x, y, mark_errors)) ret = -1; } } } if (ret == 1 && latin_check(grid, o)) ret = -1; return ret; } static char n2c(digit n, int order) { if (n == 0) return ' '; if (order < 10) { if (n < 10) return '0' + n; } else { if (n < 11) return '0' + n-1; n -= 11; if (n <= 26) return 'A' + n; } return '?'; } /* should be 'digit', but includes -1 for 'not a digit'. * Includes keypresses (0 especially) for interpret_move. */ static int c2n(int c, int order) { if (c < 0 || c > 0xff) return -1; if (c == ' ' || c == '\b') return 0; if (order < 10) { if (c >= '0' && c <= '9') return (int)(c - '0'); } else { if (c >= '0' && c <= '9') return (int)(c - '0' + 1); if (c >= 'A' && c <= 'Z') return (int)(c - 'A' + 11); if (c >= 'a' && c <= 'z') return (int)(c - 'a' + 11); } return -1; } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { int x, y, len, n; char *ret, *p; len = (state->order*2) * (state->order*2-1) + 1; ret = snewn(len, char); p = ret; for (y = 0; y < state->order; y++) { for (x = 0; x < state->order; x++) { n = GRID(state, nums, x, y); *p++ = n > 0 ? n2c(n, state->order) : '.'; if (x < (state->order-1)) { if (state->adjacent) { *p++ = (GRID(state, flags, x, y) & F_ADJ_RIGHT) ? '|' : ' '; } else { if (GRID(state, flags, x, y) & F_ADJ_RIGHT) *p++ = '>'; else if (GRID(state, flags, x+1, y) & F_ADJ_LEFT) *p++ = '<'; else *p++ = ' '; } } } *p++ = '\n'; if (y < (state->order-1)) { for (x = 0; x < state->order; x++) { if (state->adjacent) { *p++ = (GRID(state, flags, x, y) & F_ADJ_DOWN) ? '-' : ' '; } else { if (GRID(state, flags, x, y) & F_ADJ_DOWN) *p++ = 'v'; else if (GRID(state, flags, x, y+1) & F_ADJ_UP) *p++ = '^'; else *p++ = ' '; } if (x < state->order-1) *p++ = ' '; } *p++ = '\n'; } } *p++ = '\0'; assert(p - ret == len); return ret; } #ifdef STANDALONE_SOLVER static void game_debug(game_state *state) { char *dbg = game_text_format(state); printf("%s", dbg); sfree(dbg); } #endif /* ---------------------------------------------------------- * Solver. */ struct solver_link { int len, gx, gy, lx, ly; }; struct solver_ctx { game_state *state; int nlinks, alinks; struct solver_link *links; }; static void solver_add_link(struct solver_ctx *ctx, int gx, int gy, int lx, int ly, int len) { if (ctx->alinks < ctx->nlinks+1) { ctx->alinks = ctx->alinks*2 + 1; /*debug(("resizing ctx->links, new size %d", ctx->alinks));*/ ctx->links = sresize(ctx->links, ctx->alinks, struct solver_link); } ctx->links[ctx->nlinks].gx = gx; ctx->links[ctx->nlinks].gy = gy; ctx->links[ctx->nlinks].lx = lx; ctx->links[ctx->nlinks].ly = ly; ctx->links[ctx->nlinks].len = len; ctx->nlinks++; /*debug(("Adding new link: len %d (%d,%d) < (%d,%d), nlinks now %d", len, lx, ly, gx, gy, ctx->nlinks));*/ } static struct solver_ctx *new_ctx(game_state *state) { struct solver_ctx *ctx = snew(struct solver_ctx); int o = state->order; int i, x, y; unsigned int f; ctx->nlinks = ctx->alinks = 0; ctx->links = NULL; ctx->state = state; if (state->adjacent) return ctx; /* adjacent mode doesn't use links. */ for (x = 0; x < o; x++) { for (y = 0; y < o; y++) { f = GRID(state, flags, x, y); for (i = 0; i < 4; i++) { if (f & adjthan[i].f) solver_add_link(ctx, x, y, x+adjthan[i].dx, y+adjthan[i].dy, 1); } } } return ctx; } static void *clone_ctx(void *vctx) { struct solver_ctx *ctx = (struct solver_ctx *)vctx; return new_ctx(ctx->state); } static void free_ctx(void *vctx) { struct solver_ctx *ctx = (struct solver_ctx *)vctx; if (ctx->links) sfree(ctx->links); sfree(ctx); } static void solver_nminmax(struct latin_solver *solver, int x, int y, int *min_r, int *max_r, unsigned char **ns_r) { int o = solver->o, min = o, max = 0, n; unsigned char *ns; assert(x >= 0 && y >= 0 && x < o && y < o); ns = solver->cube + cubepos(x,y,1); if (grid(x,y) > 0) { min = max = grid(x,y)-1; } else { for (n = 0; n < o; n++) { if (ns[n]) { if (n > max) max = n; if (n < min) min = n; } } } if (min_r) *min_r = min; if (max_r) *max_r = max; if (ns_r) *ns_r = ns; } static int solver_links(struct latin_solver *solver, void *vctx) { struct solver_ctx *ctx = (struct solver_ctx *)vctx; int i, j, lmin, gmax, nchanged = 0; unsigned char *gns, *lns; struct solver_link *link; for (i = 0; i < ctx->nlinks; i++) { link = &ctx->links[i]; solver_nminmax(solver, link->gx, link->gy, NULL, &gmax, &gns); solver_nminmax(solver, link->lx, link->ly, &lmin, NULL, &lns); for (j = 0; j < solver->o; j++) { /* For the 'greater' end of the link, discount all numbers * too small to satisfy the inequality. */ if (gns[j]) { if (j < (lmin+link->len)) { #ifdef STANDALONE_SOLVER if (solver_show_working) { printf("%*slink elimination, (%d,%d) > (%d,%d):\n", solver_recurse_depth*4, "", link->gx+1, link->gy+1, link->lx+1, link->ly+1); printf("%*s ruling out %d at (%d,%d)\n", solver_recurse_depth*4, "", j+1, link->gx+1, link->gy+1); } #endif cube(link->gx, link->gy, j+1) = FALSE; nchanged++; } } /* For the 'lesser' end of the link, discount all numbers * too large to satisfy inequality. */ if (lns[j]) { if (j > (gmax-link->len)) { #ifdef STANDALONE_SOLVER if (solver_show_working) { printf("%*slink elimination, (%d,%d) > (%d,%d):\n", solver_recurse_depth*4, "", link->gx+1, link->gy+1, link->lx+1, link->ly+1); printf("%*s ruling out %d at (%d,%d)\n", solver_recurse_depth*4, "", j+1, link->lx+1, link->ly+1); } #endif cube(link->lx, link->ly, j+1) = FALSE; nchanged++; } } } } return nchanged; } static int solver_adjacent(struct latin_solver *solver, void *vctx) { struct solver_ctx *ctx = (struct solver_ctx *)vctx; int nchanged = 0, x, y, i, n, o = solver->o, nx, ny, gd; /* Update possible values based on known values and adjacency clues. */ for (x = 0; x < o; x++) { for (y = 0; y < o; y++) { if (grid(x, y) == 0) continue; /* We have a definite number here. Make sure that any * adjacent possibles reflect the adjacent/non-adjacent clue. */ for (i = 0; i < 4; i++) { int isadjacent = (GRID(ctx->state, flags, x, y) & adjthan[i].f); nx = x + adjthan[i].dx, ny = y + adjthan[i].dy; if (nx < 0 || ny < 0 || nx >= o || ny >= o) continue; for (n = 0; n < o; n++) { /* Continue past numbers the adjacent square _could_ be, * given the clue we have. */ gd = abs((n+1) - grid(x, y)); if (isadjacent && (gd == 1)) continue; if (!isadjacent && (gd != 1)) continue; if (cube(nx, ny, n+1) == FALSE) continue; /* already discounted this possibility. */ #ifdef STANDALONE_SOLVER if (solver_show_working) { printf("%*sadjacent elimination, (%d,%d):%d %s (%d,%d):\n", solver_recurse_depth*4, "", x+1, y+1, grid(x, y), isadjacent ? "|" : "!|", nx+1, ny+1); printf("%*s ruling out %d at (%d,%d)\n", solver_recurse_depth*4, "", n+1, nx+1, ny+1); } #endif cube(nx, ny, n+1) = FALSE; nchanged++; } } } } return nchanged; } static int solver_adjacent_set(struct latin_solver *solver, void *vctx) { struct solver_ctx *ctx = (struct solver_ctx *)vctx; int x, y, i, n, nn, o = solver->o, nx, ny, gd; int nchanged = 0, *scratch = snewn(o, int); /* Update possible values based on other possible values * of adjacent squares, and adjacency clues. */ for (x = 0; x < o; x++) { for (y = 0; y < o; y++) { for (i = 0; i < 4; i++) { int isadjacent = (GRID(ctx->state, flags, x, y) & adjthan[i].f); nx = x + adjthan[i].dx, ny = y + adjthan[i].dy; if (nx < 0 || ny < 0 || nx >= o || ny >= o) continue; /* We know the current possibles for the square (x,y) * and also the adjacency clue from (x,y) to (nx,ny). * Construct a maximum set of possibles for (nx,ny) * in scratch, based on these constraints... */ memset(scratch, 0, o*sizeof(int)); for (n = 0; n < o; n++) { if (cube(x, y, n+1) == FALSE) continue; for (nn = 0; nn < o; nn++) { if (n == nn) continue; gd = abs(nn - n); if (isadjacent && (gd != 1)) continue; if (!isadjacent && (gd == 1)) continue; scratch[nn] = 1; } } /* ...and remove any possibilities for (nx,ny) that are * currently set but are not indicated in scratch. */ for (n = 0; n < o; n++) { if (scratch[n] == 1) continue; if (cube(nx, ny, n+1) == FALSE) continue; #ifdef STANDALONE_SOLVER if (solver_show_working) { printf("%*sadjacent possible elimination, (%d,%d) %s (%d,%d):\n", solver_recurse_depth*4, "", x+1, y+1, isadjacent ? "|" : "!|", nx+1, ny+1); printf("%*s ruling out %d at (%d,%d)\n", solver_recurse_depth*4, "", n+1, nx+1, ny+1); } #endif cube(nx, ny, n+1) = FALSE; nchanged++; } } } } return nchanged; } static int solver_easy(struct latin_solver *solver, void *vctx) { struct solver_ctx *ctx = (struct solver_ctx *)vctx; if (ctx->state->adjacent) return solver_adjacent(solver, vctx); else return solver_links(solver, vctx); } static int solver_set(struct latin_solver *solver, void *vctx) { struct solver_ctx *ctx = (struct solver_ctx *)vctx; if (ctx->state->adjacent) return solver_adjacent_set(solver, vctx); else return 0; } #define SOLVER(upper,title,func,lower) func, static usersolver_t const unequal_solvers[] = { DIFFLIST(SOLVER) }; static int solver_state(game_state *state, int maxdiff) { struct solver_ctx *ctx = new_ctx(state); struct latin_solver solver; int diff; latin_solver_alloc(&solver, state->nums, state->order); diff = latin_solver_main(&solver, maxdiff, DIFF_LATIN, DIFF_SET, DIFF_EXTREME, DIFF_EXTREME, DIFF_RECURSIVE, unequal_solvers, ctx, clone_ctx, free_ctx); memcpy(state->hints, solver.cube, state->order*state->order*state->order); free_ctx(ctx); latin_solver_free(&solver); if (diff == DIFF_IMPOSSIBLE) return -1; if (diff == DIFF_UNFINISHED) return 0; if (diff == DIFF_AMBIGUOUS) return 2; return 1; } static game_state *solver_hint(const game_state *state, int *diff_r, int mindiff, int maxdiff) { game_state *ret = dup_game(state); int diff, r = 0; for (diff = mindiff; diff <= maxdiff; diff++) { r = solver_state(ret, diff); debug(("solver_state after %s %d", unequal_diffnames[diff], r)); if (r != 0) goto done; } done: if (diff_r) *diff_r = (r > 0) ? diff : -1; return ret; } /* ---------------------------------------------------------- * Game generation. */ static char *latin_desc(digit *sq, size_t order) { int o2 = order*order, i; char *soln = snewn(o2+2, char); soln[0] = 'S'; for (i = 0; i < o2; i++) soln[i+1] = n2c(sq[i], order); soln[o2+1] = '\0'; return soln; } /* returns non-zero if it placed (or could have placed) clue. */ static int gg_place_clue(game_state *state, int ccode, digit *latin, int checkonly) { int loc = ccode / 5, which = ccode % 5; int x = loc % state->order, y = loc / state->order; assert(loc < state->order*state->order); if (which == 4) { /* add number */ if (state->nums[loc] != 0) { #ifdef STANDALONE_SOLVER if (state->nums[loc] != latin[loc]) { printf("inconsistency for (%d,%d): state %d latin %d\n", x+1, y+1, state->nums[loc], latin[loc]); } #endif assert(state->nums[loc] == latin[loc]); return 0; } if (!checkonly) { state->nums[loc] = latin[loc]; } } else { /* add flag */ int lx, ly, lloc; if (state->adjacent) return 0; /* never add flag clues in adjacent mode (they're always all present) */ if (state->flags[loc] & adjthan[which].f) return 0; /* already has flag. */ lx = x + adjthan[which].dx; ly = y + adjthan[which].dy; if (lx < 0 || ly < 0 || lx >= state->order || ly >= state->order) return 0; /* flag compares to off grid */ lloc = loc + adjthan[which].dx + adjthan[which].dy*state->order; if (latin[loc] <= latin[lloc]) return 0; /* flag would be incorrect */ if (!checkonly) { state->flags[loc] |= adjthan[which].f; } } return 1; } /* returns non-zero if it removed (or could have removed) the clue. */ static int gg_remove_clue(game_state *state, int ccode, int checkonly) { int loc = ccode / 5, which = ccode % 5; #ifdef STANDALONE_SOLVER int x = loc % state->order, y = loc / state->order; #endif assert(loc < state->order*state->order); if (which == 4) { /* remove number. */ if (state->nums[loc] == 0) return 0; if (!checkonly) { #ifdef STANDALONE_SOLVER if (solver_show_working) printf("gg_remove_clue: removing %d at (%d,%d)", state->nums[loc], x+1, y+1); #endif state->nums[loc] = 0; } } else { /* remove flag */ if (state->adjacent) return 0; /* never remove clues in adjacent mode. */ if (!(state->flags[loc] & adjthan[which].f)) return 0; if (!checkonly) { #ifdef STANDALONE_SOLVER if (solver_show_working) printf("gg_remove_clue: removing %c at (%d,%d)", adjthan[which].c, x+1, y+1); #endif state->flags[loc] &= ~adjthan[which].f; } } return 1; } static int gg_best_clue(game_state *state, int *scratch, digit *latin) { int ls = state->order * state->order * 5; int maxposs = 0, minclues = 5, best = -1, i, j; int nposs, nclues, loc; #ifdef STANDALONE_SOLVER if (solver_show_working) { game_debug(state); latin_solver_debug(state->hints, state->order); } #endif for (i = ls; i-- > 0 ;) { if (!gg_place_clue(state, scratch[i], latin, 1)) continue; loc = scratch[i] / 5; for (j = nposs = 0; j < state->order; j++) { if (state->hints[loc*state->order + j]) nposs++; } for (j = nclues = 0; j < 4; j++) { if (state->flags[loc] & adjthan[j].f) nclues++; } if ((nposs > maxposs) || (nposs == maxposs && nclues < minclues)) { best = i; maxposs = nposs; minclues = nclues; #ifdef STANDALONE_SOLVER if (solver_show_working) { int x = loc % state->order, y = loc / state->order; printf("gg_best_clue: b%d (%d,%d) new best [%d poss, %d clues].\n", best, x+1, y+1, nposs, nclues); } #endif } } /* if we didn't solve, we must have 1 clue to place! */ assert(best != -1); return best; } #ifdef STANDALONE_SOLVER int maxtries; #define MAXTRIES maxtries #else #define MAXTRIES 50 #endif int gg_solved; static int game_assemble(game_state *new, int *scratch, digit *latin, int difficulty) { game_state *copy = dup_game(new); int best; if (difficulty >= DIFF_RECURSIVE) { /* We mustn't use any solver that might guess answers; * if it guesses wrongly but solves, gg_place_clue will * get mighty confused. We will always trim clues down * (making it more difficult) in game_strip, which doesn't * have this problem. */ difficulty = DIFF_RECURSIVE-1; } #ifdef STANDALONE_SOLVER if (solver_show_working) { game_debug(new); latin_solver_debug(new->hints, new->order); } #endif while(1) { gg_solved++; if (solver_state(copy, difficulty) == 1) break; best = gg_best_clue(copy, scratch, latin); gg_place_clue(new, scratch[best], latin, 0); gg_place_clue(copy, scratch[best], latin, 0); } free_game(copy); #ifdef STANDALONE_SOLVER if (solver_show_working) { char *dbg = game_text_format(new); printf("game_assemble: done, %d solver iterations:\n%s\n", gg_solved, dbg); sfree(dbg); } #endif return 0; } static void game_strip(game_state *new, int *scratch, digit *latin, int difficulty) { int o = new->order, o2 = o*o, lscratch = o2*5, i; game_state *copy = blank_game(new->order, new->adjacent); /* For each symbol (if it exists in new), try and remove it and * solve again; if we couldn't solve without it put it back. */ for (i = 0; i < lscratch; i++) { if (!gg_remove_clue(new, scratch[i], 0)) continue; memcpy(copy->nums, new->nums, o2 * sizeof(digit)); memcpy(copy->flags, new->flags, o2 * sizeof(unsigned int)); gg_solved++; if (solver_state(copy, difficulty) != 1) { /* put clue back, we can't solve without it. */ int ret = gg_place_clue(new, scratch[i], latin, 0); assert(ret == 1); } else { #ifdef STANDALONE_SOLVER if (solver_show_working) printf("game_strip: clue was redundant."); #endif } } free_game(copy); #ifdef STANDALONE_SOLVER if (solver_show_working) { char *dbg = game_text_format(new); debug(("game_strip: done, %d solver iterations.", gg_solved)); debug(("%s", dbg)); sfree(dbg); } #endif } static void add_adjacent_flags(game_state *state, digit *latin) { int x, y, o = state->order; /* All clues in adjacent mode are always present (the only variables are * the numbers). This adds all the flags to state based on the supplied * latin square. */ for (y = 0; y < o; y++) { for (x = 0; x < o; x++) { if (x < (o-1) && (abs(latin[y*o+x] - latin[y*o+x+1]) == 1)) { GRID(state, flags, x, y) |= F_ADJ_RIGHT; GRID(state, flags, x+1, y) |= F_ADJ_LEFT; } if (y < (o-1) && (abs(latin[y*o+x] - latin[(y+1)*o+x]) == 1)) { GRID(state, flags, x, y) |= F_ADJ_DOWN; GRID(state, flags, x, y+1) |= F_ADJ_UP; } } } } static char *new_game_desc(const game_params *params_in, random_state *rs, char **aux, int interactive) { game_params params_copy = *params_in; /* structure copy */ game_params *params = ¶ms_copy; digit *sq = NULL; int i, x, y, retlen, k, nsol; int o2 = params->order * params->order, ntries = 1; int *scratch, lscratch = o2*5; char *ret, buf[80]; game_state *state = blank_game(params->order, params->adjacent); /* Generate a list of 'things to strip' (randomised later) */ scratch = snewn(lscratch, int); /* Put the numbers (4 mod 5) before the inequalities (0-3 mod 5) */ for (i = 0; i < lscratch; i++) scratch[i] = (i%o2)*5 + 4 - (i/o2); generate: #ifdef STANDALONE_SOLVER if (solver_show_working) printf("new_game_desc: generating %s puzzle, ntries so far %d\n", unequal_diffnames[params->diff], ntries); #endif if (sq) sfree(sq); sq = latin_generate(params->order, rs); latin_debug(sq, params->order); /* Separately shuffle the numeric and inequality clues */ shuffle(scratch, lscratch/5, sizeof(int), rs); shuffle(scratch+lscratch/5, 4*lscratch/5, sizeof(int), rs); memset(state->nums, 0, o2 * sizeof(digit)); memset(state->flags, 0, o2 * sizeof(unsigned int)); if (state->adjacent) { /* All adjacency flags are always present. */ add_adjacent_flags(state, sq); } gg_solved = 0; if (game_assemble(state, scratch, sq, params->diff) < 0) goto generate; game_strip(state, scratch, sq, params->diff); if (params->diff > 0) { game_state *copy = dup_game(state); nsol = solver_state(copy, params->diff-1); free_game(copy); if (nsol > 0) { #ifdef STANDALONE_SOLVER if (solver_show_working) printf("game_assemble: puzzle as generated is too easy.\n"); #endif if (ntries < MAXTRIES) { ntries++; goto generate; } #ifdef STANDALONE_SOLVER if (solver_show_working) printf("Unable to generate %s %dx%d after %d attempts.\n", unequal_diffnames[params->diff], params->order, params->order, MAXTRIES); #endif params->diff--; } } #ifdef STANDALONE_SOLVER if (solver_show_working) printf("new_game_desc: generated %s puzzle; %d attempts (%d solver).\n", unequal_diffnames[params->diff], ntries, gg_solved); #endif ret = NULL; retlen = 0; for (y = 0; y < params->order; y++) { for (x = 0; x < params->order; x++) { unsigned int f = GRID(state, flags, x, y); k = sprintf(buf, "%d%s%s%s%s,", GRID(state, nums, x, y), (f & F_ADJ_UP) ? "U" : "", (f & F_ADJ_RIGHT) ? "R" : "", (f & F_ADJ_DOWN) ? "D" : "", (f & F_ADJ_LEFT) ? "L" : ""); ret = sresize(ret, retlen + k + 1, char); strcpy(ret + retlen, buf); retlen += k; } } *aux = latin_desc(sq, params->order); free_game(state); sfree(sq); sfree(scratch); return ret; } static game_state *load_game(const game_params *params, const char *desc, char **why_r) { game_state *state = blank_game(params->order, params->adjacent); const char *p = desc; int i = 0, n, o = params->order, x, y; char *why = NULL; while (*p) { while (*p >= 'a' && *p <= 'z') { i += *p - 'a' + 1; p++; } if (i >= o*o) { why = "Too much data to fill grid"; goto fail; } if (*p < '0' && *p > '9') { why = "Expecting number in game description"; goto fail; } n = atoi(p); if (n < 0 || n > o) { why = "Out-of-range number in game description"; goto fail; } state->nums[i] = (digit)n; while (*p >= '0' && *p <= '9') p++; /* skip number */ if (state->nums[i] != 0) state->flags[i] |= F_IMMUTABLE; /* === number set by game description */ while (*p == 'U' || *p == 'R' || *p == 'D' || *p == 'L') { switch (*p) { case 'U': state->flags[i] |= F_ADJ_UP; break; case 'R': state->flags[i] |= F_ADJ_RIGHT; break; case 'D': state->flags[i] |= F_ADJ_DOWN; break; case 'L': state->flags[i] |= F_ADJ_LEFT; break; default: why = "Expecting flag URDL in game description"; goto fail; } p++; } i++; if (i < o*o && *p != ',') { why = "Missing separator"; goto fail; } if (*p == ',') p++; } if (i < o*o) { why = "Not enough data to fill grid"; goto fail; } i = 0; for (y = 0; y < o; y++) { for (x = 0; x < o; x++) { for (n = 0; n < 4; n++) { if (GRID(state, flags, x, y) & adjthan[n].f) { int nx = x + adjthan[n].dx; int ny = y + adjthan[n].dy; /* a flag must not point us off the grid. */ if (nx < 0 || ny < 0 || nx >= o || ny >= o) { why = "Flags go off grid"; goto fail; } if (params->adjacent) { /* if one cell is adjacent to another, the other must * also be adjacent to the first. */ if (!(GRID(state, flags, nx, ny) & adjthan[n].fo)) { why = "Flags contradicting each other"; goto fail; } } else { /* if one cell is GT another, the other must _not_ also * be GT the first. */ if (GRID(state, flags, nx, ny) & adjthan[n].fo) { why = "Flags contradicting each other"; goto fail; } } } } } } return state; fail: free_game(state); if (why_r) *why_r = why; return NULL; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { game_state *state = load_game(params, desc, NULL); if (!state) { assert("Unable to load ?validated game."); return NULL; } return state; } static char *validate_desc(const game_params *params, const char *desc) { char *why = NULL; game_state *dummy = load_game(params, desc, &why); if (dummy) { free_game(dummy); assert(!why); } else assert(why); return why; } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { game_state *solved; int r; char *ret = NULL; if (aux) return dupstr(aux); solved = dup_game(state); for (r = 0; r < state->order*state->order; r++) { if (!(solved->flags[r] & F_IMMUTABLE)) solved->nums[r] = 0; } r = solver_state(solved, DIFFCOUNT-1); /* always use full solver */ if (r > 0) ret = latin_desc(solved->nums, solved->order); free_game(solved); return ret; } /* ---------------------------------------------------------- * Game UI input processing. */ struct game_ui { int hx, hy; /* as for solo.c, highlight pos */ int hshow, hpencil, hcursor; /* show state, type, and ?cursor. */ }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->hx = ui->hy = 0; ui->hpencil = ui->hshow = ui->hcursor = 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) { /* See solo.c; if we were pencil-mode highlighting and * somehow a square has just been properly filled, cancel * pencil mode. */ if (ui->hshow && ui->hpencil && !ui->hcursor && GRID(newstate, nums, ui->hx, ui->hy) != 0) { ui->hshow = 0; } } struct game_drawstate { int tilesize, order, started, adjacent; digit *nums; /* copy of nums, o^2 */ unsigned char *hints; /* copy of hints, o^3 */ unsigned int *flags; /* o^2 */ int hx, hy, hshow, hpencil; /* as for game_ui. */ int hflash; }; static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int ox, int oy, int button) { int x = FROMCOORD(ox), y = FROMCOORD(oy), n; char buf[80]; button &= ~MOD_MASK; if (x >= 0 && x < ds->order && ((ox - COORD(x)) <= TILE_SIZE) && y >= 0 && y < ds->order && ((oy - COORD(y)) <= TILE_SIZE)) { if (button == LEFT_BUTTON) { /* normal highlighting for non-immutable squares */ if (GRID(state, flags, x, y) & F_IMMUTABLE) ui->hshow = 0; else if (x == ui->hx && y == ui->hy && ui->hshow && ui->hpencil == 0) ui->hshow = 0; else { ui->hx = x; ui->hy = y; ui->hpencil = 0; ui->hshow = 1; } ui->hcursor = 0; return ""; } if (button == RIGHT_BUTTON) { /* pencil highlighting for non-filled squares */ if (GRID(state, nums, x, y) != 0) ui->hshow = 0; else if (x == ui->hx && y == ui->hy && ui->hshow && ui->hpencil) ui->hshow = 0; else { ui->hx = x; ui->hy = y; ui->hpencil = 1; ui->hshow = 1; } ui->hcursor = 0; return ""; } } if (IS_CURSOR_MOVE(button)) { move_cursor(button, &ui->hx, &ui->hy, ds->order, ds->order, 0); ui->hshow = ui->hcursor = 1; return ""; } if (ui->hshow && IS_CURSOR_SELECT(button)) { ui->hpencil = 1 - ui->hpencil; ui->hcursor = 1; return ""; } if (ui->hshow) { debug(("button %d, cbutton %d", button, (int)((char)button))); n = c2n(button, state->order); debug(("n %d, h (%d,%d) p %d flags 0x%x nums %d", n, ui->hx, ui->hy, ui->hpencil, GRID(state, flags, ui->hx, ui->hy), GRID(state, nums, ui->hx, ui->hy))); if (n < 0 || n > ds->order) return NULL; /* out of range */ if (GRID(state, flags, ui->hx, ui->hy) & F_IMMUTABLE) return NULL; /* can't edit immutable square (!) */ if (ui->hpencil && GRID(state, nums, ui->hx, ui->hy) > 0) return NULL; /* can't change hints on filled square (!) */ sprintf(buf, "%c%d,%d,%d", (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n); if (!ui->hcursor) ui->hshow = 0; return dupstr(buf); } if (button == 'H' || button == 'h') return dupstr("H"); if (button == 'M' || button == 'm') return dupstr("M"); return NULL; } static game_state *execute_move(const game_state *state, const char *move) { game_state *ret = NULL; int x, y, n, i, rc; debug(("execute_move: %s", move)); if ((move[0] == 'P' || move[0] == 'R') && sscanf(move+1, "%d,%d,%d", &x, &y, &n) == 3 && x >= 0 && x < state->order && y >= 0 && y < state->order && n >= 0 && n <= state->order) { ret = dup_game(state); if (move[0] == 'P' && n > 0) HINT(ret, x, y, n-1) = !HINT(ret, x, y, n-1); else { GRID(ret, nums, x, y) = n; for (i = 0; i < state->order; i++) HINT(ret, x, y, i) = 0; /* real change to grid; check for completion */ if (!ret->completed && check_complete(ret->nums, ret, 1) > 0) ret->completed = TRUE; } return ret; } else if (move[0] == 'S') { const char *p; ret = dup_game(state); ret->completed = ret->cheated = TRUE; p = move+1; for (i = 0; i < state->order*state->order; i++) { n = c2n((int)*p, state->order); if (!*p || n <= 0 || n > state->order) goto badmove; ret->nums[i] = n; p++; } if (*p) goto badmove; rc = check_complete(ret->nums, ret, 1); assert(rc > 0); return ret; } else if (move[0] == 'M') { ret = dup_game(state); for (x = 0; x < state->order; x++) { for (y = 0; y < state->order; y++) { for (n = 0; n < state->order; n++) { HINT(ret, x, y, n) = 1; } } } return ret; } else if (move[0] == 'H') { return solver_hint(state, NULL, DIFF_EASY, DIFF_EASY); } badmove: if (ret) free_game(ret); return NULL; } /* ---------------------------------------------------------------------- * Drawing/printing routines. */ #define DRAW_SIZE (TILE_SIZE*ds->order + GAP_SIZE*(ds->order-1) + BORDER*2) 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, order; } ads, *ds = &ads; ads.tilesize = tilesize; ads.order = params->order; *x = *y = DRAW_SIZE; } 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); for (i = 0; i < 3; i++) { ret[COL_TEXT * 3 + i] = 0.0F; ret[COL_GRID * 3 + i] = 0.5F; } /* Lots of these were taken from solo.c. */ ret[COL_GUESS * 3 + 0] = 0.0F; ret[COL_GUESS * 3 + 1] = 0.6F * ret[COL_BACKGROUND * 3 + 1]; ret[COL_GUESS * 3 + 2] = 0.0F; 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]; *ncolours = NCOLOURS; return ret; } static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { struct game_drawstate *ds = snew(struct game_drawstate); int o2 = state->order*state->order, o3 = o2*state->order; ds->tilesize = 0; ds->order = state->order; ds->adjacent = state->adjacent; ds->nums = snewn(o2, digit); ds->hints = snewn(o3, unsigned char); ds->flags = snewn(o2, unsigned int); memset(ds->nums, 0, o2*sizeof(digit)); memset(ds->hints, 0, o3); memset(ds->flags, 0, o2*sizeof(unsigned int)); ds->hx = ds->hy = 0; ds->started = ds->hshow = ds->hpencil = ds->hflash = 0; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->nums); sfree(ds->hints); sfree(ds->flags); sfree(ds); } static void draw_gt(drawing *dr, int ox, int oy, int dx1, int dy1, int dx2, int dy2, int col) { int coords[12]; int xdx = (dx1+dx2 ? 0 : 1), xdy = (dx1+dx2 ? 1 : 0); coords[0] = ox + xdx; coords[1] = oy + xdy; coords[2] = ox + xdx + dx1; coords[3] = oy + xdy + dy1; coords[4] = ox + xdx + dx1 + dx2; coords[5] = oy + xdy + dy1 + dy2; coords[6] = ox - xdx + dx1 + dx2; coords[7] = oy - xdy + dy1 + dy2; coords[8] = ox - xdx + dx1; coords[9] = oy - xdy + dy1; coords[10] = ox - xdx; coords[11] = oy - xdy; draw_polygon(dr, coords, 6, col, col); } static void draw_gts(drawing *dr, game_drawstate *ds, int ox, int oy, unsigned int f, int bg, int fg) { int g = GAP_SIZE, g2 = (g+1)/2, g4 = (g+1)/4; /* Draw all the greater-than signs emanating from this tile. */ if (f & F_ADJ_UP) { if (bg >= 0) draw_rect(dr, ox, oy - g, TILE_SIZE, g, bg); draw_gt(dr, ox+g2, oy-g4, g2, -g2, g2, g2, (f & F_ERROR_UP) ? COL_ERROR : fg); draw_update(dr, ox, oy-g, TILE_SIZE, g); } if (f & F_ADJ_RIGHT) { if (bg >= 0) draw_rect(dr, ox + TILE_SIZE, oy, g, TILE_SIZE, bg); draw_gt(dr, ox+TILE_SIZE+g4, oy+g2, g2, g2, -g2, g2, (f & F_ERROR_RIGHT) ? COL_ERROR : fg); draw_update(dr, ox+TILE_SIZE, oy, g, TILE_SIZE); } if (f & F_ADJ_DOWN) { if (bg >= 0) draw_rect(dr, ox, oy + TILE_SIZE, TILE_SIZE, g, bg); draw_gt(dr, ox+g2, oy+TILE_SIZE+g4, g2, g2, g2, -g2, (f & F_ERROR_DOWN) ? COL_ERROR : fg); draw_update(dr, ox, oy+TILE_SIZE, TILE_SIZE, g); } if (f & F_ADJ_LEFT) { if (bg >= 0) draw_rect(dr, ox - g, oy, g, TILE_SIZE, bg); draw_gt(dr, ox-g4, oy+g2, -g2, g2, g2, g2, (f & F_ERROR_LEFT) ? COL_ERROR : fg); draw_update(dr, ox-g, oy, g, TILE_SIZE); } } static void draw_adjs(drawing *dr, game_drawstate *ds, int ox, int oy, unsigned int f, int bg, int fg) { int g = GAP_SIZE, g38 = 3*(g+1)/8, g4 = (g+1)/4; /* Draw all the adjacency bars relevant to this tile; we only have * to worry about F_ADJ_RIGHT and F_ADJ_DOWN. * * If we _only_ have the error flag set (i.e. it's not supposed to be * adjacent, but adjacent numbers were entered) draw an outline red bar. */ if (f & (F_ADJ_RIGHT|F_ERROR_RIGHT)) { if (f & F_ADJ_RIGHT) { draw_rect(dr, ox+TILE_SIZE+g38, oy, g4, TILE_SIZE, (f & F_ERROR_RIGHT) ? COL_ERROR : fg); } else { draw_rect_outline(dr, ox+TILE_SIZE+g38, oy, g4, TILE_SIZE, COL_ERROR); } } else if (bg >= 0) { draw_rect(dr, ox+TILE_SIZE+g38, oy, g4, TILE_SIZE, bg); } draw_update(dr, ox+TILE_SIZE, oy, g, TILE_SIZE); if (f & (F_ADJ_DOWN|F_ERROR_DOWN)) { if (f & F_ADJ_DOWN) { draw_rect(dr, ox, oy+TILE_SIZE+g38, TILE_SIZE, g4, (f & F_ERROR_DOWN) ? COL_ERROR : fg); } else { draw_rect_outline(dr, ox, oy+TILE_SIZE+g38, TILE_SIZE, g4, COL_ERROR); } } else if (bg >= 0) { draw_rect(dr, ox, oy+TILE_SIZE+g38, TILE_SIZE, g4, bg); } draw_update(dr, ox, oy+TILE_SIZE, TILE_SIZE, g); } static void draw_furniture(drawing *dr, game_drawstate *ds, const game_state *state, const game_ui *ui, int x, int y, int hflash) { int ox = COORD(x), oy = COORD(y), bg, hon; unsigned int f = GRID(state, flags, x, y); bg = hflash ? COL_HIGHLIGHT : COL_BACKGROUND; hon = (ui->hshow && x == ui->hx && y == ui->hy); /* Clear square. */ draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE, (hon && !ui->hpencil) ? COL_HIGHLIGHT : bg); /* Draw the highlight (pencil or full), if we're the highlight */ if (hon && ui->hpencil) { int coords[6]; coords[0] = ox; coords[1] = oy; coords[2] = ox + TILE_SIZE/2; coords[3] = oy; coords[4] = ox; coords[5] = oy + TILE_SIZE/2; draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT); } /* Draw the square outline (which is the cursor, if we're the cursor). */ draw_rect_outline(dr, ox, oy, TILE_SIZE, TILE_SIZE, COL_GRID); draw_update(dr, ox, oy, TILE_SIZE, TILE_SIZE); /* Draw the adjacent clue signs. */ if (ds->adjacent) draw_adjs(dr, ds, ox, oy, f, COL_BACKGROUND, COL_GRID); else draw_gts(dr, ds, ox, oy, f, COL_BACKGROUND, COL_TEXT); } static void draw_num(drawing *dr, game_drawstate *ds, int x, int y) { int ox = COORD(x), oy = COORD(y); unsigned int f = GRID(ds,flags,x,y); char str[2]; /* (can assume square has just been cleared) */ /* Draw number, choosing appropriate colour */ str[0] = n2c(GRID(ds, nums, x, y), ds->order); str[1] = '\0'; draw_text(dr, ox + TILE_SIZE/2, oy + TILE_SIZE/2, FONT_VARIABLE, 3*TILE_SIZE/4, ALIGN_VCENTRE | ALIGN_HCENTRE, (f & F_IMMUTABLE) ? COL_TEXT : (f & F_ERROR) ? COL_ERROR : COL_GUESS, str); } static void draw_hints(drawing *dr, game_drawstate *ds, int x, int y) { int ox = COORD(x), oy = COORD(y); int nhints, i, j, hw, hh, hmax, fontsz; char str[2]; /* (can assume square has just been cleared) */ /* Draw hints; steal ingenious algorithm (basically) * from solo.c:draw_number() */ for (i = nhints = 0; i < ds->order; i++) { if (HINT(ds, x, y, i)) nhints++; } for (hw = 1; hw * hw < nhints; hw++); if (hw < 3) hw = 3; hh = (nhints + hw - 1) / hw; if (hh < 2) hh = 2; hmax = max(hw, hh); fontsz = TILE_SIZE/(hmax*(11-hmax)/8); for (i = j = 0; i < ds->order; i++) { if (HINT(ds,x,y,i)) { int hx = j % hw, hy = j / hw; str[0] = n2c(i+1, ds->order); str[1] = '\0'; draw_text(dr, ox + (4*hx+3) * TILE_SIZE / (4*hw+2), oy + (4*hy+3) * TILE_SIZE / (4*hh+2), FONT_VARIABLE, fontsz, ALIGN_VCENTRE | ALIGN_HCENTRE, COL_PENCIL, str); j++; } } } 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 x, y, i, hchanged = 0, stale, hflash = 0; debug(("highlight old (%d,%d), new (%d,%d)", ds->hx, ds->hy, ui->hx, ui->hy)); if (flashtime > 0 && (flashtime <= FLASH_TIME/3 || flashtime >= FLASH_TIME*2/3)) hflash = 1; if (!ds->started) { draw_rect(dr, 0, 0, DRAW_SIZE, DRAW_SIZE, COL_BACKGROUND); draw_update(dr, 0, 0, DRAW_SIZE, DRAW_SIZE); } if (ds->hx != ui->hx || ds->hy != ui->hy || ds->hshow != ui->hshow || ds->hpencil != ui->hpencil) hchanged = 1; for (x = 0; x < ds->order; x++) { for (y = 0; y < ds->order; y++) { if (!ds->started) stale = 1; else if (hflash != ds->hflash) stale = 1; else stale = 0; if (hchanged) { if ((x == ui->hx && y == ui->hy) || (x == ds->hx && y == ds->hy)) stale = 1; } if (GRID(state, nums, x, y) != GRID(ds, nums, x, y)) { GRID(ds, nums, x, y) = GRID(state, nums, x, y); stale = 1; } if (GRID(state, flags, x, y) != GRID(ds, flags, x, y)) { GRID(ds, flags, x, y) = GRID(state, flags, x, y); stale = 1; } if (GRID(ds, nums, x, y) == 0) { /* We're not a number square (therefore we might * display hints); do we need to update? */ for (i = 0; i < ds->order; i++) { if (HINT(state, x, y, i) != HINT(ds, x, y, i)) { HINT(ds, x, y, i) = HINT(state, x, y, i); stale = 1; } } } if (stale) { draw_furniture(dr, ds, state, ui, x, y, hflash); if (GRID(ds, nums, x, y) > 0) draw_num(dr, ds, x, y); else draw_hints(dr, ds, x, y); } } } ds->hx = ui->hx; ds->hy = ui->hy; ds->hshow = ui->hshow; ds->hpencil = ui->hpencil; ds->started = 1; ds->hflash = hflash; } 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 int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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) { int pw, ph; /* 10mm squares by default, roughly the same as Grauniad. */ game_compute_size(params, 1000, &pw, &ph); *x = pw / 100.0F; *y = ph / 100.0F; } static void game_print(drawing *dr, const game_state *state, int tilesize) { int ink = print_mono_colour(dr, 0); int x, y, o = state->order, ox, oy, n; char str[2]; /* Ick: fake up `ds->tilesize' for macro expansion purposes */ game_drawstate ads, *ds = &ads; game_set_size(dr, ds, NULL, tilesize); print_line_width(dr, 2 * TILE_SIZE / 40); /* Squares, numbers, gt signs */ for (y = 0; y < o; y++) { for (x = 0; x < o; x++) { ox = COORD(x); oy = COORD(y); n = GRID(state, nums, x, y); draw_rect_outline(dr, ox, oy, TILE_SIZE, TILE_SIZE, ink); str[0] = n ? n2c(n, state->order) : ' '; str[1] = '\0'; draw_text(dr, ox + TILE_SIZE/2, oy + TILE_SIZE/2, FONT_VARIABLE, TILE_SIZE/2, ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str); if (state->adjacent) draw_adjs(dr, ds, ox, oy, GRID(state, flags, x, y), -1, ink); else draw_gts(dr, ds, ox, oy, GRID(state, flags, x, y), -1, ink); } } } /* ---------------------------------------------------------------------- * Housekeeping. */ #ifdef COMBINED #define thegame unequal #endif const struct game thegame = { "Unequal", "games.unequal", "unequal", default_params, game_fetch_preset, 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, game_changed_state, interpret_move, execute_move, PREFERRED_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */ }; /* ---------------------------------------------------------------------- * Standalone solver. */ #ifdef STANDALONE_SOLVER #include #include const char *quis = NULL; #if 0 /* currently unused */ static void debug_printf(char *fmt, ...) { char buf[4096]; va_list ap; va_start(ap, fmt); vsprintf(buf, fmt, ap); puts(buf); va_end(ap); } static void game_printf(game_state *state) { char *dbg = game_text_format(state); printf("%s", dbg); sfree(dbg); } static void game_printf_wide(game_state *state) { int x, y, i, n; for (y = 0; y < state->order; y++) { for (x = 0; x < state->order; x++) { n = GRID(state, nums, x, y); for (i = 0; i < state->order; i++) { if (n > 0) printf("%c", n2c(n, state->order)); else if (HINT(state, x, y, i)) printf("%c", n2c(i+1, state->order)); else printf("."); } printf(" "); } printf("\n"); } printf("\n"); } #endif static void pdiff(int diff) { if (diff == DIFF_IMPOSSIBLE) printf("Game is impossible.\n"); else if (diff == DIFF_UNFINISHED) printf("Game has incomplete.\n"); else if (diff == DIFF_AMBIGUOUS) printf("Game has multiple solutions.\n"); else printf("Game has difficulty %s.\n", unequal_diffnames[diff]); } static int solve(game_params *p, char *desc, int debug) { game_state *state = new_game(NULL, p, desc); struct solver_ctx *ctx = new_ctx(state); struct latin_solver solver; int diff; solver_show_working = debug; game_debug(state); latin_solver_alloc(&solver, state->nums, state->order); diff = latin_solver_main(&solver, DIFF_RECURSIVE, DIFF_LATIN, DIFF_SET, DIFF_EXTREME, DIFF_EXTREME, DIFF_RECURSIVE, unequal_solvers, ctx, clone_ctx, free_ctx); free_ctx(ctx); latin_solver_free(&solver); if (debug) pdiff(diff); game_debug(state); free_game(state); return diff; } static void check(game_params *p) { char *msg = validate_params(p, 1); if (msg) { fprintf(stderr, "%s: %s", quis, msg); exit(1); } } static int gen(game_params *p, random_state *rs, int debug) { char *desc, *aux; int diff; check(p); solver_show_working = debug; desc = new_game_desc(p, rs, &aux, 0); diff = solve(p, desc, debug); sfree(aux); sfree(desc); return diff; } static void soak(game_params *p, random_state *rs) { time_t tt_start, tt_now, tt_last; char *aux, *desc; game_state *st; int n = 0, neasy = 0, realdiff = p->diff; check(p); solver_show_working = 0; maxtries = 1; tt_start = tt_now = time(NULL); printf("Soak-generating an %s %dx%d grid, difficulty %s.\n", p->adjacent ? "adjacent" : "unequal", p->order, p->order, unequal_diffnames[p->diff]); while (1) { p->diff = realdiff; desc = new_game_desc(p, rs, &aux, 0); st = new_game(NULL, p, desc); solver_state(st, DIFF_RECURSIVE); free_game(st); sfree(aux); sfree(desc); n++; if (realdiff != p->diff) neasy++; tt_last = time(NULL); if (tt_last > tt_now) { tt_now = tt_last; printf("%d total, %3.1f/s; %d/%2.1f%% easy, %3.1f/s good.\n", n, (double)n / ((double)tt_now - tt_start), neasy, (double)neasy*100.0/(double)n, (double)(n - neasy) / ((double)tt_now - tt_start)); } } } static void usage_exit(const char *msg) { if (msg) fprintf(stderr, "%s: %s\n", quis, msg); fprintf(stderr, "Usage: %s [--seed SEED] --soak | [game_id [game_id ...]]\n", quis); exit(1); } int main(int argc, const char *argv[]) { random_state *rs; time_t seed = time(NULL); int do_soak = 0, diff; game_params *p; maxtries = 50; quis = argv[0]; while (--argc > 0) { const char *p = *++argv; if (!strcmp(p, "--soak")) do_soak = 1; else if (!strcmp(p, "--seed")) { if (argc == 0) usage_exit("--seed needs an argument"); seed = (time_t)atoi(*++argv); argc--; } else if (*p == '-') usage_exit("unrecognised option"); else break; } rs = random_new((void*)&seed, sizeof(time_t)); if (do_soak == 1) { if (argc != 1) usage_exit("only one argument for --soak"); p = default_params(); decode_params(p, *argv); soak(p, rs); } else if (argc > 0) { int i; for (i = 0; i < argc; i++) { const char *id = *argv++; char *desc = strchr(id, ':'), *err; p = default_params(); if (desc) { *desc++ = '\0'; decode_params(p, id); err = validate_desc(p, desc); if (err) { fprintf(stderr, "%s: %s\n", quis, err); exit(1); } solve(p, desc, 1); } else { decode_params(p, id); diff = gen(p, rs, 1); } } } else { while(1) { p = default_params(); p->order = random_upto(rs, 7) + 3; p->diff = random_upto(rs, 4); diff = gen(p, rs, 0); pdiff(diff); } } return 0; } #endif /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/unruly.c0000644000175300017530000016224512141556310014137 0ustar simonsimon/* * unruly.c: Implementation for Binary Puzzles. * (C) 2012 Lennard Sprong * Created for Simon Tatham's Portable Puzzle Collection * See LICENCE for licence details * * Objective of the game: Fill the grid with zeros and ones, with the * following rules: * - There can't be a run of three or more equal numbers. * - Each row and column contains an equal amount of zeros and ones. * * This puzzle type is known under several names, including * Tohu-Wa-Vohu, One and Two and Binairo. * * Some variants include an extra constraint, stating that no two rows or two * columns may contain the same exact sequence of zeros and ones. * This rule is rarely used, so it is not enabled in the default presets * (but it can be selected via the Custom configurer). * * More information: * http://www.janko.at/Raetsel/Tohu-Wa-Vohu/index.htm */ /* * Possible future improvements: * * More solver cleverness * * - a counting-based deduction in which you find groups of squares * which must each contain at least one of a given colour, plus * other squares which are already known to be that colour, and see * if you have any squares left over when you've worked out where * they all have to be. This is a generalisation of the current * check_near_complete: where that only covers rows with three * unfilled squares, this would handle more, such as * 0 . . 1 0 1 . . 0 . * in which each of the two-square gaps must contain a 0, and there * are three 0s placed, and that means the rightmost square can't * be a 0. * * - an 'Unreasonable' difficulty level, supporting recursion and * backtracking. */ #include #include #include #include #include #include #include "puzzles.h" #ifdef STANDALONE_SOLVER int solver_verbose = FALSE; #endif enum { COL_BACKGROUND, COL_GRID, COL_EMPTY, /* * When editing this enum, maintain the invariants * COL_n_HIGHLIGHT = COL_n + 1 * COL_n_LOWLIGHT = COL_n + 2 */ COL_0, COL_0_HIGHLIGHT, COL_0_LOWLIGHT, COL_1, COL_1_HIGHLIGHT, COL_1_LOWLIGHT, COL_CURSOR, COL_ERROR, NCOLOURS }; struct game_params { int w2, h2; /* full grid width and height respectively */ int unique; /* should row and column patterns be unique? */ int diff; }; #define DIFFLIST(A) \ A(EASY,Easy, e) \ A(NORMAL,Normal, n) \ #define ENUM(upper,title,lower) DIFF_ ## upper, #define TITLE(upper,title,lower) #title, #define ENCODE(upper,title,lower) #lower #define CONFIG(upper,title,lower) ":" #title enum { DIFFLIST(ENUM) DIFFCOUNT }; static char const *const unruly_diffnames[] = { DIFFLIST(TITLE) }; static char const unruly_diffchars[] = DIFFLIST(ENCODE); #define DIFFCONFIG DIFFLIST(CONFIG) const static struct game_params unruly_presets[] = { { 8, 8, FALSE, DIFF_EASY}, { 8, 8, FALSE, DIFF_NORMAL}, {10, 10, FALSE, DIFF_EASY}, {10, 10, FALSE, DIFF_NORMAL}, {14, 14, FALSE, DIFF_EASY}, {14, 14, FALSE, DIFF_NORMAL} }; #define DEFAULT_PRESET 0 enum { EMPTY, N_ONE, N_ZERO, BOGUS }; #define FE_HOR_ROW_LEFT 0x0001 #define FE_HOR_ROW_MID 0x0003 #define FE_HOR_ROW_RIGHT 0x0002 #define FE_VER_ROW_TOP 0x0004 #define FE_VER_ROW_MID 0x000C #define FE_VER_ROW_BOTTOM 0x0008 #define FE_COUNT 0x0010 #define FE_ROW_MATCH 0x0020 #define FE_COL_MATCH 0x0040 #define FF_ONE 0x0080 #define FF_ZERO 0x0100 #define FF_CURSOR 0x0200 #define FF_FLASH1 0x0400 #define FF_FLASH2 0x0800 #define FF_IMMUTABLE 0x1000 struct game_state { int w2, h2; int unique; char *grid; unsigned char *immutable; int completed, cheated; }; static game_params *default_params(void) { game_params *ret = snew(game_params); *ret = unruly_presets[DEFAULT_PRESET]; /* structure copy */ return ret; } static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char buf[80]; if (i < 0 || i >= lenof(unruly_presets)) return FALSE; ret = snew(game_params); *ret = unruly_presets[i]; /* structure copy */ sprintf(buf, "%dx%d %s", ret->w2, ret->h2, unruly_diffnames[ret->diff]); *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->unique = FALSE; params->w2 = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; if (*p == 'x') { p++; params->h2 = atoi(p); while (*p && isdigit((unsigned char)*p)) p++; } else { params->h2 = params->w2; } if (*p == 'u') { p++; params->unique = TRUE; } if (*p == 'd') { int i; p++; params->diff = DIFFCOUNT + 1; /* ...which is invalid */ if (*p) { for (i = 0; i < DIFFCOUNT; i++) { if (*p == unruly_diffchars[i]) params->diff = i; } p++; } } } static char *encode_params(const game_params *params, int full) { char buf[80]; sprintf(buf, "%dx%d", params->w2, params->h2); if (params->unique) strcat(buf, "u"); if (full) sprintf(buf + strlen(buf), "d%c", unruly_diffchars[params->diff]); return dupstr(buf); } static config_item *game_configure(const game_params *params) { config_item *ret; char buf[80]; ret = snewn(5, config_item); ret[0].name = "Width"; ret[0].type = C_STRING; sprintf(buf, "%d", params->w2); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = "Height"; ret[1].type = C_STRING; sprintf(buf, "%d", params->h2); ret[1].sval = dupstr(buf); ret[1].ival = 0; ret[2].name = "Unique rows and columns"; ret[2].type = C_BOOLEAN; ret[2].ival = params->unique; ret[3].name = "Difficulty"; ret[3].type = C_CHOICES; ret[3].sval = DIFFCONFIG; ret[3].ival = params->diff; ret[4].name = NULL; ret[4].type = C_END; ret[4].sval = NULL; ret[4].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->w2 = atoi(cfg[0].sval); ret->h2 = atoi(cfg[1].sval); ret->unique = cfg[2].ival; ret->diff = cfg[3].ival; return ret; } static char *validate_params(const game_params *params, int full) { if ((params->w2 & 1) || (params->h2 & 1)) return "Width and height must both be even"; if (params->w2 < 6 || params->h2 < 6) return "Width and height must be at least 6"; if (params->unique) { static const long A177790[] = { /* * The nth element of this array gives the number of * distinct possible Unruly rows of length 2n (that is, * containing exactly n 1s and n 0s and not containing * three consecutive elements the same) for as long as * those numbers fit in a 32-bit signed int. * * So in unique-rows mode, if the puzzle width is 2n, then * the height must be at most (this array)[n], and vice * versa. * * This is sequence A177790 in the Online Encyclopedia of * Integer Sequences: http://oeis.org/A177790 */ 1L, 2L, 6L, 14L, 34L, 84L, 208L, 518L, 1296L, 3254L, 8196L, 20700L, 52404L, 132942L, 337878L, 860142L, 2192902L, 5598144L, 14308378L, 36610970L, 93770358L, 240390602L, 616787116L, 1583765724L }; if (params->w2 < 2*lenof(A177790) && params->h2 > A177790[params->w2/2]) { return "Puzzle is too tall for unique-rows mode"; } if (params->h2 < 2*lenof(A177790) && params->w2 > A177790[params->h2/2]) { return "Puzzle is too long for unique-rows mode"; } } if (params->diff >= DIFFCOUNT) return "Unknown difficulty rating"; return NULL; } static char *validate_desc(const game_params *params, const char *desc) { int w2 = params->w2, h2 = params->h2; int s = w2 * h2; const char *p = desc; int pos = 0; while (*p) { if (*p >= 'a' && *p < 'z') pos += 1 + (*p - 'a'); else if (*p >= 'A' && *p < 'Z') pos += 1 + (*p - 'A'); else if (*p == 'Z' || *p == 'z') pos += 25; else return "Description contains invalid characters"; ++p; } if (pos < s+1) return "Description too short"; if (pos > s+1) return "Description too long"; return NULL; } static game_state *blank_state(int w2, int h2, int unique) { game_state *state = snew(game_state); int s = w2 * h2; state->w2 = w2; state->h2 = h2; state->unique = unique; state->grid = snewn(s, char); state->immutable = snewn(s, unsigned char); memset(state->grid, EMPTY, s); memset(state->immutable, FALSE, s); state->completed = state->cheated = FALSE; return state; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { int w2 = params->w2, h2 = params->h2; int s = w2 * h2; game_state *state = blank_state(w2, h2, params->unique); const char *p = desc; int pos = 0; while (*p) { if (*p >= 'a' && *p < 'z') { pos += (*p - 'a'); if (pos < s) { state->grid[pos] = N_ZERO; state->immutable[pos] = TRUE; } pos++; } else if (*p >= 'A' && *p < 'Z') { pos += (*p - 'A'); if (pos < s) { state->grid[pos] = N_ONE; state->immutable[pos] = TRUE; } pos++; } else if (*p == 'Z' || *p == 'z') { pos += 25; } else assert(!"Description contains invalid characters"); ++p; } assert(pos == s+1); return state; } static game_state *dup_game(const game_state *state) { int w2 = state->w2, h2 = state->h2; int s = w2 * h2; game_state *ret = blank_state(w2, h2, state->unique); memcpy(ret->grid, state->grid, s); memcpy(ret->immutable, state->immutable, s); ret->completed = state->completed; ret->cheated = state->cheated; return ret; } static void free_game(game_state *state) { sfree(state->grid); sfree(state->immutable); sfree(state); } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { int w2 = state->w2, h2 = state->h2; int lr = w2*2 + 1; char *ret = snewn(lr * h2 + 1, char); char *p = ret; int x, y; for (y = 0; y < h2; y++) { for (x = 0; x < w2; x++) { /* Place number */ char c = state->grid[y * w2 + x]; *p++ = (c == N_ONE ? '1' : c == N_ZERO ? '0' : '.'); *p++ = ' '; } /* End line */ *p++ = '\n'; } /* End with NUL */ *p++ = '\0'; return ret; } /* ****** * * Solver * * ****** */ struct unruly_scratch { int *ones_rows; int *ones_cols; int *zeros_rows; int *zeros_cols; }; static void unruly_solver_update_remaining(const game_state *state, struct unruly_scratch *scratch) { int w2 = state->w2, h2 = state->h2; int x, y; /* Reset all scratch data */ memset(scratch->ones_rows, 0, h2 * sizeof(int)); memset(scratch->ones_cols, 0, w2 * sizeof(int)); memset(scratch->zeros_rows, 0, h2 * sizeof(int)); memset(scratch->zeros_cols, 0, w2 * sizeof(int)); for (x = 0; x < w2; x++) for (y = 0; y < h2; y++) { if (state->grid[y * w2 + x] == N_ONE) { scratch->ones_rows[y]++; scratch->ones_cols[x]++; } else if (state->grid[y * w2 + x] == N_ZERO) { scratch->zeros_rows[y]++; scratch->zeros_cols[x]++; } } } static struct unruly_scratch *unruly_new_scratch(const game_state *state) { int w2 = state->w2, h2 = state->h2; struct unruly_scratch *ret = snew(struct unruly_scratch); ret->ones_rows = snewn(h2, int); ret->ones_cols = snewn(w2, int); ret->zeros_rows = snewn(h2, int); ret->zeros_cols = snewn(w2, int); unruly_solver_update_remaining(state, ret); return ret; } static void unruly_free_scratch(struct unruly_scratch *scratch) { sfree(scratch->ones_rows); sfree(scratch->ones_cols); sfree(scratch->zeros_rows); sfree(scratch->zeros_cols); sfree(scratch); } static int unruly_solver_check_threes(game_state *state, int *rowcount, int *colcount, int horizontal, char check, char block) { int w2 = state->w2, h2 = state->h2; int dx = horizontal ? 1 : 0, dy = 1 - dx; int sx = dx, sy = dy; int ex = w2 - dx, ey = h2 - dy; int x, y; int ret = 0; /* Check for any three squares which almost form three in a row */ for (y = sy; y < ey; y++) { for (x = sx; x < ex; x++) { int i1 = (y-dy) * w2 + (x-dx); int i2 = y * w2 + x; int i3 = (y+dy) * w2 + (x+dx); if (state->grid[i1] == check && state->grid[i2] == check && state->grid[i3] == EMPTY) { ret++; #ifdef STANDALONE_SOLVER if (solver_verbose) { printf("Solver: %i,%i and %i,%i confirm %c at %i,%i\n", i1 % w2, i1 / w2, i2 % w2, i2 / w2, (block == N_ONE ? '1' : '0'), i3 % w2, i3 / w2); } #endif state->grid[i3] = block; rowcount[i3 / w2]++; colcount[i3 % w2]++; } if (state->grid[i1] == check && state->grid[i2] == EMPTY && state->grid[i3] == check) { ret++; #ifdef STANDALONE_SOLVER if (solver_verbose) { printf("Solver: %i,%i and %i,%i confirm %c at %i,%i\n", i1 % w2, i1 / w2, i3 % w2, i3 / w2, (block == N_ONE ? '1' : '0'), i2 % w2, i2 / w2); } #endif state->grid[i2] = block; rowcount[i2 / w2]++; colcount[i2 % w2]++; } if (state->grid[i1] == EMPTY && state->grid[i2] == check && state->grid[i3] == check) { ret++; #ifdef STANDALONE_SOLVER if (solver_verbose) { printf("Solver: %i,%i and %i,%i confirm %c at %i,%i\n", i2 % w2, i2 / w2, i3 % w2, i3 / w2, (block == N_ONE ? '1' : '0'), i1 % w2, i1 / w2); } #endif state->grid[i1] = block; rowcount[i1 / w2]++; colcount[i1 % w2]++; } } } return ret; } static int unruly_solver_check_all_threes(game_state *state, struct unruly_scratch *scratch) { int ret = 0; ret += unruly_solver_check_threes(state, scratch->zeros_rows, scratch->zeros_cols, TRUE, N_ONE, N_ZERO); ret += unruly_solver_check_threes(state, scratch->ones_rows, scratch->ones_cols, TRUE, N_ZERO, N_ONE); ret += unruly_solver_check_threes(state, scratch->zeros_rows, scratch->zeros_cols, FALSE, N_ONE, N_ZERO); ret += unruly_solver_check_threes(state, scratch->ones_rows, scratch->ones_cols, FALSE, N_ZERO, N_ONE); return ret; } static int unruly_solver_check_uniques(game_state *state, int *rowcount, int horizontal, char check, char block, struct unruly_scratch *scratch) { int w2 = state->w2, h2 = state->h2; int rmult = (horizontal ? w2 : 1); int cmult = (horizontal ? 1 : w2); int nr = (horizontal ? h2 : w2); int nc = (horizontal ? w2 : h2); int max = nc / 2; int r, r2, c; int ret = 0; /* * Find each row that has max entries of type 'check', and see if * all those entries match those in any row with max-1 entries. If * so, set the last non-matching entry of the latter row to ensure * that it's different. */ for (r = 0; r < nr; r++) { if (rowcount[r] != max) continue; for (r2 = 0; r2 < nr; r2++) { int nmatch = 0, nonmatch = -1; if (rowcount[r2] != max-1) continue; for (c = 0; c < nc; c++) { if (state->grid[r*rmult + c*cmult] == check) { if (state->grid[r2*rmult + c*cmult] == check) nmatch++; else nonmatch = c; } } if (nmatch == max-1) { int i1 = r2 * rmult + nonmatch * cmult; assert(nonmatch != -1); if (state->grid[i1] == block) continue; assert(state->grid[i1] == EMPTY); #ifdef STANDALONE_SOLVER if (solver_verbose) { printf("Solver: matching %s %i, %i gives %c at %i,%i\n", horizontal ? "rows" : "cols", r, r2, (block == N_ONE ? '1' : '0'), i1 % w2, i1 / w2); } #endif state->grid[i1] = block; if (block == N_ONE) { scratch->ones_rows[i1 / w2]++; scratch->ones_cols[i1 % w2]++; } else { scratch->zeros_rows[i1 / w2]++; scratch->zeros_cols[i1 % w2]++; } ret++; } } } return ret; } static int unruly_solver_check_all_uniques(game_state *state, struct unruly_scratch *scratch) { int ret = 0; ret += unruly_solver_check_uniques(state, scratch->ones_rows, TRUE, N_ONE, N_ZERO, scratch); ret += unruly_solver_check_uniques(state, scratch->zeros_rows, TRUE, N_ZERO, N_ONE, scratch); ret += unruly_solver_check_uniques(state, scratch->ones_cols, FALSE, N_ONE, N_ZERO, scratch); ret += unruly_solver_check_uniques(state, scratch->zeros_cols, FALSE, N_ZERO, N_ONE, scratch); return ret; } static int unruly_solver_fill_row(game_state *state, int i, int horizontal, int *rowcount, int *colcount, char fill) { int ret = 0; int w2 = state->w2, h2 = state->h2; int j; #ifdef STANDALONE_SOLVER if (solver_verbose) { printf("Solver: Filling %s %i with %c:", (horizontal ? "Row" : "Col"), i, (fill == N_ZERO ? '0' : '1')); } #endif /* Place a number in every empty square in a row/column */ for (j = 0; j < (horizontal ? w2 : h2); j++) { int p = (horizontal ? i * w2 + j : j * w2 + i); if (state->grid[p] == EMPTY) { #ifdef STANDALONE_SOLVER if (solver_verbose) { printf(" (%i,%i)", (horizontal ? j : i), (horizontal ? i : j)); } #endif ret++; state->grid[p] = fill; rowcount[(horizontal ? i : j)]++; colcount[(horizontal ? j : i)]++; } } #ifdef STANDALONE_SOLVER if (solver_verbose) { printf("\n"); } #endif return ret; } static int unruly_solver_check_complete_nums(game_state *state, int *complete, int horizontal, int *rowcount, int *colcount, char fill) { int w2 = state->w2, h2 = state->h2; int count = (horizontal ? h2 : w2); /* number of rows to check */ int target = (horizontal ? w2 : h2) / 2; /* target number of 0s/1s */ int *other = (horizontal ? rowcount : colcount); int ret = 0; int i; /* Check for completed rows/cols for one number, then fill in the rest */ for (i = 0; i < count; i++) { if (complete[i] == target && other[i] < target) { #ifdef STANDALONE_SOLVER if (solver_verbose) { printf("Solver: Row %i satisfied for %c\n", i, (fill != N_ZERO ? '0' : '1')); } #endif ret += unruly_solver_fill_row(state, i, horizontal, rowcount, colcount, fill); } } return ret; } static int unruly_solver_check_all_complete_nums(game_state *state, struct unruly_scratch *scratch) { int ret = 0; ret += unruly_solver_check_complete_nums(state, scratch->ones_rows, TRUE, scratch->zeros_rows, scratch->zeros_cols, N_ZERO); ret += unruly_solver_check_complete_nums(state, scratch->ones_cols, FALSE, scratch->zeros_rows, scratch->zeros_cols, N_ZERO); ret += unruly_solver_check_complete_nums(state, scratch->zeros_rows, TRUE, scratch->ones_rows, scratch->ones_cols, N_ONE); ret += unruly_solver_check_complete_nums(state, scratch->zeros_cols, FALSE, scratch->ones_rows, scratch->ones_cols, N_ONE); return ret; } static int unruly_solver_check_near_complete(game_state *state, int *complete, int horizontal, int *rowcount, int *colcount, char fill) { int w2 = state->w2, h2 = state->h2; int w = w2/2, h = h2/2; int dx = horizontal ? 1 : 0, dy = 1 - dx; int sx = dx, sy = dy; int ex = w2 - dx, ey = h2 - dy; int x, y; int ret = 0; /* * This function checks for a row with one Y remaining, then looks * for positions that could cause the remaining squares in the row * to make 3 X's in a row. Example: * * Consider the following row: * 1 1 0 . . . * If the last 1 was placed in the last square, the remaining * squares would be 0: * 1 1 0 0 0 1 * This violates the 3 in a row rule. We now know that the last 1 * shouldn't be in the last cell. * 1 1 0 . . 0 */ /* Check for any two blank and one filled square */ for (y = sy; y < ey; y++) { /* One type must have 1 remaining, the other at least 2 */ if (horizontal && (complete[y] < w - 1 || rowcount[y] > w - 2)) continue; for (x = sx; x < ex; x++) { int i, i1, i2, i3; if (!horizontal && (complete[x] < h - 1 || colcount[x] > h - 2)) continue; i = (horizontal ? y : x); i1 = (y-dy) * w2 + (x-dx); i2 = y * w2 + x; i3 = (y+dy) * w2 + (x+dx); if (state->grid[i1] == fill && state->grid[i2] == EMPTY && state->grid[i3] == EMPTY) { /* * Temporarily fill the empty spaces with something else. * This avoids raising the counts for the row and column */ state->grid[i2] = BOGUS; state->grid[i3] = BOGUS; #ifdef STANDALONE_SOLVER if (solver_verbose) { printf("Solver: Row %i nearly satisfied for %c\n", i, (fill != N_ZERO ? '0' : '1')); } #endif ret += unruly_solver_fill_row(state, i, horizontal, rowcount, colcount, fill); state->grid[i2] = EMPTY; state->grid[i3] = EMPTY; } else if (state->grid[i1] == EMPTY && state->grid[i2] == fill && state->grid[i3] == EMPTY) { state->grid[i1] = BOGUS; state->grid[i3] = BOGUS; #ifdef STANDALONE_SOLVER if (solver_verbose) { printf("Solver: Row %i nearly satisfied for %c\n", i, (fill != N_ZERO ? '0' : '1')); } #endif ret += unruly_solver_fill_row(state, i, horizontal, rowcount, colcount, fill); state->grid[i1] = EMPTY; state->grid[i3] = EMPTY; } else if (state->grid[i1] == EMPTY && state->grid[i2] == EMPTY && state->grid[i3] == fill) { state->grid[i1] = BOGUS; state->grid[i2] = BOGUS; #ifdef STANDALONE_SOLVER if (solver_verbose) { printf("Solver: Row %i nearly satisfied for %c\n", i, (fill != N_ZERO ? '0' : '1')); } #endif ret += unruly_solver_fill_row(state, i, horizontal, rowcount, colcount, fill); state->grid[i1] = EMPTY; state->grid[i2] = EMPTY; } else if (state->grid[i1] == EMPTY && state->grid[i2] == EMPTY && state->grid[i3] == EMPTY) { state->grid[i1] = BOGUS; state->grid[i2] = BOGUS; state->grid[i3] = BOGUS; #ifdef STANDALONE_SOLVER if (solver_verbose) { printf("Solver: Row %i nearly satisfied for %c\n", i, (fill != N_ZERO ? '0' : '1')); } #endif ret += unruly_solver_fill_row(state, i, horizontal, rowcount, colcount, fill); state->grid[i1] = EMPTY; state->grid[i2] = EMPTY; state->grid[i3] = EMPTY; } } } return ret; } static int unruly_solver_check_all_near_complete(game_state *state, struct unruly_scratch *scratch) { int ret = 0; ret += unruly_solver_check_near_complete(state, scratch->ones_rows, TRUE, scratch->zeros_rows, scratch->zeros_cols, N_ZERO); ret += unruly_solver_check_near_complete(state, scratch->ones_cols, FALSE, scratch->zeros_rows, scratch->zeros_cols, N_ZERO); ret += unruly_solver_check_near_complete(state, scratch->zeros_rows, TRUE, scratch->ones_rows, scratch->ones_cols, N_ONE); ret += unruly_solver_check_near_complete(state, scratch->zeros_cols, FALSE, scratch->ones_rows, scratch->ones_cols, N_ONE); return ret; } static int unruly_validate_rows(const game_state *state, int horizontal, char check, int *errors) { int w2 = state->w2, h2 = state->h2; int dx = horizontal ? 1 : 0, dy = 1 - dx; int sx = dx, sy = dy; int ex = w2 - dx, ey = h2 - dy; int x, y; int ret = 0; int err1 = (horizontal ? FE_HOR_ROW_LEFT : FE_VER_ROW_TOP); int err2 = (horizontal ? FE_HOR_ROW_MID : FE_VER_ROW_MID); int err3 = (horizontal ? FE_HOR_ROW_RIGHT : FE_VER_ROW_BOTTOM); /* Check for any three in a row, and mark errors accordingly (if * required) */ for (y = sy; y < ey; y++) { for (x = sx; x < ex; x++) { int i1 = (y-dy) * w2 + (x-dx); int i2 = y * w2 + x; int i3 = (y+dy) * w2 + (x+dx); if (state->grid[i1] == check && state->grid[i2] == check && state->grid[i3] == check) { ret++; if (errors) { errors[i1] |= err1; errors[i2] |= err2; errors[i3] |= err3; } } } } return ret; } static int unruly_validate_unique(const game_state *state, int horizontal, int *errors) { int w2 = state->w2, h2 = state->h2; int rmult = (horizontal ? w2 : 1); int cmult = (horizontal ? 1 : w2); int nr = (horizontal ? h2 : w2); int nc = (horizontal ? w2 : h2); int err = (horizontal ? FE_ROW_MATCH : FE_COL_MATCH); int r, r2, c; int ret = 0; /* Check for any two full rows matching exactly, and mark errors * accordingly (if required) */ for (r = 0; r < nr; r++) { int nfull = 0; for (c = 0; c < nc; c++) if (state->grid[r*rmult + c*cmult] != EMPTY) nfull++; if (nfull != nr) continue; for (r2 = r+1; r2 < nr; r2++) { int match = TRUE; for (c = 0; c < nc; c++) if (state->grid[r*rmult + c*cmult] != state->grid[r2*rmult + c*cmult]) match = FALSE; if (match) { if (errors) { for (c = 0; c < nc; c++) { errors[r*rmult + c*cmult] |= err; errors[r2*rmult + c*cmult] |= err; } } ret++; } } } return ret; } static int unruly_validate_all_rows(const game_state *state, int *errors) { int errcount = 0; errcount += unruly_validate_rows(state, TRUE, N_ONE, errors); errcount += unruly_validate_rows(state, FALSE, N_ONE, errors); errcount += unruly_validate_rows(state, TRUE, N_ZERO, errors); errcount += unruly_validate_rows(state, FALSE, N_ZERO, errors); if (state->unique) { errcount += unruly_validate_unique(state, TRUE, errors); errcount += unruly_validate_unique(state, FALSE, errors); } if (errcount) return -1; return 0; } static int unruly_validate_counts(const game_state *state, struct unruly_scratch *scratch, int *errors) { int w2 = state->w2, h2 = state->h2; int w = w2/2, h = h2/2; char below = FALSE; char above = FALSE; int i; /* See if all rows/columns are satisfied. If one is exceeded, * mark it as an error (if required) */ char hasscratch = TRUE; if (!scratch) { scratch = unruly_new_scratch(state); hasscratch = FALSE; } for (i = 0; i < w2; i++) { if (scratch->ones_cols[i] < h) below = TRUE; if (scratch->zeros_cols[i] < h) below = TRUE; if (scratch->ones_cols[i] > h) { above = TRUE; if (errors) errors[2*h2 + i] = TRUE; } else if (errors) errors[2*h2 + i] = FALSE; if (scratch->zeros_cols[i] > h) { above = TRUE; if (errors) errors[2*h2 + w2 + i] = TRUE; } else if (errors) errors[2*h2 + w2 + i] = FALSE; } for (i = 0; i < h2; i++) { if (scratch->ones_rows[i] < w) below = TRUE; if (scratch->zeros_rows[i] < w) below = TRUE; if (scratch->ones_rows[i] > w) { above = TRUE; if (errors) errors[i] = TRUE; } else if (errors) errors[i] = FALSE; if (scratch->zeros_rows[i] > w) { above = TRUE; if (errors) errors[h2 + i] = TRUE; } else if (errors) errors[h2 + i] = FALSE; } if (!hasscratch) unruly_free_scratch(scratch); return (above ? -1 : below ? 1 : 0); } static int unruly_solve_game(game_state *state, struct unruly_scratch *scratch, int diff) { int done, maxdiff = -1; while (TRUE) { done = 0; /* Check for impending 3's */ done += unruly_solver_check_all_threes(state, scratch); /* Keep using the simpler techniques while they produce results */ if (done) { if (maxdiff < DIFF_EASY) maxdiff = DIFF_EASY; continue; } /* Check for completed rows */ done += unruly_solver_check_all_complete_nums(state, scratch); if (done) { if (maxdiff < DIFF_EASY) maxdiff = DIFF_EASY; continue; } /* Check for impending failures of row/column uniqueness, if * it's enabled in this game mode */ if (state->unique) { done += unruly_solver_check_all_uniques(state, scratch); if (done) { if (maxdiff < DIFF_EASY) maxdiff = DIFF_EASY; continue; } } /* Normal techniques */ if (diff < DIFF_NORMAL) break; /* Check for nearly completed rows */ done += unruly_solver_check_all_near_complete(state, scratch); if (done) { if (maxdiff < DIFF_NORMAL) maxdiff = DIFF_NORMAL; continue; } break; } return maxdiff; } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { game_state *solved = dup_game(state); struct unruly_scratch *scratch = unruly_new_scratch(solved); char *ret = NULL; int result; unruly_solve_game(solved, scratch, DIFFCOUNT); result = unruly_validate_counts(solved, scratch, NULL); if (unruly_validate_all_rows(solved, NULL) == -1) result = -1; if (result == 0) { int w2 = solved->w2, h2 = solved->h2; int s = w2 * h2; char *p; int i; ret = snewn(s + 2, char); p = ret; *p++ = 'S'; for (i = 0; i < s; i++) *p++ = (solved->grid[i] == N_ONE ? '1' : '0'); *p++ = '\0'; } else if (result == 1) *error = "No solution found."; else if (result == -1) *error = "Puzzle is invalid."; free_game(solved); unruly_free_scratch(scratch); return ret; } /* ********* * * Generator * * ********* */ static int unruly_fill_game(game_state *state, struct unruly_scratch *scratch, random_state *rs) { int w2 = state->w2, h2 = state->h2; int s = w2 * h2; int i, j; int *spaces; #ifdef STANDALONE_SOLVER if (solver_verbose) { printf("Generator: Attempt to fill grid\n"); } #endif /* Generate random array of spaces */ spaces = snewn(s, int); for (i = 0; i < s; i++) spaces[i] = i; shuffle(spaces, s, sizeof(*spaces), rs); /* * Construct a valid filled grid by repeatedly picking an unfilled * space and fill it, then calling the solver to fill in any * spaces forced by the change. */ for (j = 0; j < s; j++) { i = spaces[j]; if (state->grid[i] != EMPTY) continue; if (random_upto(rs, 2)) { state->grid[i] = N_ONE; scratch->ones_rows[i / w2]++; scratch->ones_cols[i % w2]++; } else { state->grid[i] = N_ZERO; scratch->zeros_rows[i / w2]++; scratch->zeros_cols[i % w2]++; } unruly_solve_game(state, scratch, DIFFCOUNT); } sfree(spaces); if (unruly_validate_all_rows(state, NULL) != 0 || unruly_validate_counts(state, scratch, NULL) != 0) return FALSE; return TRUE; } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { #ifdef STANDALONE_SOLVER char *debug; int temp_verbose = FALSE; #endif int w2 = params->w2, h2 = params->h2; int s = w2 * h2; int *spaces; int i, j, run; char *ret, *p; game_state *state; struct unruly_scratch *scratch; int attempts = 0; while (1) { while (TRUE) { attempts++; state = blank_state(w2, h2, params->unique); scratch = unruly_new_scratch(state); if (unruly_fill_game(state, scratch, rs)) break; free_game(state); unruly_free_scratch(scratch); } #ifdef STANDALONE_SOLVER if (solver_verbose) { printf("Puzzle generated in %i attempts\n", attempts); debug = game_text_format(state); fputs(debug, stdout); sfree(debug); temp_verbose = solver_verbose; solver_verbose = FALSE; } #endif unruly_free_scratch(scratch); /* Generate random array of spaces */ spaces = snewn(s, int); for (i = 0; i < s; i++) spaces[i] = i; shuffle(spaces, s, sizeof(*spaces), rs); /* * Winnow the clues by starting from our filled grid, repeatedly * picking a filled space and emptying it, as long as the solver * reports that the puzzle can still be solved after doing so. */ for (j = 0; j < s; j++) { char c; game_state *solver; i = spaces[j]; c = state->grid[i]; state->grid[i] = EMPTY; solver = dup_game(state); scratch = unruly_new_scratch(state); unruly_solve_game(solver, scratch, params->diff); if (unruly_validate_counts(solver, scratch, NULL) != 0) state->grid[i] = c; free_game(solver); unruly_free_scratch(scratch); } sfree(spaces); #ifdef STANDALONE_SOLVER if (temp_verbose) { solver_verbose = TRUE; printf("Final puzzle:\n"); debug = game_text_format(state); fputs(debug, stdout); sfree(debug); } #endif /* * See if the game has accidentally come out too easy. */ if (params->diff > 0) { int ok; game_state *solver; solver = dup_game(state); scratch = unruly_new_scratch(state); unruly_solve_game(solver, scratch, params->diff - 1); ok = unruly_validate_counts(solver, scratch, NULL); free_game(solver); unruly_free_scratch(scratch); if (ok) break; } else { /* * Puzzles of the easiest difficulty can't be too easy. */ break; } } /* Encode description */ ret = snewn(s + 1, char); p = ret; run = 0; for (i = 0; i < s+1; i++) { if (i == s || state->grid[i] == N_ZERO) { while (run > 24) { *p++ = 'z'; run -= 25; } *p++ = 'a' + run; run = 0; } else if (state->grid[i] == N_ONE) { while (run > 24) { *p++ = 'Z'; run -= 25; } *p++ = 'A' + run; run = 0; } else { run++; } } *p = '\0'; free_game(state); return ret; } /* ************** * * User Interface * * ************** */ struct game_ui { int cx, cy; char cursor; }; static game_ui *new_ui(const game_state *state) { game_ui *ret = snew(game_ui); ret->cx = ret->cy = 0; ret->cursor = FALSE; return ret; } 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) { } struct game_drawstate { int tilesize; int w2, h2; int started; int *gridfs; int *rowfs; int *grid; }; static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { struct game_drawstate *ds = snew(struct game_drawstate); int w2 = state->w2, h2 = state->h2; int s = w2 * h2; int i; ds->tilesize = 0; ds->w2 = w2; ds->h2 = h2; ds->started = FALSE; ds->gridfs = snewn(s, int); ds->rowfs = snewn(2 * (w2 + h2), int); ds->grid = snewn(s, int); for (i = 0; i < s; i++) ds->grid[i] = -1; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->gridfs); sfree(ds->rowfs); sfree(ds->grid); sfree(ds); } #define COORD(x) ( (x) * ds->tilesize + ds->tilesize/2 ) #define FROMCOORD(x) ( ((x)-(ds->tilesize/2)) / ds->tilesize ) static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int ox, int oy, int button) { int hx = ui->cx; int hy = ui->cy; int gx = FROMCOORD(ox); int gy = FROMCOORD(oy); int w2 = state->w2, h2 = state->h2; button &= ~MOD_MASK; /* Mouse click */ if (button == LEFT_BUTTON || button == RIGHT_BUTTON || button == MIDDLE_BUTTON) { if (ox >= (ds->tilesize / 2) && gx < w2 && oy >= (ds->tilesize / 2) && gy < h2) { hx = gx; hy = gy; ui->cursor = FALSE; } else return NULL; } /* Keyboard move */ if (IS_CURSOR_MOVE(button)) { move_cursor(button, &ui->cx, &ui->cy, w2, h2, 0); ui->cursor = TRUE; return ""; } /* Place one */ if ((ui->cursor && (button == CURSOR_SELECT || button == CURSOR_SELECT2 || button == '\b' || button == '0' || button == '1' || button == '2')) || button == LEFT_BUTTON || button == RIGHT_BUTTON || button == MIDDLE_BUTTON) { char buf[80]; char c, i; if (state->immutable[hy * w2 + hx]) return NULL; c = '-'; i = state->grid[hy * w2 + hx]; if (button == '0' || button == '2') c = '0'; else if (button == '1') c = '1'; else if (button == MIDDLE_BUTTON) c = '-'; /* Cycle through options */ else if (button == CURSOR_SELECT2 || button == RIGHT_BUTTON) c = (i == EMPTY ? '0' : i == N_ZERO ? '1' : '-'); else if (button == CURSOR_SELECT || button == LEFT_BUTTON) c = (i == EMPTY ? '1' : i == N_ONE ? '0' : '-'); if (state->grid[hy * w2 + hx] == (c == '0' ? N_ZERO : c == '1' ? N_ONE : EMPTY)) return NULL; /* don't put no-ops on the undo chain */ sprintf(buf, "P%c,%d,%d", c, hx, hy); return dupstr(buf); } return NULL; } static game_state *execute_move(const game_state *state, const char *move) { int w2 = state->w2, h2 = state->h2; int s = w2 * h2; int x, y, i; char c; game_state *ret; if (move[0] == 'S') { const char *p; ret = dup_game(state); p = move + 1; for (i = 0; i < s; i++) { if (!*p || !(*p == '1' || *p == '0')) { free_game(ret); return NULL; } ret->grid[i] = (*p == '1' ? N_ONE : N_ZERO); p++; } ret->completed = ret->cheated = TRUE; return ret; } else if (move[0] == 'P' && sscanf(move + 1, "%c,%d,%d", &c, &x, &y) == 3 && x >= 0 && x < w2 && y >= 0 && y < h2 && (c == '-' || c == '0' || c == '1')) { ret = dup_game(state); i = y * w2 + x; if (state->immutable[i]) { free_game(ret); return NULL; } ret->grid[i] = (c == '1' ? N_ONE : c == '0' ? N_ZERO : EMPTY); if (!ret->completed && unruly_validate_counts(ret, NULL, NULL) == 0 && (unruly_validate_all_rows(ret, NULL) == 0)) ret->completed = TRUE; return ret; } return NULL; } /* ---------------------------------------------------------------------- * Drawing routines. */ static void game_compute_size(const game_params *params, int tilesize, int *x, int *y) { *x = tilesize * (params->w2 + 1); *y = tilesize * (params->h2 + 1); } 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; frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); for (i = 0; i < 3; i++) { ret[COL_1 * 3 + i] = 0.2F; ret[COL_1_HIGHLIGHT * 3 + i] = 0.4F; ret[COL_1_LOWLIGHT * 3 + i] = 0.0F; ret[COL_0 * 3 + i] = 0.95F; ret[COL_0_HIGHLIGHT * 3 + i] = 1.0F; ret[COL_0_LOWLIGHT * 3 + i] = 0.9F; ret[COL_EMPTY * 3 + i] = 0.5F; ret[COL_GRID * 3 + i] = 0.3F; } game_mkhighlight_specific(fe, ret, COL_0, COL_0_HIGHLIGHT, COL_0_LOWLIGHT); game_mkhighlight_specific(fe, ret, COL_1, COL_1_HIGHLIGHT, COL_1_LOWLIGHT); ret[COL_ERROR * 3 + 0] = 1.0F; ret[COL_ERROR * 3 + 1] = 0.0F; ret[COL_ERROR * 3 + 2] = 0.0F; ret[COL_CURSOR * 3 + 0] = 0.0F; ret[COL_CURSOR * 3 + 1] = 0.7F; ret[COL_CURSOR * 3 + 2] = 0.0F; *ncolours = NCOLOURS; return ret; } static void unruly_draw_err_rectangle(drawing *dr, int x, int y, int w, int h, int tilesize) { double thick = tilesize / 10; double margin = tilesize / 20; draw_rect(dr, x+margin, y+margin, w-2*margin, thick, COL_ERROR); draw_rect(dr, x+margin, y+margin, thick, h-2*margin, COL_ERROR); draw_rect(dr, x+margin, y+h-margin-thick, w-2*margin, thick, COL_ERROR); draw_rect(dr, x+w-margin-thick, y+margin, thick, h-2*margin, COL_ERROR); } static void unruly_draw_tile(drawing *dr, int x, int y, int tilesize, int tile) { clip(dr, x, y, tilesize, tilesize); /* Draw the grid edge first, so the tile can overwrite it */ draw_rect(dr, x, y, tilesize, tilesize, COL_GRID); /* Background of the tile */ { int val = (tile & FF_ZERO ? 0 : tile & FF_ONE ? 2 : 1); val = (val == 0 ? COL_0 : val == 2 ? COL_1 : COL_EMPTY); if ((tile & (FF_FLASH1 | FF_FLASH2)) && (val == COL_0 || val == COL_1)) { val += (tile & FF_FLASH1 ? 1 : 2); } draw_rect(dr, x, y, tilesize-1, tilesize-1, val); if ((val == COL_0 || val == COL_1) && (tile & FF_IMMUTABLE)) { draw_rect(dr, x + tilesize/6, y + tilesize/6, tilesize - 2*(tilesize/6) - 2, 1, val + 2); draw_rect(dr, x + tilesize/6, y + tilesize/6, 1, tilesize - 2*(tilesize/6) - 2, val + 2); draw_rect(dr, x + tilesize/6 + 1, y + tilesize - tilesize/6 - 2, tilesize - 2*(tilesize/6) - 2, 1, val + 1); draw_rect(dr, x + tilesize - tilesize/6 - 2, y + tilesize/6 + 1, 1, tilesize - 2*(tilesize/6) - 2, val + 1); } } /* 3-in-a-row errors */ if (tile & (FE_HOR_ROW_LEFT | FE_HOR_ROW_RIGHT)) { int left = x, right = x + tilesize - 1; if ((tile & FE_HOR_ROW_LEFT)) right += tilesize/2; if ((tile & FE_HOR_ROW_RIGHT)) left -= tilesize/2; unruly_draw_err_rectangle(dr, left, y, right-left, tilesize-1, tilesize); } if (tile & (FE_VER_ROW_TOP | FE_VER_ROW_BOTTOM)) { int top = y, bottom = y + tilesize - 1; if ((tile & FE_VER_ROW_TOP)) bottom += tilesize/2; if ((tile & FE_VER_ROW_BOTTOM)) top -= tilesize/2; unruly_draw_err_rectangle(dr, x, top, tilesize-1, bottom-top, tilesize); } /* Count errors */ if (tile & FE_COUNT) { draw_text(dr, x + tilesize/2, y + tilesize/2, FONT_VARIABLE, tilesize/2, ALIGN_HCENTRE | ALIGN_VCENTRE, COL_ERROR, "!"); } /* Row-match errors */ if (tile & FE_ROW_MATCH) { draw_rect(dr, x, y+tilesize/2-tilesize/12, tilesize, 2*(tilesize/12), COL_ERROR); } if (tile & FE_COL_MATCH) { draw_rect(dr, x+tilesize/2-tilesize/12, y, 2*(tilesize/12), tilesize, COL_ERROR); } /* Cursor rectangle */ if (tile & FF_CURSOR) { draw_rect(dr, x, y, tilesize/12, tilesize-1, COL_CURSOR); draw_rect(dr, x, y, tilesize-1, tilesize/12, COL_CURSOR); draw_rect(dr, x+tilesize-1-tilesize/12, y, tilesize/12, tilesize-1, COL_CURSOR); draw_rect(dr, x, y+tilesize-1-tilesize/12, tilesize-1, tilesize/12, COL_CURSOR); } unclip(dr); draw_update(dr, x, y, tilesize, tilesize); } #define TILE_SIZE (ds->tilesize) #define DEFAULT_TILE_SIZE 32 #define FLASH_FRAME 0.12F #define FLASH_TIME (FLASH_FRAME * 3) 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 w2 = state->w2, h2 = state->h2; int s = w2 * h2; int flash; int x, y, i; if (!ds->started) { /* Main window background */ draw_rect(dr, 0, 0, TILE_SIZE * (w2+1), TILE_SIZE * (h2+1), COL_BACKGROUND); /* Outer edge of grid */ draw_rect(dr, COORD(0)-TILE_SIZE/10, COORD(0)-TILE_SIZE/10, TILE_SIZE*w2 + 2*(TILE_SIZE/10) - 1, TILE_SIZE*h2 + 2*(TILE_SIZE/10) - 1, COL_GRID); draw_update(dr, 0, 0, TILE_SIZE * (w2+1), TILE_SIZE * (h2+1)); ds->started = TRUE; } flash = 0; if (flashtime > 0) flash = (int)(flashtime / FLASH_FRAME) == 1 ? FF_FLASH2 : FF_FLASH1; for (i = 0; i < s; i++) ds->gridfs[i] = 0; unruly_validate_all_rows(state, ds->gridfs); for (i = 0; i < 2 * (h2 + w2); i++) ds->rowfs[i] = 0; unruly_validate_counts(state, NULL, ds->rowfs); for (y = 0; y < h2; y++) { for (x = 0; x < w2; x++) { int tile; i = y * w2 + x; tile = ds->gridfs[i]; if (state->grid[i] == N_ONE) { tile |= FF_ONE; if (ds->rowfs[y] || ds->rowfs[2*h2 + x]) tile |= FE_COUNT; } else if (state->grid[i] == N_ZERO) { tile |= FF_ZERO; if (ds->rowfs[h2 + y] || ds->rowfs[2*h2 + w2 + x]) tile |= FE_COUNT; } tile |= flash; if (state->immutable[i]) tile |= FF_IMMUTABLE; if (ui->cursor && ui->cx == x && ui->cy == y) tile |= FF_CURSOR; if (ds->grid[i] != tile) { ds->grid[i] = tile; unruly_draw_tile(dr, COORD(x), COORD(y), TILE_SIZE, tile); } } } } 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 int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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) { int pw, ph; /* Using 7mm squares */ game_compute_size(params, 700, &pw, &ph); *x = pw / 100.0F; *y = ph / 100.0F; } static void game_print(drawing *dr, const game_state *state, int tilesize) { int w2 = state->w2, h2 = state->h2; int x, y; int ink = print_mono_colour(dr, 0); for (y = 0; y < h2; y++) for (x = 0; x < w2; x++) { int tx = x * tilesize + (tilesize / 2); int ty = y * tilesize + (tilesize / 2); /* Draw the border */ int coords[8]; coords[0] = tx; coords[1] = ty - 1; coords[2] = tx + tilesize; coords[3] = ty - 1; coords[4] = tx + tilesize; coords[5] = ty + tilesize - 1; coords[6] = tx; coords[7] = ty + tilesize - 1; draw_polygon(dr, coords, 4, -1, ink); if (state->grid[y * w2 + x] == N_ONE) draw_rect(dr, tx, ty, tilesize, tilesize, ink); else if (state->grid[y * w2 + x] == N_ZERO) draw_circle(dr, tx + tilesize/2, ty + tilesize/2, tilesize/12, ink, ink); } } #ifdef COMBINED #define thegame unruly #endif const struct game thegame = { "Unruly", "games.unruly", "unruly", default_params, game_fetch_preset, 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, game_changed_state, interpret_move, execute_move, DEFAULT_TILE_SIZE, game_compute_size, game_set_size, game_colours, game_new_drawstate, game_free_drawstate, game_redraw, game_anim_length, game_flash_length, game_status, TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, 0, /* flags */ }; /* ***************** * * Standalone solver * * ***************** */ #ifdef STANDALONE_SOLVER #include #include /* Most of the standalone solver code was copied from unequal.c and singles.c */ const char *quis; static void usage_exit(const char *msg) { if (msg) fprintf(stderr, "%s: %s\n", quis, msg); fprintf(stderr, "Usage: %s [-v] [--seed SEED] | [game_id [game_id ...]]\n", quis); exit(1); } int main(int argc, char *argv[]) { random_state *rs; time_t seed = time(NULL); game_params *params = NULL; char *id = NULL, *desc = NULL, *err; quis = argv[0]; while (--argc > 0) { char *p = *++argv; if (!strcmp(p, "--seed")) { if (argc == 0) usage_exit("--seed needs an argument"); seed = (time_t) atoi(*++argv); argc--; } else if (!strcmp(p, "-v")) solver_verbose = TRUE; else if (*p == '-') usage_exit("unrecognised option"); else id = p; } if (id) { desc = strchr(id, ':'); if (desc) *desc++ = '\0'; params = default_params(); decode_params(params, id); err = validate_params(params, TRUE); if (err) { fprintf(stderr, "Parameters are invalid\n"); fprintf(stderr, "%s: %s", argv[0], err); exit(1); } } if (!desc) { char *desc_gen, *aux; rs = random_new((void *) &seed, sizeof(time_t)); if (!params) params = default_params(); printf("Generating puzzle with parameters %s\n", encode_params(params, TRUE)); desc_gen = new_game_desc(params, rs, &aux, FALSE); if (!solver_verbose) { char *fmt = game_text_format(new_game(NULL, params, desc_gen)); fputs(fmt, stdout); sfree(fmt); } printf("Game ID: %s\n", desc_gen); } else { game_state *input; struct unruly_scratch *scratch; int maxdiff, errcode; err = validate_desc(params, desc); if (err) { fprintf(stderr, "Description is invalid\n"); fprintf(stderr, "%s", err); exit(1); } input = new_game(NULL, params, desc); scratch = unruly_new_scratch(input); maxdiff = unruly_solve_game(input, scratch, DIFFCOUNT); errcode = unruly_validate_counts(input, scratch, NULL); if (unruly_validate_all_rows(input, NULL) == -1) errcode = -1; if (errcode != -1) { char *fmt = game_text_format(input); fputs(fmt, stdout); sfree(fmt); if (maxdiff < 0) printf("Difficulty: already solved!\n"); else printf("Difficulty: %s\n", unruly_diffnames[maxdiff]); } if (errcode == 1) printf("No solution found.\n"); else if (errcode == -1) printf("Puzzle is invalid.\n"); free_game(input); unruly_free_scratch(scratch); } return 0; } #endif puzzles-r9872/untangle.c0000644000175300017530000010771712132232554014422 0ustar simonsimon/* * untangle.c: Game about planar graphs. You are given a graph * represented by points and straight lines, with some lines * crossing; your task is to drag the points into a configuration * where none of the lines cross. * * Cloned from a Flash game called `Planarity', by John Tantalo. * at the time of writing * this. The Flash game had a fixed set of levels; my added value, * as usual, is automatic generation of random games to order. */ /* * TODO: * * - This puzzle, perhaps uniquely among the collection, could use * support for non-aspect-ratio-preserving resizes. This would * require some sort of fairly large redesign, unfortunately (since * it would invalidate the basic assumption that puzzles' size * requirements are adequately expressed by a single scalar tile * size), and probably complicate the rest of the puzzles' API as a * result. So I'm not sure I really want to do it. * * - It would be nice if we could somehow auto-detect a real `long * long' type on the host platform and use it in place of my * hand-hacked int64s. It'd be faster and more reliable. */ #include #include #include #include #include #include #include "puzzles.h" #include "tree234.h" #define CIRCLE_RADIUS 6 #define DRAG_THRESHOLD (CIRCLE_RADIUS * 2) #define PREFERRED_TILESIZE 64 #define FLASH_TIME 0.30F #define ANIM_TIME 0.13F #define SOLVEANIM_TIME 0.50F enum { COL_SYSBACKGROUND, COL_BACKGROUND, COL_LINE, #ifdef SHOW_CROSSINGS COL_CROSSEDLINE, #endif COL_OUTLINE, COL_POINT, COL_DRAGPOINT, COL_NEIGHBOUR, COL_FLASH1, COL_FLASH2, NCOLOURS }; typedef struct point { /* * Points are stored using rational coordinates, with the same * denominator for both coordinates. */ long x, y, d; } point; typedef struct edge { /* * This structure is implicitly associated with a particular * point set, so all it has to do is to store two point * indices. It is required to store them in the order (lower, * higher), i.e. a < b always. */ int a, b; } edge; struct game_params { int n; /* number of points */ }; struct graph { int refcount; /* for deallocation */ tree234 *edges; /* stores `edge' structures */ }; struct game_state { game_params params; int w, h; /* extent of coordinate system only */ point *pts; #ifdef SHOW_CROSSINGS int *crosses; /* mark edges which are crossed */ #endif struct graph *graph; int completed, cheated, just_solved; }; static int edgecmpC(const void *av, const void *bv) { const edge *a = (const edge *)av; const edge *b = (const edge *)bv; if (a->a < b->a) return -1; else if (a->a > b->a) return +1; else if (a->b < b->b) return -1; else if (a->b > b->b) return +1; return 0; } static int edgecmp(void *av, void *bv) { return edgecmpC(av, bv); } static game_params *default_params(void) { game_params *ret = snew(game_params); ret->n = 10; return ret; } static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; int n; char buf[80]; switch (i) { case 0: n = 6; break; case 1: n = 10; break; case 2: n = 15; break; case 3: n = 20; break; case 4: n = 25; break; default: return FALSE; } sprintf(buf, "%d points", n); *name = dupstr(buf); *params = ret = snew(game_params); ret->n = n; 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->n = atoi(string); } static char *encode_params(const game_params *params, int full) { char buf[80]; sprintf(buf, "%d", params->n); return dupstr(buf); } static config_item *game_configure(const game_params *params) { config_item *ret; char buf[80]; ret = snewn(3, config_item); ret[0].name = "Number of points"; ret[0].type = C_STRING; sprintf(buf, "%d", params->n); ret[0].sval = dupstr(buf); ret[0].ival = 0; ret[1].name = NULL; ret[1].type = C_END; ret[1].sval = NULL; ret[1].ival = 0; return ret; } static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); ret->n = atoi(cfg[0].sval); return ret; } static char *validate_params(const game_params *params, int full) { if (params->n < 4) return "Number of points must be at least four"; return NULL; } /* ---------------------------------------------------------------------- * Small number of 64-bit integer arithmetic operations, to prevent * integer overflow at the very core of cross(). */ typedef struct { long hi; unsigned long lo; } int64; #define greater64(i,j) ( (i).hi>(j).hi || ((i).hi==(j).hi && (i).lo>(j).lo)) #define sign64(i) ((i).hi < 0 ? -1 : (i).hi==0 && (i).lo==0 ? 0 : +1) static int64 mulu32to64(unsigned long x, unsigned long y) { unsigned long a, b, c, d, t; int64 ret; a = (x & 0xFFFF) * (y & 0xFFFF); b = (x & 0xFFFF) * (y >> 16); c = (x >> 16) * (y & 0xFFFF); d = (x >> 16) * (y >> 16); ret.lo = a; ret.hi = d + (b >> 16) + (c >> 16); t = (b & 0xFFFF) << 16; ret.lo += t; if (ret.lo < t) ret.hi++; t = (c & 0xFFFF) << 16; ret.lo += t; if (ret.lo < t) ret.hi++; #ifdef DIAGNOSTIC_VIA_LONGLONG assert(((unsigned long long)ret.hi << 32) + ret.lo == (unsigned long long)x * y); #endif return ret; } static int64 mul32to64(long x, long y) { int sign = +1; int64 ret; #ifdef DIAGNOSTIC_VIA_LONGLONG long long realret = (long long)x * y; #endif if (x < 0) x = -x, sign = -sign; if (y < 0) y = -y, sign = -sign; ret = mulu32to64(x, y); if (sign < 0) { ret.hi = -ret.hi; ret.lo = -ret.lo; if (ret.lo) ret.hi--; } #ifdef DIAGNOSTIC_VIA_LONGLONG assert(((unsigned long long)ret.hi << 32) + ret.lo == realret); #endif return ret; } static int64 dotprod64(long a, long b, long p, long q) { int64 ab, pq; ab = mul32to64(a, b); pq = mul32to64(p, q); ab.hi += pq.hi; ab.lo += pq.lo; if (ab.lo < pq.lo) ab.hi++; return ab; } /* * Determine whether the line segments between a1 and a2, and * between b1 and b2, intersect. We count it as an intersection if * any of the endpoints lies _on_ the other line. */ static int cross(point a1, point a2, point b1, point b2) { long b1x, b1y, b2x, b2y, px, py; int64 d1, d2, d3; /* * The condition for crossing is that b1 and b2 are on opposite * sides of the line a1-a2, and vice versa. We determine this * by taking the dot product of b1-a1 with a vector * perpendicular to a2-a1, and similarly with b2-a1, and seeing * if they have different signs. */ /* * Construct the vector b1-a1. We don't have to worry too much * about the denominator, because we're only going to check the * sign of this vector; we just need to get the numerator * right. */ b1x = b1.x * a1.d - a1.x * b1.d; b1y = b1.y * a1.d - a1.y * b1.d; /* Now construct b2-a1, and a vector perpendicular to a2-a1, * in the same way. */ b2x = b2.x * a1.d - a1.x * b2.d; b2y = b2.y * a1.d - a1.y * b2.d; px = a1.y * a2.d - a2.y * a1.d; py = a2.x * a1.d - a1.x * a2.d; /* Take the dot products. Here we resort to 64-bit arithmetic. */ d1 = dotprod64(b1x, px, b1y, py); d2 = dotprod64(b2x, px, b2y, py); /* If they have the same non-zero sign, the lines do not cross. */ if ((sign64(d1) > 0 && sign64(d2) > 0) || (sign64(d1) < 0 && sign64(d2) < 0)) return FALSE; /* * If the dot products are both exactly zero, then the two line * segments are collinear. At this point the intersection * condition becomes whether or not they overlap within their * line. */ if (sign64(d1) == 0 && sign64(d2) == 0) { /* Construct the vector a2-a1. */ px = a2.x * a1.d - a1.x * a2.d; py = a2.y * a1.d - a1.y * a2.d; /* Determine the dot products of b1-a1 and b2-a1 with this. */ d1 = dotprod64(b1x, px, b1y, py); d2 = dotprod64(b2x, px, b2y, py); /* If they're both strictly negative, the lines do not cross. */ if (sign64(d1) < 0 && sign64(d2) < 0) return FALSE; /* Otherwise, take the dot product of a2-a1 with itself. If * the other two dot products both exceed this, the lines do * not cross. */ d3 = dotprod64(px, px, py, py); if (greater64(d1, d3) && greater64(d2, d3)) return FALSE; } /* * We've eliminated the only important special case, and we * have determined that b1 and b2 are on opposite sides of the * line a1-a2. Now do the same thing the other way round and * we're done. */ b1x = a1.x * b1.d - b1.x * a1.d; b1y = a1.y * b1.d - b1.y * a1.d; b2x = a2.x * b1.d - b1.x * a2.d; b2y = a2.y * b1.d - b1.y * a2.d; px = b1.y * b2.d - b2.y * b1.d; py = b2.x * b1.d - b1.x * b2.d; d1 = dotprod64(b1x, px, b1y, py); d2 = dotprod64(b2x, px, b2y, py); if ((sign64(d1) > 0 && sign64(d2) > 0) || (sign64(d1) < 0 && sign64(d2) < 0)) return FALSE; /* * The lines must cross. */ return TRUE; } static unsigned long squarert(unsigned long n) { unsigned long d, a, b, di; d = n; a = 0; b = 1L << 30; /* largest available power of 4 */ do { a >>= 1; di = 2*a + b; if (di <= d) { d -= di; a += b; } b >>= 2; } while (b); return a; } /* * Our solutions are arranged on a square grid big enough that n * points occupy about 1/POINTDENSITY of the grid. */ #define POINTDENSITY 3 #define MAXDEGREE 4 #define COORDLIMIT(n) squarert((n) * POINTDENSITY) static void addedge(tree234 *edges, int a, int b) { edge *e = snew(edge); assert(a != b); e->a = min(a, b); e->b = max(a, b); add234(edges, e); } static int isedge(tree234 *edges, int a, int b) { edge e; assert(a != b); e.a = min(a, b); e.b = max(a, b); return find234(edges, &e, NULL) != NULL; } typedef struct vertex { int param; int vindex; } vertex; static int vertcmpC(const void *av, const void *bv) { const vertex *a = (vertex *)av; const vertex *b = (vertex *)bv; if (a->param < b->param) return -1; else if (a->param > b->param) return +1; else if (a->vindex < b->vindex) return -1; else if (a->vindex > b->vindex) return +1; return 0; } static int vertcmp(void *av, void *bv) { return vertcmpC(av, bv); } /* * Construct point coordinates for n points arranged in a circle, * within the bounding box (0,0) to (w,w). */ static void make_circle(point *pts, int n, int w) { long d, r, c, i; /* * First, decide on a denominator. Although in principle it * would be nice to set this really high so as to finely * distinguish all the points on the circle, I'm going to set * it at a fixed size to prevent integer overflow problems. */ d = PREFERRED_TILESIZE; /* * Leave a little space outside the circle. */ c = d * w / 2; r = d * w * 3 / 7; /* * Place the points. */ for (i = 0; i < n; i++) { double angle = i * 2 * PI / n; double x = r * sin(angle), y = - r * cos(angle); pts[i].x = (long)(c + x + 0.5); pts[i].y = (long)(c + y + 0.5); pts[i].d = d; } } static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { int n = params->n, i; long w, h, j, k, m; point *pts, *pts2; long *tmp; tree234 *edges, *vertices; edge *e, *e2; vertex *v, *vs, *vlist; char *ret; w = h = COORDLIMIT(n); /* * Choose n points from this grid. */ pts = snewn(n, point); tmp = snewn(w*h, long); for (i = 0; i < w*h; i++) tmp[i] = i; shuffle(tmp, w*h, sizeof(*tmp), rs); for (i = 0; i < n; i++) { pts[i].x = tmp[i] % w; pts[i].y = tmp[i] / w; pts[i].d = 1; } sfree(tmp); /* * Now start adding edges between the points. * * At all times, we attempt to add an edge to the lowest-degree * vertex we currently have, and we try the other vertices as * candidate second endpoints in order of distance from this * one. We stop as soon as we find an edge which * * (a) does not increase any vertex's degree beyond MAXDEGREE * (b) does not cross any existing edges * (c) does not intersect any actual point. */ vs = snewn(n, vertex); vertices = newtree234(vertcmp); for (i = 0; i < n; i++) { v = vs + i; v->param = 0; /* in this tree, param is the degree */ v->vindex = i; add234(vertices, v); } edges = newtree234(edgecmp); vlist = snewn(n, vertex); while (1) { int added = FALSE; for (i = 0; i < n; i++) { v = index234(vertices, i); j = v->vindex; if (v->param >= MAXDEGREE) break; /* nothing left to add! */ /* * Sort the other vertices into order of their distance * from this one. Don't bother looking below i, because * we've already tried those edges the other way round. * Also here we rule out target vertices with too high * a degree, and (of course) ones to which we already * have an edge. */ m = 0; for (k = i+1; k < n; k++) { vertex *kv = index234(vertices, k); int ki = kv->vindex; int dx, dy; if (kv->param >= MAXDEGREE || isedge(edges, ki, j)) continue; vlist[m].vindex = ki; dx = pts[ki].x - pts[j].x; dy = pts[ki].y - pts[j].y; vlist[m].param = dx*dx + dy*dy; m++; } qsort(vlist, m, sizeof(*vlist), vertcmpC); for (k = 0; k < m; k++) { int p; int ki = vlist[k].vindex; /* * Check to see whether this edge intersects any * existing edge or point. */ for (p = 0; p < n; p++) if (p != ki && p != j && cross(pts[ki], pts[j], pts[p], pts[p])) break; if (p < n) continue; for (p = 0; (e = index234(edges, p)) != NULL; p++) if (e->a != ki && e->a != j && e->b != ki && e->b != j && cross(pts[ki], pts[j], pts[e->a], pts[e->b])) break; if (e) continue; /* * We're done! Add this edge, modify the degrees of * the two vertices involved, and break. */ addedge(edges, j, ki); added = TRUE; del234(vertices, vs+j); vs[j].param++; add234(vertices, vs+j); del234(vertices, vs+ki); vs[ki].param++; add234(vertices, vs+ki); break; } if (k < m) break; } if (!added) break; /* we're done. */ } /* * That's our graph. Now shuffle the points, making sure that * they come out with at least one crossed line when arranged * in a circle (so that the puzzle isn't immediately solved!). */ tmp = snewn(n, long); for (i = 0; i < n; i++) tmp[i] = i; pts2 = snewn(n, point); make_circle(pts2, n, w); while (1) { shuffle(tmp, n, sizeof(*tmp), rs); for (i = 0; (e = index234(edges, i)) != NULL; i++) { for (j = i+1; (e2 = index234(edges, j)) != NULL; j++) { if (e2->a == e->a || e2->a == e->b || e2->b == e->a || e2->b == e->b) continue; if (cross(pts2[tmp[e2->a]], pts2[tmp[e2->b]], pts2[tmp[e->a]], pts2[tmp[e->b]])) break; } if (e2) break; } if (e) break; /* we've found a crossing */ } /* * We're done. Now encode the graph in a string format. Let's * use a comma-separated list of dash-separated vertex number * pairs, numbered from zero. We'll sort the list to prevent * side channels. */ ret = NULL; { char *sep; char buf[80]; int retlen; edge *ea; retlen = 0; m = count234(edges); ea = snewn(m, edge); for (i = 0; (e = index234(edges, i)) != NULL; i++) { assert(i < m); ea[i].a = min(tmp[e->a], tmp[e->b]); ea[i].b = max(tmp[e->a], tmp[e->b]); retlen += 1 + sprintf(buf, "%d-%d", ea[i].a, ea[i].b); } assert(i == m); qsort(ea, m, sizeof(*ea), edgecmpC); ret = snewn(retlen, char); sep = ""; k = 0; for (i = 0; i < m; i++) { k += sprintf(ret + k, "%s%d-%d", sep, ea[i].a, ea[i].b); sep = ","; } assert(k < retlen); sfree(ea); } /* * Encode the solution we started with as an aux_info string. */ { char buf[80]; char *auxstr; int auxlen; auxlen = 2; /* leading 'S' and trailing '\0' */ for (i = 0; i < n; i++) { j = tmp[i]; pts2[j] = pts[i]; if (pts2[j].d & 1) { pts2[j].x *= 2; pts2[j].y *= 2; pts2[j].d *= 2; } pts2[j].x += pts2[j].d / 2; pts2[j].y += pts2[j].d / 2; auxlen += sprintf(buf, ";P%d:%ld,%ld/%ld", i, pts2[j].x, pts2[j].y, pts2[j].d); } k = 0; auxstr = snewn(auxlen, char); auxstr[k++] = 'S'; for (i = 0; i < n; i++) k += sprintf(auxstr+k, ";P%d:%ld,%ld/%ld", i, pts2[i].x, pts2[i].y, pts2[i].d); assert(k < auxlen); *aux = auxstr; } sfree(pts2); sfree(tmp); sfree(vlist); freetree234(vertices); sfree(vs); while ((e = delpos234(edges, 0)) != NULL) sfree(e); freetree234(edges); sfree(pts); return ret; } static char *validate_desc(const game_params *params, const char *desc) { int a, b; while (*desc) { a = atoi(desc); if (a < 0 || a >= params->n) return "Number out of range in game description"; while (*desc && isdigit((unsigned char)*desc)) desc++; if (*desc != '-') return "Expected '-' after number in game description"; desc++; /* eat dash */ b = atoi(desc); if (b < 0 || b >= params->n) return "Number out of range in game description"; while (*desc && isdigit((unsigned char)*desc)) desc++; if (*desc) { if (*desc != ',') return "Expected ',' after number in game description"; desc++; /* eat comma */ } } return NULL; } static void mark_crossings(game_state *state) { int ok = TRUE; int i, j; edge *e, *e2; #ifdef SHOW_CROSSINGS for (i = 0; (e = index234(state->graph->edges, i)) != NULL; i++) state->crosses[i] = FALSE; #endif /* * Check correctness: for every pair of edges, see whether they * cross. */ for (i = 0; (e = index234(state->graph->edges, i)) != NULL; i++) { for (j = i+1; (e2 = index234(state->graph->edges, j)) != NULL; j++) { if (e2->a == e->a || e2->a == e->b || e2->b == e->a || e2->b == e->b) continue; if (cross(state->pts[e2->a], state->pts[e2->b], state->pts[e->a], state->pts[e->b])) { ok = FALSE; #ifdef SHOW_CROSSINGS state->crosses[i] = state->crosses[j] = TRUE; #else goto done; /* multi-level break - sorry */ #endif } } } /* * e == NULL if we've gone through all the edge pairs * without finding a crossing. */ #ifndef SHOW_CROSSINGS done: #endif if (ok) state->completed = TRUE; } static game_state *new_game(midend *me, const game_params *params, const char *desc) { int n = params->n; game_state *state = snew(game_state); int a, b; state->params = *params; state->w = state->h = COORDLIMIT(n); state->pts = snewn(n, point); make_circle(state->pts, n, state->w); state->graph = snew(struct graph); state->graph->refcount = 1; state->graph->edges = newtree234(edgecmp); state->completed = state->cheated = state->just_solved = FALSE; while (*desc) { a = atoi(desc); assert(a >= 0 && a < params->n); while (*desc && isdigit((unsigned char)*desc)) desc++; assert(*desc == '-'); desc++; /* eat dash */ b = atoi(desc); assert(b >= 0 && b < params->n); while (*desc && isdigit((unsigned char)*desc)) desc++; if (*desc) { assert(*desc == ','); desc++; /* eat comma */ } addedge(state->graph->edges, a, b); } #ifdef SHOW_CROSSINGS state->crosses = snewn(count234(state->graph->edges), int); mark_crossings(state); /* sets up `crosses' and `completed' */ #endif return state; } static game_state *dup_game(const game_state *state) { int n = state->params.n; game_state *ret = snew(game_state); ret->params = state->params; ret->w = state->w; ret->h = state->h; ret->pts = snewn(n, point); memcpy(ret->pts, state->pts, n * sizeof(point)); ret->graph = state->graph; ret->graph->refcount++; ret->completed = state->completed; ret->cheated = state->cheated; ret->just_solved = state->just_solved; #ifdef SHOW_CROSSINGS ret->crosses = snewn(count234(ret->graph->edges), int); memcpy(ret->crosses, state->crosses, count234(ret->graph->edges) * sizeof(int)); #endif return ret; } static void free_game(game_state *state) { if (--state->graph->refcount <= 0) { edge *e; while ((e = delpos234(state->graph->edges, 0)) != NULL) sfree(e); freetree234(state->graph->edges); sfree(state->graph); } sfree(state->pts); sfree(state); } static char *solve_game(const game_state *state, const game_state *currstate, const char *aux, char **error) { int n = state->params.n; int matrix[4]; point *pts; int i, j, besti; float bestd; char buf[80], *ret; int retlen, retsize; if (!aux) { *error = "Solution not known for this puzzle"; return NULL; } /* * Decode the aux_info to get the original point positions. */ pts = snewn(n, point); aux++; /* eat 'S' */ for (i = 0; i < n; i++) { int p, k; long x, y, d; int ret = sscanf(aux, ";P%d:%ld,%ld/%ld%n", &p, &x, &y, &d, &k); if (ret != 4 || p != i) { *error = "Internal error: aux_info badly formatted"; sfree(pts); return NULL; } pts[i].x = x; pts[i].y = y; pts[i].d = d; aux += k; } /* * Now go through eight possible symmetries of the point set. * For each one, work out the sum of the Euclidean distances * between the points' current positions and their new ones. * * We're squaring distances here, which means we're at risk of * integer overflow. Fortunately, there's no real need to be * massively careful about rounding errors, since this is a * non-essential bit of the code; so I'll just work in floats * internally. */ besti = -1; bestd = 0.0F; for (i = 0; i < 8; i++) { float d; matrix[0] = matrix[1] = matrix[2] = matrix[3] = 0; matrix[i & 1] = (i & 2) ? +1 : -1; matrix[3-(i&1)] = (i & 4) ? +1 : -1; d = 0.0F; for (j = 0; j < n; j++) { float px = (float)pts[j].x / pts[j].d; float py = (float)pts[j].y / pts[j].d; float sx = (float)currstate->pts[j].x / currstate->pts[j].d; float sy = (float)currstate->pts[j].y / currstate->pts[j].d; float cx = (float)currstate->w / 2; float cy = (float)currstate->h / 2; float ox, oy, dx, dy; px -= cx; py -= cy; ox = matrix[0] * px + matrix[1] * py; oy = matrix[2] * px + matrix[3] * py; ox += cx; oy += cy; dx = ox - sx; dy = oy - sy; d += dx*dx + dy*dy; } if (besti < 0 || bestd > d) { besti = i; bestd = d; } } assert(besti >= 0); /* * Now we know which symmetry is closest to the points' current * positions. Use it. */ matrix[0] = matrix[1] = matrix[2] = matrix[3] = 0; matrix[besti & 1] = (besti & 2) ? +1 : -1; matrix[3-(besti&1)] = (besti & 4) ? +1 : -1; retsize = 256; ret = snewn(retsize, char); retlen = 0; ret[retlen++] = 'S'; ret[retlen] = '\0'; for (i = 0; i < n; i++) { float px = (float)pts[i].x / pts[i].d; float py = (float)pts[i].y / pts[i].d; float cx = (float)currstate->w / 2; float cy = (float)currstate->h / 2; float ox, oy; int extra; px -= cx; py -= cy; ox = matrix[0] * px + matrix[1] * py; oy = matrix[2] * px + matrix[3] * py; ox += cx; oy += cy; /* * Use a fixed denominator of 2, because we know the * original points were on an integer grid offset by 1/2. */ pts[i].d = 2; ox *= pts[i].d; oy *= pts[i].d; pts[i].x = (long)(ox + 0.5F); pts[i].y = (long)(oy + 0.5F); extra = sprintf(buf, ";P%d:%ld,%ld/%ld", i, pts[i].x, pts[i].y, pts[i].d); if (retlen + extra >= retsize) { retsize = retlen + extra + 256; ret = sresize(ret, retsize, char); } strcpy(ret + retlen, buf); retlen += extra; } sfree(pts); return ret; } static int game_can_format_as_text_now(const game_params *params) { return TRUE; } static char *game_text_format(const game_state *state) { return NULL; } struct game_ui { int dragpoint; /* point being dragged; -1 if none */ point newpoint; /* where it's been dragged to so far */ int just_dragged; /* reset in game_changed_state */ int just_moved; /* _set_ in game_changed_state */ float anim_length; }; static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->dragpoint = -1; ui->just_moved = ui->just_dragged = FALSE; 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) { ui->dragpoint = -1; ui->just_moved = ui->just_dragged; ui->just_dragged = FALSE; } struct game_drawstate { long tilesize; int bg, dragpoint; long *x, *y; }; static char *interpret_move(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button) { int n = state->params.n; if (IS_MOUSE_DOWN(button)) { int i, best; long bestd; /* * Begin drag. We drag the vertex _nearest_ to the pointer, * just in case one is nearly on top of another and we want * to drag the latter. However, we drag nothing at all if * the nearest vertex is outside DRAG_THRESHOLD. */ best = -1; bestd = 0; for (i = 0; i < n; i++) { long px = state->pts[i].x * ds->tilesize / state->pts[i].d; long py = state->pts[i].y * ds->tilesize / state->pts[i].d; long dx = px - x; long dy = py - y; long d = dx*dx + dy*dy; if (best == -1 || bestd > d) { best = i; bestd = d; } } if (bestd <= DRAG_THRESHOLD * DRAG_THRESHOLD) { ui->dragpoint = best; ui->newpoint.x = x; ui->newpoint.y = y; ui->newpoint.d = ds->tilesize; return ""; } } else if (IS_MOUSE_DRAG(button) && ui->dragpoint >= 0) { ui->newpoint.x = x; ui->newpoint.y = y; ui->newpoint.d = ds->tilesize; return ""; } else if (IS_MOUSE_RELEASE(button) && ui->dragpoint >= 0) { int p = ui->dragpoint; char buf[80]; ui->dragpoint = -1; /* terminate drag, no matter what */ /* * First, see if we're within range. The user can cancel a * drag by dragging the point right off the window. */ if (ui->newpoint.x < 0 || ui->newpoint.x >= (long)state->w*ui->newpoint.d || ui->newpoint.y < 0 || ui->newpoint.y >= (long)state->h*ui->newpoint.d) return ""; /* * We aren't cancelling the drag. Construct a move string * indicating where this point is going to. */ sprintf(buf, "P%d:%ld,%ld/%ld", p, ui->newpoint.x, ui->newpoint.y, ui->newpoint.d); ui->just_dragged = TRUE; return dupstr(buf); } return NULL; } static game_state *execute_move(const game_state *state, const char *move) { int n = state->params.n; int p, k; long x, y, d; game_state *ret = dup_game(state); ret->just_solved = FALSE; while (*move) { if (*move == 'S') { move++; if (*move == ';') move++; ret->cheated = ret->just_solved = TRUE; } if (*move == 'P' && sscanf(move+1, "%d:%ld,%ld/%ld%n", &p, &x, &y, &d, &k) == 4 && p >= 0 && p < n && d > 0) { ret->pts[p].x = x; ret->pts[p].y = y; ret->pts[p].d = d; move += k+1; if (*move == ';') move++; } else { free_game(ret); return NULL; } } mark_crossings(ret); return ret; } /* ---------------------------------------------------------------------- * Drawing routines. */ static void game_compute_size(const game_params *params, int tilesize, int *x, int *y) { *x = *y = COORDLIMIT(params->n) * 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); /* * COL_BACKGROUND is what we use as the normal background colour. * Unusually, though, it isn't colour #0: COL_SYSBACKGROUND, a bit * darker, takes that place. This means that if the user resizes * an Untangle window so as to change its aspect ratio, the * still-square playable area will be distinguished from the dead * space around it. */ game_mkhighlight(fe, ret, COL_BACKGROUND, -1, COL_SYSBACKGROUND); ret[COL_LINE * 3 + 0] = 0.0F; ret[COL_LINE * 3 + 1] = 0.0F; ret[COL_LINE * 3 + 2] = 0.0F; #ifdef SHOW_CROSSINGS ret[COL_CROSSEDLINE * 3 + 0] = 1.0F; ret[COL_CROSSEDLINE * 3 + 1] = 0.0F; ret[COL_CROSSEDLINE * 3 + 2] = 0.0F; #endif ret[COL_OUTLINE * 3 + 0] = 0.0F; ret[COL_OUTLINE * 3 + 1] = 0.0F; ret[COL_OUTLINE * 3 + 2] = 0.0F; ret[COL_POINT * 3 + 0] = 0.0F; ret[COL_POINT * 3 + 1] = 0.0F; ret[COL_POINT * 3 + 2] = 1.0F; ret[COL_DRAGPOINT * 3 + 0] = 1.0F; ret[COL_DRAGPOINT * 3 + 1] = 1.0F; ret[COL_DRAGPOINT * 3 + 2] = 1.0F; ret[COL_NEIGHBOUR * 3 + 0] = 1.0F; ret[COL_NEIGHBOUR * 3 + 1] = 0.0F; ret[COL_NEIGHBOUR * 3 + 2] = 0.0F; ret[COL_FLASH1 * 3 + 0] = 0.5F; ret[COL_FLASH1 * 3 + 1] = 0.5F; ret[COL_FLASH1 * 3 + 2] = 0.5F; ret[COL_FLASH2 * 3 + 0] = 1.0F; ret[COL_FLASH2 * 3 + 1] = 1.0F; ret[COL_FLASH2 * 3 + 2] = 1.0F; *ncolours = NCOLOURS; return ret; } static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { struct game_drawstate *ds = snew(struct game_drawstate); int i; ds->tilesize = 0; ds->x = snewn(state->params.n, long); ds->y = snewn(state->params.n, long); for (i = 0; i < state->params.n; i++) ds->x[i] = ds->y[i] = -1; ds->bg = -1; ds->dragpoint = -1; return ds; } static void game_free_drawstate(drawing *dr, game_drawstate *ds) { sfree(ds->y); sfree(ds->x); sfree(ds); } static point mix(point a, point b, float distance) { point ret; ret.d = a.d * b.d; ret.x = (long)(a.x * b.d + distance * (b.x * a.d - a.x * b.d)); ret.y = (long)(a.y * b.d + distance * (b.y * a.d - a.y * b.d)); return ret; } 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, h; edge *e; int i, j; int bg, points_moved; /* * There's no terribly sensible way to do partial redraws of * this game, so I'm going to have to resort to redrawing the * whole thing every time. */ if (flashtime == 0) bg = COL_BACKGROUND; else if ((int)(flashtime * 4 / FLASH_TIME) % 2 == 0) bg = COL_FLASH1; else bg = COL_FLASH2; /* * To prevent excessive spinning on redraw during a completion * flash, we first check to see if _either_ the flash * background colour has changed _or_ at least one point has * moved _or_ a drag has begun or ended, and abandon the redraw * if neither is the case. * * Also in this loop we work out the coordinates of all the * points for this redraw. */ points_moved = FALSE; for (i = 0; i < state->params.n; i++) { point p = state->pts[i]; long x, y; if (ui->dragpoint == i) p = ui->newpoint; if (oldstate) p = mix(oldstate->pts[i], p, animtime / ui->anim_length); x = p.x * ds->tilesize / p.d; y = p.y * ds->tilesize / p.d; if (ds->x[i] != x || ds->y[i] != y) points_moved = TRUE; ds->x[i] = x; ds->y[i] = y; } if (ds->bg == bg && ds->dragpoint == ui->dragpoint && !points_moved) return; /* nothing to do */ ds->dragpoint = ui->dragpoint; ds->bg = bg; game_compute_size(&state->params, ds->tilesize, &w, &h); draw_rect(dr, 0, 0, w, h, bg); /* * Draw the edges. */ for (i = 0; (e = index234(state->graph->edges, i)) != NULL; i++) { draw_line(dr, ds->x[e->a], ds->y[e->a], ds->x[e->b], ds->y[e->b], #ifdef SHOW_CROSSINGS (oldstate?oldstate:state)->crosses[i] ? COL_CROSSEDLINE : #endif COL_LINE); } /* * Draw the points. * * When dragging, we should not only vary the colours, but * leave the point being dragged until last. */ for (j = 0; j < 3; j++) { int thisc = (j == 0 ? COL_POINT : j == 1 ? COL_NEIGHBOUR : COL_DRAGPOINT); for (i = 0; i < state->params.n; i++) { int c; if (ui->dragpoint == i) { c = COL_DRAGPOINT; } else if (ui->dragpoint >= 0 && isedge(state->graph->edges, ui->dragpoint, i)) { c = COL_NEIGHBOUR; } else { c = COL_POINT; } if (c == thisc) { #ifdef VERTEX_NUMBERS draw_circle(dr, ds->x[i], ds->y[i], DRAG_THRESHOLD, bg, bg); { char buf[80]; sprintf(buf, "%d", i); draw_text(dr, ds->x[i], ds->y[i], FONT_VARIABLE, DRAG_THRESHOLD*3/2, ALIGN_VCENTRE|ALIGN_HCENTRE, c, buf); } #else draw_circle(dr, ds->x[i], ds->y[i], CIRCLE_RADIUS, c, COL_OUTLINE); #endif } } } draw_update(dr, 0, 0, w, h); } static float game_anim_length(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui) { if (ui->just_moved) return 0.0F; if ((dir < 0 ? oldstate : newstate)->just_solved) ui->anim_length = SOLVEANIM_TIME; else ui->anim_length = ANIM_TIME; return ui->anim_length; } 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 int game_status(const game_state *state) { return state->completed ? +1 : 0; } static int 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 untangle #endif const struct game thegame = { "Untangle", "games.untangle", "untangle", default_params, game_fetch_preset, 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, FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, decode_ui, 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_status, FALSE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, SOLVE_ANIMATES, /* flags */ }; puzzles-r9872/version.c0000644000175300017530000000033410241622357014260 0ustar simonsimon/* * Puzzles version numbering. */ #define STR1(x) #x #define STR(x) STR1(x) #if defined REVISION char ver[] = "Revision: r" STR(REVISION); #else char ver[] = "Unidentified build, " __DATE__ " " __TIME__; #endif puzzles-r9872/windows.c0000644000175300017530000027400012076566105014276 0ustar simonsimon/* * windows.c: Windows front end for my puzzle collection. */ #include #include #ifndef NO_HTMLHELP #include #endif /* NO_HTMLHELP */ #ifdef _WIN32_WCE #include #include #endif #include #include #include #include #include #include #include #include "puzzles.h" #ifdef _WIN32_WCE #include "resource.h" #endif #define IDM_NEW 0x0010 #define IDM_RESTART 0x0020 #define IDM_UNDO 0x0030 #define IDM_REDO 0x0040 #define IDM_COPY 0x0050 #define IDM_SOLVE 0x0060 #define IDM_QUIT 0x0070 #define IDM_CONFIG 0x0080 #define IDM_DESC 0x0090 #define IDM_SEED 0x00A0 #define IDM_HELPC 0x00B0 #define IDM_GAMEHELP 0x00C0 #define IDM_ABOUT 0x00D0 #define IDM_SAVE 0x00E0 #define IDM_LOAD 0x00F0 #define IDM_PRINT 0x0100 #define IDM_PRESETS 0x0110 #define IDM_GAMES 0x0300 #define IDM_KEYEMUL 0x0400 #define HELP_FILE_NAME "puzzles.hlp" #define HELP_CNT_NAME "puzzles.cnt" #ifndef NO_HTMLHELP #define CHM_FILE_NAME "puzzles.chm" #endif /* NO_HTMLHELP */ #ifndef NO_HTMLHELP typedef HWND (CALLBACK *htmlhelp_t)(HWND, LPCSTR, UINT, DWORD); static htmlhelp_t htmlhelp; static HINSTANCE hh_dll; #endif /* NO_HTMLHELP */ enum { NONE, HLP, CHM } help_type; char *help_path; int help_has_contents; #ifndef FILENAME_MAX #define FILENAME_MAX (260) #endif #ifndef HGDI_ERROR #define HGDI_ERROR ((HANDLE)GDI_ERROR) #endif #ifdef COMBINED #define CLASSNAME "Puzzles" #else #define CLASSNAME thegame.name #endif #ifdef _WIN32_WCE /* * Wrapper implementations of functions not supplied by the * PocketPC API. */ #define SHGetSubMenu(hWndMB,ID_MENU) (HMENU)SendMessage((hWndMB), SHCMBM_GETSUBMENU, (WPARAM)0, (LPARAM)ID_MENU) #undef MessageBox int MessageBox(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) { TCHAR wText[2048]; TCHAR wCaption[2048]; MultiByteToWideChar (CP_ACP, 0, lpText, -1, wText, 2048); MultiByteToWideChar (CP_ACP, 0, lpCaption, -1, wCaption, 2048); return MessageBoxW (hWnd, wText, wCaption, uType); } BOOL SetDlgItemTextA(HWND hDlg, int nIDDlgItem, LPCSTR lpString) { TCHAR wText[256]; MultiByteToWideChar (CP_ACP, 0, lpString, -1, wText, 256); return SetDlgItemTextW(hDlg, nIDDlgItem, wText); } LPCSTR getenv(LPCSTR buf) { return NULL; } BOOL GetKeyboardState(PBYTE pb) { return FALSE; } static TCHAR wClassName[256], wGameName[256]; #endif #ifdef DEBUGGING static FILE *debug_fp = NULL; static HANDLE debug_hdl = INVALID_HANDLE_VALUE; static int debug_got_console = 0; void dputs(char *buf) { /*DWORD dw; if (!debug_got_console) { if (AllocConsole()) { debug_got_console = 1; debug_hdl = GetStdHandle(STD_OUTPUT_HANDLE); } } if (!debug_fp) { debug_fp = fopen("debug.log", "w"); } if (debug_hdl != INVALID_HANDLE_VALUE) { WriteFile(debug_hdl, buf, strlen(buf), &dw, NULL); } if (debug_fp) { fputs(buf, debug_fp); fflush(debug_fp); }*/ OutputDebugString(buf); } void debug_printf(char *fmt, ...) { char buf[4096]; va_list ap; static int debugging = -1; if (debugging == -1) debugging = getenv("DEBUG_PUZZLES") ? 1 : 0; if (debugging) { va_start(ap, fmt); _vsnprintf(buf, 4095, fmt, ap); dputs(buf); va_end(ap); } } #endif #ifndef _WIN32_WCE #define WINFLAGS (WS_OVERLAPPEDWINDOW &~ \ (WS_MAXIMIZEBOX | WS_OVERLAPPED)) #else #define WINFLAGS (WS_CAPTION | WS_SYSMENU) #endif static void new_game_size(frontend *fe, float scale); struct font { HFONT font; int type; int size; }; struct cfg_aux { int ctlid; }; struct blitter { HBITMAP bitmap; frontend *fe; int x, y, w, h; }; enum { CFG_PRINT = CFG_FRONTEND_SPECIFIC }; struct frontend { const game *game; midend *me; HWND hwnd, statusbar, cfgbox; #ifdef _WIN32_WCE HWND numpad; /* window handle for the numeric pad */ #endif HINSTANCE inst; HBITMAP bitmap, prevbm; RECT bitmapPosition; /* game bitmap position within game window */ HDC hdc; COLORREF *colours; HBRUSH *brushes; HPEN *pens; HRGN clip; HMENU gamemenu, typemenu; UINT timer; DWORD timer_last_tickcount; int npresets; game_params **presets; struct font *fonts; int nfonts, fontsize; config_item *cfg; struct cfg_aux *cfgaux; int cfg_which, dlg_done; HFONT cfgfont; HBRUSH oldbr; HPEN oldpen; int help_running; enum { DRAWING, PRINTING, NOTHING } drawstatus; DOCINFO di; int printcount, printw, printh, printsolns, printcurr, printcolour; float printscale; int printoffsetx, printoffsety; float printpixelscale; int fontstart; int linewidth, linedotted; drawing *dr; int xmin, ymin; float puzz_scale; }; void frontend_free(frontend *fe) { midend_free(fe->me); sfree(fe->colours); sfree(fe->brushes); sfree(fe->pens); sfree(fe->presets); sfree(fe->fonts); sfree(fe); } static void update_type_menu_tick(frontend *fe); static void update_copy_menu_greying(frontend *fe); void fatal(char *fmt, ...) { char buf[2048]; va_list ap; va_start(ap, fmt); vsprintf(buf, fmt, ap); va_end(ap); MessageBox(NULL, buf, "Fatal error", MB_ICONEXCLAMATION | MB_OK); exit(1); } char *geterrstr(void) { LPVOID lpMsgBuf; DWORD dw = GetLastError(); char *ret; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); ret = dupstr(lpMsgBuf); LocalFree(lpMsgBuf); return ret; } void get_random_seed(void **randseed, int *randseedsize) { SYSTEMTIME *st = snew(SYSTEMTIME); GetLocalTime(st); *randseed = (void *)st; *randseedsize = sizeof(SYSTEMTIME); } static void win_status_bar(void *handle, char *text) { #ifdef _WIN32_WCE TCHAR wText[255]; #endif frontend *fe = (frontend *)handle; #ifdef _WIN32_WCE MultiByteToWideChar (CP_ACP, 0, text, -1, wText, 255); SendMessage(fe->statusbar, SB_SETTEXT, (WPARAM) 255 | SBT_NOBORDERS, (LPARAM) wText); #else SetWindowText(fe->statusbar, text); #endif } static blitter *win_blitter_new(void *handle, int w, int h) { blitter *bl = snew(blitter); memset(bl, 0, sizeof(blitter)); bl->w = w; bl->h = h; bl->bitmap = 0; return bl; } static void win_blitter_free(void *handle, blitter *bl) { if (bl->bitmap) DeleteObject(bl->bitmap); sfree(bl); } static void blitter_mkbitmap(frontend *fe, blitter *bl) { HDC hdc = GetDC(fe->hwnd); bl->bitmap = CreateCompatibleBitmap(hdc, bl->w, bl->h); ReleaseDC(fe->hwnd, hdc); } /* BitBlt(dstDC, dstX, dstY, dstW, dstH, srcDC, srcX, srcY, dType) */ static void win_blitter_save(void *handle, blitter *bl, int x, int y) { frontend *fe = (frontend *)handle; HDC hdc_win, hdc_blit; HBITMAP prev_blit; assert(fe->drawstatus == DRAWING); if (!bl->bitmap) blitter_mkbitmap(fe, bl); bl->x = x; bl->y = y; hdc_win = GetDC(fe->hwnd); hdc_blit = CreateCompatibleDC(hdc_win); if (!hdc_blit) fatal("hdc_blit failed: 0x%x", GetLastError()); prev_blit = SelectObject(hdc_blit, bl->bitmap); if (prev_blit == NULL || prev_blit == HGDI_ERROR) fatal("SelectObject for hdc_main failed: 0x%x", GetLastError()); if (!BitBlt(hdc_blit, 0, 0, bl->w, bl->h, fe->hdc, x, y, SRCCOPY)) fatal("BitBlt failed: 0x%x", GetLastError()); SelectObject(hdc_blit, prev_blit); DeleteDC(hdc_blit); ReleaseDC(fe->hwnd, hdc_win); } static void win_blitter_load(void *handle, blitter *bl, int x, int y) { frontend *fe = (frontend *)handle; HDC hdc_win, hdc_blit; HBITMAP prev_blit; assert(fe->drawstatus == DRAWING); assert(bl->bitmap); /* we should always have saved before loading */ if (x == BLITTER_FROMSAVED) x = bl->x; if (y == BLITTER_FROMSAVED) y = bl->y; hdc_win = GetDC(fe->hwnd); hdc_blit = CreateCompatibleDC(hdc_win); prev_blit = SelectObject(hdc_blit, bl->bitmap); BitBlt(fe->hdc, x, y, bl->w, bl->h, hdc_blit, 0, 0, SRCCOPY); SelectObject(hdc_blit, prev_blit); DeleteDC(hdc_blit); ReleaseDC(fe->hwnd, hdc_win); } void frontend_default_colour(frontend *fe, float *output) { DWORD c = GetSysColor(COLOR_MENU); /* ick */ output[0] = (float)(GetRValue(c) / 255.0); output[1] = (float)(GetGValue(c) / 255.0); output[2] = (float)(GetBValue(c) / 255.0); } static POINT win_transform_point(frontend *fe, int x, int y) { POINT ret; assert(fe->drawstatus != NOTHING); if (fe->drawstatus == PRINTING) { ret.x = (int)(fe->printoffsetx + fe->printpixelscale * x); ret.y = (int)(fe->printoffsety + fe->printpixelscale * y); } else { ret.x = x; ret.y = y; } return ret; } static void win_text_colour(frontend *fe, int colour) { assert(fe->drawstatus != NOTHING); if (fe->drawstatus == PRINTING) { int hatch; float r, g, b; print_get_colour(fe->dr, colour, fe->printcolour, &hatch, &r, &g, &b); /* * Displaying text in hatched colours is not permitted. */ assert(hatch < 0); SetTextColor(fe->hdc, RGB(r * 255, g * 255, b * 255)); } else { SetTextColor(fe->hdc, fe->colours[colour]); } } static void win_set_brush(frontend *fe, int colour) { HBRUSH br; assert(fe->drawstatus != NOTHING); if (fe->drawstatus == PRINTING) { int hatch; float r, g, b; print_get_colour(fe->dr, colour, fe->printcolour, &hatch, &r, &g, &b); if (hatch < 0) { br = CreateSolidBrush(RGB(r * 255, g * 255, b * 255)); } else { #ifdef _WIN32_WCE /* * This is only ever required during printing, and the * PocketPC port doesn't support printing. */ fatal("CreateHatchBrush not supported"); #else br = CreateHatchBrush(hatch == HATCH_BACKSLASH ? HS_FDIAGONAL : hatch == HATCH_SLASH ? HS_BDIAGONAL : hatch == HATCH_HORIZ ? HS_HORIZONTAL : hatch == HATCH_VERT ? HS_VERTICAL : hatch == HATCH_PLUS ? HS_CROSS : /* hatch == HATCH_X ? */ HS_DIAGCROSS, RGB(0,0,0)); #endif } } else { br = fe->brushes[colour]; } fe->oldbr = SelectObject(fe->hdc, br); } static void win_reset_brush(frontend *fe) { HBRUSH br; assert(fe->drawstatus != NOTHING); br = SelectObject(fe->hdc, fe->oldbr); if (fe->drawstatus == PRINTING) DeleteObject(br); } static void win_set_pen(frontend *fe, int colour, int thin) { HPEN pen; assert(fe->drawstatus != NOTHING); if (fe->drawstatus == PRINTING) { int hatch; float r, g, b; int width = thin ? 0 : fe->linewidth; if (fe->linedotted) width = 0; print_get_colour(fe->dr, colour, fe->printcolour, &hatch, &r, &g, &b); /* * Stroking in hatched colours is not permitted. */ assert(hatch < 0); pen = CreatePen(fe->linedotted ? PS_DOT : PS_SOLID, width, RGB(r * 255, g * 255, b * 255)); } else { pen = fe->pens[colour]; } fe->oldpen = SelectObject(fe->hdc, pen); } static void win_reset_pen(frontend *fe) { HPEN pen; assert(fe->drawstatus != NOTHING); pen = SelectObject(fe->hdc, fe->oldpen); if (fe->drawstatus == PRINTING) DeleteObject(pen); } static void win_clip(void *handle, int x, int y, int w, int h) { frontend *fe = (frontend *)handle; POINT p, q; if (fe->drawstatus == NOTHING) return; p = win_transform_point(fe, x, y); q = win_transform_point(fe, x+w, y+h); IntersectClipRect(fe->hdc, p.x, p.y, q.x, q.y); } static void win_unclip(void *handle) { frontend *fe = (frontend *)handle; if (fe->drawstatus == NOTHING) return; SelectClipRgn(fe->hdc, NULL); } static void win_draw_text(void *handle, int x, int y, int fonttype, int fontsize, int align, int colour, char *text) { frontend *fe = (frontend *)handle; POINT xy; int i; LOGFONT lf; if (fe->drawstatus == NOTHING) return; if (fe->drawstatus == PRINTING) fontsize = (int)(fontsize * fe->printpixelscale); xy = win_transform_point(fe, x, y); /* * Find or create the font. */ for (i = fe->fontstart; i < fe->nfonts; i++) if (fe->fonts[i].type == fonttype && fe->fonts[i].size == fontsize) break; if (i == fe->nfonts) { if (fe->fontsize <= fe->nfonts) { fe->fontsize = fe->nfonts + 10; fe->fonts = sresize(fe->fonts, fe->fontsize, struct font); } fe->nfonts++; fe->fonts[i].type = fonttype; fe->fonts[i].size = fontsize; memset (&lf, 0, sizeof(LOGFONT)); lf.lfHeight = -fontsize; lf.lfWeight = (fe->drawstatus == PRINTING ? 0 : FW_BOLD); lf.lfCharSet = DEFAULT_CHARSET; lf.lfOutPrecision = OUT_DEFAULT_PRECIS; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfQuality = DEFAULT_QUALITY; lf.lfPitchAndFamily = (fonttype == FONT_FIXED ? FIXED_PITCH | FF_DONTCARE : VARIABLE_PITCH | FF_SWISS); #ifdef _WIN32_WCE wcscpy(lf.lfFaceName, TEXT("Tahoma")); #endif fe->fonts[i].font = CreateFontIndirect(&lf); } /* * Position and draw the text. */ { HFONT oldfont; TEXTMETRIC tm; SIZE size; TCHAR wText[256]; MultiByteToWideChar (CP_UTF8, 0, text, -1, wText, 256); oldfont = SelectObject(fe->hdc, fe->fonts[i].font); if (GetTextMetrics(fe->hdc, &tm)) { if (align & ALIGN_VCENTRE) xy.y -= (tm.tmAscent+tm.tmDescent)/2; else xy.y -= tm.tmAscent; } if (GetTextExtentPoint32W(fe->hdc, wText, wcslen(wText), &size)) { if (align & ALIGN_HCENTRE) xy.x -= size.cx / 2; else if (align & ALIGN_HRIGHT) xy.x -= size.cx; } SetBkMode(fe->hdc, TRANSPARENT); win_text_colour(fe, colour); ExtTextOutW(fe->hdc, xy.x, xy.y, 0, NULL, wText, wcslen(wText), NULL); SelectObject(fe->hdc, oldfont); } } static void win_draw_rect(void *handle, int x, int y, int w, int h, int colour) { frontend *fe = (frontend *)handle; POINT p, q; if (fe->drawstatus == NOTHING) return; if (fe->drawstatus == DRAWING && w == 1 && h == 1) { /* * Rectangle() appears to get uppity if asked to draw a 1x1 * rectangle, presumably on the grounds that that's beneath * its dignity and you ought to be using SetPixel instead. * So I will. */ SetPixel(fe->hdc, x, y, fe->colours[colour]); } else { win_set_brush(fe, colour); win_set_pen(fe, colour, TRUE); p = win_transform_point(fe, x, y); q = win_transform_point(fe, x+w, y+h); Rectangle(fe->hdc, p.x, p.y, q.x, q.y); win_reset_brush(fe); win_reset_pen(fe); } } static void win_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour) { frontend *fe = (frontend *)handle; POINT pp[2]; if (fe->drawstatus == NOTHING) return; win_set_pen(fe, colour, FALSE); pp[0] = win_transform_point(fe, x1, y1); pp[1] = win_transform_point(fe, x2, y2); Polyline(fe->hdc, pp, 2); if (fe->drawstatus == DRAWING) SetPixel(fe->hdc, pp[1].x, pp[1].y, fe->colours[colour]); win_reset_pen(fe); } static void win_draw_circle(void *handle, int cx, int cy, int radius, int fillcolour, int outlinecolour) { frontend *fe = (frontend *)handle; POINT p, q; assert(outlinecolour >= 0); if (fe->drawstatus == NOTHING) return; if (fillcolour >= 0) win_set_brush(fe, fillcolour); else fe->oldbr = SelectObject(fe->hdc, GetStockObject(NULL_BRUSH)); win_set_pen(fe, outlinecolour, FALSE); p = win_transform_point(fe, cx - radius, cy - radius); q = win_transform_point(fe, cx + radius, cy + radius); Ellipse(fe->hdc, p.x, p.y, q.x+1, q.y+1); win_reset_brush(fe); win_reset_pen(fe); } static void win_draw_polygon(void *handle, int *coords, int npoints, int fillcolour, int outlinecolour) { frontend *fe = (frontend *)handle; POINT *pts; int i; if (fe->drawstatus == NOTHING) return; pts = snewn(npoints+1, POINT); for (i = 0; i <= npoints; i++) { int j = (i < npoints ? i : 0); pts[i] = win_transform_point(fe, coords[j*2], coords[j*2+1]); } assert(outlinecolour >= 0); if (fillcolour >= 0) { win_set_brush(fe, fillcolour); win_set_pen(fe, outlinecolour, FALSE); Polygon(fe->hdc, pts, npoints); win_reset_brush(fe); win_reset_pen(fe); } else { win_set_pen(fe, outlinecolour, FALSE); Polyline(fe->hdc, pts, npoints+1); win_reset_pen(fe); } sfree(pts); } static void win_start_draw(void *handle) { frontend *fe = (frontend *)handle; HDC hdc_win; assert(fe->drawstatus == NOTHING); hdc_win = GetDC(fe->hwnd); fe->hdc = CreateCompatibleDC(hdc_win); fe->prevbm = SelectObject(fe->hdc, fe->bitmap); ReleaseDC(fe->hwnd, hdc_win); fe->clip = NULL; #ifndef _WIN32_WCE SetMapMode(fe->hdc, MM_TEXT); #endif fe->drawstatus = DRAWING; } static void win_draw_update(void *handle, int x, int y, int w, int h) { frontend *fe = (frontend *)handle; RECT r; if (fe->drawstatus != DRAWING) return; r.left = x; r.top = y; r.right = x + w; r.bottom = y + h; OffsetRect(&r, fe->bitmapPosition.left, fe->bitmapPosition.top); InvalidateRect(fe->hwnd, &r, FALSE); } static void win_end_draw(void *handle) { frontend *fe = (frontend *)handle; assert(fe->drawstatus == DRAWING); SelectObject(fe->hdc, fe->prevbm); DeleteDC(fe->hdc); if (fe->clip) { DeleteObject(fe->clip); fe->clip = NULL; } fe->drawstatus = NOTHING; } static void win_line_width(void *handle, float width) { frontend *fe = (frontend *)handle; assert(fe->drawstatus != DRAWING); if (fe->drawstatus == NOTHING) return; fe->linewidth = (int)(width * fe->printpixelscale); } static void win_line_dotted(void *handle, int dotted) { frontend *fe = (frontend *)handle; assert(fe->drawstatus != DRAWING); if (fe->drawstatus == NOTHING) return; fe->linedotted = dotted; } static void win_begin_doc(void *handle, int pages) { frontend *fe = (frontend *)handle; assert(fe->drawstatus != DRAWING); if (fe->drawstatus == NOTHING) return; if (StartDoc(fe->hdc, &fe->di) <= 0) { char *e = geterrstr(); MessageBox(fe->hwnd, e, "Error starting to print", MB_ICONERROR | MB_OK); sfree(e); fe->drawstatus = NOTHING; } /* * Push a marker on the font stack so that we won't use the * same fonts for printing and drawing. (This is because * drawing seems to look generally better in bold, but printing * is better not in bold.) */ fe->fontstart = fe->nfonts; } static void win_begin_page(void *handle, int number) { frontend *fe = (frontend *)handle; assert(fe->drawstatus != DRAWING); if (fe->drawstatus == NOTHING) return; if (StartPage(fe->hdc) <= 0) { char *e = geterrstr(); MessageBox(fe->hwnd, e, "Error starting a page", MB_ICONERROR | MB_OK); sfree(e); fe->drawstatus = NOTHING; } } static void win_begin_puzzle(void *handle, float xm, float xc, float ym, float yc, int pw, int ph, float wmm) { frontend *fe = (frontend *)handle; int ppw, pph, pox, poy; float mmpw, mmph, mmox, mmoy; float scale; assert(fe->drawstatus != DRAWING); if (fe->drawstatus == NOTHING) return; ppw = GetDeviceCaps(fe->hdc, HORZRES); pph = GetDeviceCaps(fe->hdc, VERTRES); mmpw = (float)GetDeviceCaps(fe->hdc, HORZSIZE); mmph = (float)GetDeviceCaps(fe->hdc, VERTSIZE); /* * Compute the puzzle's position on the logical page. */ mmox = xm * mmpw + xc; mmoy = ym * mmph + yc; /* * Work out what that comes to in pixels. */ pox = (int)(mmox * (float)ppw / mmpw); poy = (int)(mmoy * (float)pph / mmph); /* * And determine the scale. * * I need a scale such that the maximum puzzle-coordinate * extent of the rectangle (pw * scale) is equal to the pixel * equivalent of the puzzle's millimetre width (wmm * ppw / * mmpw). */ scale = (wmm * ppw) / (mmpw * pw); /* * Now store pox, poy and scale for use in the main drawing * functions. */ fe->printoffsetx = pox; fe->printoffsety = poy; fe->printpixelscale = scale; fe->linewidth = 1; fe->linedotted = FALSE; } static void win_end_puzzle(void *handle) { /* Nothing needs to be done here. */ } static void win_end_page(void *handle, int number) { frontend *fe = (frontend *)handle; assert(fe->drawstatus != DRAWING); if (fe->drawstatus == NOTHING) return; if (EndPage(fe->hdc) <= 0) { char *e = geterrstr(); MessageBox(fe->hwnd, e, "Error finishing a page", MB_ICONERROR | MB_OK); sfree(e); fe->drawstatus = NOTHING; } } static void win_end_doc(void *handle) { frontend *fe = (frontend *)handle; assert(fe->drawstatus != DRAWING); /* * Free all the fonts created since we began printing. */ while (fe->nfonts > fe->fontstart) { fe->nfonts--; DeleteObject(fe->fonts[fe->nfonts].font); } fe->fontstart = 0; /* * The MSDN web site sample code doesn't bother to call EndDoc * if an error occurs half way through printing. I expect doing * so would cause the erroneous document to actually be * printed, or something equally undesirable. */ if (fe->drawstatus == NOTHING) return; if (EndDoc(fe->hdc) <= 0) { char *e = geterrstr(); MessageBox(fe->hwnd, e, "Error finishing printing", MB_ICONERROR | MB_OK); sfree(e); fe->drawstatus = NOTHING; } } char *win_text_fallback(void *handle, const char *const *strings, int nstrings) { /* * We assume Windows can cope with any UTF-8 likely to be * emitted by a puzzle. */ return dupstr(strings[0]); } const struct drawing_api win_drawing = { win_draw_text, win_draw_rect, win_draw_line, win_draw_polygon, win_draw_circle, win_draw_update, win_clip, win_unclip, win_start_draw, win_end_draw, win_status_bar, win_blitter_new, win_blitter_free, win_blitter_save, win_blitter_load, win_begin_doc, win_begin_page, win_begin_puzzle, win_end_puzzle, win_end_page, win_end_doc, win_line_width, win_line_dotted, win_text_fallback, }; void print(frontend *fe) { #ifndef _WIN32_WCE PRINTDLG pd; char doctitle[256]; document *doc; midend *nme = NULL; /* non-interactive midend for bulk puzzle generation */ int i; char *err = NULL; /* * Create our document structure and fill it up with puzzles. */ doc = document_new(fe->printw, fe->printh, fe->printscale / 100.0F); for (i = 0; i < fe->printcount; i++) { if (i == 0 && fe->printcurr) { err = midend_print_puzzle(fe->me, doc, fe->printsolns); } else { if (!nme) { game_params *params; nme = midend_new(NULL, fe->game, NULL, NULL); /* * Set the non-interactive mid-end to have the same * parameters as the standard one. */ params = midend_get_params(fe->me); midend_set_params(nme, params); fe->game->free_params(params); } midend_new_game(nme); err = midend_print_puzzle(nme, doc, fe->printsolns); } if (err) break; } if (nme) midend_free(nme); if (err) { MessageBox(fe->hwnd, err, "Error preparing puzzles for printing", MB_ICONERROR | MB_OK); document_free(doc); return; } memset(&pd, 0, sizeof(pd)); pd.lStructSize = sizeof(pd); pd.hwndOwner = fe->hwnd; pd.hDevMode = NULL; pd.hDevNames = NULL; pd.Flags = PD_USEDEVMODECOPIESANDCOLLATE | PD_RETURNDC | PD_NOPAGENUMS | PD_NOSELECTION; pd.nCopies = 1; pd.nFromPage = pd.nToPage = 0xFFFF; pd.nMinPage = pd.nMaxPage = 1; if (!PrintDlg(&pd)) { document_free(doc); return; } /* * Now pd.hDC is a device context for the printer. */ /* * FIXME: IWBNI we put up an Abort box here. */ memset(&fe->di, 0, sizeof(fe->di)); fe->di.cbSize = sizeof(fe->di); sprintf(doctitle, "Printed puzzles from %s (from Simon Tatham's" " Portable Puzzle Collection)", fe->game->name); fe->di.lpszDocName = doctitle; fe->di.lpszOutput = NULL; fe->di.lpszDatatype = NULL; fe->di.fwType = 0; fe->drawstatus = PRINTING; fe->hdc = pd.hDC; fe->dr = drawing_new(&win_drawing, NULL, fe); document_print(doc, fe->dr); drawing_free(fe->dr); fe->dr = NULL; fe->drawstatus = NOTHING; DeleteDC(pd.hDC); document_free(doc); #endif } void deactivate_timer(frontend *fe) { if (!fe) return; /* for non-interactive midend */ if (fe->hwnd) KillTimer(fe->hwnd, fe->timer); fe->timer = 0; } void activate_timer(frontend *fe) { if (!fe) return; /* for non-interactive midend */ if (!fe->timer) { fe->timer = SetTimer(fe->hwnd, 1, 20, NULL); fe->timer_last_tickcount = GetTickCount(); } } void write_clip(HWND hwnd, char *data) { HGLOBAL clipdata; int len, i, j; char *data2; void *lock; /* * Windows expects CRLF in the clipboard, so we must convert * any \n that has come out of the puzzle backend. */ len = 0; for (i = 0; data[i]; i++) { if (data[i] == '\n') len++; len++; } data2 = snewn(len+1, char); j = 0; for (i = 0; data[i]; i++) { if (data[i] == '\n') data2[j++] = '\r'; data2[j++] = data[i]; } assert(j == len); data2[j] = '\0'; clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1); if (!clipdata) { sfree(data2); return; } lock = GlobalLock(clipdata); if (!lock) { GlobalFree(clipdata); sfree(data2); return; } memcpy(lock, data2, len); ((unsigned char *) lock)[len] = 0; GlobalUnlock(clipdata); if (OpenClipboard(hwnd)) { EmptyClipboard(); SetClipboardData(CF_TEXT, clipdata); CloseClipboard(); } else GlobalFree(clipdata); sfree(data2); } /* * Set up Help and see if we can find a help file. */ static void init_help(void) { #ifndef _WIN32_WCE char b[2048], *p, *q, *r; FILE *fp; /* * Find the executable file path, so we can look alongside * it for help files. Trim the filename off the end. */ GetModuleFileName(NULL, b, sizeof(b) - 1); r = b; p = strrchr(b, '\\'); if (p && p >= r) r = p+1; q = strrchr(b, ':'); if (q && q >= r) r = q+1; #ifndef NO_HTMLHELP /* * Try HTML Help first. */ strcpy(r, CHM_FILE_NAME); if ( (fp = fopen(b, "r")) != NULL) { fclose(fp); /* * We have a .CHM. See if we can use it. */ hh_dll = LoadLibrary("hhctrl.ocx"); if (hh_dll) { htmlhelp = (htmlhelp_t)GetProcAddress(hh_dll, "HtmlHelpA"); if (!htmlhelp) FreeLibrary(hh_dll); } if (htmlhelp) { help_path = dupstr(b); help_type = CHM; return; } } #endif /* NO_HTMLHELP */ /* * Now try old-style .HLP. */ strcpy(r, HELP_FILE_NAME); if ( (fp = fopen(b, "r")) != NULL) { fclose(fp); help_path = dupstr(b); help_type = HLP; /* * See if there's a .CNT file alongside it. */ strcpy(r, HELP_CNT_NAME); if ( (fp = fopen(b, "r")) != NULL) { fclose(fp); help_has_contents = TRUE; } else help_has_contents = FALSE; return; } help_type = NONE; /* didn't find any */ #endif } #ifndef _WIN32_WCE /* * Start Help. */ static void start_help(frontend *fe, const char *topic) { char *str = NULL; int cmd; switch (help_type) { case HLP: assert(help_path); if (topic) { str = snewn(10+strlen(topic), char); sprintf(str, "JI(`',`%s')", topic); cmd = HELP_COMMAND; } else if (help_has_contents) { cmd = HELP_FINDER; } else { cmd = HELP_CONTENTS; } WinHelp(fe->hwnd, help_path, cmd, (DWORD)str); fe->help_running = TRUE; break; case CHM: #ifndef NO_HTMLHELP assert(help_path); assert(htmlhelp); if (topic) { str = snewn(20 + strlen(topic) + strlen(help_path), char); sprintf(str, "%s::/%s.html>main", help_path, topic); } else { str = dupstr(help_path); } htmlhelp(fe->hwnd, str, HH_DISPLAY_TOPIC, 0); fe->help_running = TRUE; break; #endif /* NO_HTMLHELP */ case NONE: assert(!"This shouldn't happen"); break; } sfree(str); } /* * Stop Help on window cleanup. */ static void stop_help(frontend *fe) { if (fe->help_running) { switch (help_type) { case HLP: WinHelp(fe->hwnd, help_path, HELP_QUIT, 0); break; case CHM: #ifndef NO_HTMLHELP assert(htmlhelp); htmlhelp(NULL, NULL, HH_CLOSE_ALL, 0); break; #endif /* NO_HTMLHELP */ case NONE: assert(!"This shouldn't happen"); break; } fe->help_running = FALSE; } } #endif /* * Terminate Help on process exit. */ static void cleanup_help(void) { /* Nothing to do currently. * (If we were running HTML Help single-threaded, this is where we'd * call HH_UNINITIALIZE.) */ } static int get_statusbar_height(frontend *fe) { int sy; if (fe->statusbar) { RECT sr; GetWindowRect(fe->statusbar, &sr); sy = sr.bottom - sr.top; } else { sy = 0; } return sy; } static void adjust_statusbar(frontend *fe, RECT *r) { int sy; if (!fe->statusbar) return; sy = get_statusbar_height(fe); #ifndef _WIN32_WCE SetWindowPos(fe->statusbar, NULL, 0, r->bottom-r->top-sy, r->right-r->left, sy, SWP_NOZORDER); #endif } static void get_menu_size(HWND wh, RECT *r) { HMENU bar = GetMenu(wh); RECT rect; int i; SetRect(r, 0, 0, 0, 0); for (i = 0; i < GetMenuItemCount(bar); i++) { GetMenuItemRect(wh, bar, i, &rect); UnionRect(r, r, &rect); } } /* * Given a proposed new puzzle size (cx,cy), work out the actual * puzzle size that would be (px,py) and the window size including * furniture (wx,wy). */ static int check_window_resize(frontend *fe, int cx, int cy, int *px, int *py, int *wx, int *wy) { RECT r; int x, y, sy = get_statusbar_height(fe), changed = 0; /* disallow making window thinner than menu bar */ x = max(cx, fe->xmin); y = max(cy - sy, fe->ymin); /* * See if we actually got the window size we wanted, and adjust * the puzzle size if not. */ midend_size(fe->me, &x, &y, TRUE); if (x != cx || y != cy) { /* * Resize the window, now we know what size we _really_ * want it to be. */ r.left = r.top = 0; r.right = x; r.bottom = y + sy; AdjustWindowRectEx(&r, WINFLAGS, TRUE, 0); *wx = r.right - r.left; *wy = r.bottom - r.top; changed = 1; } *px = x; *py = y; fe->puzz_scale = (float)midend_tilesize(fe->me) / (float)fe->game->preferred_tilesize; return changed; } /* * Given the current window size, make sure it's sane for the * current puzzle and resize if necessary. */ static void check_window_size(frontend *fe, int *px, int *py) { RECT r; int wx, wy, cx, cy; GetClientRect(fe->hwnd, &r); cx = r.right - r.left; cy = r.bottom - r.top; if (check_window_resize(fe, cx, cy, px, py, &wx, &wy)) { #ifdef _WIN32_WCE SetWindowPos(fe->hwnd, NULL, 0, 0, wx, wy, SWP_NOMOVE | SWP_NOZORDER); #endif ; } GetClientRect(fe->hwnd, &r); adjust_statusbar(fe, &r); } static void get_max_puzzle_size(frontend *fe, int *x, int *y) { RECT r, sr; if (SystemParametersInfo(SPI_GETWORKAREA, 0, &sr, FALSE)) { *x = sr.right - sr.left; *y = sr.bottom - sr.top; r.left = 100; r.right = 200; r.top = 100; r.bottom = 200; AdjustWindowRectEx(&r, WINFLAGS, TRUE, 0); *x -= r.right - r.left - 100; *y -= r.bottom - r.top - 100; } else { *x = *y = INT_MAX; } if (fe->statusbar != NULL) { GetWindowRect(fe->statusbar, &sr); *y -= sr.bottom - sr.top; } } #ifdef _WIN32_WCE /* Toolbar buttons on the numeric pad */ static TBBUTTON tbNumpadButtons[] = { {0, IDM_KEYEMUL + '1', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}, {1, IDM_KEYEMUL + '2', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}, {2, IDM_KEYEMUL + '3', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}, {3, IDM_KEYEMUL + '4', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}, {4, IDM_KEYEMUL + '5', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}, {5, IDM_KEYEMUL + '6', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}, {6, IDM_KEYEMUL + '7', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}, {7, IDM_KEYEMUL + '8', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}, {8, IDM_KEYEMUL + '9', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}, {9, IDM_KEYEMUL + ' ', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1} }; #endif /* * Allocate a new frontend structure and create its main window. */ static frontend *frontend_new(HINSTANCE inst) { frontend *fe; const char *nogame = "Puzzles (no game selected)"; fe = snew(frontend); fe->inst = inst; fe->game = NULL; fe->me = NULL; fe->timer = 0; fe->hwnd = NULL; fe->help_running = FALSE; fe->drawstatus = NOTHING; fe->dr = NULL; fe->fontstart = 0; fe->fonts = NULL; fe->nfonts = fe->fontsize = 0; fe->colours = NULL; fe->brushes = NULL; fe->pens = NULL; fe->puzz_scale = 1.0; #ifdef _WIN32_WCE MultiByteToWideChar (CP_ACP, 0, nogame, -1, wGameName, 256); fe->hwnd = CreateWindowEx(0, wClassName, wGameName, WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, inst, NULL); { SHMENUBARINFO mbi; RECT rc, rcBar, rcTB, rcClient; memset (&mbi, 0, sizeof(SHMENUBARINFO)); mbi.cbSize = sizeof(SHMENUBARINFO); mbi.hwndParent = fe->hwnd; mbi.nToolBarId = IDR_MENUBAR1; mbi.hInstRes = inst; SHCreateMenuBar(&mbi); GetWindowRect(fe->hwnd, &rc); GetWindowRect(mbi.hwndMB, &rcBar); rc.bottom -= rcBar.bottom - rcBar.top; MoveWindow(fe->hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, FALSE); fe->numpad = NULL; } #else fe->hwnd = CreateWindowEx(0, CLASSNAME, nogame, WS_OVERLAPPEDWINDOW &~ (WS_MAXIMIZEBOX), CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, inst, NULL); if (!fe->hwnd) { DWORD lerr = GetLastError(); printf("no window: 0x%x\n", lerr); } #endif fe->gamemenu = NULL; fe->presets = NULL; fe->statusbar = NULL; fe->bitmap = NULL; SetWindowLong(fe->hwnd, GWL_USERDATA, (LONG)fe); return fe; } static void savefile_write(void *wctx, void *buf, int len) { FILE *fp = (FILE *)wctx; fwrite(buf, 1, len, fp); } static int savefile_read(void *wctx, void *buf, int len) { FILE *fp = (FILE *)wctx; int ret; ret = fread(buf, 1, len, fp); return (ret == len); } /* * Create an appropriate midend structure to go in a puzzle window, * given a game type and/or a command-line argument. * * 'arg' can be either a game ID string (descriptive, random, or a * plain set of parameters) or the filename of a save file. The two * boolean flag arguments indicate which possibilities are * permissible. */ static midend *midend_for_new_game(frontend *fe, const game *cgame, char *arg, int maybe_game_id, int maybe_save_file, char **error) { midend *me = NULL; if (!arg) { if (me) midend_free(me); me = midend_new(fe, cgame, &win_drawing, fe); midend_new_game(me); } else { FILE *fp; char *err_param, *err_load; /* * See if arg is a valid filename of a save game file. */ err_load = NULL; if (maybe_save_file && (fp = fopen(arg, "r")) != NULL) { const game *loadgame; #ifdef COMBINED /* * Find out what kind of game is stored in the save * file; if we're going to end up loading that, it * will have to override our caller's judgment as to * what game to initialise our midend with. */ char *id_name; err_load = identify_game(&id_name, savefile_read, fp); if (!err_load) { int i; for (i = 0; i < gamecount; i++) if (!strcmp(id_name, gamelist[i]->name)) break; if (i == gamecount) { err_load = "Save file is for a game not supported by" " this program"; } else { loadgame = gamelist[i]; rewind(fp); /* go back to the start for actual load */ } } #else loadgame = cgame; #endif if (!err_load) { if (me) midend_free(me); me = midend_new(fe, loadgame, &win_drawing, fe); err_load = midend_deserialise(me, savefile_read, fp); } } else { err_load = "Unable to open file"; } if (maybe_game_id && (!maybe_save_file || err_load)) { /* * See if arg is a game description. */ if (me) midend_free(me); me = midend_new(fe, cgame, &win_drawing, fe); err_param = midend_game_id(me, arg); if (!err_param) { midend_new_game(me); } else { if (maybe_save_file) { *error = snewn(256 + strlen(arg) + strlen(err_param) + strlen(err_load), char); sprintf(*error, "Supplied argument \"%s\" is neither a" " game ID (%s) nor a save file (%s)", arg, err_param, err_load); } else { *error = dupstr(err_param); } midend_free(me); sfree(fe); return NULL; } } else if (err_load) { *error = dupstr(err_load); midend_free(me); sfree(fe); return NULL; } } return me; } /* * Populate a frontend structure with a new midend structure, and * create any window furniture that it needs. * * Previously-allocated memory and window furniture will be freed by * this function. * */ static int fe_set_midend(frontend *fe, midend *me) { int x, y; RECT r; if (fe->me) midend_free(fe->me); fe->me = me; fe->game = midend_which_game(fe->me); { int i, ncolours; float *colours; colours = midend_colours(fe->me, &ncolours); if (fe->colours) sfree(fe->colours); if (fe->brushes) sfree(fe->brushes); if (fe->pens) sfree(fe->pens); fe->colours = snewn(ncolours, COLORREF); fe->brushes = snewn(ncolours, HBRUSH); fe->pens = snewn(ncolours, HPEN); for (i = 0; i < ncolours; i++) { fe->colours[i] = RGB(255 * colours[i*3+0], 255 * colours[i*3+1], 255 * colours[i*3+2]); fe->brushes[i] = CreateSolidBrush(fe->colours[i]); fe->pens[i] = CreatePen(PS_SOLID, 1, fe->colours[i]); } sfree(colours); } if (fe->statusbar) DestroyWindow(fe->statusbar); if (midend_wants_statusbar(fe->me)) { fe->statusbar = CreateWindowEx(0, STATUSCLASSNAME, TEXT("ooh"), WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, /* status bar does these */ NULL, NULL, fe->inst, NULL); } else fe->statusbar = NULL; get_max_puzzle_size(fe, &x, &y); midend_size(fe->me, &x, &y, FALSE); r.left = r.top = 0; r.right = x; r.bottom = y; AdjustWindowRectEx(&r, WINFLAGS, TRUE, 0); #ifdef _WIN32_WCE if (fe->numpad) DestroyWindow(fe->numpad); if (fe->game->flags & REQUIRE_NUMPAD) { fe->numpad = CreateToolbarEx (fe->hwnd, WS_VISIBLE | WS_CHILD | CCS_NOPARENTALIGN | TBSTYLE_FLAT, 0, 10, fe->inst, IDR_PADTOOLBAR, tbNumpadButtons, sizeof (tbNumpadButtons) / sizeof (TBBUTTON), 0, 0, 14, 15, sizeof (TBBUTTON)); GetWindowRect(fe->numpad, &rcTB); GetClientRect(fe->hwnd, &rcClient); MoveWindow(fe->numpad, 0, rcClient.bottom - (rcTB.bottom - rcTB.top) - 1, rcClient.right, rcTB.bottom - rcTB.top, FALSE); SendMessage(fe->numpad, TB_SETINDENT, (rcClient.right - (10 * 21)) / 2, 0); } else { fe->numpad = NULL; } MultiByteToWideChar (CP_ACP, 0, fe->game->name, -1, wGameName, 256); SetWindowText(fe->hwnd, wGameName); #else SetWindowText(fe->hwnd, fe->game->name); #endif if (fe->statusbar) DestroyWindow(fe->statusbar); if (midend_wants_statusbar(fe->me)) { RECT sr; fe->statusbar = CreateWindowEx(0, STATUSCLASSNAME, TEXT("ooh"), WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, /* status bar does these */ fe->hwnd, NULL, fe->inst, NULL); #ifdef _WIN32_WCE /* Flat status bar looks better on the Pocket PC */ SendMessage(fe->statusbar, SB_SIMPLE, (WPARAM) TRUE, 0); SendMessage(fe->statusbar, SB_SETTEXT, (WPARAM) 255 | SBT_NOBORDERS, (LPARAM) L""); #endif /* * Now resize the window to take account of the status bar. */ GetWindowRect(fe->statusbar, &sr); GetWindowRect(fe->hwnd, &r); #ifndef _WIN32_WCE SetWindowPos(fe->hwnd, NULL, 0, 0, r.right - r.left, r.bottom - r.top + sr.bottom - sr.top, SWP_NOMOVE | SWP_NOZORDER); #endif } else { fe->statusbar = NULL; } { HMENU oldmenu = GetMenu(fe->hwnd); #ifndef _WIN32_WCE HMENU bar = CreateMenu(); HMENU menu = CreateMenu(); RECT menusize; AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)menu, "&Game"); #else HMENU menu = SHGetSubMenu(SHFindMenuBar(fe->hwnd), ID_GAME); DeleteMenu(menu, 0, MF_BYPOSITION); #endif fe->gamemenu = menu; AppendMenu(menu, MF_ENABLED, IDM_NEW, TEXT("&New")); AppendMenu(menu, MF_ENABLED, IDM_RESTART, TEXT("&Restart")); #ifndef _WIN32_WCE /* ...here I run out of sensible accelerator characters. */ AppendMenu(menu, MF_ENABLED, IDM_DESC, TEXT("Speci&fic...")); AppendMenu(menu, MF_ENABLED, IDM_SEED, TEXT("Rando&m Seed...")); #endif if (fe->presets) sfree(fe->presets); if ((fe->npresets = midend_num_presets(fe->me)) > 0 || fe->game->can_configure) { int i; #ifndef _WIN32_WCE HMENU sub = CreateMenu(); AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)sub, "&Type"); #else HMENU sub = SHGetSubMenu(SHFindMenuBar(fe->hwnd), ID_TYPE); DeleteMenu(sub, 0, MF_BYPOSITION); #endif fe->presets = snewn(fe->npresets, game_params *); for (i = 0; i < fe->npresets; i++) { char *name; #ifdef _WIN32_WCE TCHAR wName[255]; #endif midend_fetch_preset(fe->me, i, &name, &fe->presets[i]); /* * FIXME: we ought to go through and do something * with ampersands here. */ #ifndef _WIN32_WCE AppendMenu(sub, MF_ENABLED, IDM_PRESETS + 0x10 * i, name); #else MultiByteToWideChar (CP_ACP, 0, name, -1, wName, 255); AppendMenu(sub, MF_ENABLED, IDM_PRESETS + 0x10 * i, wName); #endif } if (fe->game->can_configure) { AppendMenu(sub, MF_ENABLED, IDM_CONFIG, TEXT("&Custom...")); } fe->typemenu = sub; } else { fe->typemenu = INVALID_HANDLE_VALUE; fe->presets = NULL; } #ifdef COMBINED #ifdef _WIN32_WCE #error Windows CE does not support COMBINED build. #endif { HMENU games = CreateMenu(); int i; AppendMenu(menu, MF_SEPARATOR, 0, 0); AppendMenu(menu, MF_ENABLED|MF_POPUP, (UINT)games, "&Other"); for (i = 0; i < gamecount; i++) { if (strcmp(gamelist[i]->name, fe->game->name) != 0) { /* only include those games that aren't the same as the * game we're currently playing. */ AppendMenu(games, MF_ENABLED, IDM_GAMES + i, gamelist[i]->name); } } } #endif AppendMenu(menu, MF_SEPARATOR, 0, 0); #ifndef _WIN32_WCE AppendMenu(menu, MF_ENABLED, IDM_LOAD, TEXT("&Load...")); AppendMenu(menu, MF_ENABLED, IDM_SAVE, TEXT("&Save...")); AppendMenu(menu, MF_SEPARATOR, 0, 0); if (fe->game->can_print) { AppendMenu(menu, MF_ENABLED, IDM_PRINT, TEXT("&Print...")); AppendMenu(menu, MF_SEPARATOR, 0, 0); } #endif AppendMenu(menu, MF_ENABLED, IDM_UNDO, TEXT("Undo")); AppendMenu(menu, MF_ENABLED, IDM_REDO, TEXT("Redo")); #ifndef _WIN32_WCE if (fe->game->can_format_as_text_ever) { AppendMenu(menu, MF_SEPARATOR, 0, 0); AppendMenu(menu, MF_ENABLED, IDM_COPY, TEXT("&Copy")); } #endif if (fe->game->can_solve) { AppendMenu(menu, MF_SEPARATOR, 0, 0); AppendMenu(menu, MF_ENABLED, IDM_SOLVE, TEXT("Sol&ve")); } AppendMenu(menu, MF_SEPARATOR, 0, 0); #ifndef _WIN32_WCE AppendMenu(menu, MF_ENABLED, IDM_QUIT, TEXT("E&xit")); menu = CreateMenu(); AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)menu, TEXT("&Help")); #endif AppendMenu(menu, MF_ENABLED, IDM_ABOUT, TEXT("&About")); #ifndef _WIN32_WCE if (help_type != NONE) { char *item; AppendMenu(menu, MF_SEPARATOR, 0, 0); AppendMenu(menu, MF_ENABLED, IDM_HELPC, TEXT("&Contents")); assert(fe->game->name); item = snewn(10+strlen(fe->game->name), char); /*ick*/ sprintf(item, "&Help on %s", fe->game->name); AppendMenu(menu, MF_ENABLED, IDM_GAMEHELP, item); sfree(item); } DestroyMenu(oldmenu); SetMenu(fe->hwnd, bar); get_menu_size(fe->hwnd, &menusize); fe->xmin = (menusize.right - menusize.left) + 25; #endif } if (fe->bitmap) DeleteObject(fe->bitmap); fe->bitmap = NULL; new_game_size(fe, fe->puzz_scale); /* initialises fe->bitmap */ return 0; } static void show_window(frontend *fe) { ShowWindow(fe->hwnd, SW_SHOWNORMAL); SetForegroundWindow(fe->hwnd); update_type_menu_tick(fe); update_copy_menu_greying(fe); midend_redraw(fe->me); } #ifdef _WIN32_WCE static HFONT dialog_title_font() { static HFONT hf = NULL; LOGFONT lf; if (hf) return hf; memset (&lf, 0, sizeof(LOGFONT)); lf.lfHeight = -11; /* - ((8 * GetDeviceCaps(hdc, LOGPIXELSY)) / 72) */ lf.lfWeight = FW_BOLD; wcscpy(lf.lfFaceName, TEXT("Tahoma")); return hf = CreateFontIndirect(&lf); } static void make_dialog_full_screen(HWND hwnd) { SHINITDLGINFO shidi; /* Make dialog full screen */ shidi.dwMask = SHIDIM_FLAGS; shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIZEDLGFULLSCREEN | SHIDIF_EMPTYMENU; shidi.hDlg = hwnd; SHInitDialog(&shidi); } #endif static int CALLBACK AboutDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { frontend *fe = (frontend *)GetWindowLong(hwnd, GWL_USERDATA); switch (msg) { case WM_INITDIALOG: #ifdef _WIN32_WCE { char title[256]; make_dialog_full_screen(hwnd); sprintf(title, "About %.250s", fe->game->name); SetDlgItemTextA(hwnd, IDC_ABOUT_CAPTION, title); SendDlgItemMessage(hwnd, IDC_ABOUT_CAPTION, WM_SETFONT, (WPARAM) dialog_title_font(), 0); SetDlgItemTextA(hwnd, IDC_ABOUT_GAME, fe->game->name); SetDlgItemTextA(hwnd, IDC_ABOUT_VERSION, ver); } #endif return TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK) #ifdef _WIN32_WCE EndDialog(hwnd, 1); #else fe->dlg_done = 1; #endif return 0; case WM_CLOSE: #ifdef _WIN32_WCE EndDialog(hwnd, 1); #else fe->dlg_done = 1; #endif return 0; } return 0; } /* * Wrappers on midend_{get,set}_config, which extend the CFG_* * enumeration to add CFG_PRINT. */ static config_item *frontend_get_config(frontend *fe, int which, char **wintitle) { if (which < CFG_FRONTEND_SPECIFIC) { return midend_get_config(fe->me, which, wintitle); } else if (which == CFG_PRINT) { config_item *ret; int i; *wintitle = snewn(40 + strlen(fe->game->name), char); sprintf(*wintitle, "%s print setup", fe->game->name); ret = snewn(8, config_item); i = 0; ret[i].name = "Number of puzzles to print"; ret[i].type = C_STRING; ret[i].sval = dupstr("1"); ret[i].ival = 0; i++; ret[i].name = "Number of puzzles across the page"; ret[i].type = C_STRING; ret[i].sval = dupstr("1"); ret[i].ival = 0; i++; ret[i].name = "Number of puzzles down the page"; ret[i].type = C_STRING; ret[i].sval = dupstr("1"); ret[i].ival = 0; i++; ret[i].name = "Percentage of standard size"; ret[i].type = C_STRING; ret[i].sval = dupstr("100.0"); ret[i].ival = 0; i++; ret[i].name = "Include currently shown puzzle"; ret[i].type = C_BOOLEAN; ret[i].sval = NULL; ret[i].ival = TRUE; i++; ret[i].name = "Print solutions"; ret[i].type = C_BOOLEAN; ret[i].sval = NULL; ret[i].ival = FALSE; i++; if (fe->game->can_print_in_colour) { ret[i].name = "Print in colour"; ret[i].type = C_BOOLEAN; ret[i].sval = NULL; ret[i].ival = FALSE; i++; } ret[i].name = NULL; ret[i].type = C_END; ret[i].sval = NULL; ret[i].ival = 0; i++; return ret; } else { assert(!"We should never get here"); return NULL; } } static char *frontend_set_config(frontend *fe, int which, config_item *cfg) { if (which < CFG_FRONTEND_SPECIFIC) { return midend_set_config(fe->me, which, cfg); } else if (which == CFG_PRINT) { if ((fe->printcount = atoi(cfg[0].sval)) <= 0) return "Number of puzzles to print should be at least one"; if ((fe->printw = atoi(cfg[1].sval)) <= 0) return "Number of puzzles across the page should be at least one"; if ((fe->printh = atoi(cfg[2].sval)) <= 0) return "Number of puzzles down the page should be at least one"; if ((fe->printscale = (float)atof(cfg[3].sval)) <= 0) return "Print size should be positive"; fe->printcurr = cfg[4].ival; fe->printsolns = cfg[5].ival; fe->printcolour = fe->game->can_print_in_colour && cfg[6].ival; return NULL; } else { assert(!"We should never get here"); return "Internal error"; } } #ifdef _WIN32_WCE /* Separate version of mkctrl function for the Pocket PC. */ /* Control coordinates should be specified in dialog units. */ HWND mkctrl(frontend *fe, int x1, int x2, int y1, int y2, LPCTSTR wclass, int wstyle, int exstyle, const char *wtext, int wid) { RECT rc; TCHAR wwtext[256]; /* Convert dialog units into pixels */ rc.left = x1; rc.right = x2; rc.top = y1; rc.bottom = y2; MapDialogRect(fe->cfgbox, &rc); MultiByteToWideChar (CP_ACP, 0, wtext, -1, wwtext, 256); return CreateWindowEx(exstyle, wclass, wwtext, wstyle | WS_CHILD | WS_VISIBLE, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, fe->cfgbox, (HMENU) wid, fe->inst, NULL); } static void create_config_controls(frontend * fe) { int id, nctrls; int col1l, col1r, col2l, col2r, y; config_item *i; struct cfg_aux *j; HWND ctl; /* Control placement done in dialog units */ col1l = 4; col1r = 96; /* Label column */ col2l = 100; col2r = 154; /* Input column (edit boxes and combo boxes) */ /* * Count the controls so we can allocate cfgaux. */ for (nctrls = 0, i = fe->cfg; i->type != C_END; i++) nctrls++; fe->cfgaux = snewn(nctrls, struct cfg_aux); id = 1000; y = 22; /* Leave some room for the dialog title */ for (i = fe->cfg, j = fe->cfgaux; i->type != C_END; i++, j++) { switch (i->type) { case C_STRING: /* * Edit box with a label beside it. */ mkctrl(fe, col1l, col1r, y + 1, y + 11, TEXT("Static"), SS_LEFTNOWORDWRAP, 0, i->name, id++); mkctrl(fe, col2l, col2r, y, y + 12, TEXT("EDIT"), WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL, 0, "", (j->ctlid = id++)); SetDlgItemTextA(fe->cfgbox, j->ctlid, i->sval); break; case C_BOOLEAN: /* * Simple checkbox. */ mkctrl(fe, col1l, col2r, y + 1, y + 11, TEXT("BUTTON"), BS_NOTIFY | BS_AUTOCHECKBOX | WS_TABSTOP, 0, i->name, (j->ctlid = id++)); CheckDlgButton(fe->cfgbox, j->ctlid, (i->ival != 0)); break; case C_CHOICES: /* * Drop-down list with a label beside it. */ mkctrl(fe, col1l, col1r, y + 1, y + 11, TEXT("STATIC"), SS_LEFTNOWORDWRAP, 0, i->name, id++); ctl = mkctrl(fe, col2l, col2r, y, y + 48, TEXT("COMBOBOX"), WS_BORDER | WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, 0, "", (j->ctlid = id++)); { char c, *p, *q, *str; p = i->sval; c = *p++; while (*p) { q = p; while (*q && *q != c) q++; str = snewn(q-p+1, char); strncpy(str, p, q-p); str[q-p] = '\0'; { TCHAR ws[50]; MultiByteToWideChar (CP_ACP, 0, str, -1, ws, 50); SendMessage(ctl, CB_ADDSTRING, 0, (LPARAM)ws); } sfree(str); if (*q) q++; p = q; } } SendMessage(ctl, CB_SETCURSEL, i->ival, 0); break; } y += 15; } } #endif static int CALLBACK ConfigDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { frontend *fe = (frontend *)GetWindowLong(hwnd, GWL_USERDATA); config_item *i; struct cfg_aux *j; switch (msg) { case WM_INITDIALOG: #ifdef _WIN32_WCE { char *title; fe = (frontend *) lParam; SetWindowLong(hwnd, GWL_USERDATA, lParam); fe->cfgbox = hwnd; fe->cfg = frontend_get_config(fe, fe->cfg_which, &title); make_dialog_full_screen(hwnd); SetDlgItemTextA(hwnd, IDC_CONFIG_CAPTION, title); SendDlgItemMessage(hwnd, IDC_CONFIG_CAPTION, WM_SETFONT, (WPARAM) dialog_title_font(), 0); create_config_controls(fe); } #endif return TRUE; case WM_COMMAND: /* * OK and Cancel are special cases. */ if ((LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)) { if (LOWORD(wParam) == IDOK) { char *err = frontend_set_config(fe, fe->cfg_which, fe->cfg); if (err) { MessageBox(hwnd, err, "Validation error", MB_ICONERROR | MB_OK); } else { #ifdef _WIN32_WCE EndDialog(hwnd, 2); #else fe->dlg_done = 2; #endif } } else { #ifdef _WIN32_WCE EndDialog(hwnd, 1); #else fe->dlg_done = 1; #endif } return 0; } /* * First find the control whose id this is. */ for (i = fe->cfg, j = fe->cfgaux; i->type != C_END; i++, j++) { if (j->ctlid == LOWORD(wParam)) break; } if (i->type == C_END) return 0; /* not our problem */ if (i->type == C_STRING && HIWORD(wParam) == EN_CHANGE) { char buffer[4096]; #ifdef _WIN32_WCE TCHAR wBuffer[4096]; GetDlgItemText(fe->cfgbox, j->ctlid, wBuffer, 4096); WideCharToMultiByte(CP_ACP, 0, wBuffer, -1, buffer, 4096, NULL, NULL); #else GetDlgItemText(fe->cfgbox, j->ctlid, buffer, lenof(buffer)); #endif buffer[lenof(buffer)-1] = '\0'; sfree(i->sval); i->sval = dupstr(buffer); } else if (i->type == C_BOOLEAN && (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DBLCLK)) { i->ival = IsDlgButtonChecked(fe->cfgbox, j->ctlid); } else if (i->type == C_CHOICES && HIWORD(wParam) == CBN_SELCHANGE) { i->ival = SendDlgItemMessage(fe->cfgbox, j->ctlid, CB_GETCURSEL, 0, 0); } return 0; case WM_CLOSE: fe->dlg_done = 1; return 0; } return 0; } #ifndef _WIN32_WCE HWND mkctrl(frontend *fe, int x1, int x2, int y1, int y2, char *wclass, int wstyle, int exstyle, const char *wtext, int wid) { HWND ret; ret = CreateWindowEx(exstyle, wclass, wtext, wstyle | WS_CHILD | WS_VISIBLE, x1, y1, x2-x1, y2-y1, fe->cfgbox, (HMENU) wid, fe->inst, NULL); SendMessage(ret, WM_SETFONT, (WPARAM)fe->cfgfont, MAKELPARAM(TRUE, 0)); return ret; } #endif static void about(frontend *fe) { #ifdef _WIN32_WCE DialogBox(fe->inst, MAKEINTRESOURCE(IDD_ABOUT), fe->hwnd, AboutDlgProc); #else int i; WNDCLASS wc; MSG msg; TEXTMETRIC tm; HDC hdc; HFONT oldfont; SIZE size; int gm, id; int winwidth, winheight, y; int height, width, maxwid; const char *strings[16]; int lengths[16]; int nstrings = 0; char titlebuf[512]; sprintf(titlebuf, "About %.250s", fe->game->name); strings[nstrings++] = fe->game->name; strings[nstrings++] = "from Simon Tatham's Portable Puzzle Collection"; strings[nstrings++] = ver; wc.style = CS_DBLCLKS | CS_SAVEBITS; wc.lpfnWndProc = DefDlgProc; wc.cbClsExtra = 0; wc.cbWndExtra = DLGWINDOWEXTRA + 8; wc.hInstance = fe->inst; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND +1); wc.lpszMenuName = NULL; wc.lpszClassName = "GameAboutBox"; RegisterClass(&wc); hdc = GetDC(fe->hwnd); SetMapMode(hdc, MM_TEXT); fe->dlg_done = FALSE; fe->cfgfont = CreateFont(-MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72), 0, 0, 0, 0, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_SWISS, "MS Shell Dlg"); oldfont = SelectObject(hdc, fe->cfgfont); if (GetTextMetrics(hdc, &tm)) { height = tm.tmAscent + tm.tmDescent; width = tm.tmAveCharWidth; } else { height = width = 30; } /* * Figure out the layout of the About box by measuring the * length of each piece of text. */ maxwid = 0; winheight = height/2; for (i = 0; i < nstrings; i++) { if (GetTextExtentPoint32(hdc, strings[i], strlen(strings[i]), &size)) lengths[i] = size.cx; else lengths[i] = 0; /* *shrug* */ if (maxwid < lengths[i]) maxwid = lengths[i]; winheight += height * 3 / 2 + (height / 2); } winheight += height + height * 7 / 4; /* OK button */ winwidth = maxwid + 4*width; SelectObject(hdc, oldfont); ReleaseDC(fe->hwnd, hdc); /* * Create the dialog, now that we know its size. */ { RECT r, r2; r.left = r.top = 0; r.right = winwidth; r.bottom = winheight; AdjustWindowRectEx(&r, (WS_OVERLAPPEDWINDOW /*| DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU*/) &~ (WS_MAXIMIZEBOX | WS_OVERLAPPED), FALSE, 0); /* * Centre the dialog on its parent window. */ r.right -= r.left; r.bottom -= r.top; GetWindowRect(fe->hwnd, &r2); r.left = (r2.left + r2.right - r.right) / 2; r.top = (r2.top + r2.bottom - r.bottom) / 2; r.right += r.left; r.bottom += r.top; fe->cfgbox = CreateWindowEx(0, wc.lpszClassName, titlebuf, DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU, r.left, r.top, r.right-r.left, r.bottom-r.top, fe->hwnd, NULL, fe->inst, NULL); } SendMessage(fe->cfgbox, WM_SETFONT, (WPARAM)fe->cfgfont, FALSE); SetWindowLong(fe->cfgbox, GWL_USERDATA, (LONG)fe); SetWindowLong(fe->cfgbox, DWL_DLGPROC, (LONG)AboutDlgProc); id = 1000; y = height/2; for (i = 0; i < nstrings; i++) { int border = width*2 + (maxwid - lengths[i]) / 2; mkctrl(fe, border, border+lengths[i], y+height*1/8, y+height*9/8, "Static", 0, 0, strings[i], id++); y += height*3/2; assert(y < winheight); y += height/2; } y += height/2; /* extra space before OK */ mkctrl(fe, width*2, maxwid+width*2, y, y+height*7/4, "BUTTON", BS_PUSHBUTTON | WS_TABSTOP | BS_DEFPUSHBUTTON, 0, "OK", IDOK); SendMessage(fe->cfgbox, WM_INITDIALOG, 0, 0); EnableWindow(fe->hwnd, FALSE); ShowWindow(fe->cfgbox, SW_SHOWNORMAL); while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) { if (!IsDialogMessage(fe->cfgbox, &msg)) DispatchMessage(&msg); if (fe->dlg_done) break; } EnableWindow(fe->hwnd, TRUE); SetForegroundWindow(fe->hwnd); DestroyWindow(fe->cfgbox); DeleteObject(fe->cfgfont); #endif } static int get_config(frontend *fe, int which) { #ifdef _WIN32_WCE fe->cfg_which = which; return DialogBoxParam(fe->inst, MAKEINTRESOURCE(IDD_CONFIG), fe->hwnd, ConfigDlgProc, (LPARAM) fe) == 2; #else config_item *i; struct cfg_aux *j; char *title; WNDCLASS wc; MSG msg; TEXTMETRIC tm; HDC hdc; HFONT oldfont; SIZE size; HWND ctl; int gm, id, nctrls; int winwidth, winheight, col1l, col1r, col2l, col2r, y; int height, width, maxlabel, maxcheckbox; wc.style = CS_DBLCLKS | CS_SAVEBITS; wc.lpfnWndProc = DefDlgProc; wc.cbClsExtra = 0; wc.cbWndExtra = DLGWINDOWEXTRA + 8; wc.hInstance = fe->inst; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND +1); wc.lpszMenuName = NULL; wc.lpszClassName = "GameConfigBox"; RegisterClass(&wc); hdc = GetDC(fe->hwnd); SetMapMode(hdc, MM_TEXT); fe->dlg_done = FALSE; fe->cfgfont = CreateFont(-MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72), 0, 0, 0, 0, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_SWISS, "MS Shell Dlg"); oldfont = SelectObject(hdc, fe->cfgfont); if (GetTextMetrics(hdc, &tm)) { height = tm.tmAscent + tm.tmDescent; width = tm.tmAveCharWidth; } else { height = width = 30; } fe->cfg = frontend_get_config(fe, which, &title); fe->cfg_which = which; /* * Figure out the layout of the config box by measuring the * length of each piece of text. */ maxlabel = maxcheckbox = 0; winheight = height/2; for (i = fe->cfg; i->type != C_END; i++) { switch (i->type) { case C_STRING: case C_CHOICES: /* * Both these control types have a label filling only * the left-hand column of the box. */ if (GetTextExtentPoint32(hdc, i->name, strlen(i->name), &size) && maxlabel < size.cx) maxlabel = size.cx; winheight += height * 3 / 2 + (height / 2); break; case C_BOOLEAN: /* * Checkboxes take up the whole of the box width. */ if (GetTextExtentPoint32(hdc, i->name, strlen(i->name), &size) && maxcheckbox < size.cx) maxcheckbox = size.cx; winheight += height + (height / 2); break; } } winheight += height + height * 7 / 4; /* OK / Cancel buttons */ col1l = 2*width; col1r = col1l + maxlabel; col2l = col1r + 2*width; col2r = col2l + 30*width; if (col2r < col1l+2*height+maxcheckbox) col2r = col1l+2*height+maxcheckbox; winwidth = col2r + 2*width; SelectObject(hdc, oldfont); ReleaseDC(fe->hwnd, hdc); /* * Create the dialog, now that we know its size. */ { RECT r, r2; r.left = r.top = 0; r.right = winwidth; r.bottom = winheight; AdjustWindowRectEx(&r, (WS_OVERLAPPEDWINDOW /*| DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU*/) &~ (WS_MAXIMIZEBOX | WS_OVERLAPPED), FALSE, 0); /* * Centre the dialog on its parent window. */ r.right -= r.left; r.bottom -= r.top; GetWindowRect(fe->hwnd, &r2); r.left = (r2.left + r2.right - r.right) / 2; r.top = (r2.top + r2.bottom - r.bottom) / 2; r.right += r.left; r.bottom += r.top; fe->cfgbox = CreateWindowEx(0, wc.lpszClassName, title, DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU, r.left, r.top, r.right-r.left, r.bottom-r.top, fe->hwnd, NULL, fe->inst, NULL); sfree(title); } SendMessage(fe->cfgbox, WM_SETFONT, (WPARAM)fe->cfgfont, FALSE); SetWindowLong(fe->cfgbox, GWL_USERDATA, (LONG)fe); SetWindowLong(fe->cfgbox, DWL_DLGPROC, (LONG)ConfigDlgProc); /* * Count the controls so we can allocate cfgaux. */ for (nctrls = 0, i = fe->cfg; i->type != C_END; i++) nctrls++; fe->cfgaux = snewn(nctrls, struct cfg_aux); id = 1000; y = height/2; for (i = fe->cfg, j = fe->cfgaux; i->type != C_END; i++, j++) { switch (i->type) { case C_STRING: /* * Edit box with a label beside it. */ mkctrl(fe, col1l, col1r, y+height*1/8, y+height*9/8, "Static", 0, 0, i->name, id++); ctl = mkctrl(fe, col2l, col2r, y, y+height*3/2, "EDIT", WS_TABSTOP | ES_AUTOHSCROLL, WS_EX_CLIENTEDGE, "", (j->ctlid = id++)); SetWindowText(ctl, i->sval); y += height*3/2; break; case C_BOOLEAN: /* * Simple checkbox. */ mkctrl(fe, col1l, col2r, y, y+height, "BUTTON", BS_NOTIFY | BS_AUTOCHECKBOX | WS_TABSTOP, 0, i->name, (j->ctlid = id++)); CheckDlgButton(fe->cfgbox, j->ctlid, (i->ival != 0)); y += height; break; case C_CHOICES: /* * Drop-down list with a label beside it. */ mkctrl(fe, col1l, col1r, y+height*1/8, y+height*9/8, "STATIC", 0, 0, i->name, id++); ctl = mkctrl(fe, col2l, col2r, y, y+height*41/2, "COMBOBOX", WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", (j->ctlid = id++)); { char c, *p, *q, *str; SendMessage(ctl, CB_RESETCONTENT, 0, 0); p = i->sval; c = *p++; while (*p) { q = p; while (*q && *q != c) q++; str = snewn(q-p+1, char); strncpy(str, p, q-p); str[q-p] = '\0'; SendMessage(ctl, CB_ADDSTRING, 0, (LPARAM)str); sfree(str); if (*q) q++; p = q; } } SendMessage(ctl, CB_SETCURSEL, i->ival, 0); y += height*3/2; break; } assert(y < winheight); y += height/2; } y += height/2; /* extra space before OK and Cancel */ mkctrl(fe, col1l, (col1l+col2r)/2-width, y, y+height*7/4, "BUTTON", BS_PUSHBUTTON | WS_TABSTOP | BS_DEFPUSHBUTTON, 0, "OK", IDOK); mkctrl(fe, (col1l+col2r)/2+width, col2r, y, y+height*7/4, "BUTTON", BS_PUSHBUTTON | WS_TABSTOP, 0, "Cancel", IDCANCEL); SendMessage(fe->cfgbox, WM_INITDIALOG, 0, 0); EnableWindow(fe->hwnd, FALSE); ShowWindow(fe->cfgbox, SW_SHOWNORMAL); while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) { if (!IsDialogMessage(fe->cfgbox, &msg)) DispatchMessage(&msg); if (fe->dlg_done) break; } EnableWindow(fe->hwnd, TRUE); SetForegroundWindow(fe->hwnd); DestroyWindow(fe->cfgbox); DeleteObject(fe->cfgfont); free_cfg(fe->cfg); sfree(fe->cfgaux); return (fe->dlg_done == 2); #endif } #ifdef _WIN32_WCE static void calculate_bitmap_position(frontend *fe, int x, int y) { /* Pocket PC - center the game in the full screen window */ int yMargin; RECT rcClient; GetClientRect(fe->hwnd, &rcClient); fe->bitmapPosition.left = (rcClient.right - x) / 2; yMargin = rcClient.bottom - y; if (fe->numpad != NULL) { RECT rcPad; GetWindowRect(fe->numpad, &rcPad); yMargin -= rcPad.bottom - rcPad.top; } if (fe->statusbar != NULL) { RECT rcStatus; GetWindowRect(fe->statusbar, &rcStatus); yMargin -= rcStatus.bottom - rcStatus.top; } fe->bitmapPosition.top = yMargin / 2; fe->bitmapPosition.right = fe->bitmapPosition.left + x; fe->bitmapPosition.bottom = fe->bitmapPosition.top + y; } #else static void calculate_bitmap_position(frontend *fe, int x, int y) { /* Plain Windows - position the game in the upper-left corner */ fe->bitmapPosition.left = 0; fe->bitmapPosition.top = 0; fe->bitmapPosition.right = fe->bitmapPosition.left + x; fe->bitmapPosition.bottom = fe->bitmapPosition.top + y; } #endif static void new_bitmap(frontend *fe, int x, int y) { HDC hdc; if (fe->bitmap) DeleteObject(fe->bitmap); hdc = GetDC(fe->hwnd); fe->bitmap = CreateCompatibleBitmap(hdc, x, y); calculate_bitmap_position(fe, x, y); ReleaseDC(fe->hwnd, hdc); } static void new_game_size(frontend *fe, float scale) { RECT r, sr; int x, y; get_max_puzzle_size(fe, &x, &y); midend_size(fe->me, &x, &y, FALSE); if (scale != 1.0) { x = (int)((float)x * fe->puzz_scale); y = (int)((float)y * fe->puzz_scale); midend_size(fe->me, &x, &y, TRUE); } fe->ymin = (fe->xmin * y) / x; r.left = r.top = 0; r.right = x; r.bottom = y; AdjustWindowRectEx(&r, WINFLAGS, TRUE, 0); if (fe->statusbar != NULL) { GetWindowRect(fe->statusbar, &sr); } else { sr.left = sr.right = sr.top = sr.bottom = 0; } #ifndef _WIN32_WCE SetWindowPos(fe->hwnd, NULL, 0, 0, r.right - r.left, r.bottom - r.top + sr.bottom - sr.top, SWP_NOMOVE | SWP_NOZORDER); #endif check_window_size(fe, &x, &y); #ifndef _WIN32_WCE if (fe->statusbar != NULL) SetWindowPos(fe->statusbar, NULL, 0, y, x, sr.bottom - sr.top, SWP_NOZORDER); #endif new_bitmap(fe, x, y); #ifdef _WIN32_WCE InvalidateRect(fe->hwnd, NULL, TRUE); #endif midend_redraw(fe->me); } /* * Given a proposed new window rect, work out the resulting * difference in client size (from current), and use to try * and resize the puzzle, returning (wx,wy) as the actual * new window size. */ static void adjust_game_size(frontend *fe, RECT *proposed, int isedge, int *wx_r, int *wy_r) { RECT cr, wr; int nx, ny, xdiff, ydiff, wx, wy; /* Work out the current window sizing, and thus the * difference in size we're asking for. */ GetClientRect(fe->hwnd, &cr); wr = cr; AdjustWindowRectEx(&wr, WINFLAGS, TRUE, 0); xdiff = (proposed->right - proposed->left) - (wr.right - wr.left); ydiff = (proposed->bottom - proposed->top) - (wr.bottom - wr.top); if (isedge) { /* These next four lines work around the fact that midend_size * is happy to shrink _but not grow_ if you change one dimension * but not the other. */ if (xdiff > 0 && ydiff == 0) ydiff = (xdiff * (wr.right - wr.left)) / (wr.bottom - wr.top); if (xdiff == 0 && ydiff > 0) xdiff = (ydiff * (wr.bottom - wr.top)) / (wr.right - wr.left); } if (check_window_resize(fe, (cr.right - cr.left) + xdiff, (cr.bottom - cr.top) + ydiff, &nx, &ny, &wx, &wy)) { new_bitmap(fe, nx, ny); midend_force_redraw(fe->me); } else { /* reset size to current window size */ wx = wr.right - wr.left; wy = wr.bottom - wr.top; } /* Re-fetch rectangle; size limits mean we might not have * taken it quite to the mouse drag positions. */ GetClientRect(fe->hwnd, &cr); adjust_statusbar(fe, &cr); *wx_r = wx; *wy_r = wy; } static void update_type_menu_tick(frontend *fe) { int total, n, i; if (fe->typemenu == INVALID_HANDLE_VALUE) return; total = GetMenuItemCount(fe->typemenu); n = midend_which_preset(fe->me); if (n < 0) n = total - 1; /* "Custom" item */ for (i = 0; i < total; i++) { int flag = (i == n ? MF_CHECKED : MF_UNCHECKED); CheckMenuItem(fe->typemenu, i, MF_BYPOSITION | flag); } DrawMenuBar(fe->hwnd); } static void update_copy_menu_greying(frontend *fe) { UINT enable = (midend_can_format_as_text_now(fe->me) ? MF_ENABLED : MF_GRAYED); EnableMenuItem(fe->gamemenu, IDM_COPY, MF_BYCOMMAND | enable); } static void new_game_type(frontend *fe) { midend_new_game(fe->me); new_game_size(fe, 1.0); update_type_menu_tick(fe); update_copy_menu_greying(fe); } static int is_alt_pressed(void) { BYTE keystate[256]; int r = GetKeyboardState(keystate); if (!r) return FALSE; if (keystate[VK_MENU] & 0x80) return TRUE; if (keystate[VK_RMENU] & 0x80) return TRUE; return FALSE; } static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { frontend *fe = (frontend *)GetWindowLong(hwnd, GWL_USERDATA); int cmd; switch (message) { case WM_CLOSE: DestroyWindow(hwnd); return 0; case WM_COMMAND: #ifdef _WIN32_WCE /* Numeric pad sends WM_COMMAND messages */ if ((wParam >= IDM_KEYEMUL) && (wParam < IDM_KEYEMUL + 256)) { midend_process_key(fe->me, 0, 0, wParam - IDM_KEYEMUL); } #endif cmd = wParam & ~0xF; /* low 4 bits reserved to Windows */ switch (cmd) { case IDM_NEW: if (!midend_process_key(fe->me, 0, 0, 'n')) PostQuitMessage(0); break; case IDM_RESTART: midend_restart_game(fe->me); break; case IDM_UNDO: if (!midend_process_key(fe->me, 0, 0, 'u')) PostQuitMessage(0); break; case IDM_REDO: if (!midend_process_key(fe->me, 0, 0, '\x12')) PostQuitMessage(0); break; case IDM_COPY: { char *text = midend_text_format(fe->me); if (text) write_clip(hwnd, text); else MessageBeep(MB_ICONWARNING); sfree(text); } break; case IDM_SOLVE: { char *msg = midend_solve(fe->me); if (msg) MessageBox(hwnd, msg, "Unable to solve", MB_ICONERROR | MB_OK); } break; case IDM_QUIT: if (!midend_process_key(fe->me, 0, 0, 'q')) PostQuitMessage(0); break; case IDM_CONFIG: if (get_config(fe, CFG_SETTINGS)) new_game_type(fe); break; case IDM_SEED: if (get_config(fe, CFG_SEED)) new_game_type(fe); break; case IDM_DESC: if (get_config(fe, CFG_DESC)) new_game_type(fe); break; case IDM_PRINT: if (get_config(fe, CFG_PRINT)) print(fe); break; case IDM_ABOUT: about(fe); break; case IDM_LOAD: case IDM_SAVE: { OPENFILENAME of; char filename[FILENAME_MAX]; int ret; memset(&of, 0, sizeof(of)); of.hwndOwner = hwnd; of.lpstrFilter = "All Files (*.*)\0*\0\0\0"; of.lpstrCustomFilter = NULL; of.nFilterIndex = 1; of.lpstrFile = filename; filename[0] = '\0'; of.nMaxFile = lenof(filename); of.lpstrFileTitle = NULL; of.lpstrTitle = (cmd == IDM_SAVE ? "Enter name of game file to save" : "Enter name of saved game file to load"); of.Flags = 0; #ifdef OPENFILENAME_SIZE_VERSION_400 of.lStructSize = OPENFILENAME_SIZE_VERSION_400; #else of.lStructSize = sizeof(of); #endif of.lpstrInitialDir = NULL; if (cmd == IDM_SAVE) ret = GetSaveFileName(&of); else ret = GetOpenFileName(&of); if (ret) { if (cmd == IDM_SAVE) { FILE *fp; if ((fp = fopen(filename, "r")) != NULL) { char buf[256 + FILENAME_MAX]; fclose(fp); /* file exists */ sprintf(buf, "Are you sure you want to overwrite" " the file \"%.*s\"?", FILENAME_MAX, filename); if (MessageBox(hwnd, buf, "Question", MB_YESNO | MB_ICONQUESTION) != IDYES) break; } fp = fopen(filename, "w"); if (!fp) { MessageBox(hwnd, "Unable to open save file", "Error", MB_ICONERROR | MB_OK); break; } midend_serialise(fe->me, savefile_write, fp); fclose(fp); } else { FILE *fp = fopen(filename, "r"); char *err = NULL; midend *me = fe->me; #ifdef COMBINED char *id_name; #endif if (!fp) { MessageBox(hwnd, "Unable to open saved game file", "Error", MB_ICONERROR | MB_OK); break; } #ifdef COMBINED /* * This save file might be from a different * game. */ err = identify_game(&id_name, savefile_read, fp); if (!err) { int i; for (i = 0; i < gamecount; i++) if (!strcmp(id_name, gamelist[i]->name)) break; if (i == gamecount) { err = "Save file is for a game not " "supported by this program"; } else { me = midend_for_new_game(fe, gamelist[i], NULL, FALSE, FALSE, &err); rewind(fp); /* for the actual load */ } sfree(id_name); } #endif if (!err) err = midend_deserialise(me, savefile_read, fp); fclose(fp); if (err) { MessageBox(hwnd, err, "Error", MB_ICONERROR|MB_OK); break; } if (fe->me != me) fe_set_midend(fe, me); new_game_size(fe, 1.0); } } } break; #ifndef _WIN32_WCE case IDM_HELPC: start_help(fe, NULL); break; case IDM_GAMEHELP: assert(help_type != NONE); start_help(fe, help_type == CHM ? fe->game->htmlhelp_topic : fe->game->winhelp_topic); break; #endif default: #ifdef COMBINED if (wParam >= IDM_GAMES && wParam < (IDM_GAMES + (WPARAM)gamecount)) { int p = wParam - IDM_GAMES; char *error = NULL; fe_set_midend(fe, midend_for_new_game(fe, gamelist[p], NULL, FALSE, FALSE, &error)); sfree(error); } else #endif { int p = ((wParam &~ 0xF) - IDM_PRESETS) / 0x10; if (p >= 0 && p < fe->npresets) { midend_set_params(fe->me, fe->presets[p]); new_game_type(fe); } } break; } break; case WM_DESTROY: #ifndef _WIN32_WCE stop_help(fe); #endif frontend_free(fe); PostQuitMessage(0); return 0; case WM_PAINT: { PAINTSTRUCT p; HDC hdc, hdc2; HBITMAP prevbm; RECT rcDest; hdc = BeginPaint(hwnd, &p); hdc2 = CreateCompatibleDC(hdc); prevbm = SelectObject(hdc2, fe->bitmap); #ifdef _WIN32_WCE FillRect(hdc, &(p.rcPaint), (HBRUSH) GetStockObject(WHITE_BRUSH)); #endif IntersectRect(&rcDest, &(fe->bitmapPosition), &(p.rcPaint)); BitBlt(hdc, rcDest.left, rcDest.top, rcDest.right - rcDest.left, rcDest.bottom - rcDest.top, hdc2, rcDest.left - fe->bitmapPosition.left, rcDest.top - fe->bitmapPosition.top, SRCCOPY); SelectObject(hdc2, prevbm); DeleteDC(hdc2); EndPaint(hwnd, &p); } return 0; case WM_KEYDOWN: { int key = -1; BYTE keystate[256]; int r = GetKeyboardState(keystate); int shift = (r && (keystate[VK_SHIFT] & 0x80)) ? MOD_SHFT : 0; int ctrl = (r && (keystate[VK_CONTROL] & 0x80)) ? MOD_CTRL : 0; switch (wParam) { case VK_LEFT: if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '4'; else key = shift | ctrl | CURSOR_LEFT; break; case VK_RIGHT: if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '6'; else key = shift | ctrl | CURSOR_RIGHT; break; case VK_UP: if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '8'; else key = shift | ctrl | CURSOR_UP; break; case VK_DOWN: if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '2'; else key = shift | ctrl | CURSOR_DOWN; break; /* * Diagonal keys on the numeric keypad. */ case VK_PRIOR: if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '9'; break; case VK_NEXT: if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '3'; break; case VK_HOME: if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '7'; break; case VK_END: if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '1'; break; case VK_INSERT: if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '0'; break; case VK_CLEAR: if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '5'; break; /* * Numeric keypad keys with Num Lock on. */ case VK_NUMPAD4: key = MOD_NUM_KEYPAD | '4'; break; case VK_NUMPAD6: key = MOD_NUM_KEYPAD | '6'; break; case VK_NUMPAD8: key = MOD_NUM_KEYPAD | '8'; break; case VK_NUMPAD2: key = MOD_NUM_KEYPAD | '2'; break; case VK_NUMPAD5: key = MOD_NUM_KEYPAD | '5'; break; case VK_NUMPAD9: key = MOD_NUM_KEYPAD | '9'; break; case VK_NUMPAD3: key = MOD_NUM_KEYPAD | '3'; break; case VK_NUMPAD7: key = MOD_NUM_KEYPAD | '7'; break; case VK_NUMPAD1: key = MOD_NUM_KEYPAD | '1'; break; case VK_NUMPAD0: key = MOD_NUM_KEYPAD | '0'; break; } if (key != -1) { if (!midend_process_key(fe->me, 0, 0, key)) PostQuitMessage(0); } else { MSG m; m.hwnd = hwnd; m.message = WM_KEYDOWN; m.wParam = wParam; m.lParam = lParam & 0xdfff; TranslateMessage(&m); } } break; case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: { int button; /* * Shift-clicks count as middle-clicks, since otherwise * two-button Windows users won't have any kind of * middle click to use. */ if (message == WM_MBUTTONDOWN || (wParam & MK_SHIFT)) button = MIDDLE_BUTTON; else if (message == WM_RBUTTONDOWN || is_alt_pressed()) button = RIGHT_BUTTON; else #ifndef _WIN32_WCE button = LEFT_BUTTON; #else if ((fe->game->flags & REQUIRE_RBUTTON) == 0) button = LEFT_BUTTON; else { SHRGINFO shrgi; shrgi.cbSize = sizeof(SHRGINFO); shrgi.hwndClient = hwnd; shrgi.ptDown.x = (signed short)LOWORD(lParam); shrgi.ptDown.y = (signed short)HIWORD(lParam); shrgi.dwFlags = SHRG_RETURNCMD; if (GN_CONTEXTMENU == SHRecognizeGesture(&shrgi)) button = RIGHT_BUTTON; else button = LEFT_BUTTON; } #endif if (!midend_process_key(fe->me, (signed short)LOWORD(lParam) - fe->bitmapPosition.left, (signed short)HIWORD(lParam) - fe->bitmapPosition.top, button)) PostQuitMessage(0); SetCapture(hwnd); } break; case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: { int button; /* * Shift-clicks count as middle-clicks, since otherwise * two-button Windows users won't have any kind of * middle click to use. */ if (message == WM_MBUTTONUP || (wParam & MK_SHIFT)) button = MIDDLE_RELEASE; else if (message == WM_RBUTTONUP || is_alt_pressed()) button = RIGHT_RELEASE; else button = LEFT_RELEASE; if (!midend_process_key(fe->me, (signed short)LOWORD(lParam) - fe->bitmapPosition.left, (signed short)HIWORD(lParam) - fe->bitmapPosition.top, button)) PostQuitMessage(0); ReleaseCapture(); } break; case WM_MOUSEMOVE: { int button; if (wParam & (MK_MBUTTON | MK_SHIFT)) button = MIDDLE_DRAG; else if (wParam & MK_RBUTTON || is_alt_pressed()) button = RIGHT_DRAG; else button = LEFT_DRAG; if (!midend_process_key(fe->me, (signed short)LOWORD(lParam) - fe->bitmapPosition.left, (signed short)HIWORD(lParam) - fe->bitmapPosition.top, button)) PostQuitMessage(0); } break; case WM_CHAR: if (!midend_process_key(fe->me, 0, 0, (unsigned char)wParam)) PostQuitMessage(0); return 0; case WM_TIMER: if (fe->timer) { DWORD now = GetTickCount(); float elapsed = (float) (now - fe->timer_last_tickcount) * 0.001F; midend_timer(fe->me, elapsed); fe->timer_last_tickcount = now; } return 0; #ifndef _WIN32_WCE case WM_SIZING: { RECT *sr = (RECT *)lParam; int wx, wy, isedge = 0; if (wParam == WMSZ_TOP || wParam == WMSZ_RIGHT || wParam == WMSZ_BOTTOM || wParam == WMSZ_LEFT) isedge = 1; adjust_game_size(fe, sr, isedge, &wx, &wy); /* Given the window size the puzzles constrain * us to, work out which edge we should be moving. */ if (wParam == WMSZ_TOP || wParam == WMSZ_TOPLEFT || wParam == WMSZ_TOPRIGHT) { sr->top = sr->bottom - wy; } else { sr->bottom = sr->top + wy; } if (wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT || wParam == WMSZ_BOTTOMLEFT) { sr->left = sr->right - wx; } else { sr->right = sr->left + wx; } return TRUE; } break; #endif } return DefWindowProc(hwnd, message, wParam, lParam); } #ifdef _WIN32_WCE static int FindPreviousInstance() { /* Check if application is running. If it's running then focus on the window */ HWND hOtherWnd = NULL; hOtherWnd = FindWindow (wGameName, wGameName); if (hOtherWnd) { SetForegroundWindow (hOtherWnd); return TRUE; } return FALSE; } #endif /* * Split a complete command line into argc/argv, attempting to do it * exactly the same way the Visual Studio C library would do it (so * that our console utilities, which receive argc and argv already * broken apart by the C library, will have their command lines * processed in the same way as the GUI utilities which get a whole * command line and must call this function). * * Does not modify the input command line. * * The final parameter (argstart) is used to return a second array * of char * pointers, the same length as argv, each one pointing * at the start of the corresponding element of argv in the * original command line. So if you get half way through processing * your command line in argc/argv form and then decide you want to * treat the rest as a raw string, you can. If you don't want to, * `argstart' can be safely left NULL. */ void split_into_argv(char *cmdline, int *argc, char ***argv, char ***argstart) { char *p; char *outputline, *q; char **outputargv, **outputargstart; int outputargc; /* * These argument-breaking rules apply to Visual Studio 7, which * is currently the compiler expected to be used for the Windows * port of my puzzles. Visual Studio 10 has different rules, * lacking the curious mod 3 behaviour of consecutive quotes * described below; I presume they fixed a bug. As and when we * migrate to a newer compiler, we'll have to adjust this to * match; however, for the moment we faithfully imitate in our GUI * utilities what our CLI utilities can't be prevented from doing. * * When I investigated this, at first glance the rules appeared to * be: * * - Single quotes are not special characters. * * - Double quotes are removed, but within them spaces cease * to be special. * * - Backslashes are _only_ special when a sequence of them * appear just before a double quote. In this situation, * they are treated like C backslashes: so \" just gives a * literal quote, \\" gives a literal backslash and then * opens or closes a double-quoted segment, \\\" gives a * literal backslash and then a literal quote, \\\\" gives * two literal backslashes and then opens/closes a * double-quoted segment, and so forth. Note that this * behaviour is identical inside and outside double quotes. * * - Two successive double quotes become one literal double * quote, but only _inside_ a double-quoted segment. * Outside, they just form an empty double-quoted segment * (which may cause an empty argument word). * * - That only leaves the interesting question of what happens * when one or more backslashes precedes two or more double * quotes, starting inside a double-quoted string. And the * answer to that appears somewhat bizarre. Here I tabulate * number of backslashes (across the top) against number of * quotes (down the left), and indicate how many backslashes * are output, how many quotes are output, and whether a * quoted segment is open at the end of the sequence: * * backslashes * * 0 1 2 3 4 * * 0 0,0,y | 1,0,y 2,0,y 3,0,y 4,0,y * --------+----------------------------- * 1 0,0,n | 0,1,y 1,0,n 1,1,y 2,0,n * q 2 0,1,n | 0,1,n 1,1,n 1,1,n 2,1,n * u 3 0,1,y | 0,2,n 1,1,y 1,2,n 2,1,y * o 4 0,1,n | 0,2,y 1,1,n 1,2,y 2,1,n * t 5 0,2,n | 0,2,n 1,2,n 1,2,n 2,2,n * e 6 0,2,y | 0,3,n 1,2,y 1,3,n 2,2,y * s 7 0,2,n | 0,3,y 1,2,n 1,3,y 2,2,n * 8 0,3,n | 0,3,n 1,3,n 1,3,n 2,3,n * 9 0,3,y | 0,4,n 1,3,y 1,4,n 2,3,y * 10 0,3,n | 0,4,y 1,3,n 1,4,y 2,3,n * 11 0,4,n | 0,4,n 1,4,n 1,4,n 2,4,n * * * [Test fragment was of the form "a\\\"""b c" d.] * * There is very weird mod-3 behaviour going on here in the * number of quotes, and it even applies when there aren't any * backslashes! How ghastly. * * With a bit of thought, this extremely odd diagram suddenly * coalesced itself into a coherent, if still ghastly, model of * how things work: * * - As before, backslashes are only special when one or more * of them appear contiguously before at least one double * quote. In this situation the backslashes do exactly what * you'd expect: each one quotes the next thing in front of * it, so you end up with n/2 literal backslashes (if n is * even) or (n-1)/2 literal backslashes and a literal quote * (if n is odd). In the latter case the double quote * character right after the backslashes is used up. * * - After that, any remaining double quotes are processed. A * string of contiguous unescaped double quotes has a mod-3 * behaviour: * * * inside a quoted segment, a quote ends the segment. * * _immediately_ after ending a quoted segment, a quote * simply produces a literal quote. * * otherwise, outside a quoted segment, a quote begins a * quoted segment. * * So, for example, if we started inside a quoted segment * then two contiguous quotes would close the segment and * produce a literal quote; three would close the segment, * produce a literal quote, and open a new segment. If we * started outside a quoted segment, then two contiguous * quotes would open and then close a segment, producing no * output (but potentially creating a zero-length argument); * but three quotes would open and close a segment and then * produce a literal quote. */ /* * First deal with the simplest of all special cases: if there * aren't any arguments, return 0,NULL,NULL. */ while (*cmdline && isspace(*cmdline)) cmdline++; if (!*cmdline) { if (argc) *argc = 0; if (argv) *argv = NULL; if (argstart) *argstart = NULL; return; } /* * This will guaranteeably be big enough; we can realloc it * down later. */ outputline = snewn(1+strlen(cmdline), char); outputargv = snewn(strlen(cmdline)+1 / 2, char *); outputargstart = snewn(strlen(cmdline)+1 / 2, char *); p = cmdline; q = outputline; outputargc = 0; while (*p) { int quote; /* Skip whitespace searching for start of argument. */ while (*p && isspace(*p)) p++; if (!*p) break; /* We have an argument; start it. */ outputargv[outputargc] = q; outputargstart[outputargc] = p; outputargc++; quote = 0; /* Copy data into the argument until it's finished. */ while (*p) { if (!quote && isspace(*p)) break; /* argument is finished */ if (*p == '"' || *p == '\\') { /* * We have a sequence of zero or more backslashes * followed by a sequence of zero or more quotes. * Count up how many of each, and then deal with * them as appropriate. */ int i, slashes = 0, quotes = 0; while (*p == '\\') slashes++, p++; while (*p == '"') quotes++, p++; if (!quotes) { /* * Special case: if there are no quotes, * slashes are not special at all, so just copy * n slashes to the output string. */ while (slashes--) *q++ = '\\'; } else { /* Slashes annihilate in pairs. */ while (slashes >= 2) slashes -= 2, *q++ = '\\'; /* One remaining slash takes out the first quote. */ if (slashes) quotes--, *q++ = '"'; if (quotes > 0) { /* Outside a quote segment, a quote starts one. */ if (!quote) quotes--, quote = 1; /* Now we produce (n+1)/3 literal quotes... */ for (i = 3; i <= quotes+1; i += 3) *q++ = '"'; /* ... and end in a quote segment iff 3 divides n. */ quote = (quotes % 3 == 0); } } } else { *q++ = *p++; } } /* At the end of an argument, just append a trailing NUL. */ *q++ = '\0'; } outputargv = sresize(outputargv, outputargc, char *); outputargstart = sresize(outputargstart, outputargc, char *); if (argc) *argc = outputargc; if (argv) *argv = outputargv; else sfree(outputargv); if (argstart) *argstart = outputargstart; else sfree(outputargstart); } int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { MSG msg; char *error; const game *gg; frontend *fe; midend *me; int argc; char **argv; split_into_argv(cmdline, &argc, &argv, NULL); #ifdef _WIN32_WCE MultiByteToWideChar (CP_ACP, 0, CLASSNAME, -1, wClassName, 256); if (FindPreviousInstance ()) return 0; #endif InitCommonControls(); if (!prev) { WNDCLASS wndclass; wndclass.style = 0; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = inst; wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(200)); #ifndef _WIN32_WCE if (!wndclass.hIcon) /* in case resource file is absent */ wndclass.hIcon = LoadIcon(inst, IDI_APPLICATION); #endif wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = NULL; wndclass.lpszMenuName = NULL; #ifdef _WIN32_WCE wndclass.lpszClassName = wClassName; #else wndclass.lpszClassName = CLASSNAME; #endif RegisterClass(&wndclass); } while (*cmdline && isspace((unsigned char)*cmdline)) cmdline++; init_help(); #ifdef COMBINED gg = gamelist[0]; if (argc > 0) { int i; for (i = 0; i < gamecount; i++) { const char *p = gamelist[i]->name; char *q = argv[0]; while (*p && *q) { if (isspace((unsigned char)*p)) { while (*q && isspace((unsigned char)*q)) q++; } else { if (tolower((unsigned char)*p) != tolower((unsigned char)*q)) break; q++; } p++; } if (!*p) { gg = gamelist[i]; --argc; ++argv; break; } } } #else gg = &thegame; #endif fe = frontend_new(inst); me = midend_for_new_game(fe, gg, argc > 0 ? argv[0] : NULL, TRUE, TRUE, &error); if (!me) { char buf[128]; #ifdef COMBINED sprintf(buf, "Puzzles Error"); #else sprintf(buf, "%.100s Error", gg->name); #endif MessageBox(NULL, error, buf, MB_OK|MB_ICONERROR); sfree(error); return 1; } fe_set_midend(fe, me); show_window(fe); while (GetMessage(&msg, NULL, 0, 0)) { DispatchMessage(&msg); } DestroyWindow(fe->hwnd); cleanup_help(); return msg.wParam; } /* vim: set shiftwidth=4 tabstop=8: */ puzzles-r9872/osx.m0000644000175300017530000012637611763715211013437 0ustar simonsimon/* * 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; /* ---------------------------------------------------------------------- * Miscellaneous support routines that aren't part of any object or * clearly defined subsystem. */ void fatal(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, void *buf, int len) { FILE *fp = (FILE *)wctx; fwrite(buf, 1, len, fp); } static int 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, char *title, 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; int 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; } - (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:(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; 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:@""]; [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); 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) { int 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; [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:(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:'n']; } - (void)restartGame:(id)sender { midend_restart_game(me); } - (void)saveGame:(id)sender { NSSavePanel *sp = [NSSavePanel savePanel]; if ([sp runModal] == NSFileHandlingPanelOKButton) { const char *name = [[sp filename] UTF8String]; 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 runModalForTypes:nil] == NSOKButton) { const char *name = [[[op filenames] objectAtIndex:0] cString]; 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:'u']; } - (void)redoMove:(id)sender { [self processKey:'r'&0x1F]; } - (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 { 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 { while ([typemenu numberOfItems] > 1) [typemenu removeItemAtIndex:0]; [[typemenu itemAtIndex:0] setState:NSOffState]; } - (void)updateTypeMenuTick { int i, total, n; total = [typemenu numberOfItems]; n = midend_which_preset(me); if (n < 0) n = total - 1; /* that's always where "Custom" lives */ for (i = 0; i < total; i++) [[typemenu itemAtIndex:i] setState:(i == n ? NSOnState : NSOffState)]; } - (void)becomeKeyWindow { int n; [self clearTypeMenu]; [super becomeKeyWindow]; n = midend_num_presets(me); if (n > 0) { [typemenu insertItem:[NSMenuItem separatorItem] atIndex:0]; while (n--) { char *name; game_params *params; DataMenuItem *item; midend_fetch_preset(me, n, &name, ¶ms); item = [[[DataMenuItem alloc] initWithTitle:[NSString stringWithUTF8String:name] action:NULL keyEquivalent:@""] autorelease]; [item setEnabled:YES]; [item setTarget:self]; [item setAction:@selector(presetGame:)]; [item setPayload:params]; [typemenu insertItem:item atIndex:0]; } } [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->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->ival ? 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, *p; p = i->sval; c = *p++; while (*p) { char *q, *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->ival]; [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]]; [NSApp 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); [NSApp endSheet:sheet]; [sheet orderOut:self]; sheet = NULL; if (update) { int k; config_item *i; char *error; k = 0; for (i = cfg; i->type != C_END; i++) { switch (i->type) { case C_STRING: sfree(i->sval); i->sval = dupstr([[[(id)cfg_controls[k+1] cell] title] UTF8String]); k += 2; break; case C_BOOLEAN: i->ival = [(id)cfg_controls[k] state] == NSOnState; k++; break; case C_CHOICES: i->ival = [(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:YES]; } - (void)sheetCancelButton:(id)sender { [self sheetEndWithStatus:NO]; } - (void)setStatusLine:(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_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, 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, 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, }; 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"]; [NSApplication sharedApplication]; [NSApp setApplicationIconImage:icon]; controller = [[[AppController alloc] init] autorelease]; [NSApp setDelegate:controller]; [NSApp setMainMenu: newmenu("Main Menu")]; menu = newsubmenu([NSApp mainMenu], "Apple Menu"); newitem(menu, "About Puzzles", "", NULL, @selector(about:)); [menu addItem:[NSMenuItem separatorItem]]; [NSApp setServicesMenu:newsubmenu(menu, "Services")]; [menu addItem:[NSMenuItem separatorItem]]; newitem(menu, "Hide Puzzles", "h", NSApp, @selector(hide:)); newitem(menu, "Hide Others", "o-h", NSApp, @selector(hideOtherApplications:)); newitem(menu, "Show All", "", NSApp, @selector(unhideAllApplications:)); [menu addItem:[NSMenuItem separatorItem]]; newitem(menu, "Quit", "q", NSApp, @selector(terminate:)); [NSApp setAppleMenu: menu]; menu = newsubmenu([NSApp 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([NSApp 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([NSApp mainMenu], "Type"); typemenu = menu; newitem(menu, "Custom", "", NULL, @selector(customGameType:)); menu = newsubmenu([NSApp mainMenu], "Window"); [NSApp setWindowsMenu: menu]; newitem(menu, "Minimise Window", "m", NULL, @selector(performMiniaturize:)); menu = newsubmenu([NSApp mainMenu], "Help"); newitem(menu, "Puzzles Help", "?", NSApp, @selector(showHelp:)); [NSApp run]; [pool release]; return 0; } puzzles-r9872/grid.h0000644000175300017530000001115312132232554013523 0ustar simonsimon/* * (c) Lambros Lambrou 2008 * * Code for working with general grids, which can be any planar graph * with faces, edges and vertices (dots). Includes generators for a few * types of grid, including square, hexagonal, triangular and others. */ #ifndef PUZZLES_GRID_H #define PUZZLES_GRID_H #include "puzzles.h" /* for random_state */ /* Useful macros */ #define SQ(x) ( (x) * (x) ) /* ---------------------------------------------------------------------- * Grid structures: * A grid is made up of faces, edges and dots. These structures hold * the incidence relationships between these types. For example, an * edge always joins two dots, and is adjacent to two faces. * The "grid_xxx **" members are lists of pointers which are dynamically * allocated during grid generation. * A pointer to a face/edge/dot will always point somewhere inside one of the * three lists of the main "grid" structure: faces, edges, dots. * Could have used integer offsets into these lists, but using actual * pointers instead gives us type-safety. */ /* Need forward declarations */ typedef struct grid_face grid_face; typedef struct grid_edge grid_edge; typedef struct grid_dot grid_dot; struct grid_face { int order; /* Number of edges, also the number of dots */ grid_edge **edges; /* edges around this face */ grid_dot **dots; /* corners of this face */ /* * For each face, we optionally compute and store its 'incentre'. * The incentre of a triangle is the centre of a circle tangent to * all three edges; I generalise the concept to arbitrary polygons * by defining it to be the centre of the largest circle you can fit * anywhere in the polygon. It's a useful thing to know because if * you want to draw any symbol or text in the face (e.g. clue * numbers in Loopy), that's the place it will most easily fit. * * When a grid is first generated, no face has this information * computed, because it's fiddly to do. You can call * grid_find_incentre() on a face, and it will fill in ix,iy below * and set has_incentre to indicate that it's done so. */ int has_incentre; int ix, iy; /* incentre (centre of largest inscribed circle) */ }; struct grid_edge { grid_dot *dot1, *dot2; grid_face *face1, *face2; /* Use NULL for the infinite outside face */ }; struct grid_dot { int order; grid_edge **edges; grid_face **faces; /* A NULL grid_face* means infinite outside face */ /* Position in some fairly arbitrary (Cartesian) coordinate system. * Use large enough values such that we can get away with * integer arithmetic, but small enough such that arithmetic * won't overflow. */ int x, y; }; typedef struct grid { /* These are (dynamically allocated) arrays of all the * faces, edges, dots that are in the grid. */ int num_faces; grid_face *faces; int num_edges; grid_edge *edges; int num_dots; grid_dot *dots; /* Cache the bounding-box of the grid, so the drawing-code can quickly * figure out the proper scaling to draw onto a given area. */ int lowest_x, lowest_y, highest_x, highest_y; /* A measure of tile size for this grid (in grid coordinates), to help * the renderer decide how large to draw the grid. * Roughly the size of a single tile - for example the side-length * of a square cell. */ int tilesize; /* We really don't want to copy this monstrosity! * A grid is immutable once generated. */ int refcount; } grid; /* Grids are specified by type: GRID_SQUARE, GRID_KITE, etc. */ #define GRIDGEN_LIST(A) \ A(SQUARE,square) \ A(HONEYCOMB,honeycomb) \ A(TRIANGULAR,triangular) \ A(SNUBSQUARE,snubsquare) \ A(CAIRO,cairo) \ A(GREATHEXAGONAL,greathexagonal) \ A(OCTAGONAL,octagonal) \ A(KITE,kites) \ A(FLORET,floret) \ A(DODECAGONAL,dodecagonal) \ A(GREATDODECAGONAL,greatdodecagonal) \ A(PENROSE_P2,penrose_p2_kite) \ A(PENROSE_P3,penrose_p3_thick) #define ENUM(upper,lower) GRID_ ## upper, typedef enum grid_type { GRIDGEN_LIST(ENUM) GRID_TYPE_MAX } grid_type; #undef ENUM /* Free directly after use if non-NULL. Will never contain an underscore * (so clients can safely use that as a separator). */ char *grid_new_desc(grid_type type, int width, int height, random_state *rs); char *grid_validate_desc(grid_type type, int width, int height, const char *desc); grid *grid_new(grid_type type, int width, int height, const char *desc); void grid_free(grid *g); grid_edge *grid_nearest_edge(grid *g, int x, int y); void grid_compute_size(grid_type type, int width, int height, int *tilesize, int *xextent, int *yextent); void grid_find_incentre(grid_face *f); #endif /* PUZZLES_GRID_H */ puzzles-r9872/latin.h0000644000175300017530000001004511322713103013676 0ustar simonsimon#ifndef LATIN_H #define LATIN_H #include "puzzles.h" typedef unsigned char digit; /* --- Solver structures, definitions --- */ #ifdef STANDALONE_SOLVER extern int solver_show_working, solver_recurse_depth; #endif struct latin_solver { int o; /* order of latin square */ unsigned char *cube; /* o^3, indexed by x, y, and digit: TRUE in that position indicates a possibility */ digit *grid; /* o^2, indexed by x and y: for final deductions */ unsigned char *row; /* o^2: row[y*cr+n-1] TRUE if n is in row y */ unsigned char *col; /* o^2: col[x*cr+n-1] TRUE if n is in col x */ #ifdef STANDALONE_SOLVER char **names; /* o: names[n-1] gives name of 'digit' n */ #endif }; #define cubepos(x,y,n) (((x)*solver->o+(y))*solver->o+(n)-1) #define cube(x,y,n) (solver->cube[cubepos(x,y,n)]) #define gridpos(x,y) ((y)*solver->o+(x)) #define grid(x,y) (solver->grid[gridpos(x,y)]) /* --- Solver individual strategies --- */ /* Place a value at a specific location. */ void latin_solver_place(struct latin_solver *solver, int x, int y, int n); /* Positional elimination. */ int latin_solver_elim(struct latin_solver *solver, int start, int step #ifdef STANDALONE_SOLVER , char *fmt, ... #endif ); struct latin_solver_scratch; /* private to latin.c */ /* Set elimination */ int latin_solver_set(struct latin_solver *solver, struct latin_solver_scratch *scratch, int start, int step1, int step2 #ifdef STANDALONE_SOLVER , char *fmt, ... #endif ); /* Forcing chains */ int latin_solver_forcing(struct latin_solver *solver, struct latin_solver_scratch *scratch); /* --- Solver allocation --- */ /* Fills in (and allocates members for) a latin_solver struct. * Will allocate members of snew, but not snew itself * (allowing 'struct latin_solver' to be the first element in a larger * struct, for example). */ void latin_solver_alloc(struct latin_solver *solver, digit *grid, int o); void latin_solver_free(struct latin_solver *solver); /* Allocates scratch space (for _set and _forcing) */ struct latin_solver_scratch * latin_solver_new_scratch(struct latin_solver *solver); void latin_solver_free_scratch(struct latin_solver_scratch *scratch); /* --- Solver guts --- */ /* Looped positional elimination */ int latin_solver_diff_simple(struct latin_solver *solver); /* Looped set elimination; *extreme is set if it used * the more difficult single-number elimination. */ int latin_solver_diff_set(struct latin_solver *solver, struct latin_solver_scratch *scratch, int extreme); typedef int (*usersolver_t)(struct latin_solver *solver, void *ctx); typedef void *(*ctxnew_t)(void *ctx); typedef void (*ctxfree_t)(void *ctx); /* Individual puzzles should use their enumerations for their * own difficulty levels, ensuring they don't clash with these. */ enum { diff_impossible = 10, diff_ambiguous, diff_unfinished }; /* Externally callable function that allocates and frees a latin_solver */ int latin_solver(digit *grid, int o, int maxdiff, int diff_simple, int diff_set_0, int diff_set_1, int diff_forcing, int diff_recursive, usersolver_t const *usersolvers, void *ctx, ctxnew_t ctxnew, ctxfree_t ctxfree); /* Version you can call if you want to alloc and free latin_solver yourself */ int latin_solver_main(struct latin_solver *solver, int maxdiff, int diff_simple, int diff_set_0, int diff_set_1, int diff_forcing, int diff_recursive, usersolver_t const *usersolvers, void *ctx, ctxnew_t ctxnew, ctxfree_t ctxfree); void latin_solver_debug(unsigned char *cube, int o); /* --- Generation and checking --- */ digit *latin_generate(int o, random_state *rs); /* The order of the latin rectangle is max(w,h). */ digit *latin_generate_rect(int w, int h, random_state *rs); int latin_check(digit *sq, int order); /* !0 => not a latin square */ void latin_debug(digit *sq, int order); #endif puzzles-r9872/loopgen.h0000644000175300017530000000237311707014702014245 0ustar simonsimon/* * loopgen.h: interface file for loop generation functions for grid.[ch]. */ #ifndef _LOOPGEN_H #define _LOOPGEN_H #include "puzzles.h" #include "grid.h" enum face_colour { FACE_WHITE, FACE_GREY, FACE_BLACK }; /* face should be of type grid_face* here. */ #define FACE_COLOUR(face) \ ( (face) == NULL ? FACE_BLACK : \ board[(face) - g->faces] ) typedef int (*loopgen_bias_fn_t)(void *ctx, char *board, int face); /* 'board' should be a char array whose length is the same as * g->num_faces: this will be filled in with FACE_WHITE or FACE_BLACK * after loop generation. * * If 'bias' is non-null, it should be a user-provided function which * rates a half-finished board (i.e. may include some FACE_GREYs) for * desirability; this will cause the loop generator to bias in favour * of loops with a high return value from that function. The 'face' * parameter to the bias function indicates which face of the grid has * been modified since the last call; it is guaranteed that only one * will have been (so that bias functions can work incrementally * rather than re-scanning the whole grid on every call). */ extern void generate_loop(grid *g, char *board, random_state *rs, loopgen_bias_fn_t bias, void *biasctx); #endif puzzles-r9872/maxflow.h0000644000175300017530000000710610323523700014252 0ustar simonsimon/* * Edmonds-Karp algorithm for finding a maximum flow and minimum * cut in a network. Almost identical to the Ford-Fulkerson * algorithm, but apparently using breadth-first search to find the * _shortest_ augmenting path is a good way to guarantee * termination and ensure the time complexity is not dependent on * the actual value of the maximum flow. I don't understand why * that should be, but it's claimed on the Internet that it's been * proved, and that's good enough for me. I prefer BFS to DFS * anyway :-) */ #ifndef MAXFLOW_MAXFLOW_H #define MAXFLOW_MAXFLOW_H /* * The actual algorithm. * * Inputs: * * - `scratch' is previously allocated scratch space of a size * previously determined by calling `maxflow_scratch_size'. * * - `nv' is the number of vertices. Vertices are assumed to be * numbered from 0 to nv-1. * * - `source' and `sink' are the distinguished source and sink * vertices. * * - `ne' is the number of edges in the graph. * * - `edges' is an array of 2*ne integers, giving a (source, dest) * pair for each network edge. Edge pairs are expected to be * sorted in lexicographic order. * * - `backedges' is an array of `ne' integers, each a distinct * index into `edges'. The edges in `edges', if permuted as * specified by this array, should end up sorted in the _other_ * lexicographic order, i.e. dest taking priority over source. * * - `capacity' is an array of `ne' integers, giving a maximum * flow capacity for each edge. A negative value is taken to * indicate unlimited capacity on that edge, but note that there * may not be any unlimited-capacity _path_ from source to sink * or an assertion will be failed. * * Output: * * - `flow' must be non-NULL. It is an array of `ne' integers, * each giving the final flow along each edge. * * - `cut' may be NULL. If non-NULL, it is an array of `nv' * integers, which will be set to zero or one on output, in such * a way that: * + the set of zero vertices includes the source * + the set of one vertices includes the sink * + the maximum flow capacity between the zero and one vertex * sets is achieved (i.e. all edges from a zero vertex to a * one vertex are at full capacity, while all edges from a * one vertex to a zero vertex have no flow at all). * * - the returned value from the function is the total flow * achieved. */ int maxflow_with_scratch(void *scratch, int nv, int source, int sink, int ne, const int *edges, const int *backedges, const int *capacity, int *flow, int *cut); /* * The above function expects its `scratch' and `backedges' * parameters to have already been set up. This allows you to set * them up once and use them in multiple invocates of the * algorithm. Now I provide functions to actually do the setting * up. */ int maxflow_scratch_size(int nv); void maxflow_setup_backedges(int ne, const int *edges, int *backedges); /* * Simplified version of the above function. All parameters are the * same, except that `scratch' and `backedges' are constructed * internally. This is the simplest way to call the algorithm as a * one-off; however, if you need to call it multiple times on the * same network, it is probably better to call the above version * directly so that you only construct `scratch' and `backedges' * once. * * Additional return value is now -1, meaning that scratch space * could not be allocated. */ int maxflow(int nv, int source, int sink, int ne, const int *edges, const int *capacity, int *flow, int *cut); #endif /* MAXFLOW_MAXFLOW_H */ puzzles-r9872/penrose.h0000644000175300017530000000334211561025457014261 0ustar simonsimon/* penrose.h * * Penrose tiling functions. * * Provides an interface with which to generate Penrose tilings * by recursive subdivision of an initial tile of choice (one of the * four sets of two pairs kite/dart, or thin/thick rhombus). * * You supply a callback function and a context pointer, which is * called with each tile in turn: you choose how many times to recurse. */ #ifndef _PENROSE_H #define _PENROSE_H #ifndef PHI #define PHI 1.6180339887 #endif typedef struct vector vector; double v_x(vector *vs, int i); double v_y(vector *vs, int i); typedef struct penrose_state penrose_state; /* Return non-zero to clip the tree here (i.e. not recurse * below this tile). * * Parameters are state, vector array, npoints, depth. * ctx is inside state. */ typedef int (*tile_callback)(penrose_state *, vector *, int, int); struct penrose_state { int start_size; /* initial side length */ int max_depth; /* Recursion depth */ tile_callback new_tile; void *ctx; /* for callback */ }; enum { PENROSE_P2, PENROSE_P3 }; extern int penrose(penrose_state *state, int which, int angle); /* Returns the side-length of a penrose tile at recursion level * gen, given a starting side length. */ extern double penrose_side_length(double start_size, int depth); /* Returns the count of each type of tile at a given recursion depth. */ extern void penrose_count_tiles(int gen, int *nlarge, int *nsmall); /* Calculate start size and recursion depth required to produce a * width-by-height sized patch of penrose tiles with the given tilesize */ extern void penrose_calculate_size(int which, int tilesize, int w, int h, double *required_radius, int *start_size, int *depth); #endif puzzles-r9872/puzzles.h0000644000175300017530000005303012132232554014312 0ustar simonsimon/* * puzzles.h: header file for my puzzle collection */ #ifndef PUZZLES_PUZZLES_H #define PUZZLES_PUZZLES_H #include /* for FILE */ #include /* for size_t */ #include /* for UINT_MAX */ #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #define PI 3.141592653589793238462643383279502884197169399 #define lenof(array) ( sizeof(array) / sizeof(*(array)) ) #define STR_INT(x) #x #define STR(x) STR_INT(x) /* NB not perfect because they evaluate arguments multiple times. */ #ifndef max #define max(x,y) ( (x)>(y) ? (x) : (y) ) #endif /* max */ #ifndef min #define min(x,y) ( (x)<(y) ? (x) : (y) ) #endif /* min */ enum { LEFT_BUTTON = 0x0200, MIDDLE_BUTTON, RIGHT_BUTTON, LEFT_DRAG, MIDDLE_DRAG, RIGHT_DRAG, LEFT_RELEASE, MIDDLE_RELEASE, RIGHT_RELEASE, CURSOR_UP, CURSOR_DOWN, CURSOR_LEFT, CURSOR_RIGHT, CURSOR_SELECT, CURSOR_SELECT2, /* made smaller because of 'limited range of datatype' errors. */ MOD_CTRL = 0x1000, MOD_SHFT = 0x2000, MOD_NUM_KEYPAD = 0x4000, MOD_MASK = 0x7000 /* mask for all modifiers */ }; #define IS_MOUSE_DOWN(m) ( (unsigned)((m) - LEFT_BUTTON) <= \ (unsigned)(RIGHT_BUTTON - LEFT_BUTTON)) #define IS_MOUSE_DRAG(m) ( (unsigned)((m) - LEFT_DRAG) <= \ (unsigned)(RIGHT_DRAG - LEFT_DRAG)) #define IS_MOUSE_RELEASE(m) ( (unsigned)((m) - LEFT_RELEASE) <= \ (unsigned)(RIGHT_RELEASE - LEFT_RELEASE)) #define IS_CURSOR_MOVE(m) ( (m) == CURSOR_UP || (m) == CURSOR_DOWN || \ (m) == CURSOR_RIGHT || (m) == CURSOR_LEFT ) #define IS_CURSOR_SELECT(m) ( (m) == CURSOR_SELECT || (m) == CURSOR_SELECT2) /* * Flags in the back end's `flags' word. */ /* Bit flags indicating mouse button priorities */ #define BUTTON_BEATS(x,y) ( 1 << (((x)-LEFT_BUTTON)*3+(y)-LEFT_BUTTON) ) /* Flag indicating that Solve operations should be animated */ #define SOLVE_ANIMATES ( 1 << 9 ) /* Pocket PC: Game requires right mouse button emulation */ #define REQUIRE_RBUTTON ( 1 << 10 ) /* Pocket PC: Game requires numeric input */ #define REQUIRE_NUMPAD ( 1 << 11 ) /* end of `flags' word definitions */ #ifdef _WIN32_WCE /* Pocket PC devices have small, portrait screen that requires more vivid colours */ #define SMALL_SCREEN #define PORTRAIT_SCREEN #define VIVID_COLOURS #define STYLUS_BASED #endif #define IGNOREARG(x) ( (x) = (x) ) typedef struct frontend frontend; typedef struct config_item config_item; typedef struct midend midend; typedef struct random_state random_state; typedef struct game_params game_params; typedef struct game_state game_state; typedef struct game_ui game_ui; typedef struct game_drawstate game_drawstate; typedef struct game game; typedef struct blitter blitter; typedef struct document document; typedef struct drawing_api drawing_api; typedef struct drawing drawing; typedef struct psdata psdata; #define ALIGN_VNORMAL 0x000 #define ALIGN_VCENTRE 0x100 #define ALIGN_HLEFT 0x000 #define ALIGN_HCENTRE 0x001 #define ALIGN_HRIGHT 0x002 #define FONT_FIXED 0 #define FONT_VARIABLE 1 /* For printing colours */ #define HATCH_SLASH 1 #define HATCH_BACKSLASH 2 #define HATCH_HORIZ 3 #define HATCH_VERT 4 #define HATCH_PLUS 5 #define HATCH_X 6 /* * Structure used to pass configuration data between frontend and * game */ enum { C_STRING, C_CHOICES, C_BOOLEAN, C_END }; struct config_item { /* * `name' is never dynamically allocated. */ char *name; /* * `type' contains one of the above values. */ int type; /* * For C_STRING, `sval' is always dynamically allocated and * non-NULL. For C_BOOLEAN and C_END, `sval' is always NULL. * For C_CHOICES, `sval' is non-NULL, _not_ dynamically * allocated, and contains a set of option strings separated by * a delimiter. The delimeter is also the first character in * the string, so for example ":Foo:Bar:Baz" gives three * options `Foo', `Bar' and `Baz'. */ char *sval; /* * For C_BOOLEAN, this is TRUE or FALSE. For C_CHOICES, it * indicates the chosen index from the `sval' list. In the * above example, 0==Foo, 1==Bar and 2==Baz. */ int ival; }; /* * Platform routines */ /* We can't use #ifdef DEBUG, because Cygwin defines it by default. */ #ifdef DEBUGGING #define debug(x) (debug_printf x) void debug_printf(char *fmt, ...); #else #define debug(x) #endif void fatal(char *fmt, ...); void frontend_default_colour(frontend *fe, float *output); void deactivate_timer(frontend *fe); void activate_timer(frontend *fe); void get_random_seed(void **randseed, int *randseedsize); /* * drawing.c */ drawing *drawing_new(const drawing_api *api, midend *me, void *handle); void drawing_free(drawing *dr); void draw_text(drawing *dr, int x, int y, int fonttype, int fontsize, int align, int colour, char *text); void draw_rect(drawing *dr, int x, int y, int w, int h, int colour); void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour); void draw_polygon(drawing *dr, int *coords, int npoints, int fillcolour, int outlinecolour); void draw_circle(drawing *dr, int cx, int cy, int radius, int fillcolour, int outlinecolour); void draw_thick_line(drawing *dr, float thickness, float x1, float y1, float x2, float y2, int colour); void clip(drawing *dr, int x, int y, int w, int h); void unclip(drawing *dr); void start_draw(drawing *dr); void draw_update(drawing *dr, int x, int y, int w, int h); void end_draw(drawing *dr); char *text_fallback(drawing *dr, const char *const *strings, int nstrings); void status_bar(drawing *dr, char *text); blitter *blitter_new(drawing *dr, int w, int h); void blitter_free(drawing *dr, blitter *bl); /* save puts the portion of the current display with top-left corner * (x,y) to the blitter. load puts it back again to the specified * coords, or else wherever it was saved from * (if x = y = BLITTER_FROMSAVED). */ void blitter_save(drawing *dr, blitter *bl, int x, int y); #define BLITTER_FROMSAVED (-1) void blitter_load(drawing *dr, blitter *bl, int x, int y); void print_begin_doc(drawing *dr, int pages); void print_begin_page(drawing *dr, int number); void print_begin_puzzle(drawing *dr, float xm, float xc, float ym, float yc, int pw, int ph, float wmm, float scale); void print_end_puzzle(drawing *dr); void print_end_page(drawing *dr, int number); void print_end_doc(drawing *dr); void print_get_colour(drawing *dr, int colour, int printing_in_colour, int *hatch, float *r, float *g, float *b); int print_mono_colour(drawing *dr, int grey); /* 0==black, 1==white */ int print_grey_colour(drawing *dr, float grey); int print_hatched_colour(drawing *dr, int hatch); int print_rgb_mono_colour(drawing *dr, float r, float g, float b, int mono); int print_rgb_grey_colour(drawing *dr, float r, float g, float b, float grey); int print_rgb_hatched_colour(drawing *dr, float r, float g, float b, int hatch); void print_line_width(drawing *dr, int width); void print_line_dotted(drawing *dr, int dotted); /* * midend.c */ midend *midend_new(frontend *fe, const game *ourgame, const drawing_api *drapi, void *drhandle); void midend_free(midend *me); const game *midend_which_game(midend *me); void midend_set_params(midend *me, game_params *params); game_params *midend_get_params(midend *me); void midend_size(midend *me, int *x, int *y, int user_size); void midend_reset_tilesize(midend *me); void midend_new_game(midend *me); void midend_restart_game(midend *me); void midend_stop_anim(midend *me); int midend_process_key(midend *me, int x, int y, int button); void midend_force_redraw(midend *me); void midend_redraw(midend *me); float *midend_colours(midend *me, int *ncolours); void midend_freeze_timer(midend *me, float tprop); void midend_timer(midend *me, float tplus); int midend_num_presets(midend *me); void midend_fetch_preset(midend *me, int n, char **name, game_params **params); int midend_which_preset(midend *me); int midend_wants_statusbar(midend *me); enum { CFG_SETTINGS, CFG_SEED, CFG_DESC, CFG_FRONTEND_SPECIFIC }; config_item *midend_get_config(midend *me, int which, char **wintitle); char *midend_set_config(midend *me, int which, config_item *cfg); char *midend_game_id(midend *me, char *id); char *midend_get_game_id(midend *me); char *midend_get_random_seed(midend *me); int midend_can_format_as_text_now(midend *me); char *midend_text_format(midend *me); char *midend_solve(midend *me); int midend_status(midend *me); int midend_can_undo(midend *me); int midend_can_redo(midend *me); void midend_supersede_game_desc(midend *me, char *desc, char *privdesc); char *midend_rewrite_statusbar(midend *me, char *text); void midend_serialise(midend *me, void (*write)(void *ctx, void *buf, int len), void *wctx); char *midend_deserialise(midend *me, int (*read)(void *ctx, void *buf, int len), void *rctx); char *identify_game(char **name, int (*read)(void *ctx, void *buf, int len), void *rctx); void midend_request_id_changes(midend *me, void (*notify)(void *), void *ctx); /* Printing functions supplied by the mid-end */ char *midend_print_puzzle(midend *me, document *doc, int with_soln); int midend_tilesize(midend *me); /* * malloc.c */ void *smalloc(size_t size); void *srealloc(void *p, size_t size); void sfree(void *p); char *dupstr(const char *s); #define snew(type) \ ( (type *) smalloc (sizeof (type)) ) #define snewn(number, type) \ ( (type *) smalloc ((number) * sizeof (type)) ) #define sresize(array, number, type) \ ( (type *) srealloc ((array), (number) * sizeof (type)) ) /* * misc.c */ void free_cfg(config_item *cfg); void obfuscate_bitmap(unsigned char *bmp, int bits, int decode); /* allocates output each time. len is always in bytes of binary data. * May assert (or just go wrong) if lengths are unchecked. */ char *bin2hex(const unsigned char *in, int inlen); unsigned char *hex2bin(const char *in, int outlen); /* Sets (and possibly dims) background from frontend default colour, * and auto-generates highlight and lowlight colours too. */ void game_mkhighlight(frontend *fe, float *ret, int background, int highlight, int lowlight); /* As above, but starts from a provided background colour rather * than the frontend default. */ void game_mkhighlight_specific(frontend *fe, float *ret, int background, int highlight, int lowlight); /* Randomly shuffles an array of items. */ void shuffle(void *array, int nelts, int eltsize, random_state *rs); /* Draw a rectangle outline, using the drawing API's draw_line. */ void draw_rect_outline(drawing *dr, int x, int y, int w, int h, int colour); /* Draw a set of rectangle corners (e.g. for a cursor display). */ void draw_rect_corners(drawing *dr, int cx, int cy, int r, int col); void move_cursor(int button, int *x, int *y, int maxw, int maxh, int wrap); /* Used in netslide.c and sixteen.c for cursor movement around edge. */ int c2pos(int w, int h, int cx, int cy); int c2diff(int w, int h, int cx, int cy, int button); void pos2c(int w, int h, int pos, int *cx, int *cy); /* Draws text with an 'outline' formed by offsetting the text * by one pixel; useful for highlighting. Outline is omitted if -1. */ void draw_text_outline(drawing *dr, int x, int y, int fonttype, int fontsize, int align, int text_colour, int outline_colour, char *text); /* * dsf.c */ int *snew_dsf(int size); void print_dsf(int *dsf, int size); /* Return the canonical element of the equivalence class containing element * val. If 'inverse' is non-NULL, this function will put into it a flag * indicating whether the canonical element is inverse to val. */ int edsf_canonify(int *dsf, int val, int *inverse); int dsf_canonify(int *dsf, int val); int dsf_size(int *dsf, int val); /* Allow the caller to specify that two elements should be in the same * equivalence class. If 'inverse' is TRUE, the elements are actually opposite * to one another in some sense. This function will fail an assertion if the * caller gives it self-contradictory data, ie if two elements are claimed to * be both opposite and non-opposite. */ void edsf_merge(int *dsf, int v1, int v2, int inverse); void dsf_merge(int *dsf, int v1, int v2); void dsf_init(int *dsf, int len); /* * tdq.c */ /* * Data structure implementing a 'to-do queue', a simple * de-duplicating to-do list mechanism. * * Specification: a tdq is a queue which can hold integers from 0 to * n-1, where n was some constant specified at tdq creation time. No * integer may appear in the queue's current contents more than once; * an attempt to add an already-present integer again will do nothing, * so that that integer is removed from the queue at the position * where it was _first_ inserted. The add and remove operations take * constant time. * * The idea is that you might use this in applications like solvers: * keep a tdq listing the indices of grid squares that you currently * need to process in some way. Whenever you modify a square in a way * that will require you to re-scan its neighbours, add them to the * list with tdq_add; meanwhile you're constantly taking elements off * the list when you need another square to process. In solvers where * deductions are mostly localised, this should prevent repeated * O(N^2) loops over the whole grid looking for something to do. (But * if only _most_ of the deductions are localised, then you should * respond to an empty to-do list by re-adding everything using * tdq_fill, so _then_ you rescan the whole grid looking for newly * enabled non-local deductions. Only if you've done that and emptied * the list again finding nothing new to do are you actually done.) */ typedef struct tdq tdq; tdq *tdq_new(int n); void tdq_free(tdq *tdq); void tdq_add(tdq *tdq, int k); int tdq_remove(tdq *tdq); /* returns -1 if nothing available */ void tdq_fill(tdq *tdq); /* add everything to the tdq at once */ /* * laydomino.c */ int *domino_layout(int w, int h, random_state *rs); void domino_layout_prealloc(int w, int h, random_state *rs, int *grid, int *grid2, int *list); /* * version.c */ extern char ver[]; /* * random.c */ random_state *random_new(const char *seed, int len); random_state *random_copy(random_state *tocopy); unsigned long random_bits(random_state *state, int bits); unsigned long random_upto(random_state *state, unsigned long limit); void random_free(random_state *state); char *random_state_encode(random_state *state); random_state *random_state_decode(const char *input); /* random.c also exports SHA, which occasionally comes in useful. */ #if __STDC_VERSION__ >= 199901L #include typedef uint32_t uint32; #elif UINT_MAX >= 4294967295L typedef unsigned int uint32; #else typedef unsigned long uint32; #endif typedef struct { uint32 h[5]; unsigned char block[64]; int blkused; uint32 lenhi, lenlo; } SHA_State; void SHA_Init(SHA_State *s); void SHA_Bytes(SHA_State *s, const void *p, int len); void SHA_Final(SHA_State *s, unsigned char *output); void SHA_Simple(const void *p, int len, unsigned char *output); /* * printing.c */ document *document_new(int pw, int ph, float userscale); void document_free(document *doc); void document_add_puzzle(document *doc, const game *game, game_params *par, game_state *st, game_state *st2); void document_print(document *doc, drawing *dr); /* * ps.c */ psdata *ps_init(FILE *outfile, int colour); void ps_free(psdata *ps); drawing *ps_drawing_api(psdata *ps); /* * combi.c: provides a structure and functions for iterating over * combinations (i.e. choosing r things out of n). */ typedef struct _combi_ctx { int r, n, nleft, total; int *a; } combi_ctx; combi_ctx *new_combi(int r, int n); void reset_combi(combi_ctx *combi); combi_ctx *next_combi(combi_ctx *combi); /* returns NULL for end */ void free_combi(combi_ctx *combi); /* * divvy.c */ /* divides w*h rectangle into pieces of size k. Returns w*h dsf. */ int *divvy_rectangle(int w, int h, int k, random_state *rs); /* * Data structure containing the function calls and data specific * to a particular game. This is enclosed in a data structure so * that a particular platform can choose, if it wishes, to compile * all the games into a single combined executable rather than * having lots of little ones. */ struct game { const char *name; const char *winhelp_topic, *htmlhelp_topic; game_params *(*default_params)(void); int (*fetch_preset)(int i, char **name, game_params **params); void (*decode_params)(game_params *, char const *string); char *(*encode_params)(const game_params *, int full); void (*free_params)(game_params *params); game_params *(*dup_params)(const game_params *params); int can_configure; config_item *(*configure)(const game_params *params); game_params *(*custom_params)(const config_item *cfg); char *(*validate_params)(const game_params *params, int full); char *(*new_desc)(const game_params *params, random_state *rs, char **aux, int interactive); char *(*validate_desc)(const game_params *params, const char *desc); game_state *(*new_game)(midend *me, const game_params *params, const char *desc); game_state *(*dup_game)(const game_state *state); void (*free_game)(game_state *state); int can_solve; char *(*solve)(const game_state *orig, const game_state *curr, const char *aux, char **error); int can_format_as_text_ever; int (*can_format_as_text_now)(const game_params *params); char *(*text_format)(const game_state *state); game_ui *(*new_ui)(const game_state *state); void (*free_ui)(game_ui *ui); char *(*encode_ui)(const game_ui *ui); void (*decode_ui)(game_ui *ui, const char *encoding); void (*changed_state)(game_ui *ui, const game_state *oldstate, const game_state *newstate); char *(*interpret_move)(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button); game_state *(*execute_move)(const game_state *state, const char *move); int preferred_tilesize; void (*compute_size)(const game_params *params, int tilesize, int *x, int *y); void (*set_size)(drawing *dr, game_drawstate *ds, const game_params *params, int tilesize); float *(*colours)(frontend *fe, int *ncolours); game_drawstate *(*new_drawstate)(drawing *dr, const game_state *state); void (*free_drawstate)(drawing *dr, game_drawstate *ds); void (*redraw)(drawing *dr, game_drawstate *ds, const game_state *oldstate, const game_state *newstate, int dir, const game_ui *ui, float anim_time, float flash_time); float (*anim_length)(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui); float (*flash_length)(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui); int (*status)(const game_state *state); int can_print, can_print_in_colour; void (*print_size)(const game_params *params, float *x, float *y); void (*print)(drawing *dr, const game_state *state, int tilesize); int wants_statusbar; int is_timed; int (*timing_state)(const game_state *state, game_ui *ui); int flags; }; /* * Data structure containing the drawing API implemented by the * front end and also by cross-platform printing modules such as * PostScript. */ struct drawing_api { void (*draw_text)(void *handle, int x, int y, int fonttype, int fontsize, int align, int colour, char *text); void (*draw_rect)(void *handle, int x, int y, int w, int h, int colour); void (*draw_line)(void *handle, int x1, int y1, int x2, int y2, int colour); void (*draw_polygon)(void *handle, int *coords, int npoints, int fillcolour, int outlinecolour); void (*draw_circle)(void *handle, int cx, int cy, int radius, int fillcolour, int outlinecolour); void (*draw_update)(void *handle, int x, int y, int w, int h); void (*clip)(void *handle, int x, int y, int w, int h); void (*unclip)(void *handle); void (*start_draw)(void *handle); void (*end_draw)(void *handle); void (*status_bar)(void *handle, char *text); blitter *(*blitter_new)(void *handle, int w, int h); void (*blitter_free)(void *handle, blitter *bl); void (*blitter_save)(void *handle, blitter *bl, int x, int y); void (*blitter_load)(void *handle, blitter *bl, int x, int y); void (*begin_doc)(void *handle, int pages); void (*begin_page)(void *handle, int number); void (*begin_puzzle)(void *handle, float xm, float xc, float ym, float yc, int pw, int ph, float wmm); void (*end_puzzle)(void *handle); void (*end_page)(void *handle, int number); void (*end_doc)(void *handle); void (*line_width)(void *handle, float width); void (*line_dotted)(void *handle, int dotted); char *(*text_fallback)(void *handle, const char *const *strings, int nstrings); void (*draw_thick_line)(void *handle, float thickness, float x1, float y1, float x2, float y2, int colour); }; /* * For one-game-at-a-time platforms, there's a single structure * like the above, under a fixed name. For all-at-once platforms, * there's a list of all available puzzles in array form. */ #ifdef COMBINED extern const game *gamelist[]; extern const int gamecount; #else extern const game thegame; #endif #endif /* PUZZLES_PUZZLES_H */ puzzles-r9872/resource.h0000644000175300017530000000100610570642243014425 0ustar simonsimon #define IDR_MENUBAR1 101 #define ID_GAME 40005 #define ID_TYPE 40006 #define IDS_CAP_GAME 40105 #define IDS_CAP_TYPE 40106 #define IDD_ABOUT 2000 #define IDC_ABOUT_CAPTION 2001 #define IDC_ABOUT_LINE 2002 #define IDC_ABOUT_GAME 2003 #define IDC_ABOUT_VERSION 2004 #define IDD_CONFIG 2100 #define IDC_CONFIG_CAPTION 2101 #define IDC_CONFIG_LINE 2102 #define IDR_PADTOOLBAR 4000 puzzles-r9872/tree234.h0000644000175300017530000001607210146416104013771 0ustar simonsimon/* * tree234.h: header defining functions in tree234.c. * * This file is copyright 1999-2001 Simon Tatham. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef TREE234_H #define TREE234_H /* * This typedef is opaque outside tree234.c itself. */ typedef struct tree234_Tag tree234; typedef int (*cmpfn234)(void *, void *); typedef void *(*copyfn234)(void *state, void *element); /* * Create a 2-3-4 tree. If `cmp' is NULL, the tree is unsorted, and * lookups by key will fail: you can only look things up by numeric * index, and you have to use addpos234() and delpos234(). */ tree234 *newtree234(cmpfn234 cmp); /* * Free a 2-3-4 tree (not including freeing the elements). */ void freetree234(tree234 *t); /* * Add an element e to a sorted 2-3-4 tree t. Returns e on success, * or if an existing element compares equal, returns that. */ void *add234(tree234 *t, void *e); /* * Add an element e to an unsorted 2-3-4 tree t. Returns e on * success, NULL on failure. (Failure should only occur if the * index is out of range or the tree is sorted.) * * Index range can be from 0 to the tree's current element count, * inclusive. */ void *addpos234(tree234 *t, void *e, int index); /* * Look up the element at a given numeric index in a 2-3-4 tree. * Returns NULL if the index is out of range. * * One obvious use for this function is in iterating over the whole * of a tree (sorted or unsorted): * * for (i = 0; (p = index234(tree, i)) != NULL; i++) consume(p); * * or * * int maxcount = count234(tree); * for (i = 0; i < maxcount; i++) { * p = index234(tree, i); * assert(p != NULL); * consume(p); * } */ void *index234(tree234 *t, int index); /* * Find an element e in a sorted 2-3-4 tree t. Returns NULL if not * found. e is always passed as the first argument to cmp, so cmp * can be an asymmetric function if desired. cmp can also be passed * as NULL, in which case the compare function from the tree proper * will be used. * * Three of these functions are special cases of findrelpos234. The * non-`pos' variants lack the `index' parameter: if the parameter * is present and non-NULL, it must point to an integer variable * which will be filled with the numeric index of the returned * element. * * The non-`rel' variants lack the `relation' parameter. This * parameter allows you to specify what relation the element you * provide has to the element you're looking for. This parameter * can be: * * REL234_EQ - find only an element that compares equal to e * REL234_LT - find the greatest element that compares < e * REL234_LE - find the greatest element that compares <= e * REL234_GT - find the smallest element that compares > e * REL234_GE - find the smallest element that compares >= e * * Non-`rel' variants assume REL234_EQ. * * If `rel' is REL234_GT or REL234_LT, the `e' parameter may be * NULL. In this case, REL234_GT will return the smallest element * in the tree, and REL234_LT will return the greatest. This gives * an alternative means of iterating over a sorted tree, instead of * using index234: * * // to loop forwards * for (p = NULL; (p = findrel234(tree, p, NULL, REL234_GT)) != NULL ;) * consume(p); * * // to loop backwards * for (p = NULL; (p = findrel234(tree, p, NULL, REL234_LT)) != NULL ;) * consume(p); */ enum { REL234_EQ, REL234_LT, REL234_LE, REL234_GT, REL234_GE }; void *find234(tree234 *t, void *e, cmpfn234 cmp); void *findrel234(tree234 *t, void *e, cmpfn234 cmp, int relation); void *findpos234(tree234 *t, void *e, cmpfn234 cmp, int *index); void *findrelpos234(tree234 *t, void *e, cmpfn234 cmp, int relation, int *index); /* * Delete an element e in a 2-3-4 tree. Does not free the element, * merely removes all links to it from the tree nodes. * * delpos234 deletes the element at a particular tree index: it * works on both sorted and unsorted trees. * * del234 deletes the element passed to it, so it only works on * sorted trees. (It's equivalent to using findpos234 to determine * the index of an element, and then passing that index to * delpos234.) * * Both functions return a pointer to the element they delete, for * the user to free or pass on elsewhere or whatever. If the index * is out of range (delpos234) or the element is already not in the * tree (del234) then they return NULL. */ void *del234(tree234 *t, void *e); void *delpos234(tree234 *t, int index); /* * Return the total element count of a tree234. */ int count234(tree234 *t); /* * Split a tree234 into two valid tree234s. * * splitpos234 splits at a given index. If `before' is TRUE, the * items at and after that index are left in t and the ones before * are returned; if `before' is FALSE, the items before that index * are left in t and the rest are returned. * * split234 splits at a given key. You can pass any of the * relations used with findrel234, except for REL234_EQ. The items * in the tree that satisfy the relation are returned; the * remainder are left. */ tree234 *splitpos234(tree234 *t, int index, int before); tree234 *split234(tree234 *t, void *e, cmpfn234 cmp, int rel); /* * Join two tree234s together into a single one. * * All the elements in t1 are placed to the left of all the * elements in t2. If the trees are sorted, there will be a test to * ensure that this satisfies the ordering criterion, and NULL will * be returned otherwise. If the trees are unsorted, there is no * restriction on the use of join234. * * The tree returned is t1 (join234) or t2 (join234r), if the * operation is successful. */ tree234 *join234(tree234 *t1, tree234 *t2); tree234 *join234r(tree234 *t1, tree234 *t2); /* * Make a complete copy of a tree234. Element pointers will be * reused unless copyfn is non-NULL, in which case it will be used * to copy each element. (copyfn takes two `void *' parameters; the * first is private state and the second is the element. A simple * copy routine probably won't need private state.) */ tree234 *copytree234(tree234 *t, copyfn234 copyfn, void *copyfnstate); #endif /* TREE234_H */ puzzles-r9872/blackbox.R0000644000175300017530000000050112154655733014344 0ustar simonsimon# -*- makefile -*- blackbox : [X] GTK COMMON blackbox blackbox-icon|no-icon blackbox : [G] WINDOWS COMMON blackbox blackbox.res|noicon.res ALL += blackbox[COMBINED] !begin gtk GAMES += blackbox !end !begin >list.c A(blackbox) \ !end !begin >gamedesc.txt blackbox:blackbox.exe:Black Box:Ball-finding puzzle !end puzzles-r9872/bridges.R0000644000175300017530000000056712154655733014212 0ustar simonsimon# -*- makefile -*- BRIDGES_EXTRA = dsf bridges : [X] GTK COMMON bridges BRIDGES_EXTRA bridges-icon|no-icon bridges : [G] WINDOWS COMMON bridges BRIDGES_EXTRA bridges.res|noicon.res ALL += bridges[COMBINED] BRIDGES_EXTRA !begin gtk GAMES += bridges !end !begin >list.c A(bridges) \ !end !begin >gamedesc.txt bridges:bridges.exe:Bridges:Bridge-placing puzzle !end puzzles-r9872/cube.R0000644000175300017530000000043012154655733013476 0ustar simonsimon# -*- makefile -*- cube : [X] GTK COMMON cube cube-icon|no-icon cube : [G] WINDOWS COMMON cube cube.res|noicon.res ALL += cube[COMBINED] !begin gtk GAMES += cube !end !begin >list.c A(cube) \ !end !begin >gamedesc.txt cube:cube.exe:Cube:Rolling cube puzzle !end puzzles-r9872/dominosa.R0000644000175300017530000000061212154655733014373 0ustar simonsimon# -*- makefile -*- DOMINOSA_EXTRA = laydomino dominosa : [X] GTK COMMON dominosa DOMINOSA_EXTRA dominosa-icon|no-icon dominosa : [G] WINDOWS COMMON dominosa DOMINOSA_EXTRA dominosa.res|noicon.res ALL += dominosa[COMBINED] DOMINOSA_EXTRA !begin gtk GAMES += dominosa !end !begin >list.c A(dominosa) \ !end !begin >gamedesc.txt dominosa:dominosa.exe:Dominosa:Domino tiling puzzle !end puzzles-r9872/fifteen.R0000644000175300017530000000046712154655733014212 0ustar simonsimon# -*- makefile -*- fifteen : [X] GTK COMMON fifteen fifteen-icon|no-icon fifteen : [G] WINDOWS COMMON fifteen fifteen.res|noicon.res ALL += fifteen[COMBINED] !begin gtk GAMES += fifteen !end !begin >list.c A(fifteen) \ !end !begin >gamedesc.txt fifteen:fifteen.exe:Fifteen:Sliding block puzzle !end puzzles-r9872/filling.R0000644000175300017530000000100112154655733014177 0ustar simonsimon# -*- makefile -*- FILLING_EXTRA = dsf fillingsolver : [U] filling[STANDALONE_SOLVER] FILLING_EXTRA STANDALONE fillingsolver : [C] filling[STANDALONE_SOLVER] FILLING_EXTRA STANDALONE filling : [X] GTK COMMON filling FILLING_EXTRA filling-icon|no-icon filling : [G] WINDOWS COMMON filling FILLING_EXTRA filling.res|noicon.res ALL += filling[COMBINED] FILLING_EXTRA !begin gtk GAMES += filling !end !begin >list.c A(filling) \ !end !begin >gamedesc.txt filling:filling.exe:Filling:Polyomino puzzle !end puzzles-r9872/flip.R0000644000175300017530000000052112154655733013513 0ustar simonsimon# -*- makefile -*- FLIP_EXTRA = tree234 flip : [X] GTK COMMON flip FLIP_EXTRA flip-icon|no-icon flip : [G] WINDOWS COMMON flip FLIP_EXTRA flip.res|noicon.res ALL += flip[COMBINED] FLIP_EXTRA !begin gtk GAMES += flip !end !begin >list.c A(flip) \ !end !begin >gamedesc.txt flip:flip.exe:Flip:Tile inversion puzzle !end puzzles-r9872/galaxies.R0000644000175300017530000000135612154655733014365 0ustar simonsimon# -*- makefile -*- GALAXIES_EXTRA = dsf galaxies : [X] GTK COMMON galaxies GALAXIES_EXTRA galaxies-icon|no-icon galaxies : [G] WINDOWS COMMON galaxies GALAXIES_EXTRA galaxies.res|noicon.res galaxiessolver : [U] galaxies[STANDALONE_SOLVER] GALAXIES_EXTRA STANDALONE m.lib galaxiessolver : [C] galaxies[STANDALONE_SOLVER] GALAXIES_EXTRA STANDALONE galaxiespicture : [U] galaxies[STANDALONE_PICTURE_GENERATOR] GALAXIES_EXTRA STANDALONE + m.lib galaxiespicture : [C] galaxies[STANDALONE_PICTURE_GENERATOR] GALAXIES_EXTRA STANDALONE ALL += galaxies[COMBINED] GALAXIES_EXTRA !begin gtk GAMES += galaxies !end !begin >list.c A(galaxies) \ !end !begin >gamedesc.txt galaxies:galaxies.exe:Galaxies:Symmetric polyomino puzzle !end puzzles-r9872/guess.R0000644000175300017530000000045212154655733013712 0ustar simonsimon# -*- makefile -*- guess : [X] GTK COMMON guess guess-icon|no-icon guess : [G] WINDOWS COMMON guess guess.res|noicon.res ALL += guess[COMBINED] !begin gtk GAMES += guess !end !begin >list.c A(guess) \ !end !begin >gamedesc.txt guess:guess.exe:Guess:Combination-guessing puzzle !end puzzles-r9872/inertia.R0000644000175300017530000000047012154655733014217 0ustar simonsimon# -*- makefile -*- inertia : [X] GTK COMMON inertia inertia-icon|no-icon inertia : [G] WINDOWS COMMON inertia inertia.res|noicon.res ALL += inertia[COMBINED] !begin gtk GAMES += inertia !end !begin >list.c A(inertia) \ !end !begin >gamedesc.txt inertia:inertia.exe:Inertia:Gem-collecting puzzle !end puzzles-r9872/keen.R0000644000175300017530000000111312154655733013501 0ustar simonsimon# -*- makefile -*- KEEN_LATIN_EXTRA = tree234 maxflow dsf KEEN_EXTRA = latin KEEN_LATIN_EXTRA keen : [X] GTK COMMON keen KEEN_EXTRA keen-icon|no-icon keen : [G] WINDOWS COMMON keen KEEN_EXTRA keen.res|noicon.res keensolver : [U] keen[STANDALONE_SOLVER] latin[STANDALONE_SOLVER] KEEN_LATIN_EXTRA STANDALONE keensolver : [C] keen[STANDALONE_SOLVER] latin[STANDALONE_SOLVER] KEEN_LATIN_EXTRA STANDALONE ALL += keen[COMBINED] KEEN_EXTRA !begin gtk GAMES += keen !end !begin >list.c A(keen) \ !end !begin >gamedesc.txt keen:keen.exe:Keen:Arithmetic Latin square puzzle !end puzzles-r9872/lightup.R0000644000175300017530000000101712154655733014236 0ustar simonsimon# -*- makefile -*- LIGHTUP_EXTRA = combi lightup : [X] GTK COMMON lightup LIGHTUP_EXTRA lightup-icon|no-icon lightup : [G] WINDOWS COMMON lightup LIGHTUP_EXTRA lightup.res|noicon.res lightupsolver : [U] lightup[STANDALONE_SOLVER] LIGHTUP_EXTRA STANDALONE lightupsolver : [C] lightup[STANDALONE_SOLVER] LIGHTUP_EXTRA STANDALONE ALL += lightup[COMBINED] LIGHTUP_EXTRA !begin gtk GAMES += lightup !end !begin >list.c A(lightup) \ !end !begin >gamedesc.txt lightup:lightup.exe:Light Up:Light-bulb placing puzzle !end puzzles-r9872/loopy.R0000644000175300017530000000136212154655733013727 0ustar simonsimon# -*- makefile -*- LOOPY_EXTRA = tree234 dsf grid penrose loopgen loopy : [X] GTK COMMON loopy LOOPY_EXTRA loopy-icon|no-icon loopy : [G] WINDOWS COMMON loopy LOOPY_EXTRA loopy.res|noicon.res loopysolver : [U] loopy[STANDALONE_SOLVER] LOOPY_EXTRA STANDALONE m.lib loopysolver : [C] loopy[STANDALONE_SOLVER] LOOPY_EXTRA STANDALONE #penrose : [U] penrose[TEST_PENROSE] STANDALONE m.lib #penrose : [C] penrose[TEST_PENROSE] STANDALONE #test-basis : [U] penrose[TEST_VECTORS] tree234 STANDALONE m.lib #test-basis : [C] penrose[TEST_VECTORS] tree234 STANDALONE ALL += loopy[COMBINED] LOOPY_EXTRA !begin gtk GAMES += loopy !end !begin >list.c A(loopy) \ !end !begin >gamedesc.txt loopy:loopy.exe:Loopy:Loop-drawing puzzle !end puzzles-r9872/magnets.R0000644000175300017530000000104412154655733014220 0ustar simonsimon# -*- makefile -*- MAGNETS_EXTRA = laydomino magnets : [X] GTK COMMON magnets MAGNETS_EXTRA magnets-icon|no-icon magnets : [G] WINDOWS COMMON magnets MAGNETS_EXTRA magnets.res|noicon.res magnetssolver : [U] magnets[STANDALONE_SOLVER] MAGNETS_EXTRA STANDALONE m.lib magnetssolver : [C] magnets[STANDALONE_SOLVER] MAGNETS_EXTRA STANDALONE ALL += magnets[COMBINED] MAGNETS_EXTRA !begin gtk GAMES += magnets !end !begin >list.c A(magnets) \ !end !begin >gamedesc.txt magnets:magnets.exe:Magnets:Magnet-placing puzzle !end puzzles-r9872/map.R0000644000175300017530000000070512154655733013342 0ustar simonsimon# -*- makefile -*- MAP_EXTRA = dsf map : [X] GTK COMMON map MAP_EXTRA map-icon|no-icon map : [G] WINDOWS COMMON map MAP_EXTRA map.res|noicon.res mapsolver : [U] map[STANDALONE_SOLVER] MAP_EXTRA STANDALONE m.lib mapsolver : [C] map[STANDALONE_SOLVER] MAP_EXTRA STANDALONE ALL += map[COMBINED] MAP_EXTRA !begin gtk GAMES += map !end !begin >list.c A(map) \ !end !begin >gamedesc.txt map:map.exe:Map:Map-colouring puzzle !end puzzles-r9872/mines.R0000644000175300017530000000075612154655733013706 0ustar simonsimon# -*- makefile -*- MINES_EXTRA = tree234 mines : [X] GTK COMMON mines MINES_EXTRA mines-icon|no-icon mines : [G] WINDOWS COMMON mines MINES_EXTRA mines.res|noicon.res mineobfusc : [U] mines[STANDALONE_OBFUSCATOR] MINES_EXTRA STANDALONE mineobfusc : [C] mines[STANDALONE_OBFUSCATOR] MINES_EXTRA STANDALONE ALL += mines[COMBINED] MINES_EXTRA !begin gtk GAMES += mines !end !begin >list.c A(mines) \ !end !begin >gamedesc.txt mines:mines.exe:Mines:Mine-finding puzzle !end puzzles-r9872/net.R0000644000175300017530000000071412154655733013353 0ustar simonsimon# -*- makefile -*- NET_EXTRA = tree234 dsf net : [X] GTK COMMON net NET_EXTRA net-icon|no-icon # The Windows Net shouldn't be called `net.exe' since Windows # already has a reasonably important utility program by that name! netgame : [G] WINDOWS COMMON net NET_EXTRA net.res|noicon.res ALL += net[COMBINED] NET_EXTRA !begin gtk GAMES += net !end !begin >list.c A(net) \ !end !begin >gamedesc.txt net:netgame.exe:Net:Network jigsaw puzzle !end puzzles-r9872/netslide.R0000644000175300017530000000062312154655733014373 0ustar simonsimon# -*- makefile -*- NETSLIDE_EXTRA = tree234 netslide : [X] GTK COMMON netslide NETSLIDE_EXTRA netslide-icon|no-icon netslide : [G] WINDOWS COMMON netslide NETSLIDE_EXTRA netslide.res|noicon.res ALL += netslide[COMBINED] NETSLIDE_EXTRA !begin gtk GAMES += netslide !end !begin >list.c A(netslide) \ !end !begin >gamedesc.txt netslide:netslide.exe:Netslide:Toroidal sliding network puzzle !end puzzles-r9872/nullgame.R0000644000175300017530000000116310571114544014357 0ustar simonsimon# -*- makefile -*- # The `nullgame' source file is a largely blank one, which contains # all the correct function definitions to compile and link, but # which defines the null game in which nothing is ever drawn and # there are no valid moves. Its main purpose is to act as a # template for writing new game definition source files. I include # it in the Makefile because it will be worse than useless if it # ever fails to compile, so it's important that it should actually # be built on a regular basis. nullgame : [X] GTK COMMON nullgame nullgame-icon|no-icon nullgame : [G] WINDOWS COMMON nullgame nullgame.res|noicon.res puzzles-r9872/pattern.R0000644000175300017530000000064612154655733014246 0ustar simonsimon# -*- makefile -*- pattern : [X] GTK COMMON pattern pattern-icon|no-icon pattern : [G] WINDOWS COMMON pattern pattern.res|noicon.res patternsolver : [U] pattern[STANDALONE_SOLVER] STANDALONE patternsolver : [C] pattern[STANDALONE_SOLVER] STANDALONE ALL += pattern[COMBINED] !begin gtk GAMES += pattern !end !begin >list.c A(pattern) \ !end !begin >gamedesc.txt pattern:pattern.exe:Pattern:Pattern puzzle !end puzzles-r9872/pearl.R0000644000175300017530000000101712154655733013665 0ustar simonsimon# -*- makefile -*- PEARL_EXTRA = dsf tree234 grid penrose loopgen tdq pearl : [X] GTK COMMON pearl PEARL_EXTRA pearl-icon|no-icon pearl : [G] WINDOWS COMMON pearl PEARL_EXTRA pearl.res? pearlbench : [U] pearl[STANDALONE_SOLVER] PEARL_EXTRA STANDALONE m.lib pearlbench : [C] pearl[STANDALONE_SOLVER] PEARL_EXTRA STANDALONE ALL += pearl[COMBINED] PEARL_EXTRA !begin gtk GAMES += pearl !end !begin >list.c A(pearl) \ !end !begin >gamedesc.txt pearl:pearl.exe:Pearl:Loop-drawing puzzle !end puzzles-r9872/pegs.R0000644000175300017530000000052012154655733013516 0ustar simonsimon# -*- makefile -*- PEGS_EXTRA = tree234 pegs : [X] GTK COMMON pegs PEGS_EXTRA pegs-icon|no-icon pegs : [G] WINDOWS COMMON pegs PEGS_EXTRA pegs.res|noicon.res ALL += pegs[COMBINED] PEGS_EXTRA !begin gtk GAMES += pegs !end !begin >list.c A(pegs) \ !end !begin >gamedesc.txt pegs:pegs.exe:Pegs:Peg solitaire puzzle !end puzzles-r9872/range.R0000644000175300017530000000044612154655733013663 0ustar simonsimon# -*- makefile -*- range : [X] GTK COMMON range range-icon|no-icon range : [G] WINDOWS COMMON range range.res|noicon.res ALL += range[COMBINED] !begin gtk GAMES += range !end !begin >list.c A(range) \ !end !begin >gamedesc.txt range:range.exe:Range:Visible-distance puzzle !end puzzles-r9872/rect.R0000644000175300017530000000043412154655733013521 0ustar simonsimon# -*- makefile -*- rect : [X] GTK COMMON rect rect-icon|no-icon rect : [G] WINDOWS COMMON rect rect.res|noicon.res ALL += rect[COMBINED] !begin gtk GAMES += rect !end !begin >list.c A(rect) \ !end !begin >gamedesc.txt rect:rect.exe:Rectangles:Rectangles puzzle !end puzzles-r9872/samegame.R0000644000175300017530000000050312154655733014340 0ustar simonsimon# -*- makefile -*- samegame : [X] GTK COMMON samegame samegame-icon|no-icon samegame : [G] WINDOWS COMMON samegame samegame.res|noicon.res ALL += samegame[COMBINED] !begin gtk GAMES += samegame !end !begin >list.c A(samegame) \ !end !begin >gamedesc.txt samegame:samegame.exe:Same Game:Block-clearing puzzle !end puzzles-r9872/signpost.R0000644000175300017530000000104412154655733014430 0ustar simonsimon# -*- makefile -*- SIGNPOST_EXTRA = dsf signpost : [X] GTK COMMON signpost SIGNPOST_EXTRA signpost-icon|no-icon signpost : [G] WINDOWS COMMON signpost SIGNPOST_EXTRA signpost.res|noicon.res signpostsolver : [U] signpost[STANDALONE_SOLVER] SIGNPOST_EXTRA STANDALONE m.lib signpostsolver : [C] signpost[STANDALONE_SOLVER] SIGNPOST_EXTRA STANDALONE ALL += signpost[COMBINED] SIGNPOST_EXTRA !begin gtk GAMES += signpost !end !begin >list.c A(signpost) \ !end !begin >gamedesc.txt signpost:signpost.exe:Signpost:Square-connecting puzzle !end puzzles-r9872/singles.R0000644000175300017530000000103412154655733014225 0ustar simonsimon# -*- makefile -*- SINGLES_EXTRA = dsf latin maxflow tree234 singles : [X] GTK COMMON singles SINGLES_EXTRA singles-icon|no-icon singles : [G] WINDOWS COMMON singles SINGLES_EXTRA singles.res|noicon.res ALL += singles[COMBINED] SINGLES_EXTRA singlessolver : [U] singles[STANDALONE_SOLVER] SINGLES_EXTRA STANDALONE singlessolver : [C] singles[STANDALONE_SOLVER] SINGLES_EXTRA STANDALONE !begin gtk GAMES += singles !end !begin >list.c A(singles) \ !end !begin >gamedesc.txt singles:singles.exe:Singles:Number-removing puzzle !end puzzles-r9872/sixteen.R0000644000175300017530000000050012154655733014235 0ustar simonsimon# -*- makefile -*- sixteen : [X] GTK COMMON sixteen sixteen-icon|no-icon sixteen : [G] WINDOWS COMMON sixteen sixteen.res|noicon.res ALL += sixteen[COMBINED] !begin gtk GAMES += sixteen !end !begin >list.c A(sixteen) \ !end !begin >gamedesc.txt sixteen:sixteen.exe:Sixteen:Toroidal sliding block puzzle !end puzzles-r9872/slant.R0000644000175300017530000000074212154655733013707 0ustar simonsimon# -*- makefile -*- SLANT_EXTRA = dsf slant : [X] GTK COMMON slant SLANT_EXTRA slant-icon|no-icon slant : [G] WINDOWS COMMON slant SLANT_EXTRA slant.res|noicon.res slantsolver : [U] slant[STANDALONE_SOLVER] SLANT_EXTRA STANDALONE slantsolver : [C] slant[STANDALONE_SOLVER] SLANT_EXTRA STANDALONE ALL += slant[COMBINED] SLANT_EXTRA !begin gtk GAMES += slant !end !begin >list.c A(slant) \ !end !begin >gamedesc.txt slant:slant.exe:Slant:Maze-drawing puzzle !end puzzles-r9872/solo.R0000644000175300017530000000073212154655733013541 0ustar simonsimon# -*- makefile -*- SOLO_EXTRA = divvy dsf solo : [X] GTK COMMON solo SOLO_EXTRA solo-icon|no-icon solo : [G] WINDOWS COMMON solo SOLO_EXTRA solo.res|noicon.res solosolver : [U] solo[STANDALONE_SOLVER] SOLO_EXTRA STANDALONE solosolver : [C] solo[STANDALONE_SOLVER] SOLO_EXTRA STANDALONE ALL += solo[COMBINED] SOLO_EXTRA !begin gtk GAMES += solo !end !begin >list.c A(solo) \ !end !begin >gamedesc.txt solo:solo.exe:Solo:Number placement puzzle !end puzzles-r9872/tents.R0000644000175300017530000000075212154655733013724 0ustar simonsimon# -*- makefile -*- TENTS_EXTRA = maxflow dsf tents : [X] GTK COMMON tents TENTS_EXTRA tents-icon|no-icon tents : [G] WINDOWS COMMON tents TENTS_EXTRA tents.res|noicon.res ALL += tents[COMBINED] TENTS_EXTRA tentssolver : [U] tents[STANDALONE_SOLVER] TENTS_EXTRA STANDALONE tentssolver : [C] tents[STANDALONE_SOLVER] TENTS_EXTRA STANDALONE !begin gtk GAMES += tents !end !begin >list.c A(tents) \ !end !begin >gamedesc.txt tents:tents.exe:Tents:Tent-placing puzzle !end puzzles-r9872/towers.R0000644000175300017530000000117212154655733014107 0ustar simonsimon# -*- makefile -*- TOWERS_LATIN_EXTRA = tree234 maxflow TOWERS_EXTRA = latin TOWERS_LATIN_EXTRA towers : [X] GTK COMMON towers TOWERS_EXTRA towers-icon|no-icon towers : [G] WINDOWS COMMON towers TOWERS_EXTRA towers.res|noicon.res towerssolver : [U] towers[STANDALONE_SOLVER] latin[STANDALONE_SOLVER] TOWERS_LATIN_EXTRA STANDALONE towerssolver : [C] towers[STANDALONE_SOLVER] latin[STANDALONE_SOLVER] TOWERS_LATIN_EXTRA STANDALONE ALL += towers[COMBINED] TOWERS_EXTRA !begin gtk GAMES += towers !end !begin >list.c A(towers) \ !end !begin >gamedesc.txt towers:towers.exe:Towers:Tower-placing Latin square puzzle !end puzzles-r9872/twiddle.R0000644000175300017530000000050212154655733014214 0ustar simonsimon# -*- makefile -*- twiddle : [X] GTK COMMON twiddle twiddle-icon|no-icon twiddle : [G] WINDOWS COMMON twiddle twiddle.res|noicon.res ALL += twiddle[COMBINED] !begin gtk GAMES += twiddle !end !begin >list.c A(twiddle) \ !end !begin >gamedesc.txt twiddle:twiddle.exe:Twiddle:Rotational sliding block puzzle !end puzzles-r9872/undead.R0000644000175300017530000000045212154655733014024 0ustar simonsimon# -*- makefile -*- undead : [X] GTK COMMON undead undead-icon|no-icon undead : [G] WINDOWS COMMON undead undead.res|noicon.res ALL += undead[COMBINED] !begin gtk GAMES += undead !end !begin >list.c A(undead) \ !end !begin >gamedesc.txt undead:undead.exe:Undead:Monster-placing puzzle !end puzzles-r9872/unequal.R0000644000175300017530000000134112154655733014234 0ustar simonsimon# -*- makefile -*- UNEQUAL_EXTRA = latin tree234 maxflow unequal : [X] GTK COMMON unequal UNEQUAL_EXTRA unequal-icon|no-icon unequal : [G] WINDOWS COMMON unequal UNEQUAL_EXTRA unequal.res|noicon.res unequalsolver : [U] unequal[STANDALONE_SOLVER] latin[STANDALONE_SOLVER] tree234 maxflow STANDALONE unequalsolver : [C] unequal[STANDALONE_SOLVER] latin[STANDALONE_SOLVER] tree234 maxflow STANDALONE latincheck : [U] latin[STANDALONE_LATIN_TEST] tree234 maxflow STANDALONE latincheck : [C] latin[STANDALONE_LATIN_TEST] tree234 maxflow STANDALONE ALL += unequal[COMBINED] UNEQUAL_EXTRA !begin gtk GAMES += unequal !end !begin >list.c A(unequal) \ !end !begin >gamedesc.txt unequal:unequal.exe:Unequal:Latin square puzzle !end puzzles-r9872/unruly.R0000644000175300017530000000064012154655733014121 0ustar simonsimon# -*- makefile -*- unruly : [X] GTK COMMON unruly unruly-icon|no-icon unruly : [G] WINDOWS COMMON unruly unruly.res|noicon.res unrulysolver : [U] unruly[STANDALONE_SOLVER] STANDALONE unrulysolver : [C] unruly[STANDALONE_SOLVER] STANDALONE ALL += unruly[COMBINED] !begin gtk GAMES += unruly !end !begin >list.c A(unruly) \ !end !begin >gamedesc.txt unruly:unruly.exe:Unruly:Black and white grid puzzle !end puzzles-r9872/untangle.R0000644000175300017530000000061612154655733014403 0ustar simonsimon# -*- makefile -*- UNTANGLE_EXTRA = tree234 untangle : [X] GTK COMMON untangle UNTANGLE_EXTRA untangle-icon|no-icon untangle : [G] WINDOWS COMMON untangle UNTANGLE_EXTRA untangle.res|noicon.res ALL += untangle[COMBINED] UNTANGLE_EXTRA !begin gtk GAMES += untangle !end !begin >list.c A(untangle) \ !end !begin >gamedesc.txt untangle:untangle.exe:Untangle:Planar graph layout puzzle !end puzzles-r9872/noicon.rc0000644000175300017530000000060610571374374014255 0ustar simonsimon/* 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 puzzles-r9872/chm.but0000644000175300017530000000133510543166203013712 0ustar simonsimon\# File containing the magic HTML configuration directives to create \# an MS HTML Help project. We put this on the end of the Puzzles \# docs build command line to build the HHP and friends. \cfg{html-leaf-level}{infinite} \cfg{html-leaf-contains-contents}{false} \cfg{html-suppress-navlinks}{true} \cfg{html-suppress-address}{true} \cfg{html-contents-filename}{index.html} \cfg{html-template-filename}{%k.html} \cfg{html-template-fragment}{%k} \cfg{html-mshtmlhelp-chm}{puzzles.chm} \cfg{html-mshtmlhelp-project}{puzzles.hhp} \cfg{html-mshtmlhelp-contents}{puzzles.hhc} \cfg{html-mshtmlhelp-index}{puzzles.hhk} \cfg{html-body-end}{} \cfg{html-head-end}{} \versionid $Id$ puzzles-r9872/devel.but0000644000175300017530000061630412132234311014242 0ustar simonsimon\cfg{text-indent}{0} \cfg{text-width}{72} \cfg{text-title-align}{left} \cfg{text-chapter-align}{left} \cfg{text-chapter-numeric}{true} \cfg{text-chapter-suffix}{. } \cfg{text-chapter-underline}{-} \cfg{text-section-align}{0}{left} \cfg{text-section-numeric}{0}{true} \cfg{text-section-suffix}{0}{. } \cfg{text-section-underline}{0}{-} \cfg{text-section-align}{1}{left} \cfg{text-section-numeric}{1}{true} \cfg{text-section-suffix}{1}{. } \cfg{text-section-underline}{1}{-} \cfg{text-versionid}{0} \cfg{html-contents-filename}{index.html} \cfg{html-template-filename}{%k.html} \cfg{html-index-filename}{docindex.html} \cfg{html-leaf-level}{1} \cfg{html-contents-depth-0}{1} \cfg{html-contents-depth-1}{3} \cfg{html-leaf-contains-contents}{true} \define{dash} \u2013{-} \title Developer documentation for Simon Tatham's puzzle collection This is a guide to the internal structure of Simon Tatham's Portable Puzzle Collection (henceforth referred to simply as \q{Puzzles}), for use by anyone attempting to implement a new puzzle or port to a new platform. This guide is believed correct as of r6190. Hopefully it will be updated along with the code in future, but if not, I've at least left this version number in here so you can figure out what's changed by tracking commit comments from there onwards. \C{intro} Introduction The Puzzles code base is divided into four parts: a set of interchangeable front ends, a set of interchangeable back ends, a universal \q{middle end} which acts as a buffer between the two, and a bunch of miscellaneous utility functions. In the following sections I give some general discussion of each of these parts. \H{intro-frontend} Front end The front end is the non-portable part of the code: it's the bit that you replace completely when you port to a different platform. So it's responsible for all system calls, all GUI interaction, and anything else platform-specific. The current front ends in the main code base are for Windows, GTK and MacOS X; I also know of a third-party front end for PalmOS. The front end contains \cw{main()} or the local platform's equivalent. Top-level control over the application's execution flow belongs to the front end (it isn't, for example, a set of functions called by a universal \cw{main()} somewhere else). The front end has complete freedom to design the GUI for any given port of Puzzles. There is no centralised mechanism for maintaining the menu layout, for example. This has a cost in consistency (when I \e{do} want the same menu layout on more than one platform, I have to edit two pieces of code in parallel every time I make a change), but the advantage is that local GUI conventions can be conformed to and local constraints adapted to. For example, MacOS X has strict human interface guidelines which specify a different menu layout from the one I've used on Windows and GTK; there's nothing stopping the OS X front end from providing a menu layout consistent with those guidelines. Although the front end is mostly caller rather than the callee in its interactions with other parts of the code, it is required to implement a small API for other modules to call, mostly of drawing functions for games to use when drawing their graphics. The drawing API is documented in \k{drawing}; the other miscellaneous front end API functions are documented in \k{frontend-api}. \H{intro-backend} Back end A \q{back end}, in this collection, is synonymous with a \q{puzzle}. Each back end implements a different game. At the top level, a back end is simply a data structure, containing a few constants (flag words, preferred pixel size) and a large number of function pointers. Back ends are almost invariably callee rather than caller, which means there's a limitation on what a back end can do on its own initiative. The persistent state in a back end is divided into a number of data structures, which are used for different purposes and therefore likely to be switched around, changed without notice, and otherwise updated by the rest of the code. It is important when designing a back end to put the right pieces of data into the right structures, or standard midend-provided features (such as Undo) may fail to work. The functions and variables provided in the back end data structure are documented in \k{backend}. \H{intro-midend} Middle end Puzzles has a single and universal \q{middle end}. This code is common to all platforms and all games; it sits in between the front end and the back end and provides standard functionality everywhere. People adding new back ends or new front ends should generally not need to edit the middle end. On rare occasions there might be a change that can be made to the middle end to permit a new game to do something not currently anticipated by the middle end's present design; however, this is terribly easy to get wrong and should probably not be undertaken without consulting the primary maintainer (me). Patch submissions containing unannounced mid-end changes will be treated on their merits like any other patch; this is just a friendly warning that mid-end changes will need quite a lot of merits to make them acceptable. Functionality provided by the mid-end includes: \b Maintaining a list of game state structures and moving back and forth along that list to provide Undo and Redo. \b Handling timers (for move animations, flashes on completion, and in some cases actually timing the game). \b Handling the container format of game IDs: receiving them, picking them apart into parameters, description and/or random seed, and so on. The game back end need only handle the individual parts of a game ID (encoded parameters and encoded game description); everything else is handled centrally by the mid-end. \b Handling standard keystrokes and menu commands, such as \q{New Game}, \q{Restart Game} and \q{Quit}. \b Pre-processing mouse events so that the game back ends can rely on them arriving in a sensible order (no missing button-release events, no sudden changes of which button is currently pressed, etc). \b Handling the dialog boxes which ask the user for a game ID. \b Handling serialisation of entire games (for loading and saving a half-finished game to a disk file, or for handling application shutdown and restart on platforms such as PalmOS where state is expected to be saved). Thus, there's a lot of work done once by the mid-end so that individual back ends don't have to worry about it. All the back end has to do is cooperate in ensuring the mid-end can do its work properly. The API of functions provided by the mid-end to be called by the front end is documented in \k{midend}. \H{intro-utils} Miscellaneous utilities In addition to these three major structural components, the Puzzles code also contains a variety of utility modules usable by all of the above components. There is a set of functions to provide platform-independent random number generation; functions to make memory allocation easier; functions which implement a balanced tree structure to be used as necessary in complex algorithms; and a few other miscellaneous functions. All of these are documented in \k{utils}. \H{intro-structure} Structure of this guide There are a number of function call interfaces within Puzzles, and this guide will discuss each one in a chapter of its own. After that, \k{writing} discusses how to design new games, with some general design thoughts and tips. \C{backend} Interface to the back end This chapter gives a detailed discussion of the interface that each back end must implement. At the top level, each back end source file exports a single global symbol, which is a \c{const struct game} containing a large number of function pointers and a small amount of constant data. This structure is called by different names depending on what kind of platform the puzzle set is being compiled on: \b On platforms such as Windows and GTK, which build a separate binary for each puzzle, the game structure in every back end has the same name, \cq{thegame}; the front end refers directly to this name, so that compiling the same front end module against a different back end module builds a different puzzle. \b On platforms such as MacOS X and PalmOS, which build all the puzzles into a single monolithic binary, the game structure in each back end must have a different name, and there's a helper module \c{list.c} (constructed automatically by the same Perl script that builds the \cw{Makefile}s) which contains a complete list of those game structures. On the latter type of platform, source files may assume that the preprocessor symbol \c{COMBINED} has been defined. Thus, the usual code to declare the game structure looks something like this: \c #ifdef COMBINED \c #define thegame net /* or whatever this game is called */ \e iii iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii \c #endif \c \c const struct game thegame = { \c /* lots of structure initialisation in here */ \e iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii \c }; Game back ends must also internally define a number of data structures, for storing their various persistent state. This chapter will first discuss the nature and use of those structures, and then go on to give details of every element of the game structure. \H{backend-structs} Data structures Each game is required to define four separate data structures. This section discusses each one and suggests what sorts of things need to be put in it. \S{backend-game-params} \c{game_params} The \c{game_params} structure contains anything which affects the automatic generation of new puzzles. So if puzzle generation is parametrised in any way, those parameters need to be stored in \c{game_params}. Most puzzles currently in this collection are played on a grid of squares, meaning that the most obvious parameter is the grid size. Many puzzles have additional parameters; for example, Mines allows you to control the number of mines in the grid independently of its size, Net can be wrapping or non-wrapping, Solo has difficulty levels and symmetry settings, and so on. A simple rule for deciding whether a data item needs to go in \c{game_params} is: would the user expect to be able to control this data item from either the preset-game-types menu or the \q{Custom} game type configuration? If so, it's part of \c{game_params}. \c{game_params} structures are permitted to contain pointers to subsidiary data if they need to. The back end is required to provide functions to create and destroy \c{game_params}, and those functions can allocate and free additional memory if necessary. (It has not yet been necessary to do this in any puzzle so far, but the capability is there just in case.) \c{game_params} is also the only structure which the game's \cw{compute_size()} function may refer to; this means that any aspect of the game which affects the size of the window it needs to be drawn in must be stored in \c{game_params}. In particular, this imposes the fundamental limitation that random game generation may not have a random effect on the window size: game generation algorithms are constrained to work by starting from the grid size rather than generating it as an emergent phenomenon. (Although this is a restriction in theory, it has not yet seemed to be a problem.) \S{backend-game-state} \c{game_state} While the user is actually playing a puzzle, the \c{game_state} structure stores all the data corresponding to the current state of play. The mid-end keeps \c{game_state}s in a list, and adds to the list every time the player makes a move; the Undo and Redo functions step back and forth through that list. Therefore, a good means of deciding whether a data item needs to go in \c{game_state} is: would a player expect that data item to be restored on undo? If so, put it in \c{game_state}, and this will automatically happen without you having to lift a finger. If not \dash for example, the deaths counter in Mines is precisely something that does \e{not} want to be reset to its previous state on an undo \dash then you might have found a data item that needs to go in \c{game_ui} instead. During play, \c{game_state}s are often passed around without an accompanying \c{game_params} structure. Therefore, any information in \c{game_params} which is important during play (such as the grid size) must be duplicated within the \c{game_state}. One simple method of doing this is to have the \c{game_state} structure \e{contain} a \c{game_params} structure as one of its members, although this isn't obligatory if you prefer to do it another way. \S{backend-game-drawstate} \c{game_drawstate} \c{game_drawstate} carries persistent state relating to the current graphical contents of the puzzle window. The same \c{game_drawstate} is passed to every call to the game redraw function, so that it can remember what it has already drawn and what needs redrawing. A typical use for a \c{game_drawstate} is to have an array mirroring the array of grid squares in the \c{game_state}; then every time the redraw function was passed a \c{game_state}, it would loop over all the squares, and physically redraw any whose description in the \c{game_state} (i.e. what the square needs to look like when the redraw is completed) did not match its description in the \c{game_drawstate} (i.e. what the square currently looks like). \c{game_drawstate} is occasionally completely torn down and reconstructed by the mid-end, if the user somehow forces a full redraw. Therefore, no data should be stored in \c{game_drawstate} which is \e{not} related to the state of the puzzle window, because it might be unexpectedly destroyed. The back end provides functions to create and destroy \c{game_drawstate}, which means it can contain pointers to subsidiary allocated data if it needs to. A common thing to want to allocate in a \c{game_drawstate} is a \c{blitter}; see \k{drawing-blitter} for more on this subject. \S{backend-game-ui} \c{game_ui} \c{game_ui} contains whatever doesn't fit into the above three structures! A new \c{game_ui} is created when the user begins playing a new instance of a puzzle (i.e. during \q{New Game} or after entering a game ID etc). It persists until the user finishes playing that game and begins another one (or closes the window); in particular, \q{Restart Game} does \e{not} destroy the \c{game_ui}. \c{game_ui} is useful for implementing user-interface state which is not part of \c{game_state}. Common examples are keyboard control (you wouldn't want to have to separately Undo through every cursor motion) and mouse dragging. See \k{writing-keyboard-cursor} and \k{writing-howto-dragging}, respectively, for more details. Another use for \c{game_ui} is to store highly persistent data such as the Mines death counter. This is conceptually rather different: where the Net cursor position was \e{not important enough} to preserve for the player to restore by Undo, the Mines death counter is \e{too important} to permit the player to revert by Undo! A final use for \c{game_ui} is to pass information to the redraw function about recent changes to the game state. This is used in Mines, for example, to indicate whether a requested \q{flash} should be a white flash for victory or a red flash for defeat; see \k{writing-flash-types}. \H{backend-simple} Simple data in the back end In this section I begin to discuss each individual element in the back end structure. To begin with, here are some simple self-contained data elements. \S{backend-name} \c{name} \c const char *name; This is a simple ASCII string giving the name of the puzzle. This name will be used in window titles, in game selection menus on monolithic platforms, and anywhere else that the front end needs to know the name of a game. \S{backend-winhelp} \c{winhelp_topic} \c const char *winhelp_topic; This member is used on Windows only, to provide online help. Although the Windows front end provides a separate binary for each puzzle, it has a single monolithic help file; so when a user selects \q{Help} from the menu, the program needs to open the help file and jump to the chapter describing that particular puzzle. Therefore, each chapter in \c{puzzles.but} is labelled with a \e{help topic} name, similar to this: \c \cfg{winhelp-topic}{games.net} And then the corresponding game back end encodes the topic string (here \cq{games.net}) in the \c{winhelp_topic} element of the game structure. \H{backend-params} Handling game parameter sets In this section I present the various functions which handle the \c{game_params} structure. \S{backend-default-params} \cw{default_params()} \c game_params *(*default_params)(void); This function allocates a new \c{game_params} structure, fills it with the default values, and returns a pointer to it. \S{backend-fetch-preset} \cw{fetch_preset()} \c int (*fetch_preset)(int i, char **name, game_params **params); This function is used to populate the \q{Type} menu, which provides a list of conveniently accessible preset parameters for most games. The function is called with \c{i} equal to the index of the preset required (numbering from zero). It returns \cw{FALSE} if that preset does not exist (if \c{i} is less than zero or greater than the largest preset index). Otherwise, it sets \c{*params} to point at a newly allocated \c{game_params} structure containing the preset information, sets \c{*name} to point at a newly allocated C string containing the preset title (to go on the \q{Type} menu), and returns \cw{TRUE}. If the game does not wish to support any presets at all, this function is permitted to return \cw{FALSE} always. \S{backend-encode-params} \cw{encode_params()} \c char *(*encode_params)(const game_params *params, int full); The job of this function is to take a \c{game_params}, and encode it in a string form for use in game IDs. The return value must be a newly allocated C string, and \e{must} not contain a colon or a hash (since those characters are used to mark the end of the parameter section in a game ID). Ideally, it should also not contain any other potentially controversial punctuation; bear in mind when designing a string parameter format that it will probably be used on both Windows and Unix command lines under a variety of exciting shell quoting and metacharacter rules. Sticking entirely to alphanumerics is the safest thing; if you really need punctuation, you can probably get away with commas, periods or underscores without causing anybody any major inconvenience. If you venture far beyond that, you're likely to irritate \e{somebody}. (At the time of writing this, all existing games have purely alphanumeric string parameter formats. Usually these involve a letter denoting a parameter, followed optionally by a number giving the value of that parameter, with a few mandatory parts at the beginning such as numeric width and height separated by \cq{x}.) If the \c{full} parameter is \cw{TRUE}, this function should encode absolutely everything in the \c{game_params}, such that a subsequent call to \cw{decode_params()} (\k{backend-decode-params}) will yield an identical structure. If \c{full} is \cw{FALSE}, however, you should leave out anything which is not necessary to describe a \e{specific puzzle instance}, i.e. anything which only takes effect when a new puzzle is \e{generated}. For example, the Solo \c{game_params} includes a difficulty rating used when constructing new puzzles; but a Solo game ID need not explicitly include the difficulty, since to describe a puzzle once generated it's sufficient to give the grid dimensions and the location and contents of the clue squares. (Indeed, one might very easily type in a puzzle out of a newspaper without \e{knowing} what its difficulty level is in Solo's terminology.) Therefore, Solo's \cw{encode_params()} only encodes the difficulty level if \c{full} is set. \S{backend-decode-params} \cw{decode_params()} \c void (*decode_params)(game_params *params, char const *string); This function is the inverse of \cw{encode_params()} (\k{backend-encode-params}). It parses the supplied string and fills in the supplied \c{game_params} structure. Note that the structure will \e{already} have been allocated: this function is not expected to create a \e{new} \c{game_params}, but to modify an existing one. This function can receive a string which only encodes a subset of the parameters. The most obvious way in which this can happen is if the string was constructed by \cw{encode_params()} with its \c{full} parameter set to \cw{FALSE}; however, it could also happen if the user typed in a parameter set manually and missed something out. Be prepared to deal with a wide range of possibilities. When dealing with a parameter which is not specified in the input string, what to do requires a judgment call on the part of the programmer. Sometimes it makes sense to adjust other parameters to bring them into line with the new ones. In Mines, for example, you would probably not want to keep the same mine count if the user dropped the grid size and didn't specify one, since you might easily end up with more mines than would actually fit in the grid! On the other hand, sometimes it makes sense to leave the parameter alone: a Solo player might reasonably expect to be able to configure size and difficulty independently of one another. This function currently has no direct means of returning an error if the string cannot be parsed at all. However, the returned \c{game_params} is almost always subsequently passed to \cw{validate_params()} (\k{backend-validate-params}), so if you really want to signal parse errors, you could always have a \c{char *} in your parameters structure which stored an error message, and have \cw{validate_params()} return it if it is non-\cw{NULL}. \S{backend-free-params} \cw{free_params()} \c void (*free_params)(game_params *params); This function frees a \c{game_params} structure, and any subsidiary allocations contained within it. \S{backend-dup-params} \cw{dup_params()} \c game_params *(*dup_params)(const game_params *params); This function allocates a new \c{game_params} structure and initialises it with an exact copy of the information in the one provided as input. It returns a pointer to the new duplicate. \S{backend-can-configure} \c{can_configure} \c int can_configure; This boolean data element is set to \cw{TRUE} if the back end supports custom parameter configuration via a dialog box. If it is \cw{TRUE}, then the functions \cw{configure()} and \cw{custom_params()} are expected to work. See \k{backend-configure} and \k{backend-custom-params} for more details. \S{backend-configure} \cw{configure()} \c config_item *(*configure)(const game_params *params); This function is called when the user requests a dialog box for custom parameter configuration. It returns a newly allocated array of \cw{config_item} structures, describing the GUI elements required in the dialog box. The array should have one more element than the number of controls, since it is terminated with a \cw{C_END} marker (see below). Each array element describes the control together with its initial value; the front end will modify the value fields and return the updated array to \cw{custom_params()} (see \k{backend-custom-params}). The \cw{config_item} structure contains the following elements: \c char *name; \c int type; \c char *sval; \c int ival; \c{name} is an ASCII string giving the textual label for a GUI control. It is \e{not} expected to be dynamically allocated. \c{type} contains one of a small number of \c{enum} values defining what type of control is being described. The meaning of the \c{sval} and \c{ival} fields depends on the value in \c{type}. The valid values are: \dt \c{C_STRING} \dd Describes a text input box. (This is also used for numeric input. The back end does not bother informing the front end that the box is numeric rather than textual; some front ends do have the capacity to take this into account, but I decided it wasn't worth the extra complexity in the interface.) For this type, \c{ival} is unused, and \c{sval} contains a dynamically allocated string representing the contents of the input box. \dt \c{C_BOOLEAN} \dd Describes a simple checkbox. For this type, \c{sval} is unused, and \c{ival} is \cw{TRUE} or \cw{FALSE}. \dt \c{C_CHOICES} \dd Describes a drop-down list presenting one of a small number of fixed choices. For this type, \c{sval} contains a list of strings describing the choices; the very first character of \c{sval} is used as a delimiter when processing the rest (so that the strings \cq{:zero:one:two}, \cq{!zero!one!two} and \cq{xzeroxonextwo} all define a three-element list containing \cq{zero}, \cq{one} and \cq{two}). \c{ival} contains the index of the currently selected element, numbering from zero (so that in the above example, 0 would mean \cq{zero} and 2 would mean \cq{two}). \lcont{ Note that for this control type, \c{sval} is \e{not} dynamically allocated, whereas it was for \c{C_STRING}. } \dt \c{C_END} \dd Marks the end of the array of \c{config_item}s. All other fields are unused. The array returned from this function is expected to have filled in the initial values of all the controls according to the input \c{game_params} structure. If the game's \c{can_configure} flag is set to \cw{FALSE}, this function is never called and need not do anything at all. \S{backend-custom-params} \cw{custom_params()} \c game_params *(*custom_params)(const config_item *cfg); This function is the counterpart to \cw{configure()} (\k{backend-configure}). It receives as input an array of \c{config_item}s which was originally created by \cw{configure()}, but in which the control values have since been changed in accordance with user input. Its function is to read the new values out of the controls and return a newly allocated \c{game_params} structure representing the user's chosen parameter set. (The front end will have modified the controls' \e{values}, but there will still always be the same set of controls, in the same order, as provided by \cw{configure()}. It is not necessary to check the \c{name} and \c{type} fields, although you could use \cw{assert()} if you were feeling energetic.) This function is not expected to (and indeed \e{must not}) free the input \c{config_item} array. (If the parameters fail to validate, the dialog box will stay open.) If the game's \c{can_configure} flag is set to \cw{FALSE}, this function is never called and need not do anything at all. \S{backend-validate-params} \cw{validate_params()} \c char *(*validate_params)(const game_params *params, int full); This function takes a \c{game_params} structure as input, and checks that the parameters described in it fall within sensible limits. (At the very least, grid dimensions should almost certainly be strictly positive, for example.) Return value is \cw{NULL} if no problems were found, or alternatively a (non-dynamically-allocated) ASCII string describing the error in human-readable form. If the \c{full} parameter is set, full validation should be performed: any set of parameters which would not permit generation of a sensible puzzle should be faulted. If \c{full} is \e{not} set, the implication is that these parameters are not going to be used for \e{generating} a puzzle; so parameters which can't even sensibly \e{describe} a valid puzzle should still be faulted, but parameters which only affect puzzle generation should not be. (The \c{full} option makes a difference when parameter combinations are non-orthogonal. For example, Net has a boolean option controlling whether it enforces a unique solution; it turns out that it's impossible to generate a uniquely soluble puzzle with wrapping walls and width 2, so \cw{validate_params()} will complain if you ask for one. However, if the user had just been playing a unique wrapping puzzle of a more sensible width, and then pastes in a game ID acquired from somebody else which happens to describe a \e{non}-unique wrapping width-2 puzzle, then \cw{validate_params()} will be passed a \c{game_params} containing the width and wrapping settings from the new game ID and the uniqueness setting from the old one. This would be faulted, if it weren't for the fact that \c{full} is not set during this call, so Net ignores the inconsistency. The resulting \c{game_params} is never subsequently used to generate a puzzle; this is a promise made by the mid-end when it asks for a non-full validation.) \H{backend-descs} Handling game descriptions In this section I present the functions that deal with a textual description of a puzzle, i.e. the part that comes after the colon in a descriptive-format game ID. \S{backend-new-desc} \cw{new_desc()} \c char *(*new_desc)(const game_params *params, random_state *rs, \c char **aux, int interactive); This function is where all the really hard work gets done. This is the function whose job is to randomly generate a new puzzle, ensuring solubility and uniqueness as appropriate. As input it is given a \c{game_params} structure and a random state (see \k{utils-random} for the random number API). It must invent a puzzle instance, encode it in string form, and return a dynamically allocated C string containing that encoding. Additionally, it may return a second dynamically allocated string in \c{*aux}. (If it doesn't want to, then it can leave that parameter completely alone; it isn't required to set it to \cw{NULL}, although doing so is harmless.) That string, if present, will be passed to \cw{solve()} (\k{backend-solve}) later on; so if the puzzle is generated in such a way that a solution is known, then information about that solution can be saved in \c{*aux} for \cw{solve()} to use. The \c{interactive} parameter should be ignored by almost all puzzles. Its purpose is to distinguish between generating a puzzle within a GUI context for immediate play, and generating a puzzle in a command-line context for saving to be played later. The only puzzle that currently uses this distinction (and, I fervently hope, the only one which will \e{ever} need to use it) is Mines, which chooses a random first-click location when generating puzzles non-interactively, but which waits for the user to place the first click when interactive. If you think you have come up with another puzzle which needs to make use of this parameter, please think for at least ten minutes about whether there is \e{any} alternative! Note that game description strings are not required to contain an encoding of parameters such as grid size; a game description is never separated from the \c{game_params} it was generated with, so any information contained in that structure need not be encoded again in the game description. \S{backend-validate-desc} \cw{validate_desc()} \c char *(*validate_desc)(const game_params *params, const char *desc); This function is given a game description, and its job is to validate that it describes a puzzle which makes sense. To some extent it's up to the user exactly how far they take the phrase \q{makes sense}; there are no particularly strict rules about how hard the user is permitted to shoot themself in the foot when typing in a bogus game description by hand. (For example, Rectangles will not verify that the sum of all the numbers in the grid equals the grid's area. So a user could enter a puzzle which was provably not soluble, and the program wouldn't complain; there just wouldn't happen to be any sequence of moves which solved it.) The one non-negotiable criterion is that any game description which makes it through \cw{validate_desc()} \e{must not} subsequently cause a crash or an assertion failure when fed to \cw{new_game()} and thence to the rest of the back end. The return value is \cw{NULL} on success, or a non-dynamically-allocated C string containing an error message. \S{backend-new-game} \cw{new_game()} \c game_state *(*new_game)(midend *me, const game_params *params, \c const char *desc); This function takes a game description as input, together with its accompanying \c{game_params}, and constructs a \c{game_state} describing the initial state of the puzzle. It returns a newly allocated \c{game_state} structure. Almost all puzzles should ignore the \c{me} parameter. It is required by Mines, which needs it for later passing to \cw{midend_supersede_game_desc()} (see \k{backend-supersede}) once the user has placed the first click. I fervently hope that no other puzzle will be awkward enough to require it, so everybody else should ignore it. As with the \c{interactive} parameter in \cw{new_desc()} (\k{backend-new-desc}), if you think you have a reason to need this parameter, please try very hard to think of an alternative approach! \H{backend-states} Handling game states This section describes the functions which create and destroy \c{game_state} structures. (Well, except \cw{new_game()}, which is in \k{backend-new-game} instead of under here; but it deals with game descriptions \e{and} game states and it had to go in one section or the other.) \S{backend-dup-game} \cw{dup_game()} \c game_state *(*dup_game)(const game_state *state); This function allocates a new \c{game_state} structure and initialises it with an exact copy of the information in the one provided as input. It returns a pointer to the new duplicate. \S{backend-free-game} \cw{free_game()} \c void (*free_game)(game_state *state); This function frees a \c{game_state} structure, and any subsidiary allocations contained within it. \H{backend-ui} Handling \c{game_ui} \S{backend-new-ui} \cw{new_ui()} \c game_ui *(*new_ui)(const game_state *state); This function allocates and returns a new \c{game_ui} structure for playing a particular puzzle. It is passed a pointer to the initial \c{game_state}, in case it needs to refer to that when setting up the initial values for the new game. \S{backend-free-ui} \cw{free_ui()} \c void (*free_ui)(game_ui *ui); This function frees a \c{game_ui} structure, and any subsidiary allocations contained within it. \S{backend-encode-ui} \cw{encode_ui()} \c char *(*encode_ui)(const game_ui *ui); This function encodes any \e{important} data in a \c{game_ui} structure in string form. It is only called when saving a half-finished game to a file. It should be used sparingly. Almost all data in a \c{game_ui} is not important enough to save. The location of the keyboard-controlled cursor, for example, can be reset to a default position on reloading the game without impacting the user experience. If the user should somehow manage to save a game while a mouse drag was in progress, then discarding that mouse drag would be an outright \e{feature}. A typical thing that \e{would} be worth encoding in this function is the Mines death counter: it's in the \c{game_ui} rather than the \c{game_state} because it's too important to allow the user to revert it by using Undo, and therefore it's also too important to allow the user to revert it by saving and reloading. (Of course, the user could edit the save file by hand... But if the user is \e{that} determined to cheat, they could just as easily modify the game's source.) \S{backend-decode-ui} \cw{decode_ui()} \c void (*decode_ui)(game_ui *ui, const char *encoding); This function parses a string previously output by \cw{encode_ui()}, and writes the decoded data back into the provided \c{game_ui} structure. \S{backend-changed-state} \cw{changed_state()} \c void (*changed_state)(game_ui *ui, const game_state *oldstate, \c const game_state *newstate); This function is called by the mid-end whenever the current game state changes, for any reason. Those reasons include: \b a fresh move being made by \cw{interpret_move()} and \cw{execute_move()} \b a solve operation being performed by \cw{solve()} and \cw{execute_move()} \b the user moving back and forth along the undo list by means of the Undo and Redo operations \b the user selecting Restart to go back to the initial game state. The job of \cw{changed_state()} is to update the \c{game_ui} for consistency with the new game state, if any update is necessary. For example, Same Game stores data about the currently selected tile group in its \c{game_ui}, and this data is intrinsically related to the game state it was derived from. So it's very likely to become invalid when the game state changes; thus, Same Game's \cw{changed_state()} function clears the current selection whenever it is called. When \cw{anim_length()} or \cw{flash_length()} are called, you can be sure that there has been a previous call to \cw{changed_state()}. So \cw{changed_state()} can set up data in the \c{game_ui} which will be read by \cw{anim_length()} and \cw{flash_length()}, and those functions will not have to worry about being called without the data having been initialised. \H{backend-moves} Making moves This section describes the functions which actually make moves in the game: that is, the functions which process user input and end up producing new \c{game_state}s. \S{backend-interpret-move} \cw{interpret_move()} \c char *(*interpret_move)(const game_state *state, game_ui *ui, \c const game_drawstate *ds, \c int x, int y, int button); This function receives user input and processes it. Its input parameters are the current \c{game_state}, the current \c{game_ui} and the current \c{game_drawstate}, plus details of the input event. \c{button} is either an ASCII value or a special code (listed below) indicating an arrow or function key or a mouse event; when \c{button} is a mouse event, \c{x} and \c{y} contain the pixel coordinates of the mouse pointer relative to the top left of the puzzle's drawing area. (The pointer to the \c{game_drawstate} is marked \c{const}, because \c{interpret_move} should not write to it. The normal use of that pointer will be to read the game's tile size parameter in order to divide mouse coordinates by it.) \cw{interpret_move()} may return in three different ways: \b Returning \cw{NULL} indicates that no action whatsoever occurred in response to the input event; the puzzle was not interested in it at all. \b Returning the empty string (\cw{""}) indicates that the input event has resulted in a change being made to the \c{game_ui} which will require a redraw of the game window, but that no actual \e{move} was made (i.e. no new \c{game_state} needs to be created). \b Returning anything else indicates that a move was made and that a new \c{game_state} must be created. However, instead of actually constructing a new \c{game_state} itself, this function is required to return a string description of the details of the move. This string will be passed to \cw{execute_move()} (\k{backend-execute-move}) to actually create the new \c{game_state}. (Encoding moves as strings in this way means that the mid-end can keep the strings as well as the game states, and the strings can be written to disk when saving the game and fed to \cw{execute_move()} again on reloading.) The return value from \cw{interpret_move()} is expected to be dynamically allocated if and only if it is not either \cw{NULL} \e{or} the empty string. After this function is called, the back end is permitted to rely on some subsequent operations happening in sequence: \b \cw{execute_move()} will be called to convert this move description into a new \c{game_state} \b \cw{changed_state()} will be called with the new \c{game_state}. This means that if \cw{interpret_move()} needs to do updates to the \c{game_ui} which are easier to perform by referring to the new \c{game_state}, it can safely leave them to be done in \cw{changed_state()} and not worry about them failing to happen. (Note, however, that \cw{execute_move()} may \e{also} be called in other circumstances. It is only \cw{interpret_move()} which can rely on a subsequent call to \cw{changed_state()}.) The special key codes supported by this function are: \dt \cw{LEFT_BUTTON}, \cw{MIDDLE_BUTTON}, \cw{RIGHT_BUTTON} \dd Indicate that one of the mouse buttons was pressed down. \dt \cw{LEFT_DRAG}, \cw{MIDDLE_DRAG}, \cw{RIGHT_DRAG} \dd Indicate that the mouse was moved while one of the mouse buttons was still down. The mid-end guarantees that when one of these events is received, it will always have been preceded by a button-down event (and possibly other drag events) for the same mouse button, and no event involving another mouse button will have appeared in between. \dt \cw{LEFT_RELEASE}, \cw{MIDDLE_RELEASE}, \cw{RIGHT_RELEASE} \dd Indicate that a mouse button was released. The mid-end guarantees that when one of these events is received, it will always have been preceded by a button-down event (and possibly some drag events) for the same mouse button, and no event involving another mouse button will have appeared in between. \dt \cw{CURSOR_UP}, \cw{CURSOR_DOWN}, \cw{CURSOR_LEFT}, \cw{CURSOR_RIGHT} \dd Indicate that an arrow key was pressed. \dt \cw{CURSOR_SELECT} \dd On platforms which have a prominent \q{select} button alongside their cursor keys, indicates that that button was pressed. In addition, there are some modifiers which can be bitwise-ORed into the \c{button} parameter: \dt \cw{MOD_CTRL}, \cw{MOD_SHFT} \dd These indicate that the Control or Shift key was pressed alongside the key. They only apply to the cursor keys, not to mouse buttons or anything else. \dt \cw{MOD_NUM_KEYPAD} \dd This applies to some ASCII values, and indicates that the key code was input via the numeric keypad rather than the main keyboard. Some puzzles may wish to treat this differently (for example, a puzzle might want to use the numeric keypad as an eight-way directional pad), whereas others might not (a game involving numeric input probably just wants to treat the numeric keypad as numbers). \dt \cw{MOD_MASK} \dd This mask is the bitwise OR of all the available modifiers; you can bitwise-AND with \cw{~MOD_MASK} to strip all the modifiers off any input value. \S{backend-execute-move} \cw{execute_move()} \c game_state *(*execute_move)(const game_state *state, char *move); This function takes an input \c{game_state} and a move string as output from \cw{interpret_move()}. It returns a newly allocated \c{game_state} which contains the result of applying the specified move to the input game state. This function may return \cw{NULL} if it cannot parse the move string (and this is definitely preferable to crashing or failing an assertion, since one way this can happen is if loading a corrupt save file). However, it must not return \cw{NULL} for any move string that really was output from \cw{interpret_move()}: this is punishable by assertion failure in the mid-end. \S{backend-can-solve} \c{can_solve} \c int can_solve; This boolean field is set to \cw{TRUE} if the game's \cw{solve()} function does something. If it's set to \cw{FALSE}, the game will not even offer the \q{Solve} menu option. \S{backend-solve} \cw{solve()} \c char *(*solve)(const game_state *orig, const game_state *curr, \c const char *aux, char **error); This function is called when the user selects the \q{Solve} option from the menu. It is passed two input game states: \c{orig} is the game state from the very start of the puzzle, and \c{curr} is the current one. (Different games find one or other or both of these convenient.) It is also passed the \c{aux} string saved by \cw{new_desc()} (\k{backend-new-desc}), in case that encodes important information needed to provide the solution. If this function is unable to produce a solution (perhaps, for example, the game has no in-built solver so it can only solve puzzles it invented internally and has an \c{aux} string for) then it may return \cw{NULL}. If it does this, it must also set \c{*error} to an error message to be presented to the user (such as \q{Solution not known for this puzzle}); that error message is not expected to be dynamically allocated. If this function \e{does} produce a solution, it returns a move string suitable for feeding to \cw{execute_move()} (\k{backend-execute-move}). Like a (non-empty) string returned from \cw{interpret_move()}, the returned string should be dynamically allocated. \H{backend-drawing} Drawing the game graphics This section discusses the back end functions that deal with drawing. \S{backend-new-drawstate} \cw{new_drawstate()} \c game_drawstate *(*new_drawstate)(drawing *dr, \c const game_state *state); This function allocates and returns a new \c{game_drawstate} structure for drawing a particular puzzle. It is passed a pointer to a \c{game_state}, in case it needs to refer to that when setting up any initial data. This function may not rely on the puzzle having been newly started; a new draw state can be constructed at any time if the front end requests a forced redraw. For games like Pattern, in which initial game states are much simpler than general ones, this might be important to keep in mind. The parameter \c{dr} is a drawing object (see \k{drawing}) which the function might need to use to allocate blitters. (However, this isn't recommended; it's usually more sensible to wait to allocate a blitter until \cw{set_size()} is called, because that way you can tailor it to the scale at which the puzzle is being drawn.) \S{backend-free-drawstate} \cw{free_drawstate()} \c void (*free_drawstate)(drawing *dr, game_drawstate *ds); This function frees a \c{game_drawstate} structure, and any subsidiary allocations contained within it. The parameter \c{dr} is a drawing object (see \k{drawing}), which might be required if you are freeing a blitter. \S{backend-preferred-tilesize} \c{preferred_tilesize} \c int preferred_tilesize; Each game is required to define a single integer parameter which expresses, in some sense, the scale at which it is drawn. This is described in the APIs as \cq{tilesize}, since most puzzles are on a square (or possibly triangular or hexagonal) grid and hence a sensible interpretation of this parameter is to define it as the size of one grid tile in pixels; however, there's no actual requirement that the \q{tile size} be proportional to the game window size. Window size is required to increase monotonically with \q{tile size}, however. The data element \c{preferred_tilesize} indicates the tile size which should be used in the absence of a good reason to do otherwise (such as the screen being too small, or the user explicitly requesting a resize if that ever gets implemented). \S{backend-compute-size} \cw{compute_size()} \c void (*compute_size)(const game_params *params, int tilesize, \c int *x, int *y); This function is passed a \c{game_params} structure and a tile size. It returns, in \c{*x} and \c{*y}, the size in pixels of the drawing area that would be required to render a puzzle with those parameters at that tile size. \S{backend-set-size} \cw{set_size()} \c void (*set_size)(drawing *dr, game_drawstate *ds, \c const game_params *params, int tilesize); This function is responsible for setting up a \c{game_drawstate} to draw at a given tile size. Typically this will simply involve copying the supplied \c{tilesize} parameter into a \c{tilesize} field inside the draw state; for some more complex games it might also involve setting up other dimension fields, or possibly allocating a blitter (see \k{drawing-blitter}). The parameter \c{dr} is a drawing object (see \k{drawing}), which is required if a blitter needs to be allocated. Back ends may assume (and may enforce by assertion) that this function will be called at most once for any \c{game_drawstate}. If a puzzle needs to be redrawn at a different size, the mid-end will create a fresh drawstate. \S{backend-colours} \cw{colours()} \c float *(*colours)(frontend *fe, int *ncolours); This function is responsible for telling the front end what colours the puzzle will need to draw itself. It returns the number of colours required in \c{*ncolours}, and the return value from the function itself is a dynamically allocated array of three times that many \c{float}s, containing the red, green and blue components of each colour respectively as numbers in the range [0,1]. The second parameter passed to this function is a front end handle. The only things it is permitted to do with this handle are to call the front-end function called \cw{frontend_default_colour()} (see \k{frontend-default-colour}) or the utility function called \cw{game_mkhighlight()} (see \k{utils-game-mkhighlight}). (The latter is a wrapper on the former, so front end implementors only need to provide \cw{frontend_default_colour()}.) This allows \cw{colours()} to take local configuration into account when deciding on its own colour allocations. Most games use the front end's default colour as their background, apart from a few which depend on drawing relief highlights so they adjust the background colour if it's too light for highlights to show up against it. Note that the colours returned from this function are for \e{drawing}, not for printing. Printing has an entirely different colour allocation policy. \S{backend-anim-length} \cw{anim_length()} \c float (*anim_length)(const game_state *oldstate, \c const game_state *newstate, \c int dir, game_ui *ui); This function is called when a move is made, undone or redone. It is given the old and the new \c{game_state}, and its job is to decide whether the transition between the two needs to be animated or can be instant. \c{oldstate} is the state that was current until this call; \c{newstate} is the state that will be current after it. \c{dir} specifies the chronological order of those states: if it is positive, then the transition is the result of a move or a redo (and so \c{newstate} is the later of the two moves), whereas if it is negative then the transition is the result of an undo (so that \c{newstate} is the \e{earlier} move). If this function decides the transition should be animated, it returns the desired length of the animation in seconds. If not, it returns zero. State changes as a result of a Restart operation are never animated; the mid-end will handle them internally and never consult this function at all. State changes as a result of Solve operations are also not animated by default, although you can change this for a particular game by setting a flag in \c{flags} (\k{backend-flags}). The function is also passed a pointer to the local \c{game_ui}. It may refer to information in here to help with its decision (see \k{writing-conditional-anim} for an example of this), and/or it may \e{write} information about the nature of the animation which will be read later by \cw{redraw()}. When this function is called, it may rely on \cw{changed_state()} having been called previously, so if \cw{anim_length()} needs to refer to information in the \c{game_ui}, then \cw{changed_state()} is a reliable place to have set that information up. Move animations do not inhibit further input events. If the user continues playing before a move animation is complete, the animation will be abandoned and the display will jump straight to the final state. \S{backend-flash-length} \cw{flash_length()} \c float (*flash_length)(const game_state *oldstate, \c const game_state *newstate, \c int dir, game_ui *ui); This function is called when a move is completed. (\q{Completed} means that not only has the move been made, but any animation which accompanied it has finished.) It decides whether the transition from \c{oldstate} to \c{newstate} merits a \q{flash}. A flash is much like a move animation, but it is \e{not} interrupted by further user interface activity; it runs to completion in parallel with whatever else might be going on on the display. The only thing which will rush a flash to completion is another flash. The purpose of flashes is to indicate that the game has been completed. They were introduced as a separate concept from move animations because of Net: the habit of most Net players (and certainly me) is to rotate a tile into place and immediately lock it, then move on to another tile. When you make your last move, at the instant the final tile is rotated into place the screen starts to flash to indicate victory \dash but if you then press the lock button out of habit, then the move animation is cancelled, and the victory flash does not complete. (And if you \e{don't} press the lock button, the completed grid will look untidy because there will be one unlocked square.) Therefore, I introduced a specific concept of a \q{flash} which is separate from a move animation and can proceed in parallel with move animations and any other display activity, so that the victory flash in Net is not cancelled by that final locking move. The input parameters to \cw{flash_length()} are exactly the same as the ones to \cw{anim_length()}. Just like \cw{anim_length()}, when this function is called, it may rely on \cw{changed_state()} having been called previously, so if it needs to refer to information in the \c{game_ui} then \cw{changed_state()} is a reliable place to have set that information up. (Some games use flashes to indicate defeat as well as victory; Mines, for example, flashes in a different colour when you tread on a mine from the colour it uses when you complete the game. In order to achieve this, its \cw{flash_length()} function has to store a flag in the \c{game_ui} to indicate which flash type is required.) \S{backend-status} \cw{status()} \c int (*status)(const game_state *state); This function returns a status value indicating whether the current game is still in play, or has been won, or has been conclusively lost. The mid-end uses this to implement \cw{midend_status()} (\k{midend-status}). The return value should be +1 if the game has been successfully solved. If the game has been lost in a situation where further play is unlikely, the return value should be -1. If neither is true (so play is still ongoing), return zero. Front ends may wish to use a non-zero status as a cue to proactively offer the option of starting a new game. Therefore, back ends should not return -1 if the game has been \e{technically} lost but undoing and continuing is still a realistic possibility. (For instance, games with hidden information such as Guess or Mines might well return a non-zero status whenever they reveal the solution, whether or not the player guessed it correctly, on the grounds that a player would be unlikely to hide the solution and continue playing after the answer was spoiled. On the other hand, games where you can merely get into a dead end such as Same Game or Inertia might choose to return 0 in that situation, on the grounds that the player would quite likely press Undo and carry on playing.) \S{backend-redraw} \cw{redraw()} \c void (*redraw)(drawing *dr, game_drawstate *ds, \c const game_state *oldstate, \c const game_state *newstate, \c int dir, const game_ui *ui, \c float anim_time, float flash_time); This function is responsible for actually drawing the contents of the game window, and for redrawing every time the game state or the \c{game_ui} changes. The parameter \c{dr} is a drawing object which may be passed to the drawing API functions (see \k{drawing} for documentation of the drawing API). This function may not save \c{dr} and use it elsewhere; it must only use it for calling back to the drawing API functions within its own lifetime. \c{ds} is the local \c{game_drawstate}, of course, and \c{ui} is the local \c{game_ui}. \c{newstate} is the semantically-current game state, and is always non-\cw{NULL}. If \c{oldstate} is also non-\cw{NULL}, it means that a move has recently been made and the game is still in the process of displaying an animation linking the old and new states; in this situation, \c{anim_time} will give the length of time (in seconds) that the animation has already been running. If \c{oldstate} is \cw{NULL}, then \c{anim_time} is unused (and will hopefully be set to zero to avoid confusion). \c{flash_time}, if it is is non-zero, denotes that the game is in the middle of a flash, and gives the time since the start of the flash. See \k{backend-flash-length} for general discussion of flashes. The very first time this function is called for a new \c{game_drawstate}, it is expected to redraw the \e{entire} drawing area. Since this often involves drawing visual furniture which is never subsequently altered, it is often simplest to arrange this by having a special \q{first time} flag in the draw state, and resetting it after the first redraw. When this function (or any subfunction) calls the drawing API, it is expected to pass colour indices which were previously defined by the \cw{colours()} function. \H{backend-printing} Printing functions This section discusses the back end functions that deal with printing puzzles out on paper. \S{backend-can-print} \c{can_print} \c int can_print; This flag is set to \cw{TRUE} if the puzzle is capable of printing itself on paper. (This makes sense for some puzzles, such as Solo, which can be filled in with a pencil. Other puzzles, such as Twiddle, inherently involve moving things around and so would not make sense to print.) If this flag is \cw{FALSE}, then the functions \cw{print_size()} and \cw{print()} will never be called. \S{backend-can-print-in-colour} \c{can_print_in_colour} \c int can_print_in_colour; This flag is set to \cw{TRUE} if the puzzle is capable of printing itself differently when colour is available. For example, Map can actually print coloured regions in different \e{colours} rather than resorting to cross-hatching. If the \c{can_print} flag is \cw{FALSE}, then this flag will be ignored. \S{backend-print-size} \cw{print_size()} \c void (*print_size)(const game_params *params, float *x, float *y); This function is passed a \c{game_params} structure and a tile size. It returns, in \c{*x} and \c{*y}, the preferred size in \e{millimetres} of that puzzle if it were to be printed out on paper. If the \c{can_print} flag is \cw{FALSE}, this function will never be called. \S{backend-print} \cw{print()} \c void (*print)(drawing *dr, const game_state *state, int tilesize); This function is called when a puzzle is to be printed out on paper. It should use the drawing API functions (see \k{drawing}) to print itself. This function is separate from \cw{redraw()} because it is often very different: \b The printing function may not depend on pixel accuracy, since printer resolution is variable. Draw as if your canvas had infinite resolution. \b The printing function sometimes needs to display things in a completely different style. Net, for example, is very different as an on-screen puzzle and as a printed one. \b The printing function is often much simpler since it has no need to deal with repeated partial redraws. However, there's no reason the printing and redraw functions can't share some code if they want to. When this function (or any subfunction) calls the drawing API, the colour indices it passes should be colours which have been allocated by the \cw{print_*_colour()} functions within this execution of \cw{print()}. This is very different from the fixed small number of colours used in \cw{redraw()}, because printers do not have a limitation on the total number of colours that may be used. Some puzzles' printing functions might wish to allocate only one \q{ink} colour and use it for all drawing; others might wish to allocate \e{more} colours than are used on screen. One possible colour policy worth mentioning specifically is that a puzzle's printing function might want to allocate the \e{same} colour indices as are used by the redraw function, so that code shared between drawing and printing does not have to keep switching its colour indices. In order to do this, the simplest thing is to make use of the fact that colour indices returned from \cw{print_*_colour()} are guaranteed to be in increasing order from zero. So if you have declared an \c{enum} defining three colours \cw{COL_BACKGROUND}, \cw{COL_THIS} and \cw{COL_THAT}, you might then write \c int c; \c c = print_mono_colour(dr, 1); assert(c == COL_BACKGROUND); \c c = print_mono_colour(dr, 0); assert(c == COL_THIS); \c c = print_mono_colour(dr, 0); assert(c == COL_THAT); If the \c{can_print} flag is \cw{FALSE}, this function will never be called. \H{backend-misc} Miscellaneous \S{backend-can-format-as-text-ever} \c{can_format_as_text_ever} \c int can_format_as_text_ever; This boolean field is \cw{TRUE} if the game supports formatting a game state as ASCII text (typically ASCII art) for copying to the clipboard and pasting into other applications. If it is \cw{FALSE}, front ends will not offer the \q{Copy} command at all. If this field is \cw{TRUE}, the game does not necessarily have to support text formatting for \e{all} games: e.g. a game which can be played on a square grid or a triangular one might only support copy and paste for the former, because triangular grids in ASCII art are just too difficult. If this field is \cw{FALSE}, the functions \cw{can_format_as_text_now()} (\k{backend-can-format-as-text-now}) and \cw{text_format()} (\k{backend-text-format}) are never called. \S{backend-can-format-as-text-now} \c{can_format_as_text_now()} \c int (*can_format_as_text_now)(const game_params *params); This function is passed a \c{game_params} and returns a boolean, which is \cw{TRUE} if the game can support ASCII text output for this particular game type. If it returns \cw{FALSE}, front ends will grey out or otherwise disable the \q{Copy} command. Games may enable and disable the copy-and-paste function for different game \e{parameters}, but are currently constrained to return the same answer from this function for all game \e{states} sharing the same parameters. In other words, the \q{Copy} function may enable or disable itself when the player changes game preset, but will never change during play of a single game or when another game of exactly the same type is generated. This function should not take into account aspects of the game parameters which are not encoded by \cw{encode_params()} (\k{backend-encode-params}) when the \c{full} parameter is set to \cw{FALSE}. Such parameters will not necessarily match up between a call to this function and a subsequent call to \cw{text_format()} itself. (For instance, game \e{difficulty} should not affect whether the game can be copied to the clipboard. Only the actual visible \e{shape} of the game can affect that.) \S{backend-text-format} \cw{text_format()} \c char *(*text_format)(const game_state *state); This function is passed a \c{game_state}, and returns a newly allocated C string containing an ASCII representation of that game state. It is used to implement the \q{Copy} operation in many front ends. This function will only ever be called if the back end field \c{can_format_as_text_ever} (\k{backend-can-format-as-text-ever}) is \cw{TRUE} \e{and} the function \cw{can_format_as_text_now()} (\k{backend-can-format-as-text-now}) has returned \cw{TRUE} for the currently selected game parameters. The returned string may contain line endings (and will probably want to), using the normal C internal \cq{\\n} convention. For consistency between puzzles, all multi-line textual puzzle representations should \e{end} with a newline as well as containing them internally. (There are currently no puzzles which have a one-line ASCII representation, so there's no precedent yet for whether that should come with a newline or not.) \S{backend-wants-statusbar} \cw{wants_statusbar} \c int wants_statusbar; This boolean field is set to \cw{TRUE} if the puzzle has a use for a textual status line (to display score, completion status, currently active tiles, etc). \S{backend-is-timed} \c{is_timed} \c int is_timed; This boolean field is \cw{TRUE} if the puzzle is time-critical. If so, the mid-end will maintain a game timer while the user plays. If this field is \cw{FALSE}, then \cw{timing_state()} will never be called and need not do anything. \S{backend-timing-state} \cw{timing_state()} \c int (*timing_state)(const game_state *state, game_ui *ui); This function is passed the current \c{game_state} and the local \c{game_ui}; it returns \cw{TRUE} if the game timer should currently be running. A typical use for the \c{game_ui} in this function is to note when the game was first completed (by setting a flag in \cw{changed_state()} \dash see \k{backend-changed-state}), and freeze the timer thereafter so that the user can undo back through their solution process without altering their time. \S{backend-flags} \c{flags} \c int flags; This field contains miscellaneous per-backend flags. It consists of the bitwise OR of some combination of the following: \dt \cw{BUTTON_BEATS(x,y)} \dd Given any \cw{x} and \cw{y} from the set \{\cw{LEFT_BUTTON}, \cw{MIDDLE_BUTTON}, \cw{RIGHT_BUTTON}\}, this macro evaluates to a bit flag which indicates that when buttons \cw{x} and \cw{y} are both pressed simultaneously, the mid-end should consider \cw{x} to have priority. (In the absence of any such flags, the mid-end will always consider the most recently pressed button to have priority.) \dt \cw{SOLVE_ANIMATES} \dd This flag indicates that moves generated by \cw{solve()} (\k{backend-solve}) are candidates for animation just like any other move. For most games, solve moves should not be animated, so the mid-end doesn't even bother calling \cw{anim_length()} (\k{backend-anim-length}), thus saving some special-case code in each game. On the rare occasion that animated solve moves are actually required, you can set this flag. \dt \cw{REQUIRE_RBUTTON} \dd This flag indicates that the puzzle cannot be usefully played without the use of mouse buttons other than the left one. On some PDA platforms, this flag is used by the front end to enable right-button emulation through an appropriate gesture. Note that a puzzle is not required to set this just because it \e{uses} the right button, but only if its use of the right button is critical to playing the game. (Slant, for example, uses the right button to cycle through the three square states in the opposite order from the left button, and hence can manage fine without it.) \dt \cw{REQUIRE_NUMPAD} \dd This flag indicates that the puzzle cannot be usefully played without the use of number-key input. On some PDA platforms it causes an emulated number pad to appear on the screen. Similarly to \cw{REQUIRE_RBUTTON}, a puzzle need not specify this simply if its use of the number keys is not critical. \H{backend-initiative} Things a back end may do on its own initiative This section describes a couple of things that a back end may choose to do by calling functions elsewhere in the program, which would not otherwise be obvious. \S{backend-newrs} Create a random state If a back end needs random numbers at some point during normal play, it can create a fresh \c{random_state} by first calling \c{get_random_seed} (\k{frontend-get-random-seed}) and then passing the returned seed data to \cw{random_new()}. This is likely not to be what you want. If a puzzle needs randomness in the middle of play, it's likely to be more sensible to store some sort of random state within the \c{game_state}, so that the random numbers are tied to the particular game state and hence the player can't simply keep undoing their move until they get numbers they like better. This facility is currently used only in Net, to implement the \q{jumble} command, which sets every unlocked tile to a new random orientation. This randomness \e{is} a reasonable use of the feature, because it's non-adversarial \dash there's no advantage to the user in getting different random numbers. \S{backend-supersede} Supersede its own game description In response to a move, a back end is (reluctantly) permitted to call \cw{midend_supersede_game_desc()}: \c void midend_supersede_game_desc(midend *me, \c char *desc, char *privdesc); When the user selects \q{New Game}, the mid-end calls \cw{new_desc()} (\k{backend-new-desc}) to get a new game description, and (as well as using that to generate an initial game state) stores it for the save file and for telling to the user. The function above overwrites that game description, and also splits it in two. \c{desc} becomes the new game description which is provided to the user on request, and is also the one used to construct a new initial game state if the user selects \q{Restart}. \c{privdesc} is a \q{private} game description, used to reconstruct the game's initial state when reloading. The distinction between the two, as well as the need for this function at all, comes from Mines. Mines begins with a blank grid and no idea of where the mines actually are; \cw{new_desc()} does almost no work in interactive mode, and simply returns a string encoding the \c{random_state}. When the user first clicks to open a tile, \e{then} Mines generates the mine positions, in such a way that the game is soluble from that starting point. Then it uses this function to supersede the random-state game description with a proper one. But it needs two: one containing the initial click location (because that's what you want to happen if you restart the game, and also what you want to send to a friend so that they play \e{the same game} as you), and one without the initial click location (because when you save and reload the game, you expect to see the same blank initial state as you had before saving). I should stress again that this function is a horrid hack. Nobody should use it if they're not Mines; if you think you need to use it, think again repeatedly in the hope of finding a better way to do whatever it was you needed to do. \C{drawing} The drawing API The back end function \cw{redraw()} (\k{backend-redraw}) is required to draw the puzzle's graphics on the window's drawing area, or on paper if the puzzle is printable. To do this portably, it is provided with a drawing API allowing it to talk directly to the front end. In this chapter I document that API, both for the benefit of back end authors trying to use it and for front end authors trying to implement it. The drawing API as seen by the back end is a collection of global functions, each of which takes a pointer to a \c{drawing} structure (a \q{drawing object}). These objects are supplied as parameters to the back end's \cw{redraw()} and \cw{print()} functions. In fact these global functions are not implemented directly by the front end; instead, they are implemented centrally in \c{drawing.c} and form a small piece of middleware. The drawing API as supplied by the front end is a structure containing a set of function pointers, plus a \cq{void *} handle which is passed to each of those functions. This enables a single front end to switch between multiple implementations of the drawing API if necessary. For example, the Windows API supplies a printing mechanism integrated into the same GDI which deals with drawing in windows, and therefore the same API implementation can handle both drawing and printing; but on Unix, the most common way for applications to print is by producing PostScript output directly, and although it would be \e{possible} to write a single (say) \cw{draw_rect()} function which checked a global flag to decide whether to do GTK drawing operations or output PostScript to a file, it's much nicer to have two separate functions and switch between them as appropriate. When drawing, the puzzle window is indexed by pixel coordinates, with the top left pixel defined as \cw{(0,0)} and the bottom right pixel \cw{(w-1,h-1)}, where \c{w} and \c{h} are the width and height values returned by the back end function \cw{compute_size()} (\k{backend-compute-size}). When printing, the puzzle's print area is indexed in exactly the same way (with an arbitrary tile size provided by the printing module \c{printing.c}), to facilitate sharing of code between the drawing and printing routines. However, when printing, puzzles may no longer assume that the coordinate unit has any relationship to a pixel; the printer's actual resolution might very well not even be known at print time, so the coordinate unit might be smaller or larger than a pixel. Puzzles' print functions should restrict themselves to drawing geometric shapes rather than fiddly pixel manipulation. \e{Puzzles' redraw functions may assume that the surface they draw on is persistent}. It is the responsibility of every front end to preserve the puzzle's window contents in the face of GUI window expose issues and similar. It is not permissible to request that the back end redraw any part of a window that it has already drawn, unless something has actually changed as a result of making moves in the puzzle. Most front ends accomplish this by having the drawing routines draw on a stored bitmap rather than directly on the window, and copying the bitmap to the window every time a part of the window needs to be redrawn. Therefore, it is vitally important that whenever the back end does any drawing it informs the front end of which parts of the window it has accessed, and hence which parts need repainting. This is done by calling \cw{draw_update()} (\k{drawing-draw-update}). Persistence of old drawing is convenient. However, a puzzle should be very careful about how it updates its drawing area. The problem is that some front ends do anti-aliased drawing: rather than simply choosing between leaving each pixel untouched or painting it a specified colour, an antialiased drawing function will \e{blend} the original and new colours in pixels at a figure's boundary according to the proportion of the pixel occupied by the figure (probably modified by some heuristic fudge factors). All of this produces a smoother appearance for curves and diagonal lines. An unfortunate effect of drawing an anti-aliased figure repeatedly is that the pixels around the figure's boundary come steadily more saturated with \q{ink} and the boundary appears to \q{spread out}. Worse, redrawing a figure in a different colour won't fully paint over the old boundary pixels, so the end result is a rather ugly smudge. A good strategy to avoid unpleasant anti-aliasing artifacts is to identify a number of rectangular areas which need to be redrawn, clear them to the background colour, and then redraw their contents from scratch, being careful all the while not to stray beyond the boundaries of the original rectangles. The \cw{clip()} function (\k{drawing-clip}) comes in very handy here. Games based on a square grid can often do this fairly easily. Other games may need to be somewhat more careful. For example, Loopy's redraw function first identifies portions of the display which need to be updated. Then, if the changes are fairly well localised, it clears and redraws a rectangle containing each changed area. Otherwise, it gives up and redraws the entire grid from scratch. It is possible to avoid clearing to background and redrawing from scratch if one is very careful about which drawing functions one uses: if a function is documented as not anti-aliasing under some circumstances, you can rely on each pixel in a drawing either being left entirely alone or being set to the requested colour, with no blending being performed. In the following sections I first discuss the drawing API as seen by the back end, and then the \e{almost} identical function-pointer form seen by the front end. \H{drawing-backend} Drawing API as seen by the back end This section documents the back-end drawing API, in the form of functions which take a \c{drawing} object as an argument. \S{drawing-draw-rect} \cw{draw_rect()} \c void draw_rect(drawing *dr, int x, int y, int w, int h, \c int colour); Draws a filled rectangle in the puzzle window. \c{x} and \c{y} give the coordinates of the top left pixel of the rectangle. \c{w} and \c{h} give its width and height. Thus, the horizontal extent of the rectangle runs from \c{x} to \c{x+w-1} inclusive, and the vertical extent from \c{y} to \c{y+h-1} inclusive. \c{colour} is an integer index into the colours array returned by the back end function \cw{colours()} (\k{backend-colours}). There is no separate pixel-plotting function. If you want to plot a single pixel, the approved method is to use \cw{draw_rect()} with width and height set to 1. Unlike many of the other drawing functions, this function is guaranteed to be pixel-perfect: the rectangle will be sharply defined and not anti-aliased or anything like that. This function may be used for both drawing and printing. \S{drawing-draw-rect-outline} \cw{draw_rect_outline()} \c void draw_rect_outline(drawing *dr, int x, int y, int w, int h, \c int colour); Draws an outline rectangle in the puzzle window. \c{x} and \c{y} give the coordinates of the top left pixel of the rectangle. \c{w} and \c{h} give its width and height. Thus, the horizontal extent of the rectangle runs from \c{x} to \c{x+w-1} inclusive, and the vertical extent from \c{y} to \c{y+h-1} inclusive. \c{colour} is an integer index into the colours array returned by the back end function \cw{colours()} (\k{backend-colours}). From a back end perspective, this function may be considered to be part of the drawing API. However, front ends are not required to implement it, since it is actually implemented centrally (in \cw{misc.c}) as a wrapper on \cw{draw_polygon()}. This function may be used for both drawing and printing. \S{drawing-draw-line} \cw{draw_line()} \c void draw_line(drawing *dr, int x1, int y1, int x2, int y2, \c int colour); Draws a straight line in the puzzle window. \c{x1} and \c{y1} give the coordinates of one end of the line. \c{x2} and \c{y2} give the coordinates of the other end. The line drawn includes both those points. \c{colour} is an integer index into the colours array returned by the back end function \cw{colours()} (\k{backend-colours}). Some platforms may perform anti-aliasing on this function. Therefore, do not assume that you can erase a line by drawing the same line over it in the background colour; anti-aliasing might lead to perceptible ghost artefacts around the vanished line. Horizontal and vertical lines, however, are pixel-perfect and not anti-aliased. This function may be used for both drawing and printing. \S{drawing-draw-polygon} \cw{draw_polygon()} \c void draw_polygon(drawing *dr, int *coords, int npoints, \c int fillcolour, int outlinecolour); Draws an outlined or filled polygon in the puzzle window. \c{coords} is an array of \cw{(2*npoints)} integers, containing the \c{x} and \c{y} coordinates of \c{npoints} vertices. \c{fillcolour} and \c{outlinecolour} are integer indices into the colours array returned by the back end function \cw{colours()} (\k{backend-colours}). \c{fillcolour} may also be \cw{-1} to indicate that the polygon should be outlined only. The polygon defined by the specified list of vertices is first filled in \c{fillcolour}, if specified, and then outlined in \c{outlinecolour}. \c{outlinecolour} may \e{not} be \cw{-1}; it must be a valid colour (and front ends are permitted to enforce this by assertion). This is because different platforms disagree on whether a filled polygon should include its boundary line or not, so drawing \e{only} a filled polygon would have non-portable effects. If you want your filled polygon not to have a visible outline, you must set \c{outlinecolour} to the same as \c{fillcolour}. Some platforms may perform anti-aliasing on this function. Therefore, do not assume that you can erase a polygon by drawing the same polygon over it in the background colour. Also, be prepared for the polygon to extend a pixel beyond its obvious bounding box as a result of this; if you really need it not to do this to avoid interfering with other delicate graphics, you should probably use \cw{clip()} (\k{drawing-clip}). You can rely on horizontal and vertical lines not being anti-aliased. This function may be used for both drawing and printing. \S{drawing-draw-circle} \cw{draw_circle()} \c void draw_circle(drawing *dr, int cx, int cy, int radius, \c int fillcolour, int outlinecolour); Draws an outlined or filled circle in the puzzle window. \c{cx} and \c{cy} give the coordinates of the centre of the circle. \c{radius} gives its radius. The total horizontal pixel extent of the circle is from \c{cx-radius+1} to \c{cx+radius-1} inclusive, and the vertical extent similarly around \c{cy}. \c{fillcolour} and \c{outlinecolour} are integer indices into the colours array returned by the back end function \cw{colours()} (\k{backend-colours}). \c{fillcolour} may also be \cw{-1} to indicate that the circle should be outlined only. The circle is first filled in \c{fillcolour}, if specified, and then outlined in \c{outlinecolour}. \c{outlinecolour} may \e{not} be \cw{-1}; it must be a valid colour (and front ends are permitted to enforce this by assertion). This is because different platforms disagree on whether a filled circle should include its boundary line or not, so drawing \e{only} a filled circle would have non-portable effects. If you want your filled circle not to have a visible outline, you must set \c{outlinecolour} to the same as \c{fillcolour}. Some platforms may perform anti-aliasing on this function. Therefore, do not assume that you can erase a circle by drawing the same circle over it in the background colour. Also, be prepared for the circle to extend a pixel beyond its obvious bounding box as a result of this; if you really need it not to do this to avoid interfering with other delicate graphics, you should probably use \cw{clip()} (\k{drawing-clip}). This function may be used for both drawing and printing. \S{drawing-draw-thick-line} \cw{draw_thick_line()} \c void draw_thick_line(drawing *dr, float thickness, \c float x1, float y1, float x2, float y2, \c int colour) Draws a line in the puzzle window, giving control over the line's thickness. \c{x1} and \c{y1} give the coordinates of one end of the line. \c{x2} and \c{y2} give the coordinates of the other end. \c{thickness} gives the thickness of the line, in pixels. Note that the coordinates and thickness are floating-point: the continuous coordinate system is in effect here. It's important to be able to address points with better-than-pixel precision in this case, because one can't otherwise properly express the endpoints of lines with both odd and even thicknesses. Some platforms may perform anti-aliasing on this function. The precise pixels affected by a thick-line drawing operation may vary between platforms, and no particular guarantees are provided. Indeed, even horizontal or vertical lines may be anti-aliased. This function may be used for both drawing and printing. \S{drawing-draw-text} \cw{draw_text()} \c void draw_text(drawing *dr, int x, int y, int fonttype, \c int fontsize, int align, int colour, char *text); Draws text in the puzzle window. \c{x} and \c{y} give the coordinates of a point. The relation of this point to the location of the text is specified by \c{align}, which is a bitwise OR of horizontal and vertical alignment flags: \dt \cw{ALIGN_VNORMAL} \dd Indicates that \c{y} is aligned with the baseline of the text. \dt \cw{ALIGN_VCENTRE} \dd Indicates that \c{y} is aligned with the vertical centre of the text. (In fact, it's aligned with the vertical centre of normal \e{capitalised} text: displaying two pieces of text with \cw{ALIGN_VCENTRE} at the same \cw{y}-coordinate will cause their baselines to be aligned with one another, even if one is an ascender and the other a descender.) \dt \cw{ALIGN_HLEFT} \dd Indicates that \c{x} is aligned with the left-hand end of the text. \dt \cw{ALIGN_HCENTRE} \dd Indicates that \c{x} is aligned with the horizontal centre of the text. \dt \cw{ALIGN_HRIGHT} \dd Indicates that \c{x} is aligned with the right-hand end of the text. \c{fonttype} is either \cw{FONT_FIXED} or \cw{FONT_VARIABLE}, for a monospaced or proportional font respectively. (No more detail than that may be specified; it would only lead to portability issues between different platforms.) \c{fontsize} is the desired size, in pixels, of the text. This size corresponds to the overall point size of the text, not to any internal dimension such as the cap-height. \c{colour} is an integer index into the colours array returned by the back end function \cw{colours()} (\k{backend-colours}). This function may be used for both drawing and printing. The character set used to encode the text passed to this function is specified \e{by the drawing object}, although it must be a superset of ASCII. If a puzzle wants to display text that is not contained in ASCII, it should use the \cw{text_fallback()} function (\k{drawing-text-fallback}) to query the drawing object for an appropriate representation of the characters it wants. \S{drawing-text-fallback} \cw{text_fallback()} \c char *text_fallback(drawing *dr, const char *const *strings, \c int nstrings); This function is used to request a translation of UTF-8 text into whatever character encoding is expected by the drawing object's implementation of \cw{draw_text()}. The input is a list of strings encoded in UTF-8: \cw{nstrings} gives the number of strings in the list, and \cw{strings[0]}, \cw{strings[1]}, ..., \cw{strings[nstrings-1]} are the strings themselves. The returned string (which is dynamically allocated and must be freed when finished with) is derived from the first string in the list that the drawing object expects to be able to display reliably; it will consist of that string translated into the character set expected by \cw{draw_text()}. Drawing implementations are not required to handle anything outside ASCII, but are permitted to assume that \e{some} string will be successfully translated. So every call to this function must include a string somewhere in the list (presumably the last element) which consists of nothing but ASCII, to be used by any front end which cannot handle anything else. For example, if a puzzle wished to display a string including a multiplication sign (U+00D7 in Unicode, represented by the bytes C3 97 in UTF-8), it might do something like this: \c static const char *const times_signs[] = { "\xC3\x97", "x" }; \c char *times_sign = text_fallback(dr, times_signs, 2); \c sprintf(buffer, "%d%s%d", width, times_sign, height); \c draw_text(dr, x, y, font, size, align, colour, buffer); \c sfree(buffer); which would draw a string with a times sign in the middle on platforms that support it, and fall back to a simple ASCII \cq{x} where there was no alternative. \S{drawing-clip} \cw{clip()} \c void clip(drawing *dr, int x, int y, int w, int h); Establishes a clipping rectangle in the puzzle window. \c{x} and \c{y} give the coordinates of the top left pixel of the clipping rectangle. \c{w} and \c{h} give its width and height. Thus, the horizontal extent of the rectangle runs from \c{x} to \c{x+w-1} inclusive, and the vertical extent from \c{y} to \c{y+h-1} inclusive. (These are exactly the same semantics as \cw{draw_rect()}.) After this call, no drawing operation will affect anything outside the specified rectangle. The effect can be reversed by calling \cw{unclip()} (\k{drawing-unclip}). The clipping rectangle is pixel-perfect: pixels within the rectangle are affected as usual by drawing functions; pixels outside are completely untouched. Back ends should not assume that a clipping rectangle will be automatically cleared up by the front end if it's left lying around; that might work on current front ends, but shouldn't be relied upon. Always explicitly call \cw{unclip()}. This function may be used for both drawing and printing. \S{drawing-unclip} \cw{unclip()} \c void unclip(drawing *dr); Reverts the effect of a previous call to \cw{clip()}. After this call, all drawing operations will be able to affect the entire puzzle window again. This function may be used for both drawing and printing. \S{drawing-draw-update} \cw{draw_update()} \c void draw_update(drawing *dr, int x, int y, int w, int h); Informs the front end that a rectangular portion of the puzzle window has been drawn on and needs to be updated. \c{x} and \c{y} give the coordinates of the top left pixel of the update rectangle. \c{w} and \c{h} give its width and height. Thus, the horizontal extent of the rectangle runs from \c{x} to \c{x+w-1} inclusive, and the vertical extent from \c{y} to \c{y+h-1} inclusive. (These are exactly the same semantics as \cw{draw_rect()}.) The back end redraw function \e{must} call this function to report any changes it has made to the window. Otherwise, those changes may not become immediately visible, and may then appear at an unpredictable subsequent time such as the next time the window is covered and re-exposed. This function is only important when drawing. It may be called when printing as well, but doing so is not compulsory, and has no effect. (So if you have a shared piece of code between the drawing and printing routines, that code may safely call \cw{draw_update()}.) \S{drawing-status-bar} \cw{status_bar()} \c void status_bar(drawing *dr, char *text); Sets the text in the game's status bar to \c{text}. The text is copied from the supplied buffer, so the caller is free to deallocate or modify the buffer after use. (This function is not exactly a \e{drawing} function, but it shares with the drawing API the property that it may only be called from within the back end redraw function, so this is as good a place as any to document it.) The supplied text is filtered through the mid-end for optional rewriting before being passed on to the front end; the mid-end will prepend the current game time if the game is timed (and may in future perform other rewriting if it seems like a good idea). This function is for drawing only; it must never be called during printing. \S{drawing-blitter} Blitter functions This section describes a group of related functions which save and restore a section of the puzzle window. This is most commonly used to implement user interfaces involving dragging a puzzle element around the window: at the end of each call to \cw{redraw()}, if an object is currently being dragged, the back end saves the window contents under that location and then draws the dragged object, and at the start of the next \cw{redraw()} the first thing it does is to restore the background. The front end defines an opaque type called a \c{blitter}, which is capable of storing a rectangular area of a specified size. Blitter functions are for drawing only; they must never be called during printing. \S2{drawing-blitter-new} \cw{blitter_new()} \c blitter *blitter_new(drawing *dr, int w, int h); Creates a new blitter object which stores a rectangle of size \c{w} by \c{h} pixels. Returns a pointer to the blitter object. Blitter objects are best stored in the \c{game_drawstate}. A good time to create them is in the \cw{set_size()} function (\k{backend-set-size}), since it is at this point that you first know how big a rectangle they will need to save. \S2{drawing-blitter-free} \cw{blitter_free()} \c void blitter_free(drawing *dr, blitter *bl); Disposes of a blitter object. Best called in \cw{free_drawstate()}. (However, check that the blitter object is not \cw{NULL} before attempting to free it; it is possible that a draw state might be created and freed without ever having \cw{set_size()} called on it in between.) \S2{drawing-blitter-save} \cw{blitter_save()} \c void blitter_save(drawing *dr, blitter *bl, int x, int y); This is a true drawing API function, in that it may only be called from within the game redraw routine. It saves a rectangular portion of the puzzle window into the specified blitter object. \c{x} and \c{y} give the coordinates of the top left corner of the saved rectangle. The rectangle's width and height are the ones specified when the blitter object was created. This function is required to cope and do the right thing if \c{x} and \c{y} are out of range. (The right thing probably means saving whatever part of the blitter rectangle overlaps with the visible area of the puzzle window.) \S2{drawing-blitter-load} \cw{blitter_load()} \c void blitter_load(drawing *dr, blitter *bl, int x, int y); This is a true drawing API function, in that it may only be called from within the game redraw routine. It restores a rectangular portion of the puzzle window from the specified blitter object. \c{x} and \c{y} give the coordinates of the top left corner of the rectangle to be restored. The rectangle's width and height are the ones specified when the blitter object was created. Alternatively, you can specify both \c{x} and \c{y} as the special value \cw{BLITTER_FROMSAVED}, in which case the rectangle will be restored to exactly where it was saved from. (This is probably what you want to do almost all the time, if you're using blitters to implement draggable puzzle elements.) This function is required to cope and do the right thing if \c{x} and \c{y} (or the equivalent ones saved in the blitter) are out of range. (The right thing probably means restoring whatever part of the blitter rectangle overlaps with the visible area of the puzzle window.) If this function is called on a blitter which had previously been saved from a partially out-of-range rectangle, then the parts of the saved bitmap which were not visible at save time are undefined. If the blitter is restored to a different position so as to make those parts visible, the effect on the drawing area is undefined. \S{print-mono-colour} \cw{print_mono_colour()} \c int print_mono_colour(drawing *dr, int grey); This function allocates a colour index for a simple monochrome colour during printing. \c{grey} must be 0 or 1. If \c{grey} is 0, the colour returned is black; if \c{grey} is 1, the colour is white. \S{print-grey-colour} \cw{print_grey_colour()} \c int print_grey_colour(drawing *dr, float grey); This function allocates a colour index for a grey-scale colour during printing. \c{grey} may be any number between 0 (black) and 1 (white); for example, 0.5 indicates a medium grey. The chosen colour will be rendered to the limits of the printer's halftoning capability. \S{print-hatched-colour} \cw{print_hatched_colour()} \c int print_hatched_colour(drawing *dr, int hatch); This function allocates a colour index which does not represent a literal \e{colour}. Instead, regions shaded in this colour will be hatched with parallel lines. The \c{hatch} parameter defines what type of hatching should be used in place of this colour: \dt \cw{HATCH_SLASH} \dd This colour will be hatched by lines slanting to the right at 45 degrees. \dt \cw{HATCH_BACKSLASH} \dd This colour will be hatched by lines slanting to the left at 45 degrees. \dt \cw{HATCH_HORIZ} \dd This colour will be hatched by horizontal lines. \dt \cw{HATCH_VERT} \dd This colour will be hatched by vertical lines. \dt \cw{HATCH_PLUS} \dd This colour will be hatched by criss-crossing horizontal and vertical lines. \dt \cw{HATCH_X} \dd This colour will be hatched by criss-crossing diagonal lines. Colours defined to use hatching may not be used for drawing lines or text; they may only be used for filling areas. That is, they may be used as the \c{fillcolour} parameter to \cw{draw_circle()} and \cw{draw_polygon()}, and as the colour parameter to \cw{draw_rect()}, but may not be used as the \c{outlinecolour} parameter to \cw{draw_circle()} or \cw{draw_polygon()}, or with \cw{draw_line()} or \cw{draw_text()}. \S{print-rgb-mono-colour} \cw{print_rgb_mono_colour()} \c int print_rgb_mono_colour(drawing *dr, float r, float g, \c float b, float grey); This function allocates a colour index for a fully specified RGB colour during printing. \c{r}, \c{g} and \c{b} may each be anywhere in the range from 0 to 1. If printing in black and white only, these values will be ignored, and either pure black or pure white will be used instead, according to the \q{grey} parameter. (The fallback colour is the same as the one which would be allocated by \cw{print_mono_colour(grey)}.) \S{print-rgb-grey-colour} \cw{print_rgb_grey_colour()} \c int print_rgb_grey_colour(drawing *dr, float r, float g, \c float b, float grey); This function allocates a colour index for a fully specified RGB colour during printing. \c{r}, \c{g} and \c{b} may each be anywhere in the range from 0 to 1. If printing in black and white only, these values will be ignored, and a shade of grey given by the \c{grey} parameter will be used instead. (The fallback colour is the same as the one which would be allocated by \cw{print_grey_colour(grey)}.) \S{print-rgb-hatched-colour} \cw{print_rgb_hatched_colour()} \c int print_rgb_hatched_colour(drawing *dr, float r, float g, \c float b, float hatched); This function allocates a colour index for a fully specified RGB colour during printing. \c{r}, \c{g} and \c{b} may each be anywhere in the range from 0 to 1. If printing in black and white only, these values will be ignored, and a form of cross-hatching given by the \c{hatch} parameter will be used instead; see \k{print-hatched-colour} for the possible values of this parameter. (The fallback colour is the same as the one which would be allocated by \cw{print_hatched_colour(hatch)}.) \S{print-line-width} \cw{print_line_width()} \c void print_line_width(drawing *dr, int width); This function is called to set the thickness of lines drawn during printing. It is meaningless in drawing: all lines drawn by \cw{draw_line()}, \cw{draw_circle} and \cw{draw_polygon()} are one pixel in thickness. However, in printing there is no clear definition of a pixel and so line widths must be explicitly specified. The line width is specified in the usual coordinate system. Note, however, that it is a hint only: the central printing system may choose to vary line thicknesses at user request or due to printer capabilities. \S{print-line-dotted} \cw{print_line_dotted()} \c void print_line_dotted(drawing *dr, int dotted); This function is called to toggle the drawing of dotted lines during printing. It is not supported during drawing. The parameter \cq{dotted} is a boolean; \cw{TRUE} means that future lines drawn by \cw{draw_line()}, \cw{draw_circle} and \cw{draw_polygon()} will be dotted, and \cw{FALSE} means that they will be solid. Some front ends may impose restrictions on the width of dotted lines. Asking for a dotted line via this front end will override any line width request if the front end requires it. \H{drawing-frontend} The drawing API as implemented by the front end This section describes the drawing API in the function-pointer form in which it is implemented by a front end. (It isn't only platform-specific front ends which implement this API; the platform-independent module \c{ps.c} also provides an implementation of it which outputs PostScript. Thus, any platform which wants to do PS printing can do so with minimum fuss.) The following entries all describe function pointer fields in a structure called \c{drawing_api}. Each of the functions takes a \cq{void *} context pointer, which it should internally cast back to a more useful type. Thus, a drawing \e{object} (\c{drawing *)} suitable for passing to the back end redraw or printing functions is constructed by passing a \c{drawing_api} and a \cq{void *} to the function \cw{drawing_new()} (see \k{drawing-new}). \S{drawingapi-draw-text} \cw{draw_text()} \c void (*draw_text)(void *handle, int x, int y, int fonttype, \c int fontsize, int align, int colour, char *text); This function behaves exactly like the back end \cw{draw_text()} function; see \k{drawing-draw-text}. \S{drawingapi-draw-rect} \cw{draw_rect()} \c void (*draw_rect)(void *handle, int x, int y, int w, int h, \c int colour); This function behaves exactly like the back end \cw{draw_rect()} function; see \k{drawing-draw-rect}. \S{drawingapi-draw-line} \cw{draw_line()} \c void (*draw_line)(void *handle, int x1, int y1, int x2, int y2, \c int colour); This function behaves exactly like the back end \cw{draw_line()} function; see \k{drawing-draw-line}. \S{drawingapi-draw-polygon} \cw{draw_polygon()} \c void (*draw_polygon)(void *handle, int *coords, int npoints, \c int fillcolour, int outlinecolour); This function behaves exactly like the back end \cw{draw_polygon()} function; see \k{drawing-draw-polygon}. \S{drawingapi-draw-circle} \cw{draw_circle()} \c void (*draw_circle)(void *handle, int cx, int cy, int radius, \c int fillcolour, int outlinecolour); This function behaves exactly like the back end \cw{draw_circle()} function; see \k{drawing-draw-circle}. \S{drawingapi-draw-thick-line} \cw{draw_thick_line()} \c void draw_thick_line(drawing *dr, float thickness, \c float x1, float y1, float x2, float y2, \c int colour) This function behaves exactly like the back end \cw{draw_thick_line()} function; see \k{drawing-draw-thick-line}. An implementation of this API which doesn't provide high-quality rendering of thick lines is permitted to define this function pointer to be \cw{NULL}. The middleware in \cw{drawing.c} will notice and provide a low-quality alternative using \cw{draw_polygon()}. \S{drawingapi-draw-update} \cw{draw_update()} \c void (*draw_update)(void *handle, int x, int y, int w, int h); This function behaves exactly like the back end \cw{draw_update()} function; see \k{drawing-draw-update}. An implementation of this API which only supports printing is permitted to define this function pointer to be \cw{NULL} rather than bothering to define an empty function. The middleware in \cw{drawing.c} will notice and avoid calling it. \S{drawingapi-clip} \cw{clip()} \c void (*clip)(void *handle, int x, int y, int w, int h); This function behaves exactly like the back end \cw{clip()} function; see \k{drawing-clip}. \S{drawingapi-unclip} \cw{unclip()} \c void (*unclip)(void *handle); This function behaves exactly like the back end \cw{unclip()} function; see \k{drawing-unclip}. \S{drawingapi-start-draw} \cw{start_draw()} \c void (*start_draw)(void *handle); This function is called at the start of drawing. It allows the front end to initialise any temporary data required to draw with, such as device contexts. Implementations of this API which do not provide drawing services may define this function pointer to be \cw{NULL}; it will never be called unless drawing is attempted. \S{drawingapi-end-draw} \cw{end_draw()} \c void (*end_draw)(void *handle); This function is called at the end of drawing. It allows the front end to do cleanup tasks such as deallocating device contexts and scheduling appropriate GUI redraw events. Implementations of this API which do not provide drawing services may define this function pointer to be \cw{NULL}; it will never be called unless drawing is attempted. \S{drawingapi-status-bar} \cw{status_bar()} \c void (*status_bar)(void *handle, char *text); This function behaves exactly like the back end \cw{status_bar()} function; see \k{drawing-status-bar}. Front ends implementing this function need not worry about it being called repeatedly with the same text; the middleware code in \cw{status_bar()} will take care of this. Implementations of this API which do not provide drawing services may define this function pointer to be \cw{NULL}; it will never be called unless drawing is attempted. \S{drawingapi-blitter-new} \cw{blitter_new()} \c blitter *(*blitter_new)(void *handle, int w, int h); This function behaves exactly like the back end \cw{blitter_new()} function; see \k{drawing-blitter-new}. Implementations of this API which do not provide drawing services may define this function pointer to be \cw{NULL}; it will never be called unless drawing is attempted. \S{drawingapi-blitter-free} \cw{blitter_free()} \c void (*blitter_free)(void *handle, blitter *bl); This function behaves exactly like the back end \cw{blitter_free()} function; see \k{drawing-blitter-free}. Implementations of this API which do not provide drawing services may define this function pointer to be \cw{NULL}; it will never be called unless drawing is attempted. \S{drawingapi-blitter-save} \cw{blitter_save()} \c void (*blitter_save)(void *handle, blitter *bl, int x, int y); This function behaves exactly like the back end \cw{blitter_save()} function; see \k{drawing-blitter-save}. Implementations of this API which do not provide drawing services may define this function pointer to be \cw{NULL}; it will never be called unless drawing is attempted. \S{drawingapi-blitter-load} \cw{blitter_load()} \c void (*blitter_load)(void *handle, blitter *bl, int x, int y); This function behaves exactly like the back end \cw{blitter_load()} function; see \k{drawing-blitter-load}. Implementations of this API which do not provide drawing services may define this function pointer to be \cw{NULL}; it will never be called unless drawing is attempted. \S{drawingapi-begin-doc} \cw{begin_doc()} \c void (*begin_doc)(void *handle, int pages); This function is called at the beginning of a printing run. It gives the front end an opportunity to initialise any required printing subsystem. It also provides the number of pages in advance. Implementations of this API which do not provide printing services may define this function pointer to be \cw{NULL}; it will never be called unless printing is attempted. \S{drawingapi-begin-page} \cw{begin_page()} \c void (*begin_page)(void *handle, int number); This function is called during printing, at the beginning of each page. It gives the page number (numbered from 1 rather than 0, so suitable for use in user-visible contexts). Implementations of this API which do not provide printing services may define this function pointer to be \cw{NULL}; it will never be called unless printing is attempted. \S{drawingapi-begin-puzzle} \cw{begin_puzzle()} \c void (*begin_puzzle)(void *handle, float xm, float xc, \c float ym, float yc, int pw, int ph, float wmm); This function is called during printing, just before printing a single puzzle on a page. It specifies the size and location of the puzzle on the page. \c{xm} and \c{xc} specify the horizontal position of the puzzle on the page, as a linear function of the page width. The front end is expected to multiply the page width by \c{xm}, add \c{xc} (measured in millimetres), and use the resulting x-coordinate as the left edge of the puzzle. Similarly, \c{ym} and \c{yc} specify the vertical position of the puzzle as a function of the page height: the page height times \c{ym}, plus \c{yc} millimetres, equals the desired distance from the top of the page to the top of the puzzle. (This unwieldy mechanism is required because not all printing systems can communicate the page size back to the software. The PostScript back end, for example, writes out PS which determines the page size at print time by means of calling \cq{clippath}, and centres the puzzles within that. Thus, exactly the same PS file works on A4 or on US Letter paper without needing local configuration, which simplifies matters.) \cw{pw} and \cw{ph} give the size of the puzzle in drawing API coordinates. The printing system will subsequently call the puzzle's own print function, which will in turn call drawing API functions in the expectation that an area \cw{pw} by \cw{ph} units is available to draw the puzzle on. Finally, \cw{wmm} gives the desired width of the puzzle in millimetres. (The aspect ratio is expected to be preserved, so if the desired puzzle height is also needed then it can be computed as \cw{wmm*ph/pw}.) Implementations of this API which do not provide printing services may define this function pointer to be \cw{NULL}; it will never be called unless printing is attempted. \S{drawingapi-end-puzzle} \cw{end_puzzle()} \c void (*end_puzzle)(void *handle); This function is called after the printing of a specific puzzle is complete. Implementations of this API which do not provide printing services may define this function pointer to be \cw{NULL}; it will never be called unless printing is attempted. \S{drawingapi-end-page} \cw{end_page()} \c void (*end_page)(void *handle, int number); This function is called after the printing of a page is finished. Implementations of this API which do not provide printing services may define this function pointer to be \cw{NULL}; it will never be called unless printing is attempted. \S{drawingapi-end-doc} \cw{end_doc()} \c void (*end_doc)(void *handle); This function is called after the printing of the entire document is finished. This is the moment to close files, send things to the print spooler, or whatever the local convention is. Implementations of this API which do not provide printing services may define this function pointer to be \cw{NULL}; it will never be called unless printing is attempted. \S{drawingapi-line-width} \cw{line_width()} \c void (*line_width)(void *handle, float width); This function is called to set the line thickness, during printing only. Note that the width is a \cw{float} here, where it was an \cw{int} as seen by the back end. This is because \cw{drawing.c} may have scaled it on the way past. However, the width is still specified in the same coordinate system as the rest of the drawing. Implementations of this API which do not provide printing services may define this function pointer to be \cw{NULL}; it will never be called unless printing is attempted. \S{drawingapi-text-fallback} \cw{text_fallback()} \c char *(*text_fallback)(void *handle, const char *const *strings, \c int nstrings); This function behaves exactly like the back end \cw{text_fallback()} function; see \k{drawing-text-fallback}. Implementations of this API which do not support any characters outside ASCII may define this function pointer to be \cw{NULL}, in which case the central code in \cw{drawing.c} will provide a default implementation. \H{drawingapi-frontend} The drawing API as called by the front end There are a small number of functions provided in \cw{drawing.c} which the front end needs to \e{call}, rather than helping to implement. They are described in this section. \S{drawing-new} \cw{drawing_new()} \c drawing *drawing_new(const drawing_api *api, midend *me, \c void *handle); This function creates a drawing object. It is passed a \c{drawing_api}, which is a structure containing nothing but function pointers; and also a \cq{void *} handle. The handle is passed back to each function pointer when it is called. The \c{midend} parameter is used for rewriting the status bar contents: \cw{status_bar()} (see \k{drawing-status-bar}) has to call a function in the mid-end which might rewrite the status bar text. If the drawing object is to be used only for printing, or if the game is known not to call \cw{status_bar()}, this parameter may be \cw{NULL}. \S{drawing-free} \cw{drawing_free()} \c void drawing_free(drawing *dr); This function frees a drawing object. Note that the \cq{void *} handle is not freed; if that needs cleaning up it must be done by the front end. \S{drawing-print-get-colour} \cw{print_get_colour()} \c void print_get_colour(drawing *dr, int colour, int printincolour, \c int *hatch, float *r, float *g, float *b) This function is called by the implementations of the drawing API functions when they are called in a printing context. It takes a colour index as input, and returns the description of the colour as requested by the back end. \c{printincolour} is \cw{TRUE} iff the implementation is printing in colour. This will alter the results returned if the colour in question was specified with a black-and-white fallback value. If the colour should be rendered by hatching, \c{*hatch} is filled with the type of hatching desired. See \k{print-grey-colour} for details of the values this integer can take. If the colour should be rendered as solid colour, \c{*hatch} is given a negative value, and \c{*r}, \c{*g} and \c{*b} are filled with the RGB values of the desired colour (if printing in colour), or all filled with the grey-scale value (if printing in black and white). \C{midend} The API provided by the mid-end This chapter documents the API provided by the mid-end to be called by the front end. You probably only need to read this if you are a front end implementor, i.e. you are porting Puzzles to a new platform. If you're only interested in writing new puzzles, you can safely skip this chapter. All the persistent state in the mid-end is encapsulated within a \c{midend} structure, to facilitate having multiple mid-ends in any port which supports multiple puzzle windows open simultaneously. Each \c{midend} is intended to handle the contents of a single puzzle window. \H{midend-new} \cw{midend_new()} \c midend *midend_new(frontend *fe, const game *ourgame, \c const drawing_api *drapi, void *drhandle) Allocates and returns a new mid-end structure. The \c{fe} argument is stored in the mid-end. It will be used when calling back to functions such as \cw{activate_timer()} (\k{frontend-activate-timer}), and will be passed on to the back end function \cw{colours()} (\k{backend-colours}). The parameters \c{drapi} and \c{drhandle} are passed to \cw{drawing_new()} (\k{drawing-new}) to construct a drawing object which will be passed to the back end function \cw{redraw()} (\k{backend-redraw}). Hence, all drawing-related function pointers defined in \c{drapi} can expect to be called with \c{drhandle} as their first argument. The \c{ourgame} argument points to a container structure describing a game back end. The mid-end thus created will only be capable of handling that one game. (So even in a monolithic front end containing all the games, this imposes the constraint that any individual puzzle window is tied to a single game. Unless, of course, you feel brave enough to change the mid-end for the window without closing the window...) \H{midend-free} \cw{midend_free()} \c void midend_free(midend *me); Frees a mid-end structure and all its associated data. \H{midend-tilesize} \cw{midend_tilesize()} \c int midend_tilesize(midend *me); Returns the \cq{tilesize} parameter being used to display the current puzzle (\k{backend-preferred-tilesize}). \H{midend-set-params} \cw{midend_set_params()} \c void midend_set_params(midend *me, game_params *params); Sets the current game parameters for a mid-end. Subsequent games generated by \cw{midend_new_game()} (\k{midend-new-game}) will use these parameters until further notice. The usual way in which the front end will have an actual \c{game_params} structure to pass to this function is if it had previously got it from \cw{midend_fetch_preset()} (\k{midend-fetch-preset}). Thus, this function is usually called in response to the user making a selection from the presets menu. \H{midend-get-params} \cw{midend_get_params()} \c game_params *midend_get_params(midend *me); Returns the current game parameters stored in this mid-end. The returned value is dynamically allocated, and should be freed when finished with by passing it to the game's own \cw{free_params()} function (see \k{backend-free-params}). \H{midend-size} \cw{midend_size()} \c void midend_size(midend *me, int *x, int *y, int user_size); Tells the mid-end to figure out its window size. On input, \c{*x} and \c{*y} should contain the maximum or requested size for the window. (Typically this will be the size of the screen that the window has to fit on, or similar.) The mid-end will repeatedly call the back end function \cw{compute_size()} (\k{backend-compute-size}), searching for a tile size that best satisfies the requirements. On exit, \c{*x} and \c{*y} will contain the size needed for the puzzle window's drawing area. (It is of course up to the front end to adjust this for any additional window furniture such as menu bars and window borders, if necessary. The status bar is also not included in this size.) Use \c{user_size} to indicate whether \c{*x} and \c{*y} are a requested size, or just a maximum size. If \c{user_size} is set to \cw{TRUE}, the mid-end will treat the input size as a request, and will pick a tile size which approximates it \e{as closely as possible}, going over the game's preferred tile size if necessary to achieve this. The mid-end will also use the resulting tile size as its preferred one until further notice, on the assumption that this size was explicitly requested by the user. Use this option if you want your front end to support dynamic resizing of the puzzle window with automatic scaling of the puzzle to fit. If \c{user_size} is set to \cw{FALSE}, then the game's tile size will never go over its preferred one, although it may go under in order to fit within the maximum bounds specified by \c{*x} and \c{*y}. This is the recommended approach when opening a new window at default size: the game will use its preferred size unless it has to use a smaller one to fit on the screen. If the tile size is shrunk for this reason, the change will not persist; if a smaller grid is subsequently chosen, the tile size will recover. The mid-end will try as hard as it can to return a size which is less than or equal to the input size, in both dimensions. In extreme circumstances it may fail (if even the lowest possible tile size gives window dimensions greater than the input), in which case it will return a size greater than the input size. Front ends should be prepared for this to happen (i.e. don't crash or fail an assertion), but may handle it in any way they see fit: by rejecting the game parameters which caused the problem, by opening a window larger than the screen regardless of inconvenience, by introducing scroll bars on the window, by drawing on a large bitmap and scaling it into a smaller window, or by any other means you can think of. It is likely that when the tile size is that small the game will be unplayable anyway, so don't put \e{too} much effort into handling it creatively. If your platform has no limit on window size (or if you're planning to use scroll bars for large puzzles), you can pass dimensions of \cw{INT_MAX} as input to this function. You should probably not do that \e{and} set the \c{user_size} flag, though! The midend relies on the frontend calling \cw{midend_new_game()} (\k{midend-new-game}) before calling \cw{midend_size()}. \H{midend-reset-tilesize} \cw{midend_reset_tilesize()} \c void midend_reset_tilesize(midend *me); This function resets the midend's preferred tile size to that of the standard puzzle. As discussed in \k{midend-size}, puzzle resizes are typically 'sticky', in that once the user has dragged the puzzle to a different window size, the resulting tile size will be remembered and used when the puzzle configuration changes. If you \e{don't} want that, e.g. if you want to provide a command to explicitly reset the puzzle size back to its default, then you can call this just before calling \cw{midend_size()} (which, in turn, you would probably call with \c{user_size} set to \cw{FALSE}). \H{midend-new-game} \cw{midend_new_game()} \c void midend_new_game(midend *me); Causes the mid-end to begin a new game. Normally the game will be a new randomly generated puzzle. However, if you have previously called \cw{midend_game_id()} or \cw{midend_set_config()}, the game generated might be dictated by the results of those functions. (In particular, you \e{must} call \cw{midend_new_game()} after calling either of those functions, or else no immediate effect will be visible.) You will probably need to call \cw{midend_size()} after calling this function, because if the game parameters have been changed since the last new game then the window size might need to change. (If you know the parameters \e{haven't} changed, you don't need to do this.) This function will create a new \c{game_drawstate}, but does not actually perform a redraw (since you often need to call \cw{midend_size()} before the redraw can be done). So after calling this function and after calling \cw{midend_size()}, you should then call \cw{midend_redraw()}. (It is not necessary to call \cw{midend_force_redraw()}; that will discard the draw state and create a fresh one, which is unnecessary in this case since there's a fresh one already. It would work, but it's usually excessive.) \H{midend-restart-game} \cw{midend_restart_game()} \c void midend_restart_game(midend *me); This function causes the current game to be restarted. This is done by placing a new copy of the original game state on the end of the undo list (so that an accidental restart can be undone). This function automatically causes a redraw, i.e. the front end can expect its drawing API to be called from \e{within} a call to this function. Some back ends require that \cw{midend_size()} (\k{midend-size}) is called before \cw{midend_restart_game()}. \H{midend-force-redraw} \cw{midend_force_redraw()} \c void midend_force_redraw(midend *me); Forces a complete redraw of the puzzle window, by means of discarding the current \c{game_drawstate} and creating a new one from scratch before calling the game's \cw{redraw()} function. The front end can expect its drawing API to be called from within a call to this function. Some back ends require that \cw{midend_size()} (\k{midend-size}) is called before \cw{midend_force_redraw()}. \H{midend-redraw} \cw{midend_redraw()} \c void midend_redraw(midend *me); Causes a partial redraw of the puzzle window, by means of simply calling the game's \cw{redraw()} function. (That is, the only things redrawn will be things that have changed since the last redraw.) The front end can expect its drawing API to be called from within a call to this function. Some back ends require that \cw{midend_size()} (\k{midend-size}) is called before \cw{midend_redraw()}. \H{midend-process-key} \cw{midend_process_key()} \c int midend_process_key(midend *me, int x, int y, int button); The front end calls this function to report a mouse or keyboard event. The parameters \c{x}, \c{y} and \c{button} are almost identical to the ones passed to the back end function \cw{interpret_move()} (\k{backend-interpret-move}), except that the front end is \e{not} required to provide the guarantees about mouse event ordering. The mid-end will sort out multiple simultaneous button presses and changes of button; the front end's responsibility is simply to pass on the mouse events it receives as accurately as possible. (Some platforms may need to emulate absent mouse buttons by means of using a modifier key such as Shift with another mouse button. This tends to mean that if Shift is pressed or released in the middle of a mouse drag, the mid-end will suddenly stop receiving, say, \cw{LEFT_DRAG} events and start receiving \cw{RIGHT_DRAG}s, with no intervening button release or press events. This too is something which the mid-end will sort out for you; the front end has no obligation to maintain sanity in this area.) The front end \e{should}, however, always eventually send some kind of button release. On some platforms this requires special effort: Windows, for example, requires a call to the system API function \cw{SetCapture()} in order to ensure that your window receives a mouse-up event even if the pointer has left the window by the time the mouse button is released. On any platform that requires this sort of thing, the front end \e{is} responsible for doing it. Calling this function is very likely to result in calls back to the front end's drawing API and/or \cw{activate_timer()} (\k{frontend-activate-timer}). The return value from \cw{midend_process_key()} is non-zero, unless the effect of the keypress was to request termination of the program. A front end should shut down the puzzle in response to a zero return. \H{midend-colours} \cw{midend_colours()} \c float *midend_colours(midend *me, int *ncolours); Returns an array of the colours required by the game, in exactly the same format as that returned by the back end function \cw{colours()} (\k{backend-colours}). Front ends should call this function rather than calling the back end's version directly, since the mid-end adds standard customisation facilities. (At the time of writing, those customisation facilities are implemented hackily by means of environment variables, but it's not impossible that they may become more full and formal in future.) \H{midend-timer} \cw{midend_timer()} \c void midend_timer(midend *me, float tplus); If the mid-end has called \cw{activate_timer()} (\k{frontend-activate-timer}) to request regular callbacks for purposes of animation or timing, this is the function the front end should call on a regular basis. The argument \c{tplus} gives the time, in seconds, since the last time either this function was called or \cw{activate_timer()} was invoked. One of the major purposes of timing in the mid-end is to perform move animation. Therefore, calling this function is very likely to result in calls back to the front end's drawing API. \H{midend-num-presets} \cw{midend_num_presets()} \c int midend_num_presets(midend *me); Returns the number of game parameter presets supplied by this game. Front ends should use this function and \cw{midend_fetch_preset()} to configure their presets menu rather than calling the back end directly, since the mid-end adds standard customisation facilities. (At the time of writing, those customisation facilities are implemented hackily by means of environment variables, but it's not impossible that they may become more full and formal in future.) \H{midend-fetch-preset} \cw{midend_fetch_preset()} \c void midend_fetch_preset(midend *me, int n, \c char **name, game_params **params); Returns one of the preset game parameter structures for the game. On input \c{n} must be a non-negative integer and less than the value returned from \cw{midend_num_presets()}. On output, \c{*name} is set to an ASCII string suitable for entering in the game's presets menu, and \c{*params} is set to the corresponding \c{game_params} structure. Both of the two output values are dynamically allocated, but they are owned by the mid-end structure: the front end should not ever free them directly, because they will be freed automatically during \cw{midend_free()}. \H{midend-which-preset} \cw{midend_which_preset()} \c int midend_which_preset(midend *me); Returns the numeric index of the preset game parameter structure which matches the current game parameters, or a negative number if no preset matches. Front ends could use this to maintain a tick beside one of the items in the menu (or tick the \q{Custom} option if the return value is less than zero). \H{midend-wants-statusbar} \cw{midend_wants_statusbar()} \c int midend_wants_statusbar(midend *me); This function returns \cw{TRUE} if the puzzle has a use for a textual status line (to display score, completion status, currently active tiles, time, or anything else). Front ends should call this function rather than talking directly to the back end. \H{midend-get-config} \cw{midend_get_config()} \c config_item *midend_get_config(midend *me, int which, \c char **wintitle); Returns a dialog box description for user configuration. On input, \cw{which} should be set to one of three values, which select which of the various dialog box descriptions is returned: \dt \cw{CFG_SETTINGS} \dd Requests the GUI parameter configuration box generated by the puzzle itself. This should be used when the user selects \q{Custom} from the game types menu (or equivalent). The mid-end passes this request on to the back end function \cw{configure()} (\k{backend-configure}). \dt \cw{CFG_DESC} \dd Requests a box suitable for entering a descriptive game ID (and viewing the existing one). The mid-end generates this dialog box description itself. This should be used when the user selects \q{Specific} from the game menu (or equivalent). \dt \cw{CFG_SEED} \dd Requests a box suitable for entering a random-seed game ID (and viewing the existing one). The mid-end generates this dialog box description itself. This should be used when the user selects \q{Random Seed} from the game menu (or equivalent). The returned value is an array of \cw{config_item}s, exactly as described in \k{backend-configure}. Another returned value is an ASCII string giving a suitable title for the configuration window, in \c{*wintitle}. Both returned values are dynamically allocated and will need to be freed. The window title can be freed in the obvious way; the \cw{config_item} array is a slightly complex structure, so a utility function \cw{free_cfg()} is provided to free it for you. See \k{utils-free-cfg}. (Of course, you will probably not want to free the \cw{config_item} array until the dialog box is dismissed, because before then you will probably need to pass it to \cw{midend_set_config}.) \H{midend-set-config} \cw{midend_set_config()} \c char *midend_set_config(midend *me, int which, \c config_item *cfg); Passes the mid-end the results of a configuration dialog box. \c{which} should have the same value which it had when \cw{midend_get_config()} was called; \c{cfg} should be the array of \c{config_item}s returned from \cw{midend_get_config()}, modified to contain the results of the user's editing operations. This function returns \cw{NULL} on success, or otherwise (if the configuration data was in some way invalid) an ASCII string containing an error message suitable for showing to the user. If the function succeeds, it is likely that the game parameters will have been changed and it is certain that a new game will be requested. The front end should therefore call \cw{midend_new_game()}, and probably also re-think the window size using \cw{midend_size()} and eventually perform a refresh using \cw{midend_redraw()}. \H{midend-game-id} \cw{midend_game_id()} \c char *midend_game_id(midend *me, char *id); Passes the mid-end a string game ID (of any of the valid forms \cq{params}, \cq{params:description} or \cq{params#seed}) which the mid-end will process and use for the next generated game. This function returns \cw{NULL} on success, or otherwise (if the configuration data was in some way invalid) an ASCII string containing an error message (not dynamically allocated) suitable for showing to the user. In the event of an error, the mid-end's internal state will be left exactly as it was before the call. If the function succeeds, it is likely that the game parameters will have been changed and it is certain that a new game will be requested. The front end should therefore call \cw{midend_new_game()}, and probably also re-think the window size using \cw{midend_size()} and eventually case a refresh using \cw{midend_redraw()}. \H{midend-get-game-id} \cw{midend_get_game_id()} \c char *midend_get_game_id(midend *me) Returns a descriptive game ID (i.e. one in the form \cq{params:description}) describing the game currently active in the mid-end. The returned string is dynamically allocated. \H{midend-get-random-seed} \cw{midend_get_random_seed()} \c char *midend_get_random_seed(midend *me) Returns a random game ID (i.e. one in the form \cq{params#seedstring}) describing the game currently active in the mid-end, if there is one. If the game was created by entering a description, no random seed will currently exist and this function will return \cw{NULL}. The returned string, if it is non-\cw{NULL}, is dynamically allocated. \H{midend-can-format-as-text-now} \cw{midend_can_format_as_text_now()} \c int midend_can_format_as_text_now(midend *me); Returns \cw{TRUE} if the game code is capable of formatting puzzles of the currently selected game type as ASCII. If this returns \cw{FALSE}, then \cw{midend_text_format()} (\k{midend-text-format}) will return \cw{NULL}. \H{midend-text-format} \cw{midend_text_format()} \c char *midend_text_format(midend *me); Formats the current game's current state as ASCII text suitable for copying to the clipboard. The returned string is dynamically allocated. If the game's \c{can_format_as_text_ever} flag is \cw{FALSE}, or if its \cw{can_format_as_text_now()} function returns \cw{FALSE}, then this function will return \cw{NULL}. If the returned string contains multiple lines (which is likely), it will use the normal C line ending convention (\cw{\\n} only). On platforms which use a different line ending convention for data in the clipboard, it is the front end's responsibility to perform the conversion. \H{midend-solve} \cw{midend_solve()} \c char *midend_solve(midend *me); Requests the mid-end to perform a Solve operation. On success, \cw{NULL} is returned. On failure, an error message (not dynamically allocated) is returned, suitable for showing to the user. The front end can expect its drawing API and/or \cw{activate_timer()} to be called from within a call to this function. Some back ends require that \cw{midend_size()} (\k{midend-size}) is called before \cw{midend_solve()}. \H{midend-status} \cw{midend_status()} \c int midend_status(midend *me); This function returns +1 if the midend is currently displaying a game in a solved state, -1 if the game is in a permanently lost state, or 0 otherwise. This function just calls the back end's \cw{status()} function. Front ends may wish to use this as a cue to proactively offer the option of starting a new game. (See \k{backend-status} for more detail about the back end's \cw{status()} function and discussion of what should count as which status code.) \H{midend-can-undo} \cw{midend_can_undo()} \c int midend_can_undo(midend *me); Returns \cw{TRUE} if the midend is currently in a state where the undo operation is meaningful (i.e. at least one position exists on the undo chain before the present one). Front ends may wish to use this to visually activate and deactivate an undo button. \H{midend-can-redo} \cw{midend_can_redo()} \c int midend_can_redo(midend *me); Returns \cw{TRUE} if the midend is currently in a state where the redo operation is meaningful (i.e. at least one position exists on the redo chain after the present one). Front ends may wish to use this to visually activate and deactivate a redo button. \H{midend-serialise} \cw{midend_serialise()} \c void midend_serialise(midend *me, \c void (*write)(void *ctx, void *buf, int len), \c void *wctx); Calling this function causes the mid-end to convert its entire internal state into a long ASCII text string, and to pass that string (piece by piece) to the supplied \c{write} function. Desktop implementations can use this function to save a game in any state (including half-finished) to a disk file, by supplying a \c{write} function which is a wrapper on \cw{fwrite()} (or local equivalent). Other implementations may find other uses for it, such as compressing the large and sprawling mid-end state into a manageable amount of memory when a palmtop application is suspended so that another one can run; in this case \cw{write} might want to write to a memory buffer rather than a file. There may be other uses for it as well. This function will call back to the supplied \c{write} function a number of times, with the first parameter (\c{ctx}) equal to \c{wctx}, and the other two parameters pointing at a piece of the output string. \H{midend-deserialise} \cw{midend_deserialise()} \c char *midend_deserialise(midend *me, \c int (*read)(void *ctx, void *buf, int len), \c void *rctx); This function is the counterpart to \cw{midend_serialise()}. It calls the supplied \cw{read} function repeatedly to read a quantity of data, and attempts to interpret that data as a serialised mid-end as output by \cw{midend_serialise()}. The \cw{read} function is called with the first parameter (\c{ctx}) equal to \c{rctx}, and should attempt to read \c{len} bytes of data into the buffer pointed to by \c{buf}. It should return \cw{FALSE} on failure or \cw{TRUE} on success. It should not report success unless it has filled the entire buffer; on platforms which might be reading from a pipe or other blocking data source, \c{read} is responsible for looping until the whole buffer has been filled. If the de-serialisation operation is successful, the mid-end's internal data structures will be replaced by the results of the load, and \cw{NULL} will be returned. Otherwise, the mid-end's state will be completely unchanged and an error message (typically some variation on \q{save file is corrupt}) will be returned. As usual, the error message string is not dynamically allocated. If this function succeeds, it is likely that the game parameters will have been changed. The front end should therefore probably re-think the window size using \cw{midend_size()}, and probably cause a refresh using \cw{midend_redraw()}. Because each mid-end is tied to a specific game back end, this function will fail if you attempt to read in a save file generated by a different game from the one configured in this mid-end, even if your application is a monolithic one containing all the puzzles. See \k{identify-game} for a helper function which will allow you to identify a save file before you instantiate your mid-end in the first place. \H{identify-game} \cw{identify_game()} \c char *identify_game(char **name, \c int (*read)(void *ctx, void *buf, int len), \c void *rctx); This function examines a serialised midend stream, of the same kind used by \cw{midend_serialise()} and \cw{midend_deserialise()}, and returns the \cw{name} field of the game back end from which it was saved. You might want this if your front end was a monolithic one containing all the puzzles, and you wanted to be able to load an arbitrary save file and automatically switch to the right game. Probably your next step would be to iterate through \cw{gamelist} (\k{frontend-backend}) looking for a game structure whose \cw{name} field matched the returned string, and give an error if you didn't find one. On success, the return value of this function is \cw{NULL}, and the game name string is written into \cw{*name}. The caller should free that string after using it. On failure, \cw{*name} is \cw{NULL}, and the return value is an error message (which does not need freeing at all). (This isn't strictly speaking a midend function, since it doesn't accept or return a pointer to a midend. You'd probably call it just \e{before} deciding what kind of midend you wanted to instantiate.) \H{midend-request-id-changes} \cw{midend_request_id_changes()} \c void midend_request_id_changes(midend *me, \c void (*notify)(void *), void *ctx); This function is called by the front end to request notification by the mid-end when the current game IDs (either descriptive or random-seed) change. This can occur as a result of keypresses ('n' for New Game, for example) or when a puzzle supersedes its game description (see \k{backend-supersede}). After this function is called, any change of the game ids will cause the mid-end to call \cw{notify(ctx)} after the change. This is for use by puzzles which want to present the game description to the user constantly (e.g. as an HTML hyperlink) instead of only showing it when the user explicitly requests it. This is a function I anticipate few front ends needing to implement, so I make it a callback rather than a static function in order to relieve most front ends of the need to provide an empty implementation. \H{frontend-backend} Direct reference to the back end structure by the front end Although \e{most} things the front end needs done should be done by calling the mid-end, there are a few situations in which the front end needs to refer directly to the game back end structure. The most obvious of these is \b passing the game back end as a parameter to \cw{midend_new()}. There are a few other back end features which are not wrapped by the mid-end because there didn't seem much point in doing so: \b fetching the \c{name} field to use in window titles and similar \b reading the \c{can_configure}, \c{can_solve} and \c{can_format_as_text_ever} fields to decide whether to add those items to the menu bar or equivalent \b reading the \c{winhelp_topic} field (Windows only) \b the GTK front end provides a \cq{--generate} command-line option which directly calls the back end to do most of its work. This is not really part of the main front end code, though, and I'm not sure it counts. In order to find the game back end structure, the front end does one of two things: \b If the particular front end is compiling a separate binary per game, then the back end structure is a global variable with the standard name \cq{thegame}: \lcont{ \c extern const game thegame; } \b If the front end is compiled as a monolithic application containing all the puzzles together (in which case the preprocessor symbol \cw{COMBINED} must be defined when compiling most of the code base), then there will be two global variables defined: \lcont{ \c extern const game *gamelist[]; \c extern const int gamecount; \c{gamelist} will be an array of \c{gamecount} game structures, declared in the automatically constructed source module \c{list.c}. The application should search that array for the game it wants, probably by reaching into each game structure and looking at its \c{name} field. } \H{frontend-api} Mid-end to front-end calls This section describes the small number of functions which a front end must provide to be called by the mid-end or other standard utility modules. \H{frontend-get-random-seed} \cw{get_random_seed()} \c void get_random_seed(void **randseed, int *randseedsize); This function is called by a new mid-end, and also occasionally by game back ends. Its job is to return a piece of data suitable for using as a seed for initialisation of a new \c{random_state}. On exit, \c{*randseed} should be set to point at a newly allocated piece of memory containing some seed data, and \c{*randseedsize} should be set to the length of that data. A simple and entirely adequate implementation is to return a piece of data containing the current system time at the highest conveniently available resolution. \H{frontend-activate-timer} \cw{activate_timer()} \c void activate_timer(frontend *fe); This is called by the mid-end to request that the front end begin calling it back at regular intervals. The timeout interval is left up to the front end; the finer it is, the smoother move animations will be, but the more CPU time will be used. Current front ends use values around 20ms (i.e. 50Hz). After this function is called, the mid-end will expect to receive calls to \cw{midend_timer()} on a regular basis. \H{frontend-deactivate-timer} \cw{deactivate_timer()} \c void deactivate_timer(frontend *fe); This is called by the mid-end to request that the front end stop calling \cw{midend_timer()}. \H{frontend-fatal} \cw{fatal()} \c void fatal(char *fmt, ...); This is called by some utility functions if they encounter a genuinely fatal error such as running out of memory. It is a variadic function in the style of \cw{printf()}, and is expected to show the formatted error message to the user any way it can and then terminate the application. It must not return. \H{frontend-default-colour} \cw{frontend_default_colour()} \c void frontend_default_colour(frontend *fe, float *output); This function expects to be passed a pointer to an array of three \cw{float}s. It returns the platform's local preferred background colour in those three floats, as red, green and blue values (in that order) ranging from \cw{0.0} to \cw{1.0}. This function should only ever be called by the back end function \cw{colours()} (\k{backend-colours}). (Thus, it isn't a \e{midend}-to-frontend function as such, but there didn't seem to be anywhere else particularly good to put it. Sorry.) \C{utils} Utility APIs This chapter documents a variety of utility APIs provided for the general use of the rest of the Puzzles code. \H{utils-random} Random number generation Platforms' local random number generators vary widely in quality and seed size. Puzzles therefore supplies its own high-quality random number generator, with the additional advantage of giving the same results if fed the same seed data on different platforms. This allows game random seeds to be exchanged between different ports of Puzzles and still generate the same games. Unlike the ANSI C \cw{rand()} function, the Puzzles random number generator has an \e{explicit} state object called a \c{random_state}. One of these is managed by each mid-end, for example, and passed to the back end to generate a game with. \S{utils-random-init} \cw{random_new()} \c random_state *random_new(char *seed, int len); Allocates, initialises and returns a new \c{random_state}. The input data is used as the seed for the random number stream (i.e. using the same seed at a later time will generate the same stream). The seed data can be any data at all; there is no requirement to use printable ASCII, or NUL-terminated strings, or anything like that. \S{utils-random-copy} \cw{random_copy()} \c random_state *random_copy(random_state *tocopy); Allocates a new \c{random_state}, copies the contents of another \c{random_state} into it, and returns the new state. If exactly the same sequence of functions is subseqently called on both the copy and the original, the results will be identical. This may be useful for speculatively performing some operation using a given random state, and later replaying that operation precisely. \S{utils-random-free} \cw{random_free()} \c void random_free(random_state *state); Frees a \c{random_state}. \S{utils-random-bits} \cw{random_bits()} \c unsigned long random_bits(random_state *state, int bits); Returns a random number from 0 to \cw{2^bits-1} inclusive. \c{bits} should be between 1 and 32 inclusive. \S{utils-random-upto} \cw{random_upto()} \c unsigned long random_upto(random_state *state, unsigned long limit); Returns a random number from 0 to \cw{limit-1} inclusive. \S{utils-random-state-encode} \cw{random_state_encode()} \c char *random_state_encode(random_state *state); Encodes the entire contents of a \c{random_state} in printable ASCII. Returns a dynamically allocated string containing that encoding. This can subsequently be passed to \cw{random_state_decode()} to reconstruct the same \c{random_state}. \S{utils-random-state-decode} \cw{random_state_decode()} \c random_state *random_state_decode(char *input); Decodes a string generated by \cw{random_state_encode()} and reconstructs an equivalent \c{random_state} to the one encoded, i.e. it should produce the same stream of random numbers. This function has no error reporting; if you pass it an invalid string it will simply generate an arbitrary random state, which may turn out to be noticeably non-random. \S{utils-shuffle} \cw{shuffle()} \c void shuffle(void *array, int nelts, int eltsize, random_state *rs); Shuffles an array into a random order. The interface is much like ANSI C \cw{qsort()}, except that there's no need for a compare function. \c{array} is a pointer to the first element of the array. \c{nelts} is the number of elements in the array; \c{eltsize} is the size of a single element (typically measured using \c{sizeof}). \c{rs} is a \c{random_state} used to generate all the random numbers for the shuffling process. \H{utils-alloc} Memory allocation Puzzles has some central wrappers on the standard memory allocation functions, which provide compile-time type checking, and run-time error checking by means of quitting the application if it runs out of memory. This doesn't provide the best possible recovery from memory shortage, but on the other hand it greatly simplifies the rest of the code, because nothing else anywhere needs to worry about \cw{NULL} returns from allocation. \S{utils-snew} \cw{snew()} \c var = snew(type); \e iii iiii This macro takes a single argument which is a \e{type name}. It allocates space for one object of that type. If allocation fails it will call \cw{fatal()} and not return; so if it does return, you can be confident that its return value is non-\cw{NULL}. The return value is cast to the specified type, so that the compiler will type-check it against the variable you assign it into. Thus, this ensures you don't accidentally allocate memory the size of the wrong type and assign it into a variable of the right one (or vice versa!). \S{utils-snewn} \cw{snewn()} \c var = snewn(n, type); \e iii i iiii This macro is the array form of \cw{snew()}. It takes two arguments; the first is a number, and the second is a type name. It allocates space for that many objects of that type, and returns a type-checked non-\cw{NULL} pointer just as \cw{snew()} does. \S{utils-sresize} \cw{sresize()} \c var = sresize(var, n, type); \e iii iii i iiii This macro is a type-checked form of \cw{realloc()}. It takes three arguments: an input memory block, a new size in elements, and a type. It re-sizes the input memory block to a size sufficient to contain that many elements of that type. It returns a type-checked non-\cw{NULL} pointer, like \cw{snew()} and \cw{snewn()}. The input memory block can be \cw{NULL}, in which case this function will behave exactly like \cw{snewn()}. (In principle any ANSI-compliant \cw{realloc()} implementation ought to cope with this, but I've never quite trusted it to work everywhere.) \S{utils-sfree} \cw{sfree()} \c void sfree(void *p); This function is pretty much equivalent to \cw{free()}. It is provided with a dynamically allocated block, and frees it. The input memory block can be \cw{NULL}, in which case this function will do nothing. (In principle any ANSI-compliant \cw{free()} implementation ought to cope with this, but I've never quite trusted it to work everywhere.) \S{utils-dupstr} \cw{dupstr()} \c char *dupstr(const char *s); This function dynamically allocates a duplicate of a C string. Like the \cw{snew()} functions, it guarantees to return non-\cw{NULL} or not return at all. (Many platforms provide the function \cw{strdup()}. As well as guaranteeing never to return \cw{NULL}, my version has the advantage of being defined \e{everywhere}, rather than inconveniently not quite everywhere.) \S{utils-free-cfg} \cw{free_cfg()} \c void free_cfg(config_item *cfg); This function correctly frees an array of \c{config_item}s, including walking the array until it gets to the end and freeing precisely those \c{sval} fields which are expected to be dynamically allocated. (See \k{backend-configure} for details of the \c{config_item} structure.) \H{utils-tree234} Sorted and counted tree functions Many games require complex algorithms for generating random puzzles, and some require moderately complex algorithms even during play. A common requirement during these algorithms is for a means of maintaining sorted or unsorted lists of items, such that items can be removed and added conveniently. For general use, Puzzles provides the following set of functions which maintain 2-3-4 trees in memory. (A 2-3-4 tree is a balanced tree structure, with the property that all lookups, insertions, deletions, splits and joins can be done in \cw{O(log N)} time.) All these functions expect you to be storing a tree of \c{void *} pointers. You can put anything you like in those pointers. By the use of per-node element counts, these tree structures have the slightly unusual ability to look elements up by their numeric index within the list represented by the tree. This means that they can be used to store an unsorted list (in which case, every time you insert a new element, you must explicitly specify the position where you wish to insert it). They can also do numeric lookups in a sorted tree, which might be useful for (for example) tracking the median of a changing data set. As well as storing sorted lists, these functions can be used for storing \q{maps} (associative arrays), by defining each element of a tree to be a (key, value) pair. \S{utils-newtree234} \cw{newtree234()} \c tree234 *newtree234(cmpfn234 cmp); Creates a new empty tree, and returns a pointer to it. The parameter \c{cmp} determines the sorting criterion on the tree. Its prototype is \c typedef int (*cmpfn234)(void *, void *); If you want a sorted tree, you should provide a function matching this prototype, which returns like \cw{strcmp()} does (negative if the first argument is smaller than the second, positive if it is bigger, zero if they compare equal). In this case, the function \cw{addpos234()} will not be usable on your tree (because all insertions must respect the sorting order). If you want an unsorted tree, pass \cw{NULL}. In this case you will not be able to use either \cw{add234()} or \cw{del234()}, or any other function such as \cw{find234()} which depends on a sorting order. Your tree will become something more like an array, except that it will efficiently support insertion and deletion as well as lookups by numeric index. \S{utils-freetree234} \cw{freetree234()} \c void freetree234(tree234 *t); Frees a tree. This function will not free the \e{elements} of the tree (because they might not be dynamically allocated, or you might be storing the same set of elements in more than one tree); it will just free the tree structure itself. If you want to free all the elements of a tree, you should empty it before passing it to \cw{freetree234()}, by means of code along the lines of \c while ((element = delpos234(tree, 0)) != NULL) \c sfree(element); /* or some more complicated free function */ \e iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii \S{utils-add234} \cw{add234()} \c void *add234(tree234 *t, void *e); Inserts a new element \c{e} into the tree \c{t}. This function expects the tree to be sorted; the new element is inserted according to the sort order. If an element comparing equal to \c{e} is already in the tree, then the insertion will fail, and the return value will be the existing element. Otherwise, the insertion succeeds, and \c{e} is returned. \S{utils-addpos234} \cw{addpos234()} \c void *addpos234(tree234 *t, void *e, int index); Inserts a new element into an unsorted tree. Since there is no sorting order to dictate where the new element goes, you must specify where you want it to go. Setting \c{index} to zero puts the new element right at the start of the list; setting \c{index} to the current number of elements in the tree puts the new element at the end. Return value is \c{e}, in line with \cw{add234()} (although this function cannot fail except by running out of memory, in which case it will bomb out and die rather than returning an error indication). \S{utils-index234} \cw{index234()} \c void *index234(tree234 *t, int index); Returns a pointer to the \c{index}th element of the tree, or \cw{NULL} if \c{index} is out of range. Elements of the tree are numbered from zero. \S{utils-find234} \cw{find234()} \c void *find234(tree234 *t, void *e, cmpfn234 cmp); Searches for an element comparing equal to \c{e} in a sorted tree. If \c{cmp} is \cw{NULL}, the tree's ordinary comparison function will be used to perform the search. However, sometimes you don't want that; suppose, for example, each of your elements is a big structure containing a \c{char *} name field, and you want to find the element with a given name. You \e{could} achieve this by constructing a fake element structure, setting its name field appropriately, and passing it to \cw{find234()}, but you might find it more convenient to pass \e{just} a name string to \cw{find234()}, supplying an alternative comparison function which expects one of its arguments to be a bare name and the other to be a large structure containing a name field. Therefore, if \c{cmp} is not \cw{NULL}, then it will be used to compare \c{e} to elements of the tree. The first argument passed to \c{cmp} will always be \c{e}; the second will be an element of the tree. (See \k{utils-newtree234} for the definition of the \c{cmpfn234} function pointer type.) The returned value is the element found, or \cw{NULL} if the search is unsuccessful. \S{utils-findrel234} \cw{findrel234()} \c void *findrel234(tree234 *t, void *e, cmpfn234 cmp, int relation); This function is like \cw{find234()}, but has the additional ability to do a \e{relative} search. The additional parameter \c{relation} can be one of the following values: \dt \cw{REL234_EQ} \dd Find only an element that compares equal to \c{e}. This is exactly the behaviour of \cw{find234()}. \dt \cw{REL234_LT} \dd Find the greatest element that compares strictly less than \c{e}. \c{e} may be \cw{NULL}, in which case it finds the greatest element in the whole tree (which could also be done by \cw{index234(t, count234(t)-1)}). \dt \cw{REL234_LE} \dd Find the greatest element that compares less than or equal to \c{e}. (That is, find an element that compares equal to \c{e} if possible, but failing that settle for something just less than it.) \dt \cw{REL234_GT} \dd Find the smallest element that compares strictly greater than \c{e}. \c{e} may be \cw{NULL}, in which case it finds the smallest element in the whole tree (which could also be done by \cw{index234(t, 0)}). \dt \cw{REL234_GE} \dd Find the smallest element that compares greater than or equal to \c{e}. (That is, find an element that compares equal to \c{e} if possible, but failing that settle for something just bigger than it.) Return value, as before, is the element found or \cw{NULL} if no element satisfied the search criterion. \S{utils-findpos234} \cw{findpos234()} \c void *findpos234(tree234 *t, void *e, cmpfn234 cmp, int *index); This function is like \cw{find234()}, but has the additional feature of returning the index of the element found in the tree; that index is written to \c{*index} in the event of a successful search (a non-\cw{NULL} return value). \c{index} may be \cw{NULL}, in which case this function behaves exactly like \cw{find234()}. \S{utils-findrelpos234} \cw{findrelpos234()} \c void *findrelpos234(tree234 *t, void *e, cmpfn234 cmp, int relation, \c int *index); This function combines all the features of \cw{findrel234()} and \cw{findpos234()}. \S{utils-del234} \cw{del234()} \c void *del234(tree234 *t, void *e); Finds an element comparing equal to \c{e} in the tree, deletes it, and returns it. The input tree must be sorted. The element found might be \c{e} itself, or might merely compare equal to it. Return value is \cw{NULL} if no such element is found. \S{utils-delpos234} \cw{delpos234()} \c void *delpos234(tree234 *t, int index); Deletes the element at position \c{index} in the tree, and returns it. Return value is \cw{NULL} if the index is out of range. \S{utils-count234} \cw{count234()} \c int count234(tree234 *t); Returns the number of elements currently in the tree. \S{utils-splitpos234} \cw{splitpos234()} \c tree234 *splitpos234(tree234 *t, int index, int before); Splits the input tree into two pieces at a given position, and creates a new tree containing all the elements on one side of that position. If \c{before} is \cw{TRUE}, then all the items at or after position \c{index} are left in the input tree, and the items before that point are returned in the new tree. Otherwise, the reverse happens: all the items at or after \c{index} are moved into the new tree, and those before that point are left in the old one. If \c{index} is equal to 0 or to the number of elements in the input tree, then one of the two trees will end up empty (and this is not an error condition). If \c{index} is further out of range in either direction, the operation will fail completely and return \cw{NULL}. This operation completes in \cw{O(log N)} time, no matter how large the tree or how balanced or unbalanced the split. \S{utils-split234} \cw{split234()} \c tree234 *split234(tree234 *t, void *e, cmpfn234 cmp, int rel); Splits a sorted tree according to its sort order. \c{rel} can be any of the relation constants described in \k{utils-findrel234}, \e{except} for \cw{REL234_EQ}. All the elements having that relation to \c{e} will be transferred into the new tree; the rest will be left in the old one. The parameter \c{cmp} has the same semantics as it does in \cw{find234()}: if it is not \cw{NULL}, it will be used in place of the tree's own comparison function when comparing elements to \c{e}, in such a way that \c{e} itself is always the first of its two operands. Again, this operation completes in \cw{O(log N)} time, no matter how large the tree or how balanced or unbalanced the split. \S{utils-join234} \cw{join234()} \c tree234 *join234(tree234 *t1, tree234 *t2); Joins two trees together by concatenating the lists they represent. All the elements of \c{t2} are moved into \c{t1}, in such a way that they appear \e{after} the elements of \c{t1}. The tree \c{t2} is freed; the return value is \c{t1}. If you apply this function to a sorted tree and it violates the sort order (i.e. the smallest element in \c{t2} is smaller than or equal to the largest element in \c{t1}), the operation will fail and return \cw{NULL}. This operation completes in \cw{O(log N)} time, no matter how large the trees being joined together. \S{utils-join234r} \cw{join234r()} \c tree234 *join234r(tree234 *t1, tree234 *t2); Joins two trees together in exactly the same way as \cw{join234()}, but this time the combined tree is returned in \c{t2}, and \c{t1} is destroyed. The elements in \c{t1} still appear before those in \c{t2}. Again, this operation completes in \cw{O(log N)} time, no matter how large the trees being joined together. \S{utils-copytree234} \cw{copytree234()} \c tree234 *copytree234(tree234 *t, copyfn234 copyfn, \c void *copyfnstate); Makes a copy of an entire tree. If \c{copyfn} is \cw{NULL}, the tree will be copied but the elements will not be; i.e. the new tree will contain pointers to exactly the same physical elements as the old one. If you want to copy each actual element during the operation, you can instead pass a function in \c{copyfn} which makes a copy of each element. That function has the prototype \c typedef void *(*copyfn234)(void *state, void *element); and every time it is called, the \c{state} parameter will be set to the value you passed in as \c{copyfnstate}. \H{utils-misc} Miscellaneous utility functions and macros This section contains all the utility functions which didn't sensibly fit anywhere else. \S{utils-truefalse} \cw{TRUE} and \cw{FALSE} The main Puzzles header file defines the macros \cw{TRUE} and \cw{FALSE}, which are used throughout the code in place of 1 and 0 (respectively) to indicate that the values are in a boolean context. For code base consistency, I'd prefer it if submissions of new code followed this convention as well. \S{utils-maxmin} \cw{max()} and \cw{min()} The main Puzzles header file defines the pretty standard macros \cw{max()} and \cw{min()}, each of which is given two arguments and returns the one which compares greater or less respectively. These macros may evaluate their arguments multiple times. Avoid side effects. \S{utils-pi} \cw{PI} The main Puzzles header file defines a macro \cw{PI} which expands to a floating-point constant representing pi. (I've never understood why ANSI's \cw{} doesn't define this. It'd be so useful!) \S{utils-obfuscate-bitmap} \cw{obfuscate_bitmap()} \c void obfuscate_bitmap(unsigned char *bmp, int bits, int decode); This function obscures the contents of a piece of data, by cryptographic methods. It is useful for games of hidden information (such as Mines, Guess or Black Box), in which the game ID theoretically reveals all the information the player is supposed to be trying to guess. So in order that players should be able to send game IDs to one another without accidentally spoiling the resulting game by looking at them, these games obfuscate their game IDs using this function. Although the obfuscation function is cryptographic, it cannot properly be called encryption because it has no key. Therefore, anybody motivated enough can re-implement it, or hack it out of the Puzzles source, and strip the obfuscation off one of these game IDs to see what lies beneath. (Indeed, they could usually do it much more easily than that, by entering the game ID into their own copy of the puzzle and hitting Solve.) The aim is not to protect against a determined attacker; the aim is simply to protect people who wanted to play the game honestly from \e{accidentally} spoiling their own fun. The input argument \c{bmp} points at a piece of memory to be obfuscated. \c{bits} gives the length of the data. Note that that length is in \e{bits} rather than bytes: if you ask for obfuscation of a partial number of bytes, then you will get it. Bytes are considered to be used from the top down: thus, for example, setting \c{bits} to 10 will cover the whole of \cw{bmp[0]} and the \e{top two} bits of \cw{bmp[1]}. The remainder of a partially used byte is undefined (i.e. it may be corrupted by the function). The parameter \c{decode} is \cw{FALSE} for an encoding operation, and \cw{TRUE} for a decoding operation. Each is the inverse of the other. (There's no particular reason you shouldn't obfuscate by decoding and restore cleartext by encoding, if you really wanted to; it should still work.) The input bitmap is processed in place. \S{utils-bin2hex} \cw{bin2hex()} \c char *bin2hex(const unsigned char *in, int inlen); This function takes an input byte array and converts it into an ASCII string encoding those bytes in (lower-case) hex. It returns a dynamically allocated string containing that encoding. This function is useful for encoding the result of \cw{obfuscate_bitmap()} in printable ASCII for use in game IDs. \S{utils-hex2bin} \cw{hex2bin()} \c unsigned char *hex2bin(const char *in, int outlen); This function takes an ASCII string containing hex digits, and converts it back into a byte array of length \c{outlen}. If there aren't enough hex digits in the string, the contents of the resulting array will be undefined. This function is the inverse of \cw{bin2hex()}. \S{utils-game-mkhighlight} \cw{game_mkhighlight()} \c void game_mkhighlight(frontend *fe, float *ret, \c int background, int highlight, int lowlight); It's reasonably common for a puzzle game's graphics to use highlights and lowlights to indicate \q{raised} or \q{lowered} sections. Fifteen, Sixteen and Twiddle are good examples of this. Puzzles using this graphical style are running a risk if they just use whatever background colour is supplied to them by the front end, because that background colour might be too light to see any highlights on at all. (In particular, it's not unheard of for the front end to specify a default background colour of white.) Therefore, such puzzles can call this utility function from their \cw{colours()} routine (\k{backend-colours}). You pass it your front end handle, a pointer to the start of your return array, and three colour indices. It will: \b call \cw{frontend_default_colour()} (\k{frontend-default-colour}) to fetch the front end's default background colour \b alter the brightness of that colour if it's unsuitable \b define brighter and darker variants of the colour to be used as highlights and lowlights \b write those results into the relevant positions in the \c{ret} array. Thus, \cw{ret[background*3]} to \cw{ret[background*3+2]} will be set to RGB values defining a sensible background colour, and similary \c{highlight} and \c{lowlight} will be set to sensible colours. \C{writing} How to write a new puzzle This chapter gives a guide to how to actually write a new puzzle: where to start, what to do first, how to solve common problems. The previous chapters have been largely composed of facts. This one is mostly advice. \H{writing-editorial} Choosing a puzzle Before you start writing a puzzle, you have to choose one. Your taste in puzzle games is up to you, of course; and, in fact, you're probably reading this guide because you've \e{already} thought of a game you want to write. But if you want to get it accepted into the official Puzzles distribution, then there's a criterion it has to meet. The current Puzzles editorial policy is that all games should be \e{fair}. A fair game is one which a player can only fail to complete through demonstrable lack of skill \dash that is, such that a better player in the same situation would have \e{known} to do something different. For a start, that means every game presented to the user must have \e{at least one solution}. Giving the unsuspecting user a puzzle which is actually impossible is not acceptable. (There is an exception: if the user has selected some non-default option which is clearly labelled as potentially unfair, \e{then} you're allowed to generate possibly insoluble puzzles, because the user isn't unsuspecting any more. Same Game and Mines both have options of this type.) Also, this actually \e{rules out} games such as Klondike, or the normal form of Mahjong Solitaire. Those games have the property that even if there is a solution (i.e. some sequence of moves which will get from the start state to the solved state), the player doesn't necessarily have enough information to \e{find} that solution. In both games, it is possible to reach a dead end because you had an arbitrary choice to make and made it the wrong way. This violates the fairness criterion, because a better player couldn't have known they needed to make the other choice. (GNOME has a variant on Mahjong Solitaire which makes it fair: there is a Shuffle operation which randomly permutes all the remaining tiles without changing their positions, which allows you to get out of a sticky situation. Using this operation adds a 60-second penalty to your solution time, so it's to the player's advantage to try to minimise the chance of having to use it. It's still possible to render the game uncompletable if you end up with only two tiles vertically stacked, but that's easy to foresee and avoid using a shuffle operation. This form of the game \e{is} fair. Implementing it in Puzzles would require an infrastructure change so that the back end could communicate time penalties to the mid-end, but that would be easy enough.) Providing a \e{unique} solution is a little more negotiable; it depends on the puzzle. Solo would have been of unacceptably low quality if it didn't always have a unique solution, whereas Twiddle inherently has multiple solutions by its very nature and it would have been meaningless to even \e{suggest} making it uniquely soluble. Somewhere in between, Flip could reasonably be made to have unique solutions (by enforcing a zero-dimension kernel in every generated matrix) but it doesn't seem like a serious quality problem that it doesn't. Of course, you don't \e{have} to care about all this. There's nothing stopping you implementing any puzzle you want to if you're happy to maintain your puzzle yourself, distribute it from your own web site, fork the Puzzles code completely, or anything like that. It's free software; you can do what you like with it. But any game that you want to be accepted into \e{my} Puzzles code base has to satisfy the fairness criterion, which means all randomly generated puzzles must have a solution (unless the user has deliberately chosen otherwise) and it must be possible \e{in theory} to find that solution without having to guess. \H{writing-gs} Getting started The simplest way to start writing a new puzzle is to copy \c{nullgame.c}. This is a template puzzle source file which does almost nothing, but which contains all the back end function prototypes and declares the back end data structure correctly. It is built every time the rest of Puzzles is built, to ensure that it doesn't get out of sync with the code and remains buildable. So start by copying \c{nullgame.c} into your new source file. Then you'll gradually add functionality until the very boring Null Game turns into your real game. Next you'll need to add your puzzle to the Makefiles, in order to compile it conveniently. \e{Do not edit the Makefiles}: they are created automatically by the script \c{mkfiles.pl}, from the file called \c{Recipe}. Edit \c{Recipe}, and then re-run \c{mkfiles.pl}. Also, don't forget to add your puzzle to \c{list.c}: if you don't, then it will still run fine on platforms which build each puzzle separately, but Mac OS X and other monolithic platforms will not include your new puzzle in their single binary. Once your source file is building, you can move on to the fun bit. \S{writing-generation} Puzzle generation Randomly generating instances of your puzzle is almost certain to be the most difficult part of the code, and also the task with the highest chance of turning out to be completely infeasible. Therefore I strongly recommend doing it \e{first}, so that if it all goes horribly wrong you haven't wasted any more time than you absolutely had to. What I usually do is to take an unmodified \c{nullgame.c}, and start adding code to \cw{new_game_desc()} which tries to generate a puzzle instance and print it out using \cw{printf()}. Once that's working, \e{then} I start connecting it up to the return value of \cw{new_game_desc()}, populating other structures like \c{game_params}, and generally writing the rest of the source file. There are many ways to generate a puzzle which is known to be soluble. In this section I list all the methods I currently know of, in case any of them can be applied to your puzzle. (Not all of these methods will work, or in some cases even make sense, for all puzzles.) Some puzzles are mathematically tractable, meaning you can work out in advance which instances are soluble. Sixteen, for example, has a parity constraint in some settings which renders exactly half the game space unreachable, but it can be mathematically proved that any position not in that half \e{is} reachable. Therefore, Sixteen's grid generation simply consists of selecting at random from a well defined subset of the game space. Cube in its default state is even easier: \e{every} possible arrangement of the blue squares and the cube's starting position is soluble! Another option is to redefine what you mean by \q{soluble}. Black Box takes this approach. There are layouts of balls in the box which are completely indistinguishable from one another no matter how many beams you fire into the box from which angles, which would normally be grounds for declaring those layouts unfair; but fortunately, detecting that indistinguishability is computationally easy. So Black Box doesn't demand that your ball placements match its own; it merely demands that your ball placements be \e{indistinguishable} from the ones it was thinking of. If you have an ambiguous puzzle, then any of the possible answers is considered to be a solution. Having redefined the rules in that way, any puzzle is soluble again. Those are the simple techniques. If they don't work, you have to get cleverer. One way to generate a soluble puzzle is to start from the solved state and make inverse moves until you reach a starting state. Then you know there's a solution, because you can just list the inverse moves you made and make them in the opposite order to return to the solved state. This method can be simple and effective for puzzles where you get to decide what's a starting state and what's not. In Pegs, for example, the generator begins with one peg in the centre of the board and makes inverse moves until it gets bored; in this puzzle, valid inverse moves are easy to detect, and \e{any} state that's reachable from the solved state by inverse moves is a reasonable starting position. So Pegs just continues making inverse moves until the board satisfies some criteria about extent and density, and then stops and declares itself done. For other puzzles, it can be a lot more difficult. Same Game uses this strategy too, and it's lucky to get away with it at all: valid inverse moves aren't easy to find (because although it's easy to insert additional squares in a Same Game position, it's difficult to arrange that \e{after} the insertion they aren't adjacent to any other squares of the same colour), so you're constantly at risk of running out of options and having to backtrack or start again. Also, Same Game grids never start off half-empty, which means you can't just stop when you run out of moves \dash you have to find a way to fill the grid up \e{completely}. The other way to generate a puzzle that's soluble is to start from the other end, and actually write a \e{solver}. This tends to ensure that a puzzle has a \e{unique} solution over and above having a solution at all, so it's a good technique to apply to puzzles for which that's important. One theoretical drawback of generating soluble puzzles by using a solver is that your puzzles are restricted in difficulty to those which the solver can handle. (Most solvers are not fully general: many sets of puzzle rules are NP-complete or otherwise nasty, so most solvers can only handle a subset of the theoretically soluble puzzles.) It's been my experience in practice, however, that this usually isn't a problem; computers are good at very different things from humans, and what the computer thinks is nice and easy might still be pleasantly challenging for a human. For example, when solving Dominosa puzzles I frequently find myself using a variety of reasoning techniques that my solver doesn't know about; in principle, therefore, I should be able to solve the puzzle using only those techniques it \e{does} know about, but this would involve repeatedly searching the entire grid for the one simple deduction I can make. Computers are good at this sort of exhaustive search, but it's been my experience that human solvers prefer to do more complex deductions than to spend ages searching for simple ones. So in many cases I don't find my own playing experience to be limited by the restrictions on the solver. (This isn't \e{always} the case. Solo is a counter-example; generating Solo puzzles using a simple solver does lead to qualitatively easier puzzles. Therefore I had to make the Solo solver rather more advanced than most of them.) There are several different ways to apply a solver to the problem of generating a soluble puzzle. I list a few of them below. The simplest approach is brute force: randomly generate a puzzle, use the solver to see if it's soluble, and if not, throw it away and try again until you get lucky. This is often a viable technique if all else fails, but it tends not to scale well: for many puzzle types, the probability of finding a uniquely soluble instance decreases sharply as puzzle size goes up, so this technique might work reasonably fast for small puzzles but take (almost) forever at larger sizes. Still, if there's no other alternative it can be usable: Pattern and Dominosa both use this technique. (However, Dominosa has a means of tweaking the randomly generated grids to increase the \e{probability} of them being soluble, by ruling out one of the most common ambiguous cases. This improved generation speed by over a factor of 10 on the highest preset!) An approach which can be more scalable involves generating a grid and then tweaking it to make it soluble. This is the technique used by Mines and also by Net: first a random puzzle is generated, and then the solver is run to see how far it gets. Sometimes the solver will get stuck; when that happens, examine the area it's having trouble with, and make a small random change in that area to allow it to make more progress. Continue solving (possibly even without restarting the solver), tweaking as necessary, until the solver finishes. Then restart the solver from the beginning to ensure that the tweaks haven't caused new problems in the process of solving old ones (which can sometimes happen). This strategy works well in situations where the usual solver failure mode is to get stuck in an easily localised spot. Thus it works well for Net and Mines, whose most common failure mode tends to be that most of the grid is fine but there are a few widely separated ambiguous sections; but it would work less well for Dominosa, in which the way you get stuck is to have scoured the whole grid and not found anything you can deduce \e{anywhere}. Also, it relies on there being a low probability that tweaking the grid introduces a new problem at the same time as solving the old one; Mines and Net also have the property that most of their deductions are local, so that it's very unlikely for a tweak to affect something half way across the grid from the location where it was applied. In Dominosa, by contrast, a lot of deductions use information about half the grid (\q{out of all the sixes, only one is next to a three}, which can depend on the values of up to 32 of the 56 squares in the default setting!), so this tweaking strategy would be rather less likely to work well. A more specialised strategy is that used in Solo and Slant. These puzzles have the property that they derive their difficulty from not presenting all the available clues. (In Solo's case, if all the possible clues were provided then the puzzle would already be solved; in Slant it would still require user action to fill in the lines, but it would present no challenge at all). Therefore, a simple generation technique is to leave the decision of which clues to provide until the last minute. In other words, first generate a random \e{filled} grid with all possible clues present, and then gradually remove clues for as long as the solver reports that it's still soluble. Unlike the methods described above, this technique \e{cannot} fail \dash once you've got a filled grid, nothing can stop you from being able to convert it into a viable puzzle. However, it wouldn't even be meaningful to apply this technique to (say) Pattern, in which clues can never be left out, so the only way to affect the set of clues is by altering the solution. (Unfortunately, Solo is complicated by the need to provide puzzles at varying difficulty levels. It's easy enough to generate a puzzle of \e{at most} a given level of difficulty; you just have a solver with configurable intelligence, and you set it to a given level and apply the above technique, thus guaranteeing that the resulting grid is solvable by someone with at most that much intelligence. However, generating a puzzle of \e{at least} a given level of difficulty is rather harder; if you go for \e{at most} Intermediate level, you're likely to find that you've accidentally generated a Trivial grid a lot of the time, because removing just one number is sufficient to take the puzzle from Trivial straight to Ambiguous. In that situation Solo has no remaining options but to throw the puzzle away and start again.) A final strategy is to use the solver \e{during} puzzle construction: lay out a bit of the grid, run the solver to see what it allows you to deduce, and then lay out a bit more to allow the solver to make more progress. There are articles on the web that recommend constructing Sudoku puzzles by this method (which is completely the opposite way round to how Solo does it); for Sudoku it has the advantage that you get to specify your clue squares in advance (so you can have them make pretty patterns). Rectangles uses a strategy along these lines. First it generates a grid by placing the actual rectangles; then it has to decide where in each rectangle to place a number. It uses a solver to help it place the numbers in such a way as to ensure a unique solution. It does this by means of running a test solver, but it runs the solver \e{before} it's placed any of the numbers \dash which means the solver must be capable of coping with uncertainty about exactly where the numbers are! It runs the solver as far as it can until it gets stuck; then it narrows down the possible positions of a number in order to allow the solver to make more progress, and so on. Most of the time this process terminates with the grid fully solved, at which point any remaining number-placement decisions can be made at random from the options not so far ruled out. Note that unlike the Net/Mines tweaking strategy described above, this algorithm does not require a checking run after it completes: if it finishes successfully at all, then it has definitely produced a uniquely soluble puzzle. Most of the strategies described above are not 100% reliable. Each one has a failure rate: every so often it has to throw out the whole grid and generate a fresh one from scratch. (Solo's strategy would be the exception, if it weren't for the need to provide configurable difficulty levels.) Occasional failures are not a fundamental problem in this sort of work, however: it's just a question of dividing the grid generation time by the success rate (if it takes 10ms to generate a candidate grid and 1/5 of them work, then it will take 50ms on average to generate a viable one), and seeing whether the expected time taken to \e{successfully} generate a puzzle is unacceptably slow. Dominosa's generator has a very low success rate (about 1 out of 20 candidate grids turn out to be usable, and if you think \e{that's} bad then go and look at the source code and find the comment showing what the figures were before the generation-time tweaks!), but the generator itself is very fast so this doesn't matter. Rectangles has a slower generator, but fails well under 50% of the time. So don't be discouraged if you have an algorithm that doesn't always work: if it \e{nearly} always works, that's probably good enough. The one place where reliability is important is that your algorithm must never produce false positives: it must not claim a puzzle is soluble when it isn't. It can produce false negatives (failing to notice that a puzzle is soluble), and it can fail to generate a puzzle at all, provided it doesn't do either so often as to become slow. One last piece of advice: for grid-based puzzles, when writing and testing your generation algorithm, it's almost always a good idea \e{not} to test it initially on a grid that's square (i.e. \cw{w==h}), because if the grid is square then you won't notice if you mistakenly write \c{h} instead of \c{w} (or vice versa) somewhere in the code. Use a rectangular grid for testing, and any size of grid will be likely to work after that. \S{writing-textformats} Designing textual description formats Another aspect of writing a puzzle which is worth putting some thought into is the design of the various text description formats: the format of the game parameter encoding, the game description encoding, and the move encoding. The first two of these should be reasonably intuitive for a user to type in; so provide some flexibility where possible. Suppose, for example, your parameter format consists of two numbers separated by an \c{x} to specify the grid dimensions (\c{10x10} or \c{20x15}), and then has some suffixes to specify other aspects of the game type. It's almost always a good idea in this situation to arrange that \cw{decode_params()} can handle the suffixes appearing in any order, even if \cw{encode_params()} only ever generates them in one order. These formats will also be expected to be reasonably stable: users will expect to be able to exchange game IDs with other users who aren't running exactly the same version of your game. So make them robust and stable: don't build too many assumptions into the game ID format which will have to be changed every time something subtle changes in the puzzle code. \H{writing-howto} Common how-to questions This section lists some common things people want to do when writing a puzzle, and describes how to achieve them within the Puzzles framework. \S{writing-howto-cursor} Drawing objects at only one position A common phenomenon is to have an object described in the \c{game_state} or the \c{game_ui} which can only be at one position. A cursor \dash probably specified in the \c{game_ui} \dash is a good example. In the \c{game_ui}, it would \e{obviously} be silly to have an array covering the whole game grid with a boolean flag stating whether the cursor was at each position. Doing that would waste space, would make it difficult to find the cursor in order to do anything with it, and would introduce the potential for synchronisation bugs in which you ended up with two cursors or none. The obviously sensible way to store a cursor in the \c{game_ui} is to have fields directly encoding the cursor's coordinates. However, it is a mistake to assume that the same logic applies to the \c{game_drawstate}. If you replicate the cursor position fields in the draw state, the redraw code will get very complicated. In the draw state, in fact, it \e{is} probably the right thing to have a cursor flag for every position in the grid. You probably have an array for the whole grid in the drawstate already (stating what is currently displayed in the window at each position); the sensible approach is to add a \q{cursor} flag to each element of that array. Then the main redraw loop will look something like this (pseudo-code): \c for (y = 0; y < h; y++) { \c for (x = 0; x < w; x++) { \c int value = state->symbol_at_position[y][x]; \c if (x == ui->cursor_x && y == ui->cursor_y) \c value |= CURSOR; \c if (ds->symbol_at_position[y][x] != value) { \c symbol_drawing_subroutine(dr, ds, x, y, value); \c ds->symbol_at_position[y][x] = value; \c } \c } \c } This loop is very simple, pretty hard to get wrong, and \e{automatically} deals both with erasing the previous cursor and drawing the new one, with no special case code required. This type of loop is generally a sensible way to write a redraw function, in fact. The best thing is to ensure that the information stored in the draw state for each position tells you \e{everything} about what was drawn there. A good way to ensure that is to pass precisely the same information, and \e{only} that information, to a subroutine that does the actual drawing; then you know there's no additional information which affects the drawing but which you don't notice changes in. \S{writing-keyboard-cursor} Implementing a keyboard-controlled cursor It is often useful to provide a keyboard control method in a basically mouse-controlled game. A keyboard-controlled cursor is best implemented by storing its location in the \c{game_ui} (since if it were in the \c{game_state} then the user would have to separately undo every cursor move operation). So the procedure would be: \b Put cursor position fields in the \c{game_ui}. \b \cw{interpret_move()} responds to arrow keys by modifying the cursor position fields and returning \cw{""}. \b \cw{interpret_move()} responds to some sort of fire button by actually performing a move based on the current cursor location. \b You might want an additional \c{game_ui} field stating whether the cursor is currently visible, and having it disappear when a mouse action occurs (so that it doesn't clutter the display when not actually in use). \b You might also want to automatically hide the cursor in \cw{changed_state()} when the current game state changes to one in which there is no move to make (which is the case in some types of completed game). \b \cw{redraw()} draws the cursor using the technique described in \k{writing-howto-cursor}. \S{writing-howto-dragging} Implementing draggable sprites Some games have a user interface which involves dragging some sort of game element around using the mouse. If you need to show a graphic moving smoothly over the top of other graphics, use a blitter (see \k{drawing-blitter} for the blitter API) to save the background underneath it. The typical scenario goes: \b Have a blitter field in the \c{game_drawstate}. \b Set the blitter field to \cw{NULL} in the game's \cw{new_drawstate()} function, since you don't yet know how big the piece of saved background needs to be. \b In the game's \cw{set_size()} function, once you know the size of the object you'll be dragging around the display and hence the required size of the blitter, actually allocate the blitter. \b In \cw{free_drawstate()}, free the blitter if it's not \cw{NULL}. \b In \cw{interpret_move()}, respond to mouse-down and mouse-drag events by updating some fields in the \cw{game_ui} which indicate that a drag is in progress. \b At the \e{very end} of \cw{redraw()}, after all other drawing has been done, draw the moving object if there is one. First save the background under the object in the blitter; then set a clip rectangle covering precisely the area you just saved (just in case anti-aliasing or some other error causes your drawing to go beyond the area you saved). Then draw the object, and call \cw{unclip()}. Finally, set a flag in the \cw{game_drawstate} that indicates that the blitter needs restoring. \b At the very start of \cw{redraw()}, before doing anything else at all, check the flag in the \cw{game_drawstate}, and if it says the blitter needs restoring then restore it. (Then clear the flag, so that this won't happen again in the next redraw if no moving object is drawn this time.) This way, you will be able to write the rest of the redraw function completely ignoring the dragged object, as if it were floating above your bitmap and being completely separate. \S{writing-ref-counting} Sharing large invariant data between all game states In some puzzles, there is a large amount of data which never changes between game states. The array of numbers in Dominosa is a good example. You \e{could} dynamically allocate a copy of that array in every \c{game_state}, and have \cw{dup_game()} make a fresh copy of it for every new \c{game_state}; but it would waste memory and time. A more efficient way is to use a reference-counted structure. \b Define a structure type containing the data in question, and also containing an integer reference count. \b Have a field in \c{game_state} which is a pointer to this structure. \b In \cw{new_game()}, when creating a fresh game state at the start of a new game, create an instance of this structure, initialise it with the invariant data, and set its reference count to 1. \b In \cw{dup_game()}, rather than making a copy of the structure for the new game state, simply set the new game state to point at the same copy of the structure, and increment its reference count. \b In \cw{free_game()}, decrement the reference count in the structure pointed to by the game state; if the count reaches zero, free the structure. This way, the invariant data will persist for only as long as it's genuinely needed; \e{as soon} as the last game state for a particular puzzle instance is freed, the invariant data for that puzzle will vanish as well. Reference counting is a very efficient form of garbage collection, when it works at all. (Which it does in this instance, of course, because there's no possibility of circular references.) \S{writing-flash-types} Implementing multiple types of flash In some games you need to flash in more than one different way. Mines, for example, flashes white when you win, and flashes red when you tread on a mine and die. The simple way to do this is: \b Have a field in the \c{game_ui} which describes the type of flash. \b In \cw{flash_length()}, examine the old and new game states to decide whether a flash is required and what type. Write the type of flash to the \c{game_ui} field whenever you return non-zero. \b In \cw{redraw()}, when you detect that \c{flash_time} is non-zero, examine the field in \c{game_ui} to decide which type of flash to draw. \cw{redraw()} will never be called with \c{flash_time} non-zero unless \cw{flash_length()} was first called to tell the mid-end that a flash was required; so whenever \cw{redraw()} notices that \c{flash_time} is non-zero, you can be sure that the field in \c{game_ui} is correctly set. \S{writing-move-anim} Animating game moves A number of puzzle types benefit from a quick animation of each move you make. For some games, such as Fifteen, this is particularly easy. Whenever \cw{redraw()} is called with \c{oldstate} non-\cw{NULL}, Fifteen simply compares the position of each tile in the two game states, and if the tile is not in the same place then it draws it some fraction of the way from its old position to its new position. This method copes automatically with undo. Other games are less obvious. In Sixteen, for example, you can't just draw each tile a fraction of the way from its old to its new position: if you did that, the end tile would zip very rapidly past all the others to get to the other end and that would look silly. (Worse, it would look inconsistent if the end tile was drawn on top going one way and on the bottom going the other way.) A useful trick here is to define a field or two in the game state that indicates what the last move was. \b Add a \q{last move} field to the \c{game_state} (or two or more fields if the move is complex enough to need them). \b \cw{new_game()} initialises this field to a null value for a new game state. \b \cw{execute_move()} sets up the field to reflect the move it just performed. \b \cw{redraw()} now needs to examine its \c{dir} parameter. If \c{dir} is positive, it determines the move being animated by looking at the last-move field in \c{newstate}; but if \c{dir} is negative, it has to look at the last-move field in \c{oldstate}, and invert whatever move it finds there. Note also that Sixteen needs to store the \e{direction} of the move, because you can't quite determine it by examining the row or column in question. You can in almost all cases, but when the row is precisely two squares long it doesn't work since a move in either direction looks the same. (You could argue that since moving a 2-element row left and right has the same effect, it doesn't matter which one you animate; but in fact it's very disorienting to click the arrow left and find the row moving right, and almost as bad to undo a move to the right and find the game animating \e{another} move to the right.) \S{writing-conditional-anim} Animating drag operations In Untangle, moves are made by dragging a node from an old position to a new position. Therefore, at the time when the move is initially made, it should not be animated, because the node has already been dragged to the right place and doesn't need moving there. However, it's nice to animate the same move if it's later undone or redone. This requires a bit of fiddling. The obvious approach is to have a flag in the \c{game_ui} which inhibits move animation, and to set that flag in \cw{interpret_move()}. The question is, when would the flag be reset again? The obvious place to do so is \cw{changed_state()}, which will be called once per move. But it will be called \e{before} \cw{anim_length()}, so if it resets the flag then \cw{anim_length()} will never see the flag set at all. The solution is to have \e{two} flags in a queue. \b Define two flags in \c{game_ui}; let's call them \q{current} and \q{next}. \b Set both to \cw{FALSE} in \c{new_ui()}. \b When a drag operation completes in \cw{interpret_move()}, set the \q{next} flag to \cw{TRUE}. \b Every time \cw{changed_state()} is called, set the value of \q{current} to the value in \q{next}, and then set the value of \q{next} to \cw{FALSE}. \b That way, \q{current} will be \cw{TRUE} \e{after} a call to \cw{changed_state()} if and only if that call to \cw{changed_state()} was the result of a drag operation processed by \cw{interpret_move()}. Any other call to \cw{changed_state()}, due to an Undo or a Redo or a Restart or a Solve, will leave \q{current} \cw{FALSE}. \b So now \cw{anim_length()} can request a move animation if and only if the \q{current} flag is \e{not} set. \S{writing-cheating} Inhibiting the victory flash when Solve is used Many games flash when you complete them, as a visual congratulation for having got to the end of the puzzle. It often seems like a good idea to disable that flash when the puzzle is brought to a solved state by means of the Solve operation. This is easily done: \b Add a \q{cheated} flag to the \c{game_state}. \b Set this flag to \cw{FALSE} in \cw{new_game()}. \b Have \cw{solve()} return a move description string which clearly identifies the move as a solve operation. \b Have \cw{execute_move()} respond to that clear identification by setting the \q{cheated} flag in the returned \c{game_state}. The flag will then be propagated to all subsequent game states, even if the user continues fiddling with the game after it is solved. \b \cw{flash_length()} now returns non-zero if \c{oldstate} is not completed and \c{newstate} is, \e{and} neither state has the \q{cheated} flag set. \H{writing-testing} Things to test once your puzzle is written Puzzle implementations written in this framework are self-testing as far as I could make them. Textual game and move descriptions, for example, are generated and parsed as part of the normal process of play. Therefore, if you can make moves in the game \e{at all} you can be reasonably confident that the mid-end serialisation interface will function correctly and you will be able to save your game. (By contrast, if I'd stuck with a single \cw{make_move()} function performing the jobs of both \cw{interpret_move()} and \cw{execute_move()}, and had separate functions to encode and decode a game state in string form, then those functions would not be used during normal play; so they could have been completely broken, and you'd never know it until you tried to save the game \dash which would have meant you'd have to test game saving \e{extensively} and make sure to test every possible type of game state. As an added bonus, doing it the way I did leads to smaller save files.) There is one exception to this, which is the string encoding of the \c{game_ui}. Most games do not store anything permanent in the \c{game_ui}, and hence do not need to put anything in its encode and decode functions; but if there is anything in there, you do need to test game loading and saving to ensure those functions work properly. It's also worth testing undo and redo of all operations, to ensure that the redraw and the animations (if any) work properly. Failing to animate undo properly seems to be a common error. Other than that, just use your common sense. puzzles-r9872/osx-help.but0000644000175300017530000000056610175170533014711 0ustar simonsimon\# Additional Halibut fragment to set up the HTML output \# appropriately for MacOS online help. \cfg{html-head-end}{ } puzzles-r9872/preprocessed.but0000644000175300017530000035764012161170556015662 0ustar simonsimon\title Simon Tatham's Portable Puzzle Collection \cfg{winhelp-filename}{puzzles.hlp} \cfg{winhelp-contents-titlepage}{Contents} \cfg{text-filename}{puzzles.txt} \cfg{html-contents-filename}{index.html} \cfg{html-template-filename}{%k.html} \cfg{html-index-filename}{docindex.html} \cfg{html-leaf-level}{1} \cfg{html-contents-depth-0}{1} \cfg{html-contents-depth-1}{2} \cfg{html-leaf-contains-contents}{true} \cfg{info-filename}{puzzles.info} \cfg{ps-filename}{puzzles.ps} \cfg{pdf-filename}{puzzles.pdf} \define{by} \u00D7{x} \define{dash} \u2013{-} \define{times} \u00D7{*} \define{divide} \u00F7{/} \define{minus} \u2212{-} This is a collection of small one-player puzzle games. \copyright This manual is copyright 2004-2012 Simon Tatham. All rights reserved. You may distribute this documentation under the MIT licence. See \k{licence} for the licence text in full. \cfg{html-local-head}{} \versionid $Id: puzzles.but 9828 2013-04-12 16:28:55Z simon $ \C{intro} Introduction I wrote this collection because I thought there should be more small desktop toys available: little games you can pop up in a window and play for two or three minutes while you take a break from whatever else you were doing. And I was also annoyed that every time I found a good game on (say) \i{Unix}, it wasn't available the next time I was sitting at a \i{Windows} machine, or vice versa; so I arranged that everything in my personal puzzle collection will happily run on both, and have more recently done a port to \i{Mac OS X} as well. When I find (or perhaps invent) further puzzle games that I like, they'll be added to this collection and will immediately be available on both platforms. And if anyone feels like writing any other front ends \dash PocketPC, Mac OS pre-10, or whatever it might be \dash then all the games in this framework will immediately become available on another platform as well. The actual games in this collection were mostly not my invention; they are re-implementations of existing game concepts within my portable puzzle framework. I do not claim credit, in general, for inventing the rules of any of these puzzles. (I don't even claim authorship of all the code; some of the puzzles have been submitted by other authors.) This collection is distributed under the \i{MIT licence} (see \k{licence}). This means that you can do pretty much anything you like with the game binaries or the code, except pretending you wrote them yourself, or suing me if anything goes wrong. The most recent versions, and \i{source code}, can be found at \I{website}\W{http://www.chiark.greenend.org.uk/~sgtatham/puzzles/}\cw{http://www.chiark.greenend.org.uk/~sgtatham/puzzles/}. Please report \I{feedback}\i{bugs} to \W{mailto:anakin@pobox.com}\cw{anakin@pobox.com}. You might find it helpful to read this article before reporting a bug: \W{http://www.chiark.greenend.org.uk/~sgtatham/bugs.html}\cw{http://www.chiark.greenend.org.uk/~sgtatham/bugs.html} \ii{Patches} are welcome. Especially if they provide a new front end (to make all these games run on another platform), or a new game. \C{common} \ii{Common features} This chapter describes features that are common to all the games. \H{common-actions} \I{controls}Common actions These actions are all available from the \I{Game menu}\q{Game} menu and via \I{keys}keyboard shortcuts, in addition to any game-specific actions. (On \i{Mac OS X}, to conform with local user interface standards, these actions are situated on the \I{File menu}\q{File} and \I{Edit menu}\q{Edit} menus instead.) \dt \ii\e{New game} (\q{N}, Ctrl+\q{N}) \dd Starts a new game, with a random initial state. \dt \ii\e{Restart game} \dd Resets the current game to its initial state. (This can be undone.) \dt \ii\e{Load} \dd Loads a saved game from a file on disk. \dt \ii\e{Save} \dd Saves the current state of your game to a file on disk. \lcont{ The Load and Save operations preserve your entire game history (so you can save, reload, and still Undo and Redo things you had done before saving). } \dt \I{printing, on Windows}\e{Print} \dd Where supported (currently only on Windows), brings up a dialog allowing you to print an arbitrary number of puzzles randomly generated from the current parameters, optionally including the current puzzle. (Only for puzzles which make sense to print, of course \dash it's hard to think of a sensible printable representation of Fifteen!) \dt \ii\e{Undo} (\q{U}, Ctrl+\q{Z}, Ctrl+\q{_}) \dd Undoes a single move. (You can undo moves back to the start of the session.) \dt \ii\e{Redo} (\q{R}, Ctrl+\q{R}) \dd Redoes a previously undone move. \dt \ii\e{Copy} \dd Copies the current state of your game to the clipboard in text format, so that you can paste it into (say) an e-mail client or a web message board if you're discussing the game with someone else. (Not all games support this feature.) \dt \ii\e{Solve} \dd Transforms the puzzle instantly into its solved state. For some games (Cube) this feature is not supported at all because it is of no particular use. For other games (such as Pattern), the solved state can be used to give you information, if you can't see how a solution can exist at all or you want to know where you made a mistake. For still other games (such as Sixteen), automatic solution tells you nothing about how to \e{get} to the solution, but it does provide a useful way to get there quickly so that you can experiment with set-piece moves and transformations. \lcont{ Some games (such as Solo) are capable of solving a game ID you have typed in from elsewhere. Other games (such as Rectangles) cannot solve a game ID they didn't invent themself, but when they did invent the game ID they know what the solution is already. Still other games (Pattern) can solve \e{some} external game IDs, but only if they aren't too difficult. The \q{Solve} command adds the solved state to the end of the undo chain for the puzzle. In other words, if you want to go back to solving it yourself after seeing the answer, you can just press Undo. } \dt \I{exit}\ii\e{Quit} (\q{Q}, Ctrl+\q{Q}) \dd Closes the application entirely. \H{common-id} Specifying games with the \ii{game ID} There are two ways to save a game specification out of a puzzle and recreate it later, or recreate it in somebody else's copy of the same puzzle. The \q{\i{Specific}} and \q{\i{Random Seed}} options from the \I{Game menu}\q{Game} menu (or the \q{File} menu, on \i{Mac OS X}) each show a piece of text (a \q{game ID}) which is sufficient to reconstruct precisely the same game at a later date. You can enter either of these pieces of text back into the program (via the same \q{Specific} or \q{Random Seed} menu options) at a later point, and it will recreate the same game. You can also use either one as a \i{command line} argument (on Windows or Unix); see \k{common-cmdline} for more detail. The difference between the two forms is that a descriptive game ID is a literal \e{description} of the \i{initial state} of the game, whereas a random seed is just a piece of arbitrary text which was provided as input to the random number generator used to create the puzzle. This means that: \b Descriptive game IDs tend to be longer in many puzzles (although some, such as Cube (\k{cube}), only need very short descriptions). So a random seed is often a \e{quicker} way to note down the puzzle you're currently playing, or to tell it to somebody else so they can play the same one as you. \b Any text at all is a valid random seed. The automatically generated ones are fifteen-digit numbers, but anything will do; you can type in your full name, or a word you just made up, and a valid puzzle will be generated from it. This provides a way for two or more people to race to complete the same puzzle: you think of a random seed, then everybody types it in at the same time, and nobody has an advantage due to having seen the generated puzzle before anybody else. \b It is often possible to convert puzzles from other sources (such as \q{nonograms} or \q{sudoku} from newspapers) into descriptive game IDs suitable for use with these programs. \b Random seeds are not guaranteed to produce the same result if you use them with a different \i\e{version} of the puzzle program. This is because the generation algorithm might have been improved or modified in later versions of the code, and will therefore produce a different result when given the same sequence of random numbers. Use a descriptive game ID if you aren't sure that it will be used on the same version of the program as yours. \lcont{(Use the \q{About} menu option to find out the version number of the program. Programs with the same version number running on different platforms should still be random-seed compatible.)} \I{ID format}A descriptive game ID starts with a piece of text which encodes the \i\e{parameters} of the current game (such as grid size). Then there is a colon, and after that is the description of the game's initial state. A random seed starts with a similar string of parameters, but then it contains a hash sign followed by arbitrary data. If you enter a descriptive game ID, the program will not be able to show you the random seed which generated it, since it wasn't generated \e{from} a random seed. If you \e{enter} a random seed, however, the program will be able to show you the descriptive game ID derived from that random seed. Note that the game parameter strings are not always identical between the two forms. For some games, there will be parameter data provided with the random seed which is not included in the descriptive game ID. This is because that parameter information is only relevant when \e{generating} puzzle grids, and is not important when playing them. Thus, for example, the difficulty level in Solo (\k{solo}) is not mentioned in the descriptive game ID. These additional parameters are also not set permanently if you type in a game ID. For example, suppose you have Solo set to \q{Advanced} difficulty level, and then a friend wants your help with a \q{Trivial} puzzle; so the friend reads out a random seed specifying \q{Trivial} difficulty, and you type it in. The program will generate you the same \q{Trivial} grid which your friend was having trouble with, but once you have finished playing it, when you ask for a new game it will automatically go back to the \q{Advanced} difficulty which it was previously set on. \H{common-type} The \q{Type} menu The \I{Type menu}\q{Type} menu, if present, may contain a list of \i{preset} game settings. Selecting one of these will start a new random game with the parameters specified. The \q{Type} menu may also contain a \q{\i{Custom}} option which allows you to fine-tune game \i{parameters}. The parameters available are specific to each game and are described in the following sections. \H{common-cmdline} Specifying game parameters on the \i{command line} (This section does not apply to the \i{Mac OS X} version.) The games in this collection deliberately do not ever save information on to the computer they run on: they have no high score tables and no saved preferences. (This is because I expect at least some people to play them at work, and those people will probably appreciate leaving as little evidence as possible!) However, if you do want to arrange for one of these games to \I{default parameters, specifying}default to a particular set of parameters, you can specify them on the command line. The easiest way to do this is to set up the parameters you want using the \q{Type} menu (see \k{common-type}), and then to select \q{Random Seed} from the \q{Game} or \q{File} menu (see \k{common-id}). The text in the \q{Game ID} box will be composed of two parts, separated by a hash. The first of these parts represents the game parameters (the size of the playing area, for example, and anything else you set using the \q{Type} menu). If you run the game with just that parameter text on the command line, it will start up with the settings you specified. For example: if you run Cube (see \k{cube}), select \q{Octahedron} from the \q{Type} menu, and then go to the game ID selection, you will see a string of the form \cq{o2x2#338686542711620}. Take only the part before the hash (\cq{o2x2}), and start Cube with that text on the command line: \cq{cube o2x2}. If you copy the \e{entire} game ID on to the command line, the game will start up in the specific game that was described. This is occasionally a more convenient way to start a particular game ID than by pasting it into the game ID selection box. (You could also retrieve the encoded game parameters using the \q{Specific} menu option instead of \q{Random Seed}, but if you do then some options, such as the difficulty level in Solo, will be missing. See \k{common-id} for more details on this.) \H{common-unix-cmdline} \i{Unix} \i{command-line} options (This section only applies to the Unix port.) In addition to being able to specify game parameters on the command line (see \k{common-cmdline}), there are various other options: \dt \cw{--game} \dt \cw{--load} \dd These options respectively determine whether the command-line argument is treated as specifying game parameters or a \i{save} file to \i{load}. Only one should be specified. If neither of these options is specified, a guess is made based on the format of the argument. \dt \cw{--generate }\e{n} \dd If this option is specified, instead of a puzzle being displayed, a number of descriptive game IDs will be \I{generating game IDs}invented and printed on standard output. This is useful for gaining access to the game generation algorithms without necessarily using the frontend. \lcont{ If game parameters are specified on the command-line, they will be used to generate the game IDs; otherwise a default set of parameters will be used. The most common use of this option is in conjunction with \c{--print}, in which case its behaviour is slightly different; see below. } \dt \I{printing, on Unix}\cw{--print }\e{w}\cw{x}\e{h} \dd If this option is specified, instead of a puzzle being displayed, a printed representation of one or more unsolved puzzles is sent to standard output, in \i{PostScript} format. \lcont{ On each page of puzzles, there will be \e{w} across and \e{h} down. If there are more puzzles than \e{w}\by\e{h}, more than one page will be printed. If \c{--generate} has also been specified, the invented game IDs will be used to generate the printed output. Otherwise, a list of game IDs is expected on standard input (which can be descriptive or random seeds; see \k{common-id}), in the same format produced by \c{--generate}. For example: \c net --generate 12 --print 2x3 7x7w | lpr will generate two pages of printed Net puzzles (each of which will have a 7\by\.7 wrapping grid), and pipe the output to the \c{lpr} command, which on many systems will send them to an actual printer. There are various other options which affect printing; see below. } \dt \cw{--save }\e{file-prefix} [ \cw{--save-suffix }\e{file-suffix} ] \dd If this option is specified, instead of a puzzle being displayed, saved-game files for one or more unsolved puzzles are written to files constructed from the supplied prefix and/or suffix. \lcont{ If \c{--generate} has also been specified, the invented game IDs will be used to generate the printed output. Otherwise, a list of game IDs is expected on standard input (which can be descriptive or random seeds; see \k{common-id}), in the same format produced by \c{--generate}. For example: \c net --generate 12 --save game --save-suffix .sav will generate twelve Net saved-game files with the names \cw{game0.sav} to \cw{game11.sav}. } \dt \cw{--version} \dd Prints version information about the game, and then quits. The following options are only meaningful if \c{--print} is also specified: \dt \cw{--with-solutions} \dd The set of pages filled with unsolved puzzles will be followed by the solutions to those puzzles. \dt \cw{--scale }\e{n} \dd Adjusts how big each puzzle is when printed. Larger numbers make puzzles bigger; the default is 1.0. \dt \cw{--colour} \dd Puzzles will be printed in colour, rather than in black and white (if supported by the puzzle). \C{net} \i{Net} \cfg{winhelp-topic}{games.net} (\e{Note:} the \i{Windows} version of this game is called \i\cw{NETGAME.EXE} to avoid clashing with Windows's own \cw{NET.EXE}.) I originally saw this in the form of a Flash game called \i{FreeNet} \k{FreeNet}, written by Pavils Jurjans; there are several other implementations under the name \i{NetWalk}. The computer prepares a network by connecting up the centres of squares in a grid, and then shuffles the network by rotating every tile randomly. Your job is to rotate it all back into place. The successful solution will be an entirely connected network, with no closed loops. \#{The latter clause means that there are no closed paths within the network. Could this be clearer? "No closed paths"?} As a visual aid, all tiles which are connected to the one in the middle are highlighted. \B{FreeNet} \W{http://www.jurjans.lv/stuff/net/FreeNet.htm}\cw{http://www.jurjans.lv/stuff/net/FreeNet.htm} \H{net-controls} \i{Net controls} \IM{Net controls} controls, for Net \IM{Net controls} keys, for Net \IM{Net controls} shortcuts (keyboard), for Net This game can be played with either the keyboard or the mouse. The controls are: \dt \e{Select tile}: mouse pointer, arrow keys \dt \e{Rotate tile anticlockwise}: left mouse button, \q{A} key \dt \e{Rotate tile clockwise}: right mouse button, \q{D} key \dt \e{Rotate tile by 180 degrees}: \q{F} key \dt \e{Lock (or unlock) tile}: middle mouse button, shift-click, \q{S} key \dd You can lock a tile once you're sure of its orientation. You can also unlock it again, but while it's locked you can't accidentally turn it. The following controls are not necessary to complete the game, but may be useful: \dt \e{Shift grid}: Shift + arrow keys \dd On grids that wrap, you can move the origin of the grid, so that tiles that were on opposite sides of the grid can be seen together. \dt \e{Move centre}: Ctrl + arrow keys \dd You can change which tile is used as the source of highlighting. (It doesn't ultimately matter which tile this is, as every tile will be connected to every other tile in a correct solution, but it may be helpful in the intermediate stages of solving the puzzle.) \dt \e{Jumble tiles}: \q{J} key \dd This key turns all tiles that are not locked to random orientations. (All the actions described in \k{common-actions} are also available.) \H{net-params} \I{parameters, for Net}Net parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in tiles. \dt \e{Walls wrap around} \dd If checked, flow can pass from the left edge to the right edge, and from top to bottom, and vice versa. \dt \e{Barrier probability} \dd A number between 0.0 and 1.0 controlling whether an immovable barrier is placed between two tiles to prevent flow between them (a higher number gives more barriers). Since barriers are immovable, they act as constraints on the solution (i.e., hints). \lcont{ The grid generation in Net has been carefully arranged so that the barriers are independent of the rest of the grid. This means that if you note down the random seed used to generate the current puzzle (see \k{common-id}), change the \e{Barrier probability} parameter, and then re-enter the same random seed, you should see exactly the same starting grid, with the only change being the number of barriers. So if you're stuck on a particular grid and need a hint, you could start up another instance of Net, set up the same parameters but a higher barrier probability, and enter the game seed from the original Net window. } \dt \e{Ensure unique solution} \dd Normally, Net will make sure that the puzzles it presents have only one solution. Puzzles with ambiguous sections can be more difficult and more subtle, so if you like you can turn off this feature and risk having ambiguous puzzles. (Also, finding \e{all} the possible solutions can be an additional challenge for an advanced player.) \C{cube} \i{Cube} \cfg{winhelp-topic}{games.cube} This is another one I originally saw as a web game. This one was a Java game \k{cube-java-game}, by Paul Scott. You have a grid of 16 squares, six of which are blue; on one square rests a cube. Your move is to use the arrow keys to roll the cube through 90 degrees so that it moves to an adjacent square. If you roll the cube on to a blue square, the blue square is picked up on one face of the cube; if you roll a blue face of the cube on to a non-blue square, the blueness is put down again. (In general, whenever you roll the cube, the two faces that come into contact swap colours.) Your job is to get all six blue squares on to the six faces of the cube at the same time. Count your moves and try to do it in as few as possible. Unlike the original Java game, my version has an additional feature: once you've mastered the game with a cube rolling on a square grid, you can change to a triangular grid and roll any of a tetrahedron, an octahedron or an icosahedron. \B{cube-java-game} \W{http://www3.sympatico.ca/paulscott/cube/cube.htm}\cw{http://www3.sympatico.ca/paulscott/cube/cube.htm} \H{cube-controls} \i{Cube controls} \IM{Cube controls} controls, for Cube \IM{Cube controls} keys, for Cube \IM{Cube controls} shortcuts (keyboard), for Cube This game can be played with either the keyboard or the mouse. Left-clicking anywhere on the window will move the cube (or other solid) towards the mouse pointer. The arrow keys can also used to roll the cube on its square grid in the four cardinal directions. On the triangular grids, the mapping of arrow keys to directions is more approximate. Vertical movement is disallowed where it doesn't make sense. The four keys surrounding the arrow keys on the numeric keypad (\q{7}, \q{9}, \q{1}, \q{3}) can be used for diagonal movement. (All the actions described in \k{common-actions} are also available.) \H{cube-params} \I{parameters, for Cube}Cube parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Type of solid} \dd Selects the solid to roll (and hence the shape of the grid): tetrahedron, cube, octahedron, or icosahedron. \dt \e{Width / top}, \e{Height / bottom} \dd On a square grid, horizontal and vertical dimensions. On a triangular grid, the number of triangles on the top and bottom rows respectively. \C{fifteen} \i{Fifteen} \cfg{winhelp-topic}{games.fifteen} The old ones are the best: this is the good old \q{\i{15-puzzle}} with sliding tiles. You have a 4\by\.4 square grid; 15 squares contain numbered tiles, and the sixteenth is empty. Your move is to choose a tile next to the empty space, and slide it into the space. The aim is to end up with the tiles in numerical order, with the space in the bottom right (so that the top row reads 1,2,3,4 and the bottom row reads 13,14,15,\e{space}). \H{fifteen-controls} \i{Fifteen controls} \IM{Fifteen controls} controls, for Fifteen \IM{Fifteen controls} keys, for Fifteen \IM{Fifteen controls} shortcuts (keyboard), for Fifteen This game can be controlled with the mouse or the keyboard. A left-click with the mouse in the row or column containing the empty space will move as many tiles as necessary to move the space to the mouse pointer. The arrow keys will move a tile adjacent to the space in the direction indicated (moving the space in the \e{opposite} direction). (All the actions described in \k{common-actions} are also available.) \H{fifteen-params} \I{parameters, for Fifteen}Fifteen parameters The only options available from the \q{Custom...} option on the \q{Type} menu are \e{Width} and \e{Height}, which are self-explanatory. (Once you've changed these, it's not a \q{15-puzzle} any more, of course!) \C{sixteen} \i{Sixteen} \cfg{winhelp-topic}{games.sixteen} Another sliding tile puzzle, visually similar to Fifteen (see \k{fifteen}) but with a different type of move. This time, there is no hole: all 16 squares on the grid contain numbered squares. Your move is to shift an entire row left or right, or shift an entire column up or down; every time you do that, the tile you shift off the grid re-appears at the other end of the same row, in the space you just vacated. To win, arrange the tiles into numerical order (1,2,3,4 on the top row, 13,14,15,16 on the bottom). When you've done that, try playing on different sizes of grid. I \e{might} have invented this game myself, though only by accident if so (and I'm sure other people have independently invented it). I thought I was imitating a screensaver I'd seen, but I have a feeling that the screensaver might actually have been a Fifteen-type puzzle rather than this slightly different kind. So this might be the one thing in my puzzle collection which represents creativity on my part rather than just engineering. \H{sixteen-controls} \I{controls, for Sixteen}Sixteen controls Left-clicking on an arrow will move the appropriate row or column in the direction indicated. Right-clicking will move it in the opposite direction. Alternatively, use the cursor keys to move the position indicator around the edge of the grid, and use the return key to move the row/column in the direction indicated. (All the actions described in \k{common-actions} are also available.) \H{sixteen-params} \I{parameters, for Sixteen}Sixteen parameters The parameters available from the \q{Custom...} option on the \q{Type} menu are: \b \e{Width} and \e{Height}, which are self-explanatory. \b You can ask for a limited shuffling operation to be performed on the grid. By default, Sixteen will shuffle the grid in such a way that any arrangement is about as probable as any other. You can override this by requesting a precise number of shuffling moves to be performed. Typically your aim is then to determine the precise set of shuffling moves and invert them exactly, so that you answer (say) a four-move shuffle with a four-move solution. Note that the more moves you ask for, the more likely it is that solutions shorter than the target length will turn out to be possible. \C{twiddle} \i{Twiddle} \cfg{winhelp-topic}{games.twiddle} Twiddle is a tile-rearrangement puzzle, visually similar to Sixteen (see \k{sixteen}): you are given a grid of square tiles, each containing a number, and your aim is to arrange the numbers into ascending order. In basic Twiddle, your move is to rotate a square group of four tiles about their common centre. (Orientation is not significant in the basic puzzle, although you can select it.) On more advanced settings, you can rotate a larger square group of tiles. I first saw this type of puzzle in the GameCube game \q{Metroid Prime 2}. In the Main Gyro Chamber in that game, there is a puzzle you solve to unlock a door, which is a special case of Twiddle. I developed this game as a generalisation of that puzzle. \H{twiddle-controls} \I{controls, for Twiddle}Twiddle controls To play Twiddle, click the mouse in the centre of the square group you wish to rotate. In the basic mode, you rotate a 2\by\.2 square, which means you have to click at a corner point where four tiles meet. In more advanced modes you might be rotating 3\by\.3 or even more at a time; if the size of the square is odd then you simply click in the centre tile of the square you want to rotate. Clicking with the left mouse button rotates the group anticlockwise. Clicking with the right button rotates it clockwise. You can also move an outline square around the grid with the cursor keys; the square is the size above (2\by\.2 by default, or larger). Pressing the return key or space bar will rotate the current square anticlockwise or clockwise respectively. (All the actions described in \k{common-actions} are also available.) \H{twiddle-parameters} \I{parameters, for Twiddle}Twiddle parameters Twiddle provides several configuration options via the \q{Custom} option on the \q{Type} menu: \b You can configure the width and height of the puzzle grid. \b You can configure the size of square block that rotates at a time. \b You can ask for every square in the grid to be distinguishable (the default), or you can ask for a simplified puzzle in which there are groups of identical numbers. In the simplified puzzle your aim is just to arrange all the 1s into the first row, all the 2s into the second row, and so on. \b You can configure whether the orientation of tiles matters. If you ask for an orientable puzzle, each tile will have a triangle drawn in it. All the triangles must be pointing upwards to complete the puzzle. \b You can ask for a limited shuffling operation to be performed on the grid. By default, Twiddle will shuffle the grid so much that any arrangement is about as probable as any other. You can override this by requesting a precise number of shuffling moves to be performed. Typically your aim is then to determine the precise set of shuffling moves and invert them exactly, so that you answer (say) a four-move shuffle with a four-move solution. Note that the more moves you ask for, the more likely it is that solutions shorter than the target length will turn out to be possible. \C{rectangles} \i{Rectangles} \cfg{winhelp-topic}{games.rectangles} You have a grid of squares, with numbers written in some (but not all) of the squares. Your task is to subdivide the grid into rectangles of various sizes, such that (a) every rectangle contains exactly one numbered square, and (b) the area of each rectangle is equal to the number written in its numbered square. Credit for this game goes to the Japanese puzzle magazine \i{Nikoli} \k{nikoli-rect}; I've also seen a Palm implementation at \i{Puzzle Palace} \k{puzzle-palace-rect}. Unlike Puzzle Palace's implementation, my version automatically generates random grids of any size you like. The quality of puzzle design is therefore not quite as good as hand-crafted puzzles would be, but on the plus side you get an inexhaustible supply of puzzles tailored to your own specification. \B{nikoli-rect} \W{http://www.nikoli.co.jp/puzzles/7/index_text-e.htm}\cw{http://www.nikoli.co.jp/puzzles/7/index_text-e.htm} \B{puzzle-palace-rect} \W{http://www.puzzle.gr.jp/puzzle/sikaku/palm/index.html.en}\cw{http://www.puzzle.gr.jp/puzzle/sikaku/palm/index.html.en} \H{rectangles-controls} \I{controls, for Rectangles}Rectangles controls This game is played with the mouse or cursor keys. Left-click any edge to toggle it on or off, or left-click and drag to draw an entire rectangle (or line) on the grid in one go (removing any existing edges within that rectangle). Right-clicking and dragging will allow you to erase the contents of a rectangle without affecting its edges. Alternatively, use the cursor keys to move the position indicator around the board. Pressing the return key then allows you to use the cursor keys to drag a rectangle out from that position, and pressing the return key again completes the rectangle. Using the space bar instead of the return key allows you to erase the contents of a rectangle without affecting its edges, as above. When a rectangle of the correct size is completed, it will be shaded. (All the actions described in \k{common-actions} are also available.) \H{rectangles-params} \I{parameters, for Rectangles}Rectangles parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid, in squares. \dt \e{Expansion factor} \dd This is a mechanism for changing the type of grids generated by the program. Some people prefer a grid containing a few large rectangles to one containing many small ones. So you can ask Rectangles to essentially generate a \e{smaller} grid than the size you specified, and then to expand it by adding rows and columns. \lcont{ The default expansion factor of zero means that Rectangles will simply generate a grid of the size you ask for, and do nothing further. If you set an expansion factor of (say) 0.5, it means that each dimension of the grid will be expanded to half again as big after generation. In other words, the initial grid will be 2/3 the size in each dimension, and will be expanded to its full size without adding any more rectangles. Setting an expansion factor of around 0.5 tends to make the game more difficult, and also (in my experience) rewards a less deductive and more intuitive playing style. If you set it \e{too} high, though, the game simply cannot generate more than a few rectangles to cover the entire grid, and the game becomes trivial. } \dt \e{Ensure unique solution} \dd Normally, Rectangles will make sure that the puzzles it presents have only one solution. Puzzles with ambiguous sections can be more difficult and more subtle, so if you like you can turn off this feature and risk having ambiguous puzzles. Also, finding \e{all} the possible solutions can be an additional challenge for an advanced player. Turning off this option can also speed up puzzle generation. \C{netslide} \i{Netslide} \cfg{winhelp-topic}{games.netslide} This game combines the grid generation of Net (see \k{net}) with the movement of Sixteen (see \k{sixteen}): you have a Net grid, but instead of rotating tiles back into place you have to slide them into place by moving a whole row at a time. As in Sixteen, \I{controls, for Netslide}control is with the mouse or cursor keys. See \k{sixteen-controls}. \I{parameters, for Netslide}The available game parameters have similar meanings to those in Net (see \k{net-params}) and Sixteen (see \k{sixteen-params}). Netslide was contributed to this collection by Richard Boulton. \C{pattern} \i{Pattern} \cfg{winhelp-topic}{games.pattern} You have a grid of squares, which must all be filled in either black or white. Beside each row of the grid are listed the lengths of the runs of black squares on that row; above each column are listed the lengths of the runs of black squares in that column. Your aim is to fill in the entire grid black or white. I first saw this puzzle form around 1995, under the name \q{\i{nonograms}}. I've seen it in various places since then, under different names. Normally, puzzles of this type turn out to be a meaningful picture of something once you've solved them. However, since this version generates the puzzles automatically, they will just look like random groupings of squares. (One user has suggested that this is actually a \e{good} thing, since it prevents you from guessing the colour of squares based on the picture, and forces you to use logic instead.) The advantage, though, is that you never run out of them. \H{pattern-controls} \I{controls, for Pattern}Pattern controls This game is played with the mouse. Left-click in a square to colour it black. Right-click to colour it white. If you make a mistake, you can middle-click, or hold down Shift while clicking with any button, to colour the square in the default grey (meaning \q{undecided}) again. You can click and drag with the left or right mouse button to colour a vertical or horizontal line of squares black or white at a time (respectively). If you click and drag with the middle button, or with Shift held down, you can colour a whole rectangle of squares grey. You can also move around the grid with the cursor keys. Pressing the return key will cycle the current cell through empty, then black, then white, then empty, and the space bar does the same cycle in reverse. (All the actions described in \k{common-actions} are also available.) \H{pattern-parameters} \I{parameters, for Pattern}Pattern parameters The only options available from the \q{Custom...} option on the \q{Type} menu are \e{Width} and \e{Height}, which are self-explanatory. \C{solo} \i{Solo} \cfg{winhelp-topic}{games.solo} You have a square grid, which is divided into as many equally sized sub-blocks as the grid has rows. Each square must be filled in with a digit from 1 to the size of the grid, in such a way that \b every row contains only one occurrence of each digit \b every column contains only one occurrence of each digit \b every block contains only one occurrence of each digit. \b (optionally, by default off) each of the square's two main diagonals contains only one occurrence of each digit. You are given some of the numbers as clues; your aim is to place the rest of the numbers correctly. Under the default settings, the sub-blocks are square or rectangular. The default puzzle size is 3\by\.3 (a 9\by\.9 actual grid, divided into nine 3\by\.3 blocks). You can also select sizes with rectangular blocks instead of square ones, such as 2\by\.3 (a 6\by\.6 grid divided into six 3\by\.2 blocks). Alternatively, you can select \q{jigsaw} mode, in which the sub-blocks are arbitrary shapes which differ between individual puzzles. Another available mode is \q{killer}. In this mode, clues are not given in the form of filled-in squares; instead, the grid is divided into \q{cages} by coloured lines, and for each cage the game tells you what the sum of all the digits in that cage should be. Also, no digit may appear more than once within a cage, even if the cage crosses the boundaries of existing regions. If you select a puzzle size which requires more than 9 digits, the additional digits will be letters of the alphabet. For example, if you select 3\by\.4 then the digits which go in your grid will be 1 to 9, plus \cq{a}, \cq{b} and \cq{c}. This cannot be selected for killer puzzles. I first saw this puzzle in \i{Nikoli} \k{nikoli-solo}, although it's also been popularised by various newspapers under the name \q{Sudoku} or \q{Su Doku}. Howard Garns is considered the inventor of the modern form of the puzzle, and it was first published in \e{Dell Pencil Puzzles and Word Games}. A more elaborate treatment of the history of the puzzle can be found on Wikipedia \k{wikipedia-solo}. \B{nikoli-solo} \W{http://www.nikoli.co.jp/puzzles/1/index_text-e.htm}\cw{http://www.nikoli.co.jp/puzzles/1/index_text-e.htm} \B{wikipedia-solo} \W{http://en.wikipedia.org/wiki/Sudoku}\cw{http://en.wikipedia.org/wiki/Sudoku} \H{solo-controls} \I{controls, for Solo}Solo controls To play Solo, simply click the mouse in any empty square and then type a digit or letter on the keyboard to fill that square. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature). If you \e{right}-click in a square and then type a number, that number will be entered in the square as a \q{pencil mark}. You can have pencil marks for multiple numbers in the same square. Squares containing filled-in numbers cannot also contain pencil marks. The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular number, or you can use them as lists of the possible numbers in a given square, or anything else you feel like. To erase a single pencil mark, right-click in the square and type the same number again. All pencil marks in a square are erased when you left-click and type a number, or when you left-click and press space. Right-clicking and pressing space will also erase pencil marks. Alternatively, use the cursor keys to move the mark around the grid. Pressing the return key toggles the mark (from a normal mark to a pencil mark), and typing a number in is entered in the square in the appropriate way; typing in a 0 or using the space bar will clear a filled square. (All the actions described in \k{common-actions} are also available.) \H{solo-parameters} \I{parameters, for Solo}Solo parameters Solo allows you to configure two separate dimensions of the puzzle grid on the \q{Type} menu: the number of columns, and the number of rows, into which the main grid is divided. (The size of a block is the inverse of this: for example, if you select 2 columns and 3 rows, each actual block will have 3 columns and 2 rows.) If you tick the \q{X} checkbox, Solo will apply the optional extra constraint that the two main diagonals of the grid also contain one of every digit. (This is sometimes known as \q{Sudoku-X} in newspapers.) In this mode, the squares on the two main diagonals will be shaded slightly so that you know it's enabled. If you tick the \q{Jigsaw} checkbox, Solo will generate randomly shaped sub-blocks. In this mode, the actual grid size will be taken to be the product of the numbers entered in the \q{Columns} and \q{Rows} boxes. There is no reason why you have to enter a number greater than 1 in both boxes; Jigsaw mode has no constraint on the grid size, and it can even be a prime number if you feel like it. If you tick the \q{Killer} checkbox, Solo will generate a set of of cages, which are randomly shaped and drawn in an outline of a different colour. Each of these regions contains a smaller clue which shows the digit sum of all the squares in this region. You can also configure the type of symmetry shown in the generated puzzles. More symmetry makes the puzzles look prettier but may also make them easier, since the symmetry constraints can force more clues than necessary to be present. Completely asymmetric puzzles have the freedom to contain as few clues as possible. Finally, you can configure the difficulty of the generated puzzles. Difficulty levels are judged by the complexity of the techniques of deduction required to solve the puzzle: each level requires a mode of reasoning which was not necessary in the previous one. In particular, on difficulty levels \q{Trivial} and \q{Basic} there will be a square you can fill in with a single number at all times, whereas at \q{Intermediate} level and beyond you will have to make partial deductions about the \e{set} of squares a number could be in (or the set of numbers that could be in a square). \#{Advanced, Extreme?} At \q{Unreasonable} level, even this is not enough, and you will eventually have to make a guess, and then backtrack if it turns out to be wrong. Generating difficult puzzles is itself difficult: if you select one of the higher difficulty levels, Solo may have to make many attempts at generating a puzzle before it finds one hard enough for you. Be prepared to wait, especially if you have also configured a large puzzle size. \C{mines} \i{Mines} \cfg{winhelp-topic}{games.mines} You have a grid of covered squares, some of which contain mines, but you don't know which. Your job is to uncover every square which does \e{not} contain a mine. If you uncover a square containing a mine, you lose. If you uncover a square which does not contain a mine, you are told how many mines are contained within the eight surrounding squares. This game needs no introduction; popularised by Windows, it is perhaps the single best known desktop puzzle game in existence. This version of it has an unusual property. By default, it will generate its mine positions in such a way as to ensure that you never need to \e{guess} where a mine is: you will always be able to deduce it somehow. So you will never, as can happen in other versions, get to the last four squares and discover that there are two mines left but you have no way of knowing for sure where they are. \H{mines-controls} \I{controls, for Mines}Mines controls This game is played with the mouse. If you left-click in a covered square, it will be uncovered. If you right-click in a covered square, it will place a flag which indicates that the square is believed to be a mine. Left-clicking in a marked square will not uncover it, for safety. You can right-click again to remove a mark placed in error. If you left-click in an \e{uncovered} square, it will \q{clear around} the square. This means: if the square has exactly as many flags surrounding it as it should have mines, then all the covered squares next to it which are \e{not} flagged will be uncovered. So once you think you know the location of all the mines around a square, you can use this function as a shortcut to avoid having to click on each of the remaining squares one by one. If you uncover a square which has \e{no} mines in the surrounding eight squares, then it is obviously safe to uncover those squares in turn, and so on if any of them also has no surrounding mines. This will be done for you automatically; so sometimes when you uncover a square, a whole new area will open up to be explored. You can also use the cursor keys to move around the minefield. Pressing the return key in a covered square uncovers it, and in an uncovered square will clear around it (so it acts as the left button), pressing the space bar in a covered square will place a flag (similarly, it acts as the right button). All the actions described in \k{common-actions} are also available. Even Undo is available, although you might consider it cheating to use it. If you step on a mine, the program will only reveal the mine in question (unlike most other implementations, which reveal all of them). You can then Undo your fatal move and continue playing if you like. The program will track the number of times you died (and Undo will not reduce that counter), so when you get to the end of the game you know whether or not you did it without making any errors. (If you really want to know the full layout of the grid, which other implementations will show you after you die, you can always use the Solve menu option.) \H{mines-parameters} \I{parameters, for Mines}Mines parameters The options available from the \q{Custom...} option on the \q{Type} menu are: \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{Mines} \dd Number of mines in the grid. You can enter this as an absolute mine count, or alternatively you can put a \cw{%} sign on the end in which case the game will arrange for that proportion of the squares in the grid to be mines. \lcont{ Beware of setting the mine count too high. At very high densities, the program may spend forever searching for a solvable grid. } \dt \e{Ensure solubility} \dd When this option is enabled (as it is by default), Mines will ensure that the entire grid can be fully deduced starting from the initial open space. If you prefer the riskier grids generated by other implementations, you can switch off this option. \C{samegame} \i{Same Game} \cfg{winhelp-topic}{games.samegame} You have a grid of coloured squares, which you have to clear by highlighting contiguous regions of more than one coloured square; the larger the region you highlight, the more points you get (and the faster you clear the arena). If you clear the grid you win. If you end up with nothing but single squares (i.e., there are no more clickable regions left) you lose. Removing a region causes the rest of the grid to shuffle up: blocks that are suspended will fall down (first), and then empty columns are filled from the right. Same Game was contributed to this collection by James Harvey. \H{samegame-controls} \i{Same Game controls} \IM{Same Game controls} controls, for Same Game \IM{Same Game controls} keys, for Same Game \IM{Same Game controls} shortcuts (keyboard), for Same Game This game can be played with either the keyboard or the mouse. If you left-click an unselected region, it becomes selected (possibly clearing the current selection). If you left-click the selected region, it will be removed (and the rest of the grid shuffled immediately). If you right-click the selected region, it will be unselected. The cursor keys move a cursor around the grid. Pressing the Space or Enter keys while the cursor is in an unselected region selects it; pressing Space or Enter again removes it as above. (All the actions described in \k{common-actions} are also available.) \H{samegame-parameters} \I{parameters, for Same Game}Same Game parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{No. of colours} \dd Number of different colours used to fill the grid; the more colours, the fewer large regions of colour and thus the more difficult it is to successfully clear the grid. \dt \e{Scoring system} \dd Controls the precise mechanism used for scoring. With the default system, \q{(n-2)^2}, only regions of three squares or more will score any points at all. With the alternative \q{(n-1)^2} system, regions of two squares score a point each, and larger regions score relatively more points. \dt \e{Ensure solubility} \dd If this option is ticked (the default state), generated grids will be guaranteed to have at least one solution. \lcont{ If you turn it off, the game generator will not try to guarantee soluble grids; it will, however, still ensure that there are at least 2 squares of each colour on the grid at the start (since a grid with exactly one square of a given colour is \e{definitely} insoluble). Grids generated with this option disabled may contain more large areas of contiguous colour, leading to opportunities for higher scores; they can also take less time to generate. } \C{flip} \i{Flip} \cfg{winhelp-topic}{games.flip} You have a grid of squares, some light and some dark. Your aim is to light all the squares up at the same time. You can choose any square and flip its state from light to dark or dark to light, but when you do so, other squares around it change state as well. Each square contains a small diagram showing which other squares change when you flip it. \H{flip-controls} \i{Flip controls} \IM{Flip controls} controls, for Flip \IM{Flip controls} keys, for Flip \IM{Flip controls} shortcuts (keyboard), for Flip This game can be played with either the keyboard or the mouse. Left-click in a square to flip it and its associated squares, or use the cursor keys to choose a square and the space bar or Enter key to flip. If you use the \q{Solve} function on this game, it will mark some of the squares in red. If you click once in every square with a red mark, the game should be solved. (If you click in a square \e{without} a red mark, a red mark will appear in it to indicate that you will need to reverse that operation to reach the solution.) (All the actions described in \k{common-actions} are also available.) \H{flip-parameters} \I{parameters, for flip}Flip parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{Shape type} \dd This control determines the shape of the region which is flipped by clicking in any given square. The default setting, \q{Crosses}, causes every square to flip itself and its four immediate neighbours (or three or two if it's at an edge or corner). The other setting, \q{Random}, causes a random shape to be chosen for every square, so the game is different every time. \C{guess} \i{Guess} \cfg{winhelp-topic}{games.guess} You have a set of coloured pegs, and have to reproduce a predetermined sequence of them (chosen by the computer) within a certain number of guesses. Each guess gets marked with the number of correctly-coloured pegs in the correct places (in black), and also the number of correctly-coloured pegs in the wrong places (in white). This game is also known (and marketed, by Hasbro, mainly) as a board game \q{\i{Mastermind}}, with 6 colours, 4 pegs per row, and 10 guesses. However, this version allows custom settings of number of colours (up to 10), number of pegs per row, and number of guesses. Guess was contributed to this collection by James Harvey. \H{guess-controls} \i{Guess controls} \IM{Guess controls} controls, for Guess \IM{Guess controls} keys, for Guess \IM{Guess controls} shortcuts (keyboard), for Guess This game can be played with either the keyboard or the mouse. With the mouse, drag a coloured peg from the tray on the left-hand side to its required position in the current guess; pegs may also be dragged from current and past guesses to copy them elsewhere. To remove a peg, drag it off its current position to somewhere invalid. Right-clicking in the current guess adds a \q{hold} marker; pegs that have hold markers will be automatically added to the next guess after marking. Alternatively, with the keyboard, the up and down cursor keys can be used to select a peg colour, the left and right keys to select a peg position, and the space bar or Enter key to place a peg of the selected colour in the chosen position. \q{D} or Backspace removes a peg, and \q{H} adds a hold marker. When the guess is complete, the smaller feedback pegs will be highlighted; clicking on these (or moving the peg cursor to them with the arrow keys and pressing the space bar or Enter key) will mark the current guess, copy any held pegs to the next guess, and move the \q{current guess} marker. If you correctly position all the pegs the solution will be displayed below; if you run out of guesses (or select \q{Solve...}) the solution will also be revealed. (All the actions described in \k{common-actions} are also available.) \H{guess-parameters} \I{parameters, for Guess}Guess parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. The default game matches the parameters for the board game \q{Mastermind}. \dt \e{Colours} \dd Number of colours the solution is chosen from; from 2 to 10 (more is harder). \dt \e{Pegs per guess} \dd Number of pegs per guess (more is harder). \dt \e{Guesses} \dd Number of guesses you have to find the solution in (fewer is harder). \dt \e{Allow blanks} \dd Allows blank pegs to be given as part of a guess (makes it easier, because you know that those will never be counted as part of the solution). This is turned off by default. Note that this doesn't allow blank pegs in the solution; if you really wanted that, use one extra colour. \dt \e{Allow duplicates} \dd Allows the solution (and the guesses) to contain colours more than once; this increases the search space (making things harder), and is turned on by default. \C{pegs} \i{Pegs} \cfg{winhelp-topic}{games.pegs} A number of pegs are placed in holes on a board. You can remove a peg by jumping an adjacent peg over it (horizontally or vertically) to a vacant hole on the other side. Your aim is to remove all but one of the pegs initially present. This game, best known as \I{Solitaire, Peg}\q{Peg Solitaire}, is possibly one of the oldest puzzle games still commonly known. \H{pegs-controls} \i{Pegs controls} \IM{Pegs controls} controls, for Pegs To move a peg, drag it with the mouse from its current position to its final position. If the final position is exactly two holes away from the initial position, is currently unoccupied by a peg, and there is a peg in the intervening square, the move will be permitted and the intervening peg will be removed. Vacant spaces which you can move a peg into are marked with holes. A space with no peg and no hole is not available for moving at all: it is an obstacle which you must work around. You can also use the cursor keys to move a position indicator around the board. Pressing the return key while over a peg, followed by a cursor key, will jump the peg in that direction (if that is a legal move). (All the actions described in \k{common-actions} are also available.) \H{pegs-parameters} \I{parameters, for Pegs}Pegs parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in holes. \dt \e{Board type} \dd Controls whether you are given a board of a standard shape or a randomly generated shape. The two standard shapes currently supported are \q{Cross} and \q{Octagon} (also commonly known as the English and European traditional board layouts respectively). Selecting \q{Random} will give you a different board shape every time (but always one that is known to have a solution). \C{dominosa} \i{Dominosa} \cfg{winhelp-topic}{games.dominosa} A normal set of dominoes \dash that is, one instance of every (unordered) pair of numbers from 0 to 6 \dash has been arranged irregularly into a rectangle; then the number in each square has been written down and the dominoes themselves removed. Your task is to reconstruct the pattern by arranging the set of dominoes to match the provided array of numbers. This puzzle is widely credited to O. S. Adler, and takes part of its name from those initials. \H{dominosa-controls} \i{Dominosa controls} \IM{Dominosa controls} controls, for Dominosa Left-clicking between any two adjacent numbers places a domino covering them, or removes one if it is already present. Trying to place a domino which overlaps existing dominoes will remove the ones it overlaps. Right-clicking between two adjacent numbers draws a line between them, which you can use to remind yourself that you know those two numbers are \e{not} covered by a single domino. Right-clicking again removes the line. You can also use the cursor keys to move a cursor around the grid. When the cursor is half way between two adjacent numbers, pressing the return key will place a domino covering those numbers, or pressing the space bar will lay a line between the two squares. Repeating either action removes the domino or line. (All the actions described in \k{common-actions} are also available.) \H{dominosa-parameters} \I{parameters, for Dominosa}Dominosa parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Maximum number on dominoes} \dd Controls the size of the puzzle, by controlling the size of the set of dominoes used to make it. Dominoes with numbers going up to N will give rise to an (N+2) \by (N+1) rectangle; so, in particular, the default value of 6 gives an 8\by\.7 grid. \dt \e{Ensure unique solution} \dd Normally, Dominosa will make sure that the puzzles it presents have only one solution. Puzzles with ambiguous sections can be more difficult and sometimes more subtle, so if you like you can turn off this feature. Also, finding \e{all} the possible solutions can be an additional challenge for an advanced player. Turning off this option can also speed up puzzle generation. \C{untangle} \i{Untangle} \cfg{winhelp-topic}{games.untangle} You are given a number of points, some of which have lines drawn between them. You can move the points about arbitrarily; your aim is to position the points so that no line crosses another. I originally saw this in the form of a Flash game called \i{Planarity} \k{Planarity}, written by John Tantalo. \B{Planarity} \W{http://home.cwru.edu/~jnt5/Planarity}\cw{http://home.cwru.edu/~jnt5/Planarity} \H{untangle-controls} \i{Untangle controls} \IM{Untangle controls} controls, for Untangle To move a point, click on it with the left mouse button and drag it into a new position. (All the actions described in \k{common-actions} are also available.) \H{untangle-parameters} \I{parameters, for Untangle}Untangle parameters There is only one parameter available from the \q{Custom...} option on the \q{Type} menu: \dt \e{Number of points} \dd Controls the size of the puzzle, by specifying the number of points in the generated graph. \C{blackbox} \i{Black Box} \cfg{winhelp-topic}{games.blackbox} A number of balls are hidden in a rectangular arena. You have to deduce the positions of the balls by firing lasers positioned at the edges of the arena and observing how their beams are deflected. Beams will travel straight from their origin until they hit the opposite side of the arena (at which point they emerge), unless affected by balls in one of the following ways: \b A beam that hits a ball head-on is absorbed and will never re-emerge. This includes beams that meet a ball on the first rank of the arena. \b A beam with a ball to its front-left square gets deflected 90 degrees to the right. \b A beam with a ball to its front-right square gets similarly deflected to the left. \b A beam that would re-emerge from its entry location is considered to be \q{reflected}. \b A beam which would get deflected before entering the arena by a ball to the front-left or front-right of its entry point is also considered to be \q{reflected}. Beams that are reflected appear as a \q{R}; beams that hit balls head-on appear as \q{H}. Otherwise, a number appears at the firing point and the location where the beam emerges (this number is unique to that shot). You can place guesses as to the location of the balls, based on the entry and exit patterns of the beams; once you have placed enough balls a button appears enabling you to have your guesses checked. Here is a diagram showing how the positions of balls can create each of the beam behaviours shown above: \c 1RHR---- \c |..O.O...| \c 2........3 \c |........| \c |........| \c 3........| \c |......O.| \c H........| \c |.....O..| \c 12-RH--- As shown, it is possible for a beam to receive multiple reflections before re-emerging (see turn 3). Similarly, a beam may be reflected (possibly more than once) before receiving a hit (the \q{H} on the left side of the example). Note that any layout with more than 4 balls may have a non-unique solution. The following diagram illustrates this; if you know the board contains 5 balls, it is impossible to determine where the fifth ball is (possible positions marked with an \cw{x}): \c -------- \c |........| \c |........| \c |..O..O..| \c |...xx...| \c |...xx...| \c |..O..O..| \c |........| \c |........| \c -------- For this reason, when you have your guesses checked, the game will check that your solution \e{produces the same results} as the computer's, rather than that your solution is identical to the computer's. So in the above example, you could put the fifth ball at \e{any} of the locations marked with an \cw{x}, and you would still win. Black Box was contributed to this collection by James Harvey. \H{blackbox-controls} \i{Black Box controls} \IM{Black Box controls} controls, for Black Box \IM{Black Box controls} keys, for Black Box \IM{Black Box controls} shortcuts (keyboard), for Black Box To fire a laser beam, left-click in a square around the edge of the arena. The results will be displayed immediately. Clicking or holding the left button on one of these squares will highlight the current go (or a previous go) to confirm the exit point for that laser, if applicable. To guess the location of a ball, left-click within the arena and a black circle will appear marking the guess; click again to remove the guessed ball. Locations in the arena may be locked against modification by right-clicking; whole rows and columns may be similarly locked by right-clicking in the laser square above/below that column, or to the left/right of that row. The cursor keys may also be used to move around the grid. Pressing the Enter key will fire a laser or add a new ball-location guess, and pressing Space will lock a cell, row, or column. When an appropriate number of balls have been guessed, a button will appear at the top-left corner of the grid; clicking that (with mouse or cursor) will check your guesses. If you click the \q{check} button and your guesses are not correct, the game will show you the minimum information necessary to demonstrate this to you, so you can try again. If your ball positions are not consistent with the beam paths you already know about, one beam path will be circled to indicate that it proves you wrong. If your positions match all the existing beam paths but are still wrong, one new beam path will be revealed (written in red) which is not consistent with your current guesses. If you decide to give up completely, you can select Solve to reveal the actual ball positions. At this point, correctly-placed balls will be displayed as filled black circles, incorrectly-placed balls as filled black circles with red crosses, and missing balls as filled red circles. In addition, a red circle marks any laser you had already fired which is not consistent with your ball layout (just as when you press the \q{check} button), and red text marks any laser you \e{could} have fired in order to distinguish your ball layout from the correct one. (All the actions described in \k{common-actions} are also available.) \H{blackbox-parameters} \I{parameters, for Black Box}Black Box parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. There are 2 \by \e{Width} \by \e{Height} lasers per grid, two per row and two per column. \dt \e{No. of balls} \dd Number of balls to place in the grid. This can be a single number, or a range (separated with a hyphen, like \q{2-6}), and determines the number of balls to place on the grid. The \q{reveal} button is only enabled if you have guessed an appropriate number of balls; a guess using a different number to the original solution is still acceptable, if all the beam inputs and outputs match. \C{slant} \i{Slant} \cfg{winhelp-topic}{games.slant} You have a grid of squares. Your aim is to draw a diagonal line through each square, and choose which way each line slants so that the following conditions are met: \b The diagonal lines never form a loop. \b Any point with a circled number has precisely that many lines meeting at it. (Thus, a 4 is the centre of a cross shape, whereas a zero is the centre of a diamond shape \dash or rather, a partial diamond shape, because a zero can never appear in the middle of the grid because that would immediately cause a loop.) Credit for this puzzle goes to \i{Nikoli} \k{nikoli-slant}. \B{nikoli-slant} \W{http://www.nikoli.co.jp/puzzles/39/index.htm}\cw{http://www.nikoli.co.jp/puzzles/39/index.htm} (in Japanese) \H{slant-controls} \i{Slant controls} \IM{Slant controls} controls, for Slant Left-clicking in a blank square will place a \cw{\\} in it (a line leaning to the left, i.e. running from the top left of the square to the bottom right). Right-clicking in a blank square will place a \cw{/} in it (leaning to the right, running from top right to bottom left). Continuing to click either button will cycle between the three possible square contents. Thus, if you left-click repeatedly in a blank square it will change from blank to \cw{\\} to \cw{/} back to blank, and if you right-click repeatedly the square will change from blank to \cw{/} to \cw{\\} back to blank. (Therefore, you can play the game entirely with one button if you need to.) You can also use the cursor keys to move around the grid. Pressing the return or space keys will place a \cw{\\} or a \cw{/}, respectively, and will then cycle them as above. (All the actions described in \k{common-actions} are also available.) \H{slant-parameters} \I{parameters, for Slant}Slant parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{Difficulty} \dd Controls the difficulty of the generated puzzle. At Hard level, you are required to do deductions based on knowledge of \e{relationships} between squares rather than always being able to deduce the exact contents of one square at a time. (For example, you might know that two squares slant in the same direction, even if you don't yet know what that direction is, and this might enable you to deduce something about still other squares.) Even at Hard level, guesswork and backtracking should never be necessary. \C{lightup} \i{Light Up} \cfg{winhelp-topic}{games.lightup} You have a grid of squares. Some are filled in black; some of the black squares are numbered. Your aim is to \q{light up} all the empty squares by placing light bulbs in some of them. Each light bulb illuminates the square it is on, plus all squares in line with it horizontally or vertically unless a black square is blocking the way. To win the game, you must satisfy the following conditions: \b All non-black squares are lit. \b No light is lit by another light. \b All numbered black squares have exactly that number of lights adjacent to them (in the four squares above, below, and to the side). Non-numbered black squares may have any number of lights adjacent to them. Credit for this puzzle goes to \i{Nikoli} \k{nikoli-lightup}. Light Up was contributed to this collection by James Harvey. \B{nikoli-lightup} \W{http://www.nikoli.co.jp/puzzles/32/index-e.htm}\cw{http://www.nikoli.co.jp/puzzles/32/index-e.htm} (beware of Flash) \H{lightup-controls} \i{Light Up controls} \IM{Light Up controls} controls, for Light Up Left-clicking in a non-black square will toggle the presence of a light in that square. Right-clicking in a non-black square toggles a mark there to aid solving; it can be used to highlight squares that cannot be lit, for example. You may not place a light in a marked square, nor place a mark in a lit square. The game will highlight obvious errors in red. Lights lit by other lights are highlighted in this way, as are numbered squares which do not (or cannot) have the right number of lights next to them. Thus, the grid is solved when all non-black squares have yellow highlights and there are no red lights. (All the actions described in \k{common-actions} are also available.) \H{lightup-parameters} \I{parameters, for Light Up}Light Up parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{%age of black squares} \dd Rough percentage of black squares in the grid. \lcont{ This is a hint rather than an instruction. If the grid generator is unable to generate a puzzle to this precise specification, it will increase the proportion of black squares until it can. } \dt \e{Symmetry} \dd Allows you to specify the required symmetry of the black squares in the grid. (This does not affect the difficulty of the puzzles noticeably.) \dt \e{Difficulty} \dd \q{Easy} means that the puzzles should be soluble without backtracking or guessing, \q{Hard} means that some guesses will probably be necessary. \C{map} \i{Map} \cfg{winhelp-topic}{games.map} You are given a map consisting of a number of regions. Your task is to colour each region with one of four colours, in such a way that no two regions sharing a boundary have the same colour. You are provided with some regions already coloured, sufficient to make the remainder of the solution unique. Only regions which share a length of border are required to be different colours. Two regions which meet at only one \e{point} (i.e. are diagonally separated) may be the same colour. I believe this puzzle is original; I've never seen an implementation of it anywhere else. The concept of a \i{four-colouring} puzzle was suggested by Owen Dunn; credit must also go to Nikoli and to Verity Allan for inspiring the train of thought that led to me realising Owen's suggestion was a viable puzzle. Thanks also to Gareth Taylor for many detailed suggestions. \H{map-controls} \i{Map controls} \IM{Map controls} controls, for Map To colour a region, click the left mouse button on an existing region of the desired colour and drag that colour into the new region. (The program will always ensure the starting puzzle has at least one region of each colour, so that this is always possible!) If you need to clear a region, you can drag from an empty region, or from the puzzle boundary if there are no empty regions left. Dragging a colour using the \e{right} mouse button will stipple the region in that colour, which you can use as a note to yourself that you think the region \e{might} be that colour. A region can contain stipples in multiple colours at once. (This is often useful at the harder difficulty levels.) You can also use the cursor keys to move around the map: the colour of the cursor indicates the position of the colour you would drag (which is not obvious if you're on a region's boundary, since it depends on the direction from which you approached the boundary). Pressing the return key starts a drag of that colour, as above, which you control with the cursor keys; pressing the return key again finishes the drag. The space bar can be used similarly to create a stippled region. Double-pressing the return key (without moving the cursor) will clear the region, as a drag from an empty region does: this is useful with the cursor mode if you have filled the entire map in but need to correct the layout. If you press L during play, the game will toggle display of a number in each region of the map. This is useful if you want to discuss a particular puzzle instance with a friend \dash having an unambiguous name for each region is much easier than trying to refer to them all by names such as \q{the one down and right of the brown one on the top border}. (All the actions described in \k{common-actions} are also available.) \H{map-parameters} \I{parameters, for Map}Map parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{Regions} \dd Number of regions in the generated map. \dt \e{Difficulty} \dd In \q{Easy} mode, there should always be at least one region whose colour can be determined trivially. In \q{Normal} and \q{Hard} modes, you will have to use increasingly complex logic to deduce the colour of some regions. However, it will always be possible without having to guess or backtrack. \lcont{ In \q{Unreasonable} mode, the program will feel free to generate puzzles which are as hard as it can possibly make them: the only constraint is that they should still have a unique solution. Solving Unreasonable puzzles may require guessing and backtracking. } \C{loopy} \i{Loopy} \cfg{winhelp-topic}{games.loopy} You are given a grid of dots, marked with yellow lines to indicate which dots you are allowed to connect directly together. Your aim is to use some subset of those yellow lines to draw a single unbroken loop from dot to dot within the grid. Some of the spaces between the lines contain numbers. These numbers indicate how many of the lines around that space form part of the loop. The loop you draw must correctly satisfy all of these clues to be considered a correct solution. In the default mode, the dots are arranged in a grid of squares; however, you can also play on triangular or hexagonal grids, or even more exotic ones. Credit for the basic puzzle idea goes to \i{Nikoli} \k{nikoli-loopy}. Loopy was originally contributed to this collection by Mike Pinna, and subsequently enhanced to handle various types of non-square grid by Lambros Lambrou. \B{nikoli-loopy} \W{http://www.nikoli.co.jp/puzzles/3/index-e.htm}\cw{http://www.nikoli.co.jp/puzzles/3/index-e.htm} (beware of Flash) \H{loopy-controls} \i{Loopy controls} \IM{Loopy controls} controls, for Loopy Click the left mouse button on a yellow line to turn it black, indicating that you think it is part of the loop. Click again to turn the line yellow again (meaning you aren't sure yet). If you are sure that a particular line segment is \e{not} part of the loop, you can click the right mouse button to remove it completely. Again, clicking a second time will turn the line back to yellow. (All the actions described in \k{common-actions} are also available.) \H{loopy-parameters} \I{parameters, for Loopy}Loopy parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid, measured in number of regions across and down. For square grids, it's clear how this is counted; for other types of grid you may have to think a bit to see how the dimensions are measured. \dt \e{Grid type} \dd Allows you to choose between a selection of types of tiling. Some have all the faces the same but may have multiple different types of vertex (e.g. the \e{Cairo} or \e{Kites} mode); others have all the vertices the same but may have different types of face (e.g. the \e{Great Hexagonal}). The square, triangular and honeycomb grids are fully regular, and have all their vertices \e{and} faces the same; this makes them the least confusing to play. \dt \e{Difficulty} \dd Controls the difficulty of the generated puzzle. \#{FIXME: what distinguishes Easy, Medium, and Hard? In particular, when are backtracking/guesswork required, if ever?} \C{inertia} \i{Inertia} \cfg{winhelp-topic}{games.inertia} You are a small green ball sitting in a grid full of obstacles. Your aim is to collect all the gems without running into any mines. You can move the ball in any orthogonal \e{or diagonal} direction. Once the ball starts moving, it will continue until something stops it. A wall directly in its path will stop it (but if it is moving diagonally, it will move through a diagonal gap between two other walls without stopping). Also, some of the squares are \q{stops}; when the ball moves on to a stop, it will stop moving no matter what direction it was going in. Gems do \e{not} stop the ball; it picks them up and keeps on going. Running into a mine is fatal. Even if you picked up the last gem in the same move which then hit a mine, the game will count you as dead rather than victorious. This game was originally implemented for Windows by Ben Olmstead \k{bem}, who was kind enough to release his source code on request so that it could be re-implemented for this collection. \B{bem} \W{http://xn13.com/}\cw{http://xn13.com/} \H{inertia-controls} \i{Inertia controls} \IM{Inertia controls} controls, for Inertia \IM{Inertia controls} keys, for Inertia \IM{Inertia controls} shortcuts (keyboard), for Inertia You can move the ball in any of the eight directions using the numeric keypad. Alternatively, if you click the left mouse button on the grid, the ball will begin a move in the general direction of where you clicked. If you use the \q{Solve} function on this game, the program will compute a path through the grid which collects all the remaining gems and returns to the current position. A hint arrow will appear on the ball indicating the direction in which you should move to begin on this path. If you then move in that direction, the arrow will update to indicate the next direction on the path. You can also press Space to automatically move in the direction of the hint arrow. If you move in a different direction from the one shown by the arrow, the hint arrows will stop appearing because you have strayed from the provided path; you can then use \q{Solve} again to generate a new path if you want to. All the actions described in \k{common-actions} are also available. In particular, if you do run into a mine and die, you can use the Undo function and resume playing from before the fatal move. The game will keep track of the number of times you have done this. \H{inertia-parameters} \I{parameters, for Inertia}Inertia parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \C{tents} \i{Tents} \cfg{winhelp-topic}{games.tents} You have a grid of squares, some of which contain trees. Your aim is to place tents in some of the remaining squares, in such a way that the following conditions are met: \b There are exactly as many tents as trees. \b The tents and trees can be matched up in such a way that each tent is directly adjacent (horizontally or vertically, but not diagonally) to its own tree. However, a tent may be adjacent to other trees as well as its own. \b No two tents are adjacent horizontally, vertically \e{or diagonally}. \b The number of tents in each row, and in each column, matches the numbers given round the sides of the grid. This puzzle can be found in several places on the Internet, and was brought to my attention by e-mail. I don't know who I should credit for inventing it. \H{tents-controls} \i{Tents controls} \IM{Tents controls} controls, for Tents Left-clicking in a blank square will place a tent in it. Right-clicking in a blank square will colour it green, indicating that you are sure it \e{isn't} a tent. Clicking either button in an occupied square will clear it. If you \e{drag} with the right button along a row or column, every blank square in the region you cover will be turned green, and no other squares will be affected. (This is useful for clearing the remainder of a row once you have placed all its tents.) You can also use the cursor keys to move around the grid. Pressing the return key over an empty square will place a tent, and pressing the space bar over an empty square will colour it green; either key will clear an occupied square. (All the actions described in \k{common-actions} are also available.) \H{tents-parameters} \I{parameters, for Tents}Tents parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{Difficulty} \dd Controls the difficulty of the generated puzzle. More difficult puzzles require more complex deductions, but at present none of the available difficulty levels requires guesswork or backtracking. \C{bridges} \i{Bridges} \cfg{winhelp-topic}{games.bridges} You have a set of islands distributed across the playing area. Each island contains a number. Your aim is to connect the islands together with bridges, in such a way that: \b Bridges run horizontally or vertically. \b The number of bridges terminating at any island is equal to the number written in that island. \b Two bridges may run in parallel between the same two islands, but no more than two may do so. \b No bridge crosses another bridge. \b All the islands are connected together. There are some configurable alternative modes, which involve changing the parallel-bridge limit to something other than 2, and introducing the additional constraint that no sequence of bridges may form a loop from one island back to the same island. The rules stated above are the default ones. Credit for this puzzle goes to \i{Nikoli} \k{nikoli-bridges}. Bridges was contributed to this collection by James Harvey. \B{nikoli-bridges} \W{http://www.nikoli.co.jp/puzzles/14/index-e.htm}\cw{http://www.nikoli.co.jp/puzzles/14/index-e.htm} \H{bridges-controls} \i{Bridges controls} \IM{Bridges controls} controls, for Bridges To place a bridge between two islands, click the mouse down on one island and drag it towards the other. You do not need to drag all the way to the other island; you only need to move the mouse far enough for the intended bridge direction to be unambiguous. (So you can keep the mouse near the starting island and conveniently throw bridges out from it in many directions.) Doing this again when a bridge is already present will add another parallel bridge. If there are already as many bridges between the two islands as permitted by the current game rules (i.e. two by default), the same dragging action will remove all of them. If you want to remind yourself that two islands definitely \e{do not} have a bridge between them, you can right-drag between them in the same way to draw a \q{non-bridge} marker. If you think you have finished with an island (i.e. you have placed all its bridges and are confident that they are in the right places), you can mark the island as finished by left-clicking on it. This will highlight it and all the bridges connected to it, and you will be prevented from accidentally modifying any of those bridges in future. Left-clicking again on a highlighted island will unmark it and restore your ability to modify it. You can also use the cursor keys to move around the grid: if possible the cursor will always move orthogonally, otherwise it will move towards the nearest island to the indicated direction. Pressing the return key followed by a cursor key will lay a bridge in that direction (if available); pressing the space bar followed by a cursor key will lay a \q{non-bridge} marker. You can mark an island as finished by pressing the return key twice. Violations of the puzzle rules will be marked in red: \b An island with too many bridges will be highlighted in red. \b An island with too few bridges will be highlighted in red if it is definitely an error (as opposed to merely not being finished yet): if adding enough bridges would involve having to cross another bridge or remove a non-bridge marker, or if the island has been highlighted as complete. \b A group of islands and bridges may be highlighted in red if it is a closed subset of the puzzle with no way to connect it to the rest of the islands. For example, if you directly connect two 1s together with a bridge and they are not the only two islands on the grid, they will light up red to indicate that such a group cannot be contained in any valid solution. \b If you have selected the (non-default) option to disallow loops in the solution, a group of bridges which forms a loop will be highlighted. (All the actions described in \k{common-actions} are also available.) \H{bridges-parameters} \I{parameters, for Bridges}Bridges parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{Difficulty} \dd Difficulty level of puzzle. \dt \e{Allow loops} \dd This is set by default. If cleared, puzzles will be generated in such a way that they are always soluble without creating a loop, and solutions which do involve a loop will be disallowed. \dt \e{Max. bridges per direction} \dd Maximum number of bridges in any particular direction. The default is 2, but you can change it to 1, 3 or 4. In general, fewer is easier. \dt \e{%age of island squares} \dd Gives a rough percentage of islands the generator will try and lay before finishing the puzzle. Certain layouts will not manage to lay enough islands; this is an upper bound. \dt \e{Expansion factor (%age)} \dd The grid generator works by picking an existing island at random (after first creating an initial island somewhere). It then decides on a direction (at random), and then works out how far it could extend before creating another island. This parameter determines how likely it is to extend as far as it can, rather than choosing somewhere closer. High expansion factors usually mean easier puzzles with fewer possible islands; low expansion factors can create lots of tightly-packed islands. \C{unequal} \i{Unequal} \cfg{winhelp-topic}{games.unequal} You have a square grid; each square may contain a digit from 1 to the size of the grid, and some squares have clue signs between them. Your aim is to fully populate the grid with numbers such that: \b Each row contains only one occurrence of each digit \b Each column contains only one occurrence of each digit \b All the clue signs are satisfied. There are two modes for this game, \q{Unequal} and \q{Adjacent}. In \q{Unequal} mode, the clue signs are greater-than symbols indicating one square's value is greater than its neighbour's. In this mode not all clues may be visible, particularly at higher difficulty levels. In \q{Adjacent} mode, the clue signs are bars indicating one square's value is numerically adjacent (i.e. one higher or one lower) than its neighbour. In this mode all clues are always visible: absence of a bar thus means that a square's value is definitely not numerically adjacent to that neighbour's. In \q{Trivial} difficulty level (available via the \q{Custom} game type selector), there are no greater-than signs in \q{Unequal} mode; the puzzle is to solve the \i{Latin square} only. At the time of writing, the \q{Unequal} mode of this puzzle is appearing in the Guardian weekly under the name \q{\i{Futoshiki}}. Unequal was contributed to this collection by James Harvey. \H{unequal-controls} \i{Unequal controls} \IM{Unequal controls} controls, for Unequal Unequal shares much of its control system with Solo. To play Unequal, simply click the mouse in any empty square and then type a digit or letter on the keyboard to fill that square. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature). If you \e{right}-click in a square and then type a number, that number will be entered in the square as a \q{pencil mark}. You can have pencil marks for multiple numbers in the same square. Squares containing filled-in numbers cannot also contain pencil marks. The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular number, or you can use them as lists of the possible numbers in a given square, or anything else you feel like. To erase a single pencil mark, right-click in the square and type the same number again. All pencil marks in a square are erased when you left-click and type a number, or when you left-click and press space. Right-clicking and pressing space will also erase pencil marks. As for Solo, the cursor keys can be used in conjunction with the digit keys to set numbers or pencil marks. You can also use the 'M' key to auto-fill every numeric hint, ready for removal as required, or the 'H' key to do the same but also to remove all obvious hints. Alternatively, use the cursor keys to move the mark around the grid. Pressing the return key toggles the mark (from a normal mark to a pencil mark), and typing a number in is entered in the square in the appropriate way; typing in a 0 or using the space bar will clear a filled square. (All the actions described in \k{common-actions} are also available.) \H{unequal-parameters} \I{parameters, for Unequal}Unequal parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Mode} \dd Mode of the puzzle (\q{Unequal} or \q{Adjacent}) \dt \e{Size (s*s)} \dd Size of grid. \dt \e{Difficulty} \dd Controls the difficulty of the generated puzzle. At Trivial level, there are no greater-than signs; the puzzle is to solve the Latin square only. At Recursive level (only available via the \q{Custom} game type selector) backtracking will be required, but the solution should still be unique. The levels in between require increasingly complex reasoning to avoid having to backtrack. \C{galaxies} \i{Galaxies} \cfg{winhelp-topic}{games.galaxies} You have a rectangular grid containing a number of dots. Your aim is to draw edges along the grid lines which divide the rectangle into regions in such a way that every region is 180\u00b0{-degree} rotationally symmetric, and contains exactly one dot which is located at its centre of symmetry. This puzzle was invented by \i{Nikoli} \k{nikoli-galaxies}, under the name \q{Tentai Show}; its name is commonly translated into English as \q{Spiral Galaxies}. Galaxies was contributed to this collection by James Harvey. \B{nikoli-galaxies} \W{http://www.nikoli.co.jp/en/puzzles/astronomical_show/}\cw{http://www.nikoli.co.jp/en/puzzles/astronomical_show/} \H{galaxies-controls} \i{Galaxies controls} \IM{Galaxies controls} controls, for Galaxies Left-click on any grid line to draw an edge if there isn't one already, or to remove one if there is. When you create a valid region (one which is closed, contains exactly one dot, is 180\u00b0{-degree} symmetric about that dot, and contains no extraneous edges inside it) it will be highlighted automatically; so your aim is to have the whole grid highlighted in that way. During solving, you might know that a particular grid square belongs to a specific dot, but not be sure of where the edges go and which other squares are connected to the dot. In order to mark this so you don't forget, you can right-click on the dot and drag, which will create an arrow marker pointing at the dot. Drop that in a square of your choice and it will remind you which dot it's associated with. You can also right-click on existing arrows to pick them up and move them, or destroy them by dropping them off the edge of the grid. (Also, if you're not sure which dot an arrow is pointing at, you can pick it up and move it around to make it clearer. It will swivel constantly as you drag it, to stay pointed at its parent dot.) You can also use the cursor keys to move around the grid squares and lines. Pressing the return key when over a grid line will draw or clear its edge, as above. Pressing the return key when over a dot will pick up an arrow, to be dropped the next time the return key is pressed; this can also be used to move existing arrows around, removing them by dropping them on a dot or another arrow. (All the actions described in \k{common-actions} are also available.) \H{galaxies-parameters} \I{parameters, for Galaxies}Galaxies parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{Difficulty} \dd Controls the difficulty of the generated puzzle. More difficult puzzles require more complex deductions, and the \q{Unreasonable} difficulty level may require backtracking. \C{filling} \i{Filling} \cfg{winhelp-topic}{games.filling} You have a grid of squares, some of which contain digits, and the rest of which are empty. Your job is to fill in digits in the empty squares, in such a way that each connected region of squares all containing the same digit has an area equal to that digit. (\q{Connected region}, for the purposes of this game, does not count diagonally separated squares as adjacent.) For example, it follows that no square can contain a zero, and that two adjacent squares can not both contain a one. No region has an area greater than 9 (because then its area would not be a single digit). Credit for this puzzle goes to \i{Nikoli} \k{nikoli-fillomino}. Filling was contributed to this collection by Jonas K\u00F6{oe}lker. \B{nikoli-fillomino} \W{http://www.nikoli.co.jp/en/puzzles/fillomino/}\cw{http://www.nikoli.co.jp/en/puzzles/fillomino/} \H{filling-controls} \I{controls, for Filling}Filling controls To play Filling, simply click the mouse in any empty square and then type a digit on the keyboard to fill that square. By dragging the mouse, you can select multiple squares to fill with a single keypress. If you make a mistake, click the mouse in the incorrect square and press 0, Space, Backspace or Enter to clear it again (or use the Undo feature). You can also move around the grid with the cursor keys; typing a digit will fill the square containing the cursor with that number, or typing 0, Space, or Enter will clear it. You can also select multiple squares for numbering or clearing by using the return key, before typing a digit to fill in the highlighted squares (as above). (All the actions described in \k{common-actions} are also available.) \H{filling-parameters} \I{parameters, for Filling}Filling parameters Filling allows you to configure the number of rows and columns of the grid, through the \q{Type} menu. \C{keen} \i{Keen} \cfg{winhelp-topic}{games.keen} You have a square grid; each square may contain a digit from 1 to the size of the grid. The grid is divided into blocks of varying shape and size, with arithmetic clues written in them. Your aim is to fully populate the grid with digits such that: \b Each row contains only one occurrence of each digit \b Each column contains only one occurrence of each digit \b The digits in each block can be combined to form the number stated in the clue, using the arithmetic operation given in the clue. That is: \lcont{ \b An addition clue means that the sum of the digits in the block must be the given number. For example, \q{15+} means the contents of the block adds up to fifteen. \b A multiplication clue (e.g. \q{60\times}), similarly, means that the product of the digits in the block must be the given number. \b A subtraction clue will always be written in a block of size two, and it means that one of the digits in the block is greater than the other by the given amount. For example, \q{2\minus} means that one of the digits in the block is 2 more than the other, or equivalently that one digit minus the other one is 2. The two digits could be either way round, though. \b A division clue (e.g. \q{3\divide}), similarly, is always in a block of size two and means that one digit divided by the other is equal to the given amount. Note that a block may contain the same digit more than once (provided the identical ones are not in the same row and column). This rule is precisely the opposite of the rule in Solo's \q{Killer} mode (see \k{solo}). } This puzzle appears in the Times under the name \q{\i{KenKen}}. \H{keen-controls} \i{Keen controls} \IM{Keen controls} controls, for Keen Keen shares much of its control system with Solo (and Unequal). To play Keen, simply click the mouse in any empty square and then type a digit on the keyboard to fill that square. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature). If you \e{right}-click in a square and then type a number, that number will be entered in the square as a \q{pencil mark}. You can have pencil marks for multiple numbers in the same square. Squares containing filled-in numbers cannot also contain pencil marks. The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular number, or you can use them as lists of the possible numbers in a given square, or anything else you feel like. To erase a single pencil mark, right-click in the square and type the same number again. All pencil marks in a square are erased when you left-click and type a number, or when you left-click and press space. Right-clicking and pressing space will also erase pencil marks. As for Solo, the cursor keys can be used in conjunction with the digit keys to set numbers or pencil marks. Use the cursor keys to move a highlight around the grid, and type a digit to enter it in the highlighted square. Pressing return toggles the highlight into a mode in which you can enter or remove pencil marks. Pressing M will fill in a full set of pencil marks in every square that does not have a main digit in it. (All the actions described in \k{common-actions} are also available.) \H{keen-parameters} \I{parameters, for Keen}Keen parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Grid size} \dd Specifies the size of the grid. Lower limit is 3; upper limit is 9 (because the user interface would become more difficult with \q{digits} bigger than 9!). \dt \e{Difficulty} \dd Controls the difficulty of the generated puzzle. At Unreasonable level, some backtracking will be required, but the solution should still be unique. The remaining levels require increasingly complex reasoning to avoid having to backtrack. \C{towers} \i{Towers} \cfg{winhelp-topic}{games.towers} You have a square grid. On each square of the grid you can build a tower, with its height ranging from 1 to the size of the grid. Around the edge of the grid are some numeric clues. Your task is to build a tower on every square, in such a way that: \b Each row contains every possible height of tower once \b Each column contains every possible height of tower once \b Each numeric clue describes the number of towers that can be seen if you look into the square from that direction, assuming that shorter towers are hidden behind taller ones. For example, in a 5\by\.5 grid, a clue marked \q{5} indicates that the five tower heights must appear in increasing order (otherwise you would not be able to see all five towers), whereas a clue marked \q{1} indicates that the tallest tower (the one marked 5) must come first. In harder or larger puzzles, some towers will be specified for you as well as the clues round the edge, and some edge clues may be missing. This puzzle appears on the web under various names, particularly \q{\i{Skyscrapers}}, but I don't know who first invented it. \H{towers-controls} \i{Towers controls} \IM{Towers controls} controls, for Towers Towers shares much of its control system with Solo, Unequal and Keen. To play Towers, simply click the mouse in any empty square and then type a digit on the keyboard to fill that square with a tower of the given height. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature). If you \e{right}-click in a square and then type a number, that number will be entered in the square as a \q{pencil mark}. You can have pencil marks for multiple numbers in the same square. A square containing a tower cannot also contain pencil marks. The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular number, or you can use them as lists of the possible numbers in a given square, or anything else you feel like. To erase a single pencil mark, right-click in the square and type the same number again. All pencil marks in a square are erased when you left-click and type a number, or when you left-click and press space. Right-clicking and pressing space will also erase pencil marks. As for Solo, the cursor keys can be used in conjunction with the digit keys to set numbers or pencil marks. Use the cursor keys to move a highlight around the grid, and type a digit to enter it in the highlighted square. Pressing return toggles the highlight into a mode in which you can enter or remove pencil marks. Pressing M will fill in a full set of pencil marks in every square that does not have a main digit in it. (All the actions described in \k{common-actions} are also available.) \H{towers-parameters} \I{parameters, for Towers}Towers parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Grid size} \dd Specifies the size of the grid. Lower limit is 3; upper limit is 9 (because the user interface would become more difficult with \q{digits} bigger than 9!). \dt \e{Difficulty} \dd Controls the difficulty of the generated puzzle. At Unreasonable level, some backtracking will be required, but the solution should still be unique. The remaining levels require increasingly complex reasoning to avoid having to backtrack. \C{singles} \i{Singles} \cfg{winhelp-topic}{games.singles} You have a grid of white squares, all of which contain numbers. Your task is to colour some of the squares black (removing the number) so as to satisfy all of the following conditions: \b No number occurs more than once in any row or column. \b No black square is horizontally or vertically adjacent to any other black square. \b The remaining white squares must all form one contiguous region (connected by edges, not just touching at corners). Credit for this puzzle goes to \i{Nikoli} \k{nikoli-hitori} who call it \i{Hitori}. Singles was contributed to this collection by James Harvey. \B{nikoli-hitori} \W{http://www.nikoli.com/en/puzzles/hitori/index.html}\cw{http://www.nikoli.com/en/puzzles/hitori/index.html} (beware of Flash) \H{singles-controls} \i{Singles controls} \IM{Singles controls} controls, for Singles Left-clicking on an empty square will colour it black; left-clicking again will restore the number. Right-clicking will add a circle (useful for indicating that a cell is definitely not black). You can also use the cursor keys to move around the grid. Pressing the return or space keys will turn a square black or add a circle respectively, and pressing the key again will restore the number or remove the circle. (All the actions described in \k{common-actions} are also available.) \H{singles-parameters} \I{parameters, for Singles}Singles parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{Difficulty} \dd Controls the difficulty of the generated puzzle. \C{magnets} \i{Magnets} \cfg{winhelp-topic}{games.magnets} A rectangular grid has been filled with a mixture of magnets (that is, dominoes with one positive end and one negative end) and blank dominoes (that is, dominoes with two neutral poles). These dominoes are initially only seen in silhouette. Around the grid are placed a number of clues indicating the number of positive and negative poles contained in certain columns and rows. Your aim is to correctly place the magnets and blank dominoes such that all the clues are satisfied, with the additional constraint that no two similar magnetic poles may be orthogonally adjacent (since they repel). Neutral poles do not repel, and can be adjacent to any other pole. Credit for this puzzle goes to \i{Janko} \k{janko-magnets}. Magnets was contributed to this collection by James Harvey. \B{janko-magnets} \W{http://www.janko.at/Raetsel/Magnete/index.htm}\cw{http://www.janko.at/Raetsel/Magnete/index.htm} \H{magnets-controls} \i{Magnets controls} \IM{Magnets controls} controls, for Magnets Left-clicking on an empty square places a magnet at that position with the positive pole on the square and the negative pole on the other half of the magnet; left-clicking again reverses the polarity, and a third click removes the magnet. Right-clicking on an empty square places a blank domino there. Right-clicking again places two question marks on the domino, signifying \q{this cannot be blank} (which can be useful to note deductions while solving), and right-clicking again empties the domino. You can also use the cursor keys to move a cursor around the grid. Pressing the return key will lay a domino with a positive pole at that position; pressing again reverses the polarity and then removes the domino, as with left-clicking. Using the space bar allows placement of blank dominoes and cannot-be-blank hints, as for right-clicking. (All the actions described in \k{common-actions} are also available.) \H{magnets-parameters} \I{parameters, for Magnets}Magnets parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. There will be half \e{Width} \by \e{Height} dominoes in the grid: if this number is odd then one square will be blank. (Grids with at least one odd dimension tend to be easier to solve.) \dt \e{Difficulty} \dd Controls the difficulty of the generated puzzle. At Tricky level, you are required to make more deductions about empty dominoes and row/column counts. \dt \e{Strip clues} \dd If true, some of the clues around the grid are removed at generation time, making the puzzle more difficult. \C{signpost} \i{Signpost} \cfg{winhelp-topic}{games.signpost} You have a grid of squares; each square (except the last one) contains an arrow, and some squares also contain numbers. Your job is to connect the squares to form a continuous list of numbers starting at 1 and linked in the direction of the arrows \dash so the arrow inside the square with the number 1 will point to the square containing the number 2, which will point to the square containing the number 3, etc. Each square can be any distance away from the previous one, as long as it is somewhere in the direction of the arrow. By convention the first and last numbers are shown; one or more interim numbers may also appear at the beginning. Credit for this puzzle goes to \i{Janko} \k{janko-arrowpath}, who call it \q{Pfeilpfad} (\q{arrow path}). Signpost was contributed to this collection by James Harvey. \B{janko-arrowpath} \W{http://janko.at/Raetsel/Pfeilpfad/index.htm}\cw{http://janko.at/Raetsel/Pfeilpfad/index.htm} \H{signpost-controls} \I{controls, for Signpost}Signpost controls To play Signpost, you connect squares together by dragging from one square to another, indicating that they are adjacent in the sequence. Drag with the left button from a square to its successor, or with the right button from a square to its predecessor. If you connect together two squares in this way and one of them has a number in it, the appropriate number will appear in the other square. If you connect two non-numbered squares, they will be assigned temporary algebraic labels: on the first occasion, they will be labelled \cq{a} and \cq{a+1}, and then \cq{b} and \cq{b+1}, and so on. Connecting more squares on to the ends of such a chain will cause them all to be labelled with the same letter. When you left-click or right-click in a square, the legal squares to connect it to will be shown. The arrow in each square starts off black, and goes grey once you connect the square to its successor. Also, each square which needs a predecessor has a small dot in the bottom left corner, which vanishes once you link a square to it. So your aim is always to connect a square with a black arrow to a square with a dot. To remove any links for a particular square (both incoming and outgoing), left-drag it off the grid. To remove a whole chain, right-drag any square in the chain off the grid. You can also use the cursor keys to move around the grid squares and lines. Pressing the return key when over a square starts a link operation, and pressing the return key again over a square will finish the link, if allowable. Pressing the space bar over a square will show the other squares pointing to it, and allow you to form a backward link, and pressing the space bar again cancels this. (All the actions described in \k{common-actions} are also available.) \H{signpost-parameters} \I{parameters, for Signpost}Signpost parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{Force start/end to corners} \dd If true, the start and end squares are always placed in opposite corners (the start at the top left, and the end at the bottom right). If false the start and end squares are placed randomly (although always both shown). \C{range} \i{Range} \cfg{winhelp-topic}{games.range} You have a grid of squares; some squares contain numbers. Your job is to colour some of the squares black, such that several criteria are satisfied: \b no square with a number is coloured black. \b no two black squares are adjacent (horizontally or vertically). \b for any two white squares, there is a path between them using only white squares. \b for each square with a number, that number denotes the total number of white squares reachable from that square going in a straight line in any horizontal or vertical direction until hitting a wall or a black square; the square with the number is included in the total (once). For instance, a square containing the number one must have four black squares as its neighbours by the last criterion; but then it's impossible for it to be connected to any outside white square, which violates the second to last criterion. So no square will contain the number one. Credit for this puzzle goes to \i{Nikoli}, who have variously called it \q{Kurodoko}, \q{Kuromasu} or \q{Where is Black Cells}. \k{nikoli-range}. Range was contributed to this collection by Jonas K\u00F6{oe}lker. \B{nikoli-range} \W{http://www.nikoli.co.jp/en/puzzles/where_is_black_cells/}\cw{http://www.nikoli.co.jp/en/puzzles/where_is_black_cells/} \H{range-controls} \I{controls, for Range}Range controls Click with the left button to paint a square black, or with the right button to mark a square with a dot to indicate that you are sure it should \e{not} be painted black. Repeated clicking with either button will cycle the square through the three possible states (filled, dotted or empty) in opposite directions. You can also use the cursor keys to move around the grid squares. Pressing Return does the same as clicking with the left button, while pressing Space does the same as a right button click. (All the actions described in \k{common-actions} are also available.) \H{range-parameters} \I{parameters, for Range}Range parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \C{pearl} \i{Pearl} \cfg{winhelp-topic}{games.pearl} You have a grid of squares. Your job is to draw lines between the centres of horizontally or vertically adjacent squares, so that the lines form a single closed loop. In the resulting grid, some of the squares that the loop passes through will contain corners, and some will be straight horizontal or vertical lines. (And some squares can be completely empty \dash the loop doesn't have to pass through every square.) Some of the squares contain black and white circles, which are clues that the loop must satisfy. A black circle in a square indicates that that square is a corner, but neither of the squares adjacent to it in the loop is also a corner. A white circle indicates that the square is a straight edge, but \e{at least one} of the squares adjacent to it in the loop is a corner. (In both cases, the clue only constrains the two squares adjacent \e{in the loop}, that is, the squares that the loop passes into after leaving the clue square. The squares that are only adjacent \e{in the grid} are not constrained.) Credit for this puzzle goes to \i{Nikoli}, who call it \q{Masyu}. \k{nikoli-pearl}. Thanks to James Harvey for assistance with the implementation. \B{nikoli-pearl} \W{http://www.nikoli.co.jp/en/puzzles/masyu/}\cw{http://www.nikoli.co.jp/en/puzzles/masyu/} \H{pearl-controls} \I{controls, for Pearl}Pearl controls Click with the left button on a grid edge to draw a segment of the loop through that edge, or to remove a segment once it is drawn. Drag with the left button through a series of squares to draw more than one segment of the loop in one go. Alternatively, drag over an existing part of the loop to undraw it, or to undraw part of it and then go in a different direction. Click with the right button on a grid edge to mark it with a cross, indicating that you are sure the loop does not go through that edge. (For instance, if you have decided which of the squares adjacent to a white clue has to be a corner, but don't yet know which way the corner turns, you might mark the one way it \e{can't} go with a cross.) Alternatively, use the cursor keys to move the cursor. Use the Enter key to begin and end keyboard `drag' operations. Use the Space key to cancel the drag. Use Ctrl-arrowkey and Shift-arrowkey to simulate a left or right click, respectively, on the edge in the given direction relative to the cursor, i.e. to draw a segment or a cross. (All the actions described in \k{common-actions} are also available.) \H{pearl-parameters} \I{parameters, for Pearl}Pearl parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \C{undead} \i{Undead} \cfg{winhelp-topic}{games.undead} You are given a grid of squares, some of which contain diagonal mirrors. Every square which is not a mirror must be filled with one of three types of undead monster: a ghost, a vampire, or a zombie. Vampires can be seen directly, but are invisible when reflected in mirrors. Ghosts are the opposite way round: they can be seen in mirrors, but are invisible when looked at directly. Zombies are visible by any means. You are also told the total number of each type of monster in the grid. Also around the edge of the grid are written numbers, which indicate how many monsters can be seen if you look into the grid along a row or column starting from that position. (The diagonal mirrors are reflective on both sides. If your reflected line of sight crosses the same monster more than once, the number will count it each time it is visible, not just once.) This puzzle type was invented by David Millar, under the name \q{Haunted Mirror Maze}. See \k{janko-undead} for more details. Undead was contributed to this collection by Steffen Bauer. \B{janko-undead} \W{http://www.janko.at/Raetsel/Spukschloss/index.htm}\cw{http://www.janko.at/Raetsel/Spukschloss/index.htm} \H{undead-controls} \I{controls, for Undead}Undead controls Undead has a similar control system to Solo, Unequal and Keen. To play Undead, click the mouse in any empty square and then type a letter on the keyboard indicating the type of monster: \q{G} for a ghost, \q{V} for a vampire, or \q{Z} for a zombie. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature). If you \e{right}-click in a square and then type a letter, the corresponding monster will be shown in reduced size in that square, as a \q{pencil mark}. You can have pencil marks for multiple monsters in the same square. A square containing a full-size monster cannot also contain pencil marks. The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular monster, or you can use them as lists of the possible monster in a given square, or anything else you feel like. To erase a single pencil mark, right-click in the square and type the same letter again. All pencil marks in a square are erased when you left-click and type a monster letter, or when you left-click and press Space. Right-clicking and pressing space will also erase pencil marks. As for Solo, the cursor keys can be used in conjunction with the letter keys to place monsters or pencil marks. Use the cursor keys to move a highlight around the grid, and type a monster letter to enter it in the highlighted square. Pressing return toggles the highlight into a mode in which you can enter or remove pencil marks. If you prefer plain letters of the alphabet to cute monster pictures, you can press \q{A} to toggle between showing the monsters as monsters or showing them as letters. (All the actions described in \k{common-actions} are also available.) \H{undead-parameters} \I{parameters, for Undead}Undead parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{Difficulty} \dd Controls the difficulty of the generated puzzle. \C{unruly} \i{Unruly} \cfg{winhelp-topic}{games.unruly} You are given a grid of squares, which you must colour either black or white. Some squares are provided as clues; the rest are left for you to fill in. Each row and column must contain the same number of black and white squares, and no row or column may contain three consecutive squares of the same colour. This puzzle type was invented by Adolfo Zanellati, under the name \q{Tohu wa Vohu}. See \k{janko-unruly} for more details. Unruly was contributed to this collection by Lennard Sprong. \B{janko-unruly} \W{http://www.janko.at/Raetsel/Tohu-Wa-Vohu/index.htm}\cw{http://www.janko.at/Raetsel/Tohu-Wa-Vohu/index.htm} \H{unruly-controls} \I{controls, for Unruly}Unruly controls To play Unruly, click the mouse in a square to change its colour. Left-clicking an empty square will turn it black, and right-clicking will turn it white. Keep clicking the same button to cycle through the three possible states for the square. If you middle-click in a square it will be reset to empty. You can also use the cursor keys to move around the grid. Pressing the return or space keys will turn an empty square black or white respectively (and then cycle the colours in the same way as the mouse buttons), and pressing Backspace will reset a square to empty. (All the actions described in \k{common-actions} are also available.) \H{unruly-parameters} \I{parameters, for Unruly}Unruly parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. (Note that the rules of the game require both the width and height to be even numbers.) \dt \e{Difficulty} \dd Controls the difficulty of the generated puzzle. \A{licence} \I{MIT licence}\ii{Licence} This software is \i{copyright} 2004-2012 Simon Tatham. Portions copyright Richard Boulton, James Harvey, Mike Pinna, Jonas K\u00F6{oe}lker, Dariusz Olszewski, Michael Schierl, Lambros Lambrou, Bernd Schmidt, Steffen Bauer, Lennard Sprong and Rogier Goossens. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \q{Software}), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED \q{AS IS}, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \IM{command-line}{command line} command line \IM{default parameters, specifying} default parameters, specifying \IM{default parameters, specifying} preferences, specifying default \IM{Unix} Unix \IM{Unix} Linux \IM{generating game IDs} generating game IDs \IM{generating game IDs} game ID, generating \IM{specific} \q{Specific}, menu option \IM{custom} \q{Custom}, menu option \IM{game ID} game ID \IM{game ID} ID, game \IM{ID format} ID format \IM{ID format} format, ID \IM{ID format} game ID, format \IM{keys} keys \IM{keys} shortcuts (keyboard) \IM{initial state} initial state \IM{initial state} state, initial \IM{MIT licence} MIT licence \IM{MIT licence} licence, MIT puzzles-r9872/puzzles.but0000644000175300017530000035766512132033107014673 0ustar simonsimon\title Simon Tatham's Portable Puzzle Collection \cfg{winhelp-filename}{puzzles.hlp} \cfg{winhelp-contents-titlepage}{Contents} \cfg{text-filename}{puzzles.txt} \cfg{html-contents-filename}{index.html} \cfg{html-template-filename}{%k.html} \cfg{html-index-filename}{docindex.html} \cfg{html-leaf-level}{1} \cfg{html-contents-depth-0}{1} \cfg{html-contents-depth-1}{2} \cfg{html-leaf-contains-contents}{true} \cfg{info-filename}{puzzles.info} \cfg{ps-filename}{puzzles.ps} \cfg{pdf-filename}{puzzles.pdf} \define{by} \u00D7{x} \define{dash} \u2013{-} \define{times} \u00D7{*} \define{divide} \u00F7{/} \define{minus} \u2212{-} This is a collection of small one-player puzzle games. \copyright This manual is copyright 2004-2012 Simon Tatham. All rights reserved. You may distribute this documentation under the MIT licence. See \k{licence} for the licence text in full. \cfg{html-local-head}{} \versionid $Id: puzzles.but 9828 2013-04-12 16:28:55Z simon $ \C{intro} Introduction I wrote this collection because I thought there should be more small desktop toys available: little games you can pop up in a window and play for two or three minutes while you take a break from whatever else you were doing. And I was also annoyed that every time I found a good game on (say) \i{Unix}, it wasn't available the next time I was sitting at a \i{Windows} machine, or vice versa; so I arranged that everything in my personal puzzle collection will happily run on both, and have more recently done a port to \i{Mac OS X} as well. When I find (or perhaps invent) further puzzle games that I like, they'll be added to this collection and will immediately be available on both platforms. And if anyone feels like writing any other front ends \dash PocketPC, Mac OS pre-10, or whatever it might be \dash then all the games in this framework will immediately become available on another platform as well. The actual games in this collection were mostly not my invention; they are re-implementations of existing game concepts within my portable puzzle framework. I do not claim credit, in general, for inventing the rules of any of these puzzles. (I don't even claim authorship of all the code; some of the puzzles have been submitted by other authors.) This collection is distributed under the \i{MIT licence} (see \k{licence}). This means that you can do pretty much anything you like with the game binaries or the code, except pretending you wrote them yourself, or suing me if anything goes wrong. The most recent versions, and \i{source code}, can be found at \I{website}\W{http://www.chiark.greenend.org.uk/~sgtatham/puzzles/}\cw{http://www.chiark.greenend.org.uk/~sgtatham/puzzles/}. Please report \I{feedback}\i{bugs} to \W{mailto:anakin@pobox.com}\cw{anakin@pobox.com}. You might find it helpful to read this article before reporting a bug: \W{http://www.chiark.greenend.org.uk/~sgtatham/bugs.html}\cw{http://www.chiark.greenend.org.uk/~sgtatham/bugs.html} \ii{Patches} are welcome. Especially if they provide a new front end (to make all these games run on another platform), or a new game. \C{common} \ii{Common features} This chapter describes features that are common to all the games. \H{common-actions} \I{controls}Common actions These actions are all available from the \I{Game menu}\q{Game} menu and via \I{keys}keyboard shortcuts, in addition to any game-specific actions. (On \i{Mac OS X}, to conform with local user interface standards, these actions are situated on the \I{File menu}\q{File} and \I{Edit menu}\q{Edit} menus instead.) \dt \ii\e{New game} (\q{N}, Ctrl+\q{N}) \dd Starts a new game, with a random initial state. \dt \ii\e{Restart game} \dd Resets the current game to its initial state. (This can be undone.) \dt \ii\e{Load} \dd Loads a saved game from a file on disk. \dt \ii\e{Save} \dd Saves the current state of your game to a file on disk. \lcont{ The Load and Save operations preserve your entire game history (so you can save, reload, and still Undo and Redo things you had done before saving). } \dt \I{printing, on Windows}\e{Print} \dd Where supported (currently only on Windows), brings up a dialog allowing you to print an arbitrary number of puzzles randomly generated from the current parameters, optionally including the current puzzle. (Only for puzzles which make sense to print, of course \dash it's hard to think of a sensible printable representation of Fifteen!) \dt \ii\e{Undo} (\q{U}, Ctrl+\q{Z}, Ctrl+\q{_}) \dd Undoes a single move. (You can undo moves back to the start of the session.) \dt \ii\e{Redo} (\q{R}, Ctrl+\q{R}) \dd Redoes a previously undone move. \dt \ii\e{Copy} \dd Copies the current state of your game to the clipboard in text format, so that you can paste it into (say) an e-mail client or a web message board if you're discussing the game with someone else. (Not all games support this feature.) \dt \ii\e{Solve} \dd Transforms the puzzle instantly into its solved state. For some games (Cube) this feature is not supported at all because it is of no particular use. For other games (such as Pattern), the solved state can be used to give you information, if you can't see how a solution can exist at all or you want to know where you made a mistake. For still other games (such as Sixteen), automatic solution tells you nothing about how to \e{get} to the solution, but it does provide a useful way to get there quickly so that you can experiment with set-piece moves and transformations. \lcont{ Some games (such as Solo) are capable of solving a game ID you have typed in from elsewhere. Other games (such as Rectangles) cannot solve a game ID they didn't invent themself, but when they did invent the game ID they know what the solution is already. Still other games (Pattern) can solve \e{some} external game IDs, but only if they aren't too difficult. The \q{Solve} command adds the solved state to the end of the undo chain for the puzzle. In other words, if you want to go back to solving it yourself after seeing the answer, you can just press Undo. } \dt \I{exit}\ii\e{Quit} (\q{Q}, Ctrl+\q{Q}) \dd Closes the application entirely. \H{common-id} Specifying games with the \ii{game ID} There are two ways to save a game specification out of a puzzle and recreate it later, or recreate it in somebody else's copy of the same puzzle. The \q{\i{Specific}} and \q{\i{Random Seed}} options from the \I{Game menu}\q{Game} menu (or the \q{File} menu, on \i{Mac OS X}) each show a piece of text (a \q{game ID}) which is sufficient to reconstruct precisely the same game at a later date. You can enter either of these pieces of text back into the program (via the same \q{Specific} or \q{Random Seed} menu options) at a later point, and it will recreate the same game. You can also use either one as a \i{command line} argument (on Windows or Unix); see \k{common-cmdline} for more detail. The difference between the two forms is that a descriptive game ID is a literal \e{description} of the \i{initial state} of the game, whereas a random seed is just a piece of arbitrary text which was provided as input to the random number generator used to create the puzzle. This means that: \b Descriptive game IDs tend to be longer in many puzzles (although some, such as Cube (\k{cube}), only need very short descriptions). So a random seed is often a \e{quicker} way to note down the puzzle you're currently playing, or to tell it to somebody else so they can play the same one as you. \b Any text at all is a valid random seed. The automatically generated ones are fifteen-digit numbers, but anything will do; you can type in your full name, or a word you just made up, and a valid puzzle will be generated from it. This provides a way for two or more people to race to complete the same puzzle: you think of a random seed, then everybody types it in at the same time, and nobody has an advantage due to having seen the generated puzzle before anybody else. \b It is often possible to convert puzzles from other sources (such as \q{nonograms} or \q{sudoku} from newspapers) into descriptive game IDs suitable for use with these programs. \b Random seeds are not guaranteed to produce the same result if you use them with a different \i\e{version} of the puzzle program. This is because the generation algorithm might have been improved or modified in later versions of the code, and will therefore produce a different result when given the same sequence of random numbers. Use a descriptive game ID if you aren't sure that it will be used on the same version of the program as yours. \lcont{(Use the \q{About} menu option to find out the version number of the program. Programs with the same version number running on different platforms should still be random-seed compatible.)} \I{ID format}A descriptive game ID starts with a piece of text which encodes the \i\e{parameters} of the current game (such as grid size). Then there is a colon, and after that is the description of the game's initial state. A random seed starts with a similar string of parameters, but then it contains a hash sign followed by arbitrary data. If you enter a descriptive game ID, the program will not be able to show you the random seed which generated it, since it wasn't generated \e{from} a random seed. If you \e{enter} a random seed, however, the program will be able to show you the descriptive game ID derived from that random seed. Note that the game parameter strings are not always identical between the two forms. For some games, there will be parameter data provided with the random seed which is not included in the descriptive game ID. This is because that parameter information is only relevant when \e{generating} puzzle grids, and is not important when playing them. Thus, for example, the difficulty level in Solo (\k{solo}) is not mentioned in the descriptive game ID. These additional parameters are also not set permanently if you type in a game ID. For example, suppose you have Solo set to \q{Advanced} difficulty level, and then a friend wants your help with a \q{Trivial} puzzle; so the friend reads out a random seed specifying \q{Trivial} difficulty, and you type it in. The program will generate you the same \q{Trivial} grid which your friend was having trouble with, but once you have finished playing it, when you ask for a new game it will automatically go back to the \q{Advanced} difficulty which it was previously set on. \H{common-type} The \q{Type} menu The \I{Type menu}\q{Type} menu, if present, may contain a list of \i{preset} game settings. Selecting one of these will start a new random game with the parameters specified. The \q{Type} menu may also contain a \q{\i{Custom}} option which allows you to fine-tune game \i{parameters}. The parameters available are specific to each game and are described in the following sections. \H{common-cmdline} Specifying game parameters on the \i{command line} (This section does not apply to the \i{Mac OS X} version.) The games in this collection deliberately do not ever save information on to the computer they run on: they have no high score tables and no saved preferences. (This is because I expect at least some people to play them at work, and those people will probably appreciate leaving as little evidence as possible!) However, if you do want to arrange for one of these games to \I{default parameters, specifying}default to a particular set of parameters, you can specify them on the command line. The easiest way to do this is to set up the parameters you want using the \q{Type} menu (see \k{common-type}), and then to select \q{Random Seed} from the \q{Game} or \q{File} menu (see \k{common-id}). The text in the \q{Game ID} box will be composed of two parts, separated by a hash. The first of these parts represents the game parameters (the size of the playing area, for example, and anything else you set using the \q{Type} menu). If you run the game with just that parameter text on the command line, it will start up with the settings you specified. For example: if you run Cube (see \k{cube}), select \q{Octahedron} from the \q{Type} menu, and then go to the game ID selection, you will see a string of the form \cq{o2x2#338686542711620}. Take only the part before the hash (\cq{o2x2}), and start Cube with that text on the command line: \cq{PREFIX-cube o2x2}. If you copy the \e{entire} game ID on to the command line, the game will start up in the specific game that was described. This is occasionally a more convenient way to start a particular game ID than by pasting it into the game ID selection box. (You could also retrieve the encoded game parameters using the \q{Specific} menu option instead of \q{Random Seed}, but if you do then some options, such as the difficulty level in Solo, will be missing. See \k{common-id} for more details on this.) \H{common-unix-cmdline} \i{Unix} \i{command-line} options (This section only applies to the Unix port.) In addition to being able to specify game parameters on the command line (see \k{common-cmdline}), there are various other options: \dt \cw{--game} \dt \cw{--load} \dd These options respectively determine whether the command-line argument is treated as specifying game parameters or a \i{save} file to \i{load}. Only one should be specified. If neither of these options is specified, a guess is made based on the format of the argument. \dt \cw{--generate }\e{n} \dd If this option is specified, instead of a puzzle being displayed, a number of descriptive game IDs will be \I{generating game IDs}invented and printed on standard output. This is useful for gaining access to the game generation algorithms without necessarily using the frontend. \lcont{ If game parameters are specified on the command-line, they will be used to generate the game IDs; otherwise a default set of parameters will be used. The most common use of this option is in conjunction with \c{--print}, in which case its behaviour is slightly different; see below. } \dt \I{printing, on Unix}\cw{--print }\e{w}\cw{x}\e{h} \dd If this option is specified, instead of a puzzle being displayed, a printed representation of one or more unsolved puzzles is sent to standard output, in \i{PostScript} format. \lcont{ On each page of puzzles, there will be \e{w} across and \e{h} down. If there are more puzzles than \e{w}\by\e{h}, more than one page will be printed. If \c{--generate} has also been specified, the invented game IDs will be used to generate the printed output. Otherwise, a list of game IDs is expected on standard input (which can be descriptive or random seeds; see \k{common-id}), in the same format produced by \c{--generate}. For example: \c PREFIX-net --generate 12 --print 2x3 7x7w | lpr will generate two pages of printed Net puzzles (each of which will have a 7\by\.7 wrapping grid), and pipe the output to the \c{lpr} command, which on many systems will send them to an actual printer. There are various other options which affect printing; see below. } \dt \cw{--save }\e{file-prefix} [ \cw{--save-suffix }\e{file-suffix} ] \dd If this option is specified, instead of a puzzle being displayed, saved-game files for one or more unsolved puzzles are written to files constructed from the supplied prefix and/or suffix. \lcont{ If \c{--generate} has also been specified, the invented game IDs will be used to generate the printed output. Otherwise, a list of game IDs is expected on standard input (which can be descriptive or random seeds; see \k{common-id}), in the same format produced by \c{--generate}. For example: \c PREFIX-net --generate 12 --save game --save-suffix .sav will generate twelve Net saved-game files with the names \cw{game0.sav} to \cw{game11.sav}. } \dt \cw{--version} \dd Prints version information about the game, and then quits. The following options are only meaningful if \c{--print} is also specified: \dt \cw{--with-solutions} \dd The set of pages filled with unsolved puzzles will be followed by the solutions to those puzzles. \dt \cw{--scale }\e{n} \dd Adjusts how big each puzzle is when printed. Larger numbers make puzzles bigger; the default is 1.0. \dt \cw{--colour} \dd Puzzles will be printed in colour, rather than in black and white (if supported by the puzzle). \C{net} \i{Net} \cfg{winhelp-topic}{games.net} (\e{Note:} the \i{Windows} version of this game is called \i\cw{NETGAME.EXE} to avoid clashing with Windows's own \cw{NET.EXE}.) I originally saw this in the form of a Flash game called \i{FreeNet} \k{FreeNet}, written by Pavils Jurjans; there are several other implementations under the name \i{NetWalk}. The computer prepares a network by connecting up the centres of squares in a grid, and then shuffles the network by rotating every tile randomly. Your job is to rotate it all back into place. The successful solution will be an entirely connected network, with no closed loops. \#{The latter clause means that there are no closed paths within the network. Could this be clearer? "No closed paths"?} As a visual aid, all tiles which are connected to the one in the middle are highlighted. \B{FreeNet} \W{http://www.jurjans.lv/stuff/net/FreeNet.htm}\cw{http://www.jurjans.lv/stuff/net/FreeNet.htm} \H{net-controls} \i{Net controls} \IM{Net controls} controls, for Net \IM{Net controls} keys, for Net \IM{Net controls} shortcuts (keyboard), for Net This game can be played with either the keyboard or the mouse. The controls are: \dt \e{Select tile}: mouse pointer, arrow keys \dt \e{Rotate tile anticlockwise}: left mouse button, \q{A} key \dt \e{Rotate tile clockwise}: right mouse button, \q{D} key \dt \e{Rotate tile by 180 degrees}: \q{F} key \dt \e{Lock (or unlock) tile}: middle mouse button, shift-click, \q{S} key \dd You can lock a tile once you're sure of its orientation. You can also unlock it again, but while it's locked you can't accidentally turn it. The following controls are not necessary to complete the game, but may be useful: \dt \e{Shift grid}: Shift + arrow keys \dd On grids that wrap, you can move the origin of the grid, so that tiles that were on opposite sides of the grid can be seen together. \dt \e{Move centre}: Ctrl + arrow keys \dd You can change which tile is used as the source of highlighting. (It doesn't ultimately matter which tile this is, as every tile will be connected to every other tile in a correct solution, but it may be helpful in the intermediate stages of solving the puzzle.) \dt \e{Jumble tiles}: \q{J} key \dd This key turns all tiles that are not locked to random orientations. (All the actions described in \k{common-actions} are also available.) \H{net-params} \I{parameters, for Net}Net parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in tiles. \dt \e{Walls wrap around} \dd If checked, flow can pass from the left edge to the right edge, and from top to bottom, and vice versa. \dt \e{Barrier probability} \dd A number between 0.0 and 1.0 controlling whether an immovable barrier is placed between two tiles to prevent flow between them (a higher number gives more barriers). Since barriers are immovable, they act as constraints on the solution (i.e., hints). \lcont{ The grid generation in Net has been carefully arranged so that the barriers are independent of the rest of the grid. This means that if you note down the random seed used to generate the current puzzle (see \k{common-id}), change the \e{Barrier probability} parameter, and then re-enter the same random seed, you should see exactly the same starting grid, with the only change being the number of barriers. So if you're stuck on a particular grid and need a hint, you could start up another instance of Net, set up the same parameters but a higher barrier probability, and enter the game seed from the original Net window. } \dt \e{Ensure unique solution} \dd Normally, Net will make sure that the puzzles it presents have only one solution. Puzzles with ambiguous sections can be more difficult and more subtle, so if you like you can turn off this feature and risk having ambiguous puzzles. (Also, finding \e{all} the possible solutions can be an additional challenge for an advanced player.) \C{cube} \i{Cube} \cfg{winhelp-topic}{games.cube} This is another one I originally saw as a web game. This one was a Java game \k{cube-java-game}, by Paul Scott. You have a grid of 16 squares, six of which are blue; on one square rests a cube. Your move is to use the arrow keys to roll the cube through 90 degrees so that it moves to an adjacent square. If you roll the cube on to a blue square, the blue square is picked up on one face of the cube; if you roll a blue face of the cube on to a non-blue square, the blueness is put down again. (In general, whenever you roll the cube, the two faces that come into contact swap colours.) Your job is to get all six blue squares on to the six faces of the cube at the same time. Count your moves and try to do it in as few as possible. Unlike the original Java game, my version has an additional feature: once you've mastered the game with a cube rolling on a square grid, you can change to a triangular grid and roll any of a tetrahedron, an octahedron or an icosahedron. \B{cube-java-game} \W{http://www3.sympatico.ca/paulscott/cube/cube.htm}\cw{http://www3.sympatico.ca/paulscott/cube/cube.htm} \H{cube-controls} \i{Cube controls} \IM{Cube controls} controls, for Cube \IM{Cube controls} keys, for Cube \IM{Cube controls} shortcuts (keyboard), for Cube This game can be played with either the keyboard or the mouse. Left-clicking anywhere on the window will move the cube (or other solid) towards the mouse pointer. The arrow keys can also used to roll the cube on its square grid in the four cardinal directions. On the triangular grids, the mapping of arrow keys to directions is more approximate. Vertical movement is disallowed where it doesn't make sense. The four keys surrounding the arrow keys on the numeric keypad (\q{7}, \q{9}, \q{1}, \q{3}) can be used for diagonal movement. (All the actions described in \k{common-actions} are also available.) \H{cube-params} \I{parameters, for Cube}Cube parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Type of solid} \dd Selects the solid to roll (and hence the shape of the grid): tetrahedron, cube, octahedron, or icosahedron. \dt \e{Width / top}, \e{Height / bottom} \dd On a square grid, horizontal and vertical dimensions. On a triangular grid, the number of triangles on the top and bottom rows respectively. \C{fifteen} \i{Fifteen} \cfg{winhelp-topic}{games.fifteen} The old ones are the best: this is the good old \q{\i{15-puzzle}} with sliding tiles. You have a 4\by\.4 square grid; 15 squares contain numbered tiles, and the sixteenth is empty. Your move is to choose a tile next to the empty space, and slide it into the space. The aim is to end up with the tiles in numerical order, with the space in the bottom right (so that the top row reads 1,2,3,4 and the bottom row reads 13,14,15,\e{space}). \H{fifteen-controls} \i{Fifteen controls} \IM{Fifteen controls} controls, for Fifteen \IM{Fifteen controls} keys, for Fifteen \IM{Fifteen controls} shortcuts (keyboard), for Fifteen This game can be controlled with the mouse or the keyboard. A left-click with the mouse in the row or column containing the empty space will move as many tiles as necessary to move the space to the mouse pointer. The arrow keys will move a tile adjacent to the space in the direction indicated (moving the space in the \e{opposite} direction). (All the actions described in \k{common-actions} are also available.) \H{fifteen-params} \I{parameters, for Fifteen}Fifteen parameters The only options available from the \q{Custom...} option on the \q{Type} menu are \e{Width} and \e{Height}, which are self-explanatory. (Once you've changed these, it's not a \q{15-puzzle} any more, of course!) \C{sixteen} \i{Sixteen} \cfg{winhelp-topic}{games.sixteen} Another sliding tile puzzle, visually similar to Fifteen (see \k{fifteen}) but with a different type of move. This time, there is no hole: all 16 squares on the grid contain numbered squares. Your move is to shift an entire row left or right, or shift an entire column up or down; every time you do that, the tile you shift off the grid re-appears at the other end of the same row, in the space you just vacated. To win, arrange the tiles into numerical order (1,2,3,4 on the top row, 13,14,15,16 on the bottom). When you've done that, try playing on different sizes of grid. I \e{might} have invented this game myself, though only by accident if so (and I'm sure other people have independently invented it). I thought I was imitating a screensaver I'd seen, but I have a feeling that the screensaver might actually have been a Fifteen-type puzzle rather than this slightly different kind. So this might be the one thing in my puzzle collection which represents creativity on my part rather than just engineering. \H{sixteen-controls} \I{controls, for Sixteen}Sixteen controls Left-clicking on an arrow will move the appropriate row or column in the direction indicated. Right-clicking will move it in the opposite direction. Alternatively, use the cursor keys to move the position indicator around the edge of the grid, and use the return key to move the row/column in the direction indicated. (All the actions described in \k{common-actions} are also available.) \H{sixteen-params} \I{parameters, for Sixteen}Sixteen parameters The parameters available from the \q{Custom...} option on the \q{Type} menu are: \b \e{Width} and \e{Height}, which are self-explanatory. \b You can ask for a limited shuffling operation to be performed on the grid. By default, Sixteen will shuffle the grid in such a way that any arrangement is about as probable as any other. You can override this by requesting a precise number of shuffling moves to be performed. Typically your aim is then to determine the precise set of shuffling moves and invert them exactly, so that you answer (say) a four-move shuffle with a four-move solution. Note that the more moves you ask for, the more likely it is that solutions shorter than the target length will turn out to be possible. \C{twiddle} \i{Twiddle} \cfg{winhelp-topic}{games.twiddle} Twiddle is a tile-rearrangement puzzle, visually similar to Sixteen (see \k{sixteen}): you are given a grid of square tiles, each containing a number, and your aim is to arrange the numbers into ascending order. In basic Twiddle, your move is to rotate a square group of four tiles about their common centre. (Orientation is not significant in the basic puzzle, although you can select it.) On more advanced settings, you can rotate a larger square group of tiles. I first saw this type of puzzle in the GameCube game \q{Metroid Prime 2}. In the Main Gyro Chamber in that game, there is a puzzle you solve to unlock a door, which is a special case of Twiddle. I developed this game as a generalisation of that puzzle. \H{twiddle-controls} \I{controls, for Twiddle}Twiddle controls To play Twiddle, click the mouse in the centre of the square group you wish to rotate. In the basic mode, you rotate a 2\by\.2 square, which means you have to click at a corner point where four tiles meet. In more advanced modes you might be rotating 3\by\.3 or even more at a time; if the size of the square is odd then you simply click in the centre tile of the square you want to rotate. Clicking with the left mouse button rotates the group anticlockwise. Clicking with the right button rotates it clockwise. You can also move an outline square around the grid with the cursor keys; the square is the size above (2\by\.2 by default, or larger). Pressing the return key or space bar will rotate the current square anticlockwise or clockwise respectively. (All the actions described in \k{common-actions} are also available.) \H{twiddle-parameters} \I{parameters, for Twiddle}Twiddle parameters Twiddle provides several configuration options via the \q{Custom} option on the \q{Type} menu: \b You can configure the width and height of the puzzle grid. \b You can configure the size of square block that rotates at a time. \b You can ask for every square in the grid to be distinguishable (the default), or you can ask for a simplified puzzle in which there are groups of identical numbers. In the simplified puzzle your aim is just to arrange all the 1s into the first row, all the 2s into the second row, and so on. \b You can configure whether the orientation of tiles matters. If you ask for an orientable puzzle, each tile will have a triangle drawn in it. All the triangles must be pointing upwards to complete the puzzle. \b You can ask for a limited shuffling operation to be performed on the grid. By default, Twiddle will shuffle the grid so much that any arrangement is about as probable as any other. You can override this by requesting a precise number of shuffling moves to be performed. Typically your aim is then to determine the precise set of shuffling moves and invert them exactly, so that you answer (say) a four-move shuffle with a four-move solution. Note that the more moves you ask for, the more likely it is that solutions shorter than the target length will turn out to be possible. \C{rectangles} \i{Rectangles} \cfg{winhelp-topic}{games.rectangles} You have a grid of squares, with numbers written in some (but not all) of the squares. Your task is to subdivide the grid into rectangles of various sizes, such that (a) every rectangle contains exactly one numbered square, and (b) the area of each rectangle is equal to the number written in its numbered square. Credit for this game goes to the Japanese puzzle magazine \i{Nikoli} \k{nikoli-rect}; I've also seen a Palm implementation at \i{Puzzle Palace} \k{puzzle-palace-rect}. Unlike Puzzle Palace's implementation, my version automatically generates random grids of any size you like. The quality of puzzle design is therefore not quite as good as hand-crafted puzzles would be, but on the plus side you get an inexhaustible supply of puzzles tailored to your own specification. \B{nikoli-rect} \W{http://www.nikoli.co.jp/puzzles/7/index_text-e.htm}\cw{http://www.nikoli.co.jp/puzzles/7/index_text-e.htm} \B{puzzle-palace-rect} \W{http://www.puzzle.gr.jp/puzzle/sikaku/palm/index.html.en}\cw{http://www.puzzle.gr.jp/puzzle/sikaku/palm/index.html.en} \H{rectangles-controls} \I{controls, for Rectangles}Rectangles controls This game is played with the mouse or cursor keys. Left-click any edge to toggle it on or off, or left-click and drag to draw an entire rectangle (or line) on the grid in one go (removing any existing edges within that rectangle). Right-clicking and dragging will allow you to erase the contents of a rectangle without affecting its edges. Alternatively, use the cursor keys to move the position indicator around the board. Pressing the return key then allows you to use the cursor keys to drag a rectangle out from that position, and pressing the return key again completes the rectangle. Using the space bar instead of the return key allows you to erase the contents of a rectangle without affecting its edges, as above. When a rectangle of the correct size is completed, it will be shaded. (All the actions described in \k{common-actions} are also available.) \H{rectangles-params} \I{parameters, for Rectangles}Rectangles parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid, in squares. \dt \e{Expansion factor} \dd This is a mechanism for changing the type of grids generated by the program. Some people prefer a grid containing a few large rectangles to one containing many small ones. So you can ask Rectangles to essentially generate a \e{smaller} grid than the size you specified, and then to expand it by adding rows and columns. \lcont{ The default expansion factor of zero means that Rectangles will simply generate a grid of the size you ask for, and do nothing further. If you set an expansion factor of (say) 0.5, it means that each dimension of the grid will be expanded to half again as big after generation. In other words, the initial grid will be 2/3 the size in each dimension, and will be expanded to its full size without adding any more rectangles. Setting an expansion factor of around 0.5 tends to make the game more difficult, and also (in my experience) rewards a less deductive and more intuitive playing style. If you set it \e{too} high, though, the game simply cannot generate more than a few rectangles to cover the entire grid, and the game becomes trivial. } \dt \e{Ensure unique solution} \dd Normally, Rectangles will make sure that the puzzles it presents have only one solution. Puzzles with ambiguous sections can be more difficult and more subtle, so if you like you can turn off this feature and risk having ambiguous puzzles. Also, finding \e{all} the possible solutions can be an additional challenge for an advanced player. Turning off this option can also speed up puzzle generation. \C{netslide} \i{Netslide} \cfg{winhelp-topic}{games.netslide} This game combines the grid generation of Net (see \k{net}) with the movement of Sixteen (see \k{sixteen}): you have a Net grid, but instead of rotating tiles back into place you have to slide them into place by moving a whole row at a time. As in Sixteen, \I{controls, for Netslide}control is with the mouse or cursor keys. See \k{sixteen-controls}. \I{parameters, for Netslide}The available game parameters have similar meanings to those in Net (see \k{net-params}) and Sixteen (see \k{sixteen-params}). Netslide was contributed to this collection by Richard Boulton. \C{pattern} \i{Pattern} \cfg{winhelp-topic}{games.pattern} You have a grid of squares, which must all be filled in either black or white. Beside each row of the grid are listed the lengths of the runs of black squares on that row; above each column are listed the lengths of the runs of black squares in that column. Your aim is to fill in the entire grid black or white. I first saw this puzzle form around 1995, under the name \q{\i{nonograms}}. I've seen it in various places since then, under different names. Normally, puzzles of this type turn out to be a meaningful picture of something once you've solved them. However, since this version generates the puzzles automatically, they will just look like random groupings of squares. (One user has suggested that this is actually a \e{good} thing, since it prevents you from guessing the colour of squares based on the picture, and forces you to use logic instead.) The advantage, though, is that you never run out of them. \H{pattern-controls} \I{controls, for Pattern}Pattern controls This game is played with the mouse. Left-click in a square to colour it black. Right-click to colour it white. If you make a mistake, you can middle-click, or hold down Shift while clicking with any button, to colour the square in the default grey (meaning \q{undecided}) again. You can click and drag with the left or right mouse button to colour a vertical or horizontal line of squares black or white at a time (respectively). If you click and drag with the middle button, or with Shift held down, you can colour a whole rectangle of squares grey. You can also move around the grid with the cursor keys. Pressing the return key will cycle the current cell through empty, then black, then white, then empty, and the space bar does the same cycle in reverse. (All the actions described in \k{common-actions} are also available.) \H{pattern-parameters} \I{parameters, for Pattern}Pattern parameters The only options available from the \q{Custom...} option on the \q{Type} menu are \e{Width} and \e{Height}, which are self-explanatory. \C{solo} \i{Solo} \cfg{winhelp-topic}{games.solo} You have a square grid, which is divided into as many equally sized sub-blocks as the grid has rows. Each square must be filled in with a digit from 1 to the size of the grid, in such a way that \b every row contains only one occurrence of each digit \b every column contains only one occurrence of each digit \b every block contains only one occurrence of each digit. \b (optionally, by default off) each of the square's two main diagonals contains only one occurrence of each digit. You are given some of the numbers as clues; your aim is to place the rest of the numbers correctly. Under the default settings, the sub-blocks are square or rectangular. The default puzzle size is 3\by\.3 (a 9\by\.9 actual grid, divided into nine 3\by\.3 blocks). You can also select sizes with rectangular blocks instead of square ones, such as 2\by\.3 (a 6\by\.6 grid divided into six 3\by\.2 blocks). Alternatively, you can select \q{jigsaw} mode, in which the sub-blocks are arbitrary shapes which differ between individual puzzles. Another available mode is \q{killer}. In this mode, clues are not given in the form of filled-in squares; instead, the grid is divided into \q{cages} by coloured lines, and for each cage the game tells you what the sum of all the digits in that cage should be. Also, no digit may appear more than once within a cage, even if the cage crosses the boundaries of existing regions. If you select a puzzle size which requires more than 9 digits, the additional digits will be letters of the alphabet. For example, if you select 3\by\.4 then the digits which go in your grid will be 1 to 9, plus \cq{a}, \cq{b} and \cq{c}. This cannot be selected for killer puzzles. I first saw this puzzle in \i{Nikoli} \k{nikoli-solo}, although it's also been popularised by various newspapers under the name \q{Sudoku} or \q{Su Doku}. Howard Garns is considered the inventor of the modern form of the puzzle, and it was first published in \e{Dell Pencil Puzzles and Word Games}. A more elaborate treatment of the history of the puzzle can be found on Wikipedia \k{wikipedia-solo}. \B{nikoli-solo} \W{http://www.nikoli.co.jp/puzzles/1/index_text-e.htm}\cw{http://www.nikoli.co.jp/puzzles/1/index_text-e.htm} \B{wikipedia-solo} \W{http://en.wikipedia.org/wiki/Sudoku}\cw{http://en.wikipedia.org/wiki/Sudoku} \H{solo-controls} \I{controls, for Solo}Solo controls To play Solo, simply click the mouse in any empty square and then type a digit or letter on the keyboard to fill that square. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature). If you \e{right}-click in a square and then type a number, that number will be entered in the square as a \q{pencil mark}. You can have pencil marks for multiple numbers in the same square. Squares containing filled-in numbers cannot also contain pencil marks. The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular number, or you can use them as lists of the possible numbers in a given square, or anything else you feel like. To erase a single pencil mark, right-click in the square and type the same number again. All pencil marks in a square are erased when you left-click and type a number, or when you left-click and press space. Right-clicking and pressing space will also erase pencil marks. Alternatively, use the cursor keys to move the mark around the grid. Pressing the return key toggles the mark (from a normal mark to a pencil mark), and typing a number in is entered in the square in the appropriate way; typing in a 0 or using the space bar will clear a filled square. (All the actions described in \k{common-actions} are also available.) \H{solo-parameters} \I{parameters, for Solo}Solo parameters Solo allows you to configure two separate dimensions of the puzzle grid on the \q{Type} menu: the number of columns, and the number of rows, into which the main grid is divided. (The size of a block is the inverse of this: for example, if you select 2 columns and 3 rows, each actual block will have 3 columns and 2 rows.) If you tick the \q{X} checkbox, Solo will apply the optional extra constraint that the two main diagonals of the grid also contain one of every digit. (This is sometimes known as \q{Sudoku-X} in newspapers.) In this mode, the squares on the two main diagonals will be shaded slightly so that you know it's enabled. If you tick the \q{Jigsaw} checkbox, Solo will generate randomly shaped sub-blocks. In this mode, the actual grid size will be taken to be the product of the numbers entered in the \q{Columns} and \q{Rows} boxes. There is no reason why you have to enter a number greater than 1 in both boxes; Jigsaw mode has no constraint on the grid size, and it can even be a prime number if you feel like it. If you tick the \q{Killer} checkbox, Solo will generate a set of of cages, which are randomly shaped and drawn in an outline of a different colour. Each of these regions contains a smaller clue which shows the digit sum of all the squares in this region. You can also configure the type of symmetry shown in the generated puzzles. More symmetry makes the puzzles look prettier but may also make them easier, since the symmetry constraints can force more clues than necessary to be present. Completely asymmetric puzzles have the freedom to contain as few clues as possible. Finally, you can configure the difficulty of the generated puzzles. Difficulty levels are judged by the complexity of the techniques of deduction required to solve the puzzle: each level requires a mode of reasoning which was not necessary in the previous one. In particular, on difficulty levels \q{Trivial} and \q{Basic} there will be a square you can fill in with a single number at all times, whereas at \q{Intermediate} level and beyond you will have to make partial deductions about the \e{set} of squares a number could be in (or the set of numbers that could be in a square). \#{Advanced, Extreme?} At \q{Unreasonable} level, even this is not enough, and you will eventually have to make a guess, and then backtrack if it turns out to be wrong. Generating difficult puzzles is itself difficult: if you select one of the higher difficulty levels, Solo may have to make many attempts at generating a puzzle before it finds one hard enough for you. Be prepared to wait, especially if you have also configured a large puzzle size. \C{mines} \i{Mines} \cfg{winhelp-topic}{games.mines} You have a grid of covered squares, some of which contain mines, but you don't know which. Your job is to uncover every square which does \e{not} contain a mine. If you uncover a square containing a mine, you lose. If you uncover a square which does not contain a mine, you are told how many mines are contained within the eight surrounding squares. This game needs no introduction; popularised by Windows, it is perhaps the single best known desktop puzzle game in existence. This version of it has an unusual property. By default, it will generate its mine positions in such a way as to ensure that you never need to \e{guess} where a mine is: you will always be able to deduce it somehow. So you will never, as can happen in other versions, get to the last four squares and discover that there are two mines left but you have no way of knowing for sure where they are. \H{mines-controls} \I{controls, for Mines}Mines controls This game is played with the mouse. If you left-click in a covered square, it will be uncovered. If you right-click in a covered square, it will place a flag which indicates that the square is believed to be a mine. Left-clicking in a marked square will not uncover it, for safety. You can right-click again to remove a mark placed in error. If you left-click in an \e{uncovered} square, it will \q{clear around} the square. This means: if the square has exactly as many flags surrounding it as it should have mines, then all the covered squares next to it which are \e{not} flagged will be uncovered. So once you think you know the location of all the mines around a square, you can use this function as a shortcut to avoid having to click on each of the remaining squares one by one. If you uncover a square which has \e{no} mines in the surrounding eight squares, then it is obviously safe to uncover those squares in turn, and so on if any of them also has no surrounding mines. This will be done for you automatically; so sometimes when you uncover a square, a whole new area will open up to be explored. You can also use the cursor keys to move around the minefield. Pressing the return key in a covered square uncovers it, and in an uncovered square will clear around it (so it acts as the left button), pressing the space bar in a covered square will place a flag (similarly, it acts as the right button). All the actions described in \k{common-actions} are also available. Even Undo is available, although you might consider it cheating to use it. If you step on a mine, the program will only reveal the mine in question (unlike most other implementations, which reveal all of them). You can then Undo your fatal move and continue playing if you like. The program will track the number of times you died (and Undo will not reduce that counter), so when you get to the end of the game you know whether or not you did it without making any errors. (If you really want to know the full layout of the grid, which other implementations will show you after you die, you can always use the Solve menu option.) \H{mines-parameters} \I{parameters, for Mines}Mines parameters The options available from the \q{Custom...} option on the \q{Type} menu are: \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{Mines} \dd Number of mines in the grid. You can enter this as an absolute mine count, or alternatively you can put a \cw{%} sign on the end in which case the game will arrange for that proportion of the squares in the grid to be mines. \lcont{ Beware of setting the mine count too high. At very high densities, the program may spend forever searching for a solvable grid. } \dt \e{Ensure solubility} \dd When this option is enabled (as it is by default), Mines will ensure that the entire grid can be fully deduced starting from the initial open space. If you prefer the riskier grids generated by other implementations, you can switch off this option. \C{samegame} \i{Same Game} \cfg{winhelp-topic}{games.samegame} You have a grid of coloured squares, which you have to clear by highlighting contiguous regions of more than one coloured square; the larger the region you highlight, the more points you get (and the faster you clear the arena). If you clear the grid you win. If you end up with nothing but single squares (i.e., there are no more clickable regions left) you lose. Removing a region causes the rest of the grid to shuffle up: blocks that are suspended will fall down (first), and then empty columns are filled from the right. Same Game was contributed to this collection by James Harvey. \H{samegame-controls} \i{Same Game controls} \IM{Same Game controls} controls, for Same Game \IM{Same Game controls} keys, for Same Game \IM{Same Game controls} shortcuts (keyboard), for Same Game This game can be played with either the keyboard or the mouse. If you left-click an unselected region, it becomes selected (possibly clearing the current selection). If you left-click the selected region, it will be removed (and the rest of the grid shuffled immediately). If you right-click the selected region, it will be unselected. The cursor keys move a cursor around the grid. Pressing the Space or Enter keys while the cursor is in an unselected region selects it; pressing Space or Enter again removes it as above. (All the actions described in \k{common-actions} are also available.) \H{samegame-parameters} \I{parameters, for Same Game}Same Game parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{No. of colours} \dd Number of different colours used to fill the grid; the more colours, the fewer large regions of colour and thus the more difficult it is to successfully clear the grid. \dt \e{Scoring system} \dd Controls the precise mechanism used for scoring. With the default system, \q{(n-2)^2}, only regions of three squares or more will score any points at all. With the alternative \q{(n-1)^2} system, regions of two squares score a point each, and larger regions score relatively more points. \dt \e{Ensure solubility} \dd If this option is ticked (the default state), generated grids will be guaranteed to have at least one solution. \lcont{ If you turn it off, the game generator will not try to guarantee soluble grids; it will, however, still ensure that there are at least 2 squares of each colour on the grid at the start (since a grid with exactly one square of a given colour is \e{definitely} insoluble). Grids generated with this option disabled may contain more large areas of contiguous colour, leading to opportunities for higher scores; they can also take less time to generate. } \C{flip} \i{Flip} \cfg{winhelp-topic}{games.flip} You have a grid of squares, some light and some dark. Your aim is to light all the squares up at the same time. You can choose any square and flip its state from light to dark or dark to light, but when you do so, other squares around it change state as well. Each square contains a small diagram showing which other squares change when you flip it. \H{flip-controls} \i{Flip controls} \IM{Flip controls} controls, for Flip \IM{Flip controls} keys, for Flip \IM{Flip controls} shortcuts (keyboard), for Flip This game can be played with either the keyboard or the mouse. Left-click in a square to flip it and its associated squares, or use the cursor keys to choose a square and the space bar or Enter key to flip. If you use the \q{Solve} function on this game, it will mark some of the squares in red. If you click once in every square with a red mark, the game should be solved. (If you click in a square \e{without} a red mark, a red mark will appear in it to indicate that you will need to reverse that operation to reach the solution.) (All the actions described in \k{common-actions} are also available.) \H{flip-parameters} \I{parameters, for flip}Flip parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{Shape type} \dd This control determines the shape of the region which is flipped by clicking in any given square. The default setting, \q{Crosses}, causes every square to flip itself and its four immediate neighbours (or three or two if it's at an edge or corner). The other setting, \q{Random}, causes a random shape to be chosen for every square, so the game is different every time. \C{guess} \i{Guess} \cfg{winhelp-topic}{games.guess} You have a set of coloured pegs, and have to reproduce a predetermined sequence of them (chosen by the computer) within a certain number of guesses. Each guess gets marked with the number of correctly-coloured pegs in the correct places (in black), and also the number of correctly-coloured pegs in the wrong places (in white). This game is also known (and marketed, by Hasbro, mainly) as a board game \q{\i{Mastermind}}, with 6 colours, 4 pegs per row, and 10 guesses. However, this version allows custom settings of number of colours (up to 10), number of pegs per row, and number of guesses. Guess was contributed to this collection by James Harvey. \H{guess-controls} \i{Guess controls} \IM{Guess controls} controls, for Guess \IM{Guess controls} keys, for Guess \IM{Guess controls} shortcuts (keyboard), for Guess This game can be played with either the keyboard or the mouse. With the mouse, drag a coloured peg from the tray on the left-hand side to its required position in the current guess; pegs may also be dragged from current and past guesses to copy them elsewhere. To remove a peg, drag it off its current position to somewhere invalid. Right-clicking in the current guess adds a \q{hold} marker; pegs that have hold markers will be automatically added to the next guess after marking. Alternatively, with the keyboard, the up and down cursor keys can be used to select a peg colour, the left and right keys to select a peg position, and the space bar or Enter key to place a peg of the selected colour in the chosen position. \q{D} or Backspace removes a peg, and \q{H} adds a hold marker. When the guess is complete, the smaller feedback pegs will be highlighted; clicking on these (or moving the peg cursor to them with the arrow keys and pressing the space bar or Enter key) will mark the current guess, copy any held pegs to the next guess, and move the \q{current guess} marker. If you correctly position all the pegs the solution will be displayed below; if you run out of guesses (or select \q{Solve...}) the solution will also be revealed. (All the actions described in \k{common-actions} are also available.) \H{guess-parameters} \I{parameters, for Guess}Guess parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. The default game matches the parameters for the board game \q{Mastermind}. \dt \e{Colours} \dd Number of colours the solution is chosen from; from 2 to 10 (more is harder). \dt \e{Pegs per guess} \dd Number of pegs per guess (more is harder). \dt \e{Guesses} \dd Number of guesses you have to find the solution in (fewer is harder). \dt \e{Allow blanks} \dd Allows blank pegs to be given as part of a guess (makes it easier, because you know that those will never be counted as part of the solution). This is turned off by default. Note that this doesn't allow blank pegs in the solution; if you really wanted that, use one extra colour. \dt \e{Allow duplicates} \dd Allows the solution (and the guesses) to contain colours more than once; this increases the search space (making things harder), and is turned on by default. \C{pegs} \i{Pegs} \cfg{winhelp-topic}{games.pegs} A number of pegs are placed in holes on a board. You can remove a peg by jumping an adjacent peg over it (horizontally or vertically) to a vacant hole on the other side. Your aim is to remove all but one of the pegs initially present. This game, best known as \I{Solitaire, Peg}\q{Peg Solitaire}, is possibly one of the oldest puzzle games still commonly known. \H{pegs-controls} \i{Pegs controls} \IM{Pegs controls} controls, for Pegs To move a peg, drag it with the mouse from its current position to its final position. If the final position is exactly two holes away from the initial position, is currently unoccupied by a peg, and there is a peg in the intervening square, the move will be permitted and the intervening peg will be removed. Vacant spaces which you can move a peg into are marked with holes. A space with no peg and no hole is not available for moving at all: it is an obstacle which you must work around. You can also use the cursor keys to move a position indicator around the board. Pressing the return key while over a peg, followed by a cursor key, will jump the peg in that direction (if that is a legal move). (All the actions described in \k{common-actions} are also available.) \H{pegs-parameters} \I{parameters, for Pegs}Pegs parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in holes. \dt \e{Board type} \dd Controls whether you are given a board of a standard shape or a randomly generated shape. The two standard shapes currently supported are \q{Cross} and \q{Octagon} (also commonly known as the English and European traditional board layouts respectively). Selecting \q{Random} will give you a different board shape every time (but always one that is known to have a solution). \C{dominosa} \i{Dominosa} \cfg{winhelp-topic}{games.dominosa} A normal set of dominoes \dash that is, one instance of every (unordered) pair of numbers from 0 to 6 \dash has been arranged irregularly into a rectangle; then the number in each square has been written down and the dominoes themselves removed. Your task is to reconstruct the pattern by arranging the set of dominoes to match the provided array of numbers. This puzzle is widely credited to O. S. Adler, and takes part of its name from those initials. \H{dominosa-controls} \i{Dominosa controls} \IM{Dominosa controls} controls, for Dominosa Left-clicking between any two adjacent numbers places a domino covering them, or removes one if it is already present. Trying to place a domino which overlaps existing dominoes will remove the ones it overlaps. Right-clicking between two adjacent numbers draws a line between them, which you can use to remind yourself that you know those two numbers are \e{not} covered by a single domino. Right-clicking again removes the line. You can also use the cursor keys to move a cursor around the grid. When the cursor is half way between two adjacent numbers, pressing the return key will place a domino covering those numbers, or pressing the space bar will lay a line between the two squares. Repeating either action removes the domino or line. (All the actions described in \k{common-actions} are also available.) \H{dominosa-parameters} \I{parameters, for Dominosa}Dominosa parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Maximum number on dominoes} \dd Controls the size of the puzzle, by controlling the size of the set of dominoes used to make it. Dominoes with numbers going up to N will give rise to an (N+2) \by (N+1) rectangle; so, in particular, the default value of 6 gives an 8\by\.7 grid. \dt \e{Ensure unique solution} \dd Normally, Dominosa will make sure that the puzzles it presents have only one solution. Puzzles with ambiguous sections can be more difficult and sometimes more subtle, so if you like you can turn off this feature. Also, finding \e{all} the possible solutions can be an additional challenge for an advanced player. Turning off this option can also speed up puzzle generation. \C{untangle} \i{Untangle} \cfg{winhelp-topic}{games.untangle} You are given a number of points, some of which have lines drawn between them. You can move the points about arbitrarily; your aim is to position the points so that no line crosses another. I originally saw this in the form of a Flash game called \i{Planarity} \k{Planarity}, written by John Tantalo. \B{Planarity} \W{http://home.cwru.edu/~jnt5/Planarity}\cw{http://home.cwru.edu/~jnt5/Planarity} \H{untangle-controls} \i{Untangle controls} \IM{Untangle controls} controls, for Untangle To move a point, click on it with the left mouse button and drag it into a new position. (All the actions described in \k{common-actions} are also available.) \H{untangle-parameters} \I{parameters, for Untangle}Untangle parameters There is only one parameter available from the \q{Custom...} option on the \q{Type} menu: \dt \e{Number of points} \dd Controls the size of the puzzle, by specifying the number of points in the generated graph. \C{blackbox} \i{Black Box} \cfg{winhelp-topic}{games.blackbox} A number of balls are hidden in a rectangular arena. You have to deduce the positions of the balls by firing lasers positioned at the edges of the arena and observing how their beams are deflected. Beams will travel straight from their origin until they hit the opposite side of the arena (at which point they emerge), unless affected by balls in one of the following ways: \b A beam that hits a ball head-on is absorbed and will never re-emerge. This includes beams that meet a ball on the first rank of the arena. \b A beam with a ball to its front-left square gets deflected 90 degrees to the right. \b A beam with a ball to its front-right square gets similarly deflected to the left. \b A beam that would re-emerge from its entry location is considered to be \q{reflected}. \b A beam which would get deflected before entering the arena by a ball to the front-left or front-right of its entry point is also considered to be \q{reflected}. Beams that are reflected appear as a \q{R}; beams that hit balls head-on appear as \q{H}. Otherwise, a number appears at the firing point and the location where the beam emerges (this number is unique to that shot). You can place guesses as to the location of the balls, based on the entry and exit patterns of the beams; once you have placed enough balls a button appears enabling you to have your guesses checked. Here is a diagram showing how the positions of balls can create each of the beam behaviours shown above: \c 1RHR---- \c |..O.O...| \c 2........3 \c |........| \c |........| \c 3........| \c |......O.| \c H........| \c |.....O..| \c 12-RH--- As shown, it is possible for a beam to receive multiple reflections before re-emerging (see turn 3). Similarly, a beam may be reflected (possibly more than once) before receiving a hit (the \q{H} on the left side of the example). Note that any layout with more than 4 balls may have a non-unique solution. The following diagram illustrates this; if you know the board contains 5 balls, it is impossible to determine where the fifth ball is (possible positions marked with an \cw{x}): \c -------- \c |........| \c |........| \c |..O..O..| \c |...xx...| \c |...xx...| \c |..O..O..| \c |........| \c |........| \c -------- For this reason, when you have your guesses checked, the game will check that your solution \e{produces the same results} as the computer's, rather than that your solution is identical to the computer's. So in the above example, you could put the fifth ball at \e{any} of the locations marked with an \cw{x}, and you would still win. Black Box was contributed to this collection by James Harvey. \H{blackbox-controls} \i{Black Box controls} \IM{Black Box controls} controls, for Black Box \IM{Black Box controls} keys, for Black Box \IM{Black Box controls} shortcuts (keyboard), for Black Box To fire a laser beam, left-click in a square around the edge of the arena. The results will be displayed immediately. Clicking or holding the left button on one of these squares will highlight the current go (or a previous go) to confirm the exit point for that laser, if applicable. To guess the location of a ball, left-click within the arena and a black circle will appear marking the guess; click again to remove the guessed ball. Locations in the arena may be locked against modification by right-clicking; whole rows and columns may be similarly locked by right-clicking in the laser square above/below that column, or to the left/right of that row. The cursor keys may also be used to move around the grid. Pressing the Enter key will fire a laser or add a new ball-location guess, and pressing Space will lock a cell, row, or column. When an appropriate number of balls have been guessed, a button will appear at the top-left corner of the grid; clicking that (with mouse or cursor) will check your guesses. If you click the \q{check} button and your guesses are not correct, the game will show you the minimum information necessary to demonstrate this to you, so you can try again. If your ball positions are not consistent with the beam paths you already know about, one beam path will be circled to indicate that it proves you wrong. If your positions match all the existing beam paths but are still wrong, one new beam path will be revealed (written in red) which is not consistent with your current guesses. If you decide to give up completely, you can select Solve to reveal the actual ball positions. At this point, correctly-placed balls will be displayed as filled black circles, incorrectly-placed balls as filled black circles with red crosses, and missing balls as filled red circles. In addition, a red circle marks any laser you had already fired which is not consistent with your ball layout (just as when you press the \q{check} button), and red text marks any laser you \e{could} have fired in order to distinguish your ball layout from the correct one. (All the actions described in \k{common-actions} are also available.) \H{blackbox-parameters} \I{parameters, for Black Box}Black Box parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. There are 2 \by \e{Width} \by \e{Height} lasers per grid, two per row and two per column. \dt \e{No. of balls} \dd Number of balls to place in the grid. This can be a single number, or a range (separated with a hyphen, like \q{2-6}), and determines the number of balls to place on the grid. The \q{reveal} button is only enabled if you have guessed an appropriate number of balls; a guess using a different number to the original solution is still acceptable, if all the beam inputs and outputs match. \C{slant} \i{Slant} \cfg{winhelp-topic}{games.slant} You have a grid of squares. Your aim is to draw a diagonal line through each square, and choose which way each line slants so that the following conditions are met: \b The diagonal lines never form a loop. \b Any point with a circled number has precisely that many lines meeting at it. (Thus, a 4 is the centre of a cross shape, whereas a zero is the centre of a diamond shape \dash or rather, a partial diamond shape, because a zero can never appear in the middle of the grid because that would immediately cause a loop.) Credit for this puzzle goes to \i{Nikoli} \k{nikoli-slant}. \B{nikoli-slant} \W{http://www.nikoli.co.jp/puzzles/39/index.htm}\cw{http://www.nikoli.co.jp/puzzles/39/index.htm} (in Japanese) \H{slant-controls} \i{Slant controls} \IM{Slant controls} controls, for Slant Left-clicking in a blank square will place a \cw{\\} in it (a line leaning to the left, i.e. running from the top left of the square to the bottom right). Right-clicking in a blank square will place a \cw{/} in it (leaning to the right, running from top right to bottom left). Continuing to click either button will cycle between the three possible square contents. Thus, if you left-click repeatedly in a blank square it will change from blank to \cw{\\} to \cw{/} back to blank, and if you right-click repeatedly the square will change from blank to \cw{/} to \cw{\\} back to blank. (Therefore, you can play the game entirely with one button if you need to.) You can also use the cursor keys to move around the grid. Pressing the return or space keys will place a \cw{\\} or a \cw{/}, respectively, and will then cycle them as above. (All the actions described in \k{common-actions} are also available.) \H{slant-parameters} \I{parameters, for Slant}Slant parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{Difficulty} \dd Controls the difficulty of the generated puzzle. At Hard level, you are required to do deductions based on knowledge of \e{relationships} between squares rather than always being able to deduce the exact contents of one square at a time. (For example, you might know that two squares slant in the same direction, even if you don't yet know what that direction is, and this might enable you to deduce something about still other squares.) Even at Hard level, guesswork and backtracking should never be necessary. \C{lightup} \i{Light Up} \cfg{winhelp-topic}{games.lightup} You have a grid of squares. Some are filled in black; some of the black squares are numbered. Your aim is to \q{light up} all the empty squares by placing light bulbs in some of them. Each light bulb illuminates the square it is on, plus all squares in line with it horizontally or vertically unless a black square is blocking the way. To win the game, you must satisfy the following conditions: \b All non-black squares are lit. \b No light is lit by another light. \b All numbered black squares have exactly that number of lights adjacent to them (in the four squares above, below, and to the side). Non-numbered black squares may have any number of lights adjacent to them. Credit for this puzzle goes to \i{Nikoli} \k{nikoli-lightup}. Light Up was contributed to this collection by James Harvey. \B{nikoli-lightup} \W{http://www.nikoli.co.jp/puzzles/32/index-e.htm}\cw{http://www.nikoli.co.jp/puzzles/32/index-e.htm} (beware of Flash) \H{lightup-controls} \i{Light Up controls} \IM{Light Up controls} controls, for Light Up Left-clicking in a non-black square will toggle the presence of a light in that square. Right-clicking in a non-black square toggles a mark there to aid solving; it can be used to highlight squares that cannot be lit, for example. You may not place a light in a marked square, nor place a mark in a lit square. The game will highlight obvious errors in red. Lights lit by other lights are highlighted in this way, as are numbered squares which do not (or cannot) have the right number of lights next to them. Thus, the grid is solved when all non-black squares have yellow highlights and there are no red lights. (All the actions described in \k{common-actions} are also available.) \H{lightup-parameters} \I{parameters, for Light Up}Light Up parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{%age of black squares} \dd Rough percentage of black squares in the grid. \lcont{ This is a hint rather than an instruction. If the grid generator is unable to generate a puzzle to this precise specification, it will increase the proportion of black squares until it can. } \dt \e{Symmetry} \dd Allows you to specify the required symmetry of the black squares in the grid. (This does not affect the difficulty of the puzzles noticeably.) \dt \e{Difficulty} \dd \q{Easy} means that the puzzles should be soluble without backtracking or guessing, \q{Hard} means that some guesses will probably be necessary. \C{map} \i{Map} \cfg{winhelp-topic}{games.map} You are given a map consisting of a number of regions. Your task is to colour each region with one of four colours, in such a way that no two regions sharing a boundary have the same colour. You are provided with some regions already coloured, sufficient to make the remainder of the solution unique. Only regions which share a length of border are required to be different colours. Two regions which meet at only one \e{point} (i.e. are diagonally separated) may be the same colour. I believe this puzzle is original; I've never seen an implementation of it anywhere else. The concept of a \i{four-colouring} puzzle was suggested by Owen Dunn; credit must also go to Nikoli and to Verity Allan for inspiring the train of thought that led to me realising Owen's suggestion was a viable puzzle. Thanks also to Gareth Taylor for many detailed suggestions. \H{map-controls} \i{Map controls} \IM{Map controls} controls, for Map To colour a region, click the left mouse button on an existing region of the desired colour and drag that colour into the new region. (The program will always ensure the starting puzzle has at least one region of each colour, so that this is always possible!) If you need to clear a region, you can drag from an empty region, or from the puzzle boundary if there are no empty regions left. Dragging a colour using the \e{right} mouse button will stipple the region in that colour, which you can use as a note to yourself that you think the region \e{might} be that colour. A region can contain stipples in multiple colours at once. (This is often useful at the harder difficulty levels.) You can also use the cursor keys to move around the map: the colour of the cursor indicates the position of the colour you would drag (which is not obvious if you're on a region's boundary, since it depends on the direction from which you approached the boundary). Pressing the return key starts a drag of that colour, as above, which you control with the cursor keys; pressing the return key again finishes the drag. The space bar can be used similarly to create a stippled region. Double-pressing the return key (without moving the cursor) will clear the region, as a drag from an empty region does: this is useful with the cursor mode if you have filled the entire map in but need to correct the layout. If you press L during play, the game will toggle display of a number in each region of the map. This is useful if you want to discuss a particular puzzle instance with a friend \dash having an unambiguous name for each region is much easier than trying to refer to them all by names such as \q{the one down and right of the brown one on the top border}. (All the actions described in \k{common-actions} are also available.) \H{map-parameters} \I{parameters, for Map}Map parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{Regions} \dd Number of regions in the generated map. \dt \e{Difficulty} \dd In \q{Easy} mode, there should always be at least one region whose colour can be determined trivially. In \q{Normal} and \q{Hard} modes, you will have to use increasingly complex logic to deduce the colour of some regions. However, it will always be possible without having to guess or backtrack. \lcont{ In \q{Unreasonable} mode, the program will feel free to generate puzzles which are as hard as it can possibly make them: the only constraint is that they should still have a unique solution. Solving Unreasonable puzzles may require guessing and backtracking. } \C{loopy} \i{Loopy} \cfg{winhelp-topic}{games.loopy} You are given a grid of dots, marked with yellow lines to indicate which dots you are allowed to connect directly together. Your aim is to use some subset of those yellow lines to draw a single unbroken loop from dot to dot within the grid. Some of the spaces between the lines contain numbers. These numbers indicate how many of the lines around that space form part of the loop. The loop you draw must correctly satisfy all of these clues to be considered a correct solution. In the default mode, the dots are arranged in a grid of squares; however, you can also play on triangular or hexagonal grids, or even more exotic ones. Credit for the basic puzzle idea goes to \i{Nikoli} \k{nikoli-loopy}. Loopy was originally contributed to this collection by Mike Pinna, and subsequently enhanced to handle various types of non-square grid by Lambros Lambrou. \B{nikoli-loopy} \W{http://www.nikoli.co.jp/puzzles/3/index-e.htm}\cw{http://www.nikoli.co.jp/puzzles/3/index-e.htm} (beware of Flash) \H{loopy-controls} \i{Loopy controls} \IM{Loopy controls} controls, for Loopy Click the left mouse button on a yellow line to turn it black, indicating that you think it is part of the loop. Click again to turn the line yellow again (meaning you aren't sure yet). If you are sure that a particular line segment is \e{not} part of the loop, you can click the right mouse button to remove it completely. Again, clicking a second time will turn the line back to yellow. (All the actions described in \k{common-actions} are also available.) \H{loopy-parameters} \I{parameters, for Loopy}Loopy parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid, measured in number of regions across and down. For square grids, it's clear how this is counted; for other types of grid you may have to think a bit to see how the dimensions are measured. \dt \e{Grid type} \dd Allows you to choose between a selection of types of tiling. Some have all the faces the same but may have multiple different types of vertex (e.g. the \e{Cairo} or \e{Kites} mode); others have all the vertices the same but may have different types of face (e.g. the \e{Great Hexagonal}). The square, triangular and honeycomb grids are fully regular, and have all their vertices \e{and} faces the same; this makes them the least confusing to play. \dt \e{Difficulty} \dd Controls the difficulty of the generated puzzle. \#{FIXME: what distinguishes Easy, Medium, and Hard? In particular, when are backtracking/guesswork required, if ever?} \C{inertia} \i{Inertia} \cfg{winhelp-topic}{games.inertia} You are a small green ball sitting in a grid full of obstacles. Your aim is to collect all the gems without running into any mines. You can move the ball in any orthogonal \e{or diagonal} direction. Once the ball starts moving, it will continue until something stops it. A wall directly in its path will stop it (but if it is moving diagonally, it will move through a diagonal gap between two other walls without stopping). Also, some of the squares are \q{stops}; when the ball moves on to a stop, it will stop moving no matter what direction it was going in. Gems do \e{not} stop the ball; it picks them up and keeps on going. Running into a mine is fatal. Even if you picked up the last gem in the same move which then hit a mine, the game will count you as dead rather than victorious. This game was originally implemented for Windows by Ben Olmstead \k{bem}, who was kind enough to release his source code on request so that it could be re-implemented for this collection. \B{bem} \W{http://xn13.com/}\cw{http://xn13.com/} \H{inertia-controls} \i{Inertia controls} \IM{Inertia controls} controls, for Inertia \IM{Inertia controls} keys, for Inertia \IM{Inertia controls} shortcuts (keyboard), for Inertia You can move the ball in any of the eight directions using the numeric keypad. Alternatively, if you click the left mouse button on the grid, the ball will begin a move in the general direction of where you clicked. If you use the \q{Solve} function on this game, the program will compute a path through the grid which collects all the remaining gems and returns to the current position. A hint arrow will appear on the ball indicating the direction in which you should move to begin on this path. If you then move in that direction, the arrow will update to indicate the next direction on the path. You can also press Space to automatically move in the direction of the hint arrow. If you move in a different direction from the one shown by the arrow, the hint arrows will stop appearing because you have strayed from the provided path; you can then use \q{Solve} again to generate a new path if you want to. All the actions described in \k{common-actions} are also available. In particular, if you do run into a mine and die, you can use the Undo function and resume playing from before the fatal move. The game will keep track of the number of times you have done this. \H{inertia-parameters} \I{parameters, for Inertia}Inertia parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \C{tents} \i{Tents} \cfg{winhelp-topic}{games.tents} You have a grid of squares, some of which contain trees. Your aim is to place tents in some of the remaining squares, in such a way that the following conditions are met: \b There are exactly as many tents as trees. \b The tents and trees can be matched up in such a way that each tent is directly adjacent (horizontally or vertically, but not diagonally) to its own tree. However, a tent may be adjacent to other trees as well as its own. \b No two tents are adjacent horizontally, vertically \e{or diagonally}. \b The number of tents in each row, and in each column, matches the numbers given round the sides of the grid. This puzzle can be found in several places on the Internet, and was brought to my attention by e-mail. I don't know who I should credit for inventing it. \H{tents-controls} \i{Tents controls} \IM{Tents controls} controls, for Tents Left-clicking in a blank square will place a tent in it. Right-clicking in a blank square will colour it green, indicating that you are sure it \e{isn't} a tent. Clicking either button in an occupied square will clear it. If you \e{drag} with the right button along a row or column, every blank square in the region you cover will be turned green, and no other squares will be affected. (This is useful for clearing the remainder of a row once you have placed all its tents.) You can also use the cursor keys to move around the grid. Pressing the return key over an empty square will place a tent, and pressing the space bar over an empty square will colour it green; either key will clear an occupied square. (All the actions described in \k{common-actions} are also available.) \H{tents-parameters} \I{parameters, for Tents}Tents parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{Difficulty} \dd Controls the difficulty of the generated puzzle. More difficult puzzles require more complex deductions, but at present none of the available difficulty levels requires guesswork or backtracking. \C{bridges} \i{Bridges} \cfg{winhelp-topic}{games.bridges} You have a set of islands distributed across the playing area. Each island contains a number. Your aim is to connect the islands together with bridges, in such a way that: \b Bridges run horizontally or vertically. \b The number of bridges terminating at any island is equal to the number written in that island. \b Two bridges may run in parallel between the same two islands, but no more than two may do so. \b No bridge crosses another bridge. \b All the islands are connected together. There are some configurable alternative modes, which involve changing the parallel-bridge limit to something other than 2, and introducing the additional constraint that no sequence of bridges may form a loop from one island back to the same island. The rules stated above are the default ones. Credit for this puzzle goes to \i{Nikoli} \k{nikoli-bridges}. Bridges was contributed to this collection by James Harvey. \B{nikoli-bridges} \W{http://www.nikoli.co.jp/puzzles/14/index-e.htm}\cw{http://www.nikoli.co.jp/puzzles/14/index-e.htm} \H{bridges-controls} \i{Bridges controls} \IM{Bridges controls} controls, for Bridges To place a bridge between two islands, click the mouse down on one island and drag it towards the other. You do not need to drag all the way to the other island; you only need to move the mouse far enough for the intended bridge direction to be unambiguous. (So you can keep the mouse near the starting island and conveniently throw bridges out from it in many directions.) Doing this again when a bridge is already present will add another parallel bridge. If there are already as many bridges between the two islands as permitted by the current game rules (i.e. two by default), the same dragging action will remove all of them. If you want to remind yourself that two islands definitely \e{do not} have a bridge between them, you can right-drag between them in the same way to draw a \q{non-bridge} marker. If you think you have finished with an island (i.e. you have placed all its bridges and are confident that they are in the right places), you can mark the island as finished by left-clicking on it. This will highlight it and all the bridges connected to it, and you will be prevented from accidentally modifying any of those bridges in future. Left-clicking again on a highlighted island will unmark it and restore your ability to modify it. You can also use the cursor keys to move around the grid: if possible the cursor will always move orthogonally, otherwise it will move towards the nearest island to the indicated direction. Pressing the return key followed by a cursor key will lay a bridge in that direction (if available); pressing the space bar followed by a cursor key will lay a \q{non-bridge} marker. You can mark an island as finished by pressing the return key twice. Violations of the puzzle rules will be marked in red: \b An island with too many bridges will be highlighted in red. \b An island with too few bridges will be highlighted in red if it is definitely an error (as opposed to merely not being finished yet): if adding enough bridges would involve having to cross another bridge or remove a non-bridge marker, or if the island has been highlighted as complete. \b A group of islands and bridges may be highlighted in red if it is a closed subset of the puzzle with no way to connect it to the rest of the islands. For example, if you directly connect two 1s together with a bridge and they are not the only two islands on the grid, they will light up red to indicate that such a group cannot be contained in any valid solution. \b If you have selected the (non-default) option to disallow loops in the solution, a group of bridges which forms a loop will be highlighted. (All the actions described in \k{common-actions} are also available.) \H{bridges-parameters} \I{parameters, for Bridges}Bridges parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{Difficulty} \dd Difficulty level of puzzle. \dt \e{Allow loops} \dd This is set by default. If cleared, puzzles will be generated in such a way that they are always soluble without creating a loop, and solutions which do involve a loop will be disallowed. \dt \e{Max. bridges per direction} \dd Maximum number of bridges in any particular direction. The default is 2, but you can change it to 1, 3 or 4. In general, fewer is easier. \dt \e{%age of island squares} \dd Gives a rough percentage of islands the generator will try and lay before finishing the puzzle. Certain layouts will not manage to lay enough islands; this is an upper bound. \dt \e{Expansion factor (%age)} \dd The grid generator works by picking an existing island at random (after first creating an initial island somewhere). It then decides on a direction (at random), and then works out how far it could extend before creating another island. This parameter determines how likely it is to extend as far as it can, rather than choosing somewhere closer. High expansion factors usually mean easier puzzles with fewer possible islands; low expansion factors can create lots of tightly-packed islands. \C{unequal} \i{Unequal} \cfg{winhelp-topic}{games.unequal} You have a square grid; each square may contain a digit from 1 to the size of the grid, and some squares have clue signs between them. Your aim is to fully populate the grid with numbers such that: \b Each row contains only one occurrence of each digit \b Each column contains only one occurrence of each digit \b All the clue signs are satisfied. There are two modes for this game, \q{Unequal} and \q{Adjacent}. In \q{Unequal} mode, the clue signs are greater-than symbols indicating one square's value is greater than its neighbour's. In this mode not all clues may be visible, particularly at higher difficulty levels. In \q{Adjacent} mode, the clue signs are bars indicating one square's value is numerically adjacent (i.e. one higher or one lower) than its neighbour. In this mode all clues are always visible: absence of a bar thus means that a square's value is definitely not numerically adjacent to that neighbour's. In \q{Trivial} difficulty level (available via the \q{Custom} game type selector), there are no greater-than signs in \q{Unequal} mode; the puzzle is to solve the \i{Latin square} only. At the time of writing, the \q{Unequal} mode of this puzzle is appearing in the Guardian weekly under the name \q{\i{Futoshiki}}. Unequal was contributed to this collection by James Harvey. \H{unequal-controls} \i{Unequal controls} \IM{Unequal controls} controls, for Unequal Unequal shares much of its control system with Solo. To play Unequal, simply click the mouse in any empty square and then type a digit or letter on the keyboard to fill that square. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature). If you \e{right}-click in a square and then type a number, that number will be entered in the square as a \q{pencil mark}. You can have pencil marks for multiple numbers in the same square. Squares containing filled-in numbers cannot also contain pencil marks. The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular number, or you can use them as lists of the possible numbers in a given square, or anything else you feel like. To erase a single pencil mark, right-click in the square and type the same number again. All pencil marks in a square are erased when you left-click and type a number, or when you left-click and press space. Right-clicking and pressing space will also erase pencil marks. As for Solo, the cursor keys can be used in conjunction with the digit keys to set numbers or pencil marks. You can also use the 'M' key to auto-fill every numeric hint, ready for removal as required, or the 'H' key to do the same but also to remove all obvious hints. Alternatively, use the cursor keys to move the mark around the grid. Pressing the return key toggles the mark (from a normal mark to a pencil mark), and typing a number in is entered in the square in the appropriate way; typing in a 0 or using the space bar will clear a filled square. (All the actions described in \k{common-actions} are also available.) \H{unequal-parameters} \I{parameters, for Unequal}Unequal parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Mode} \dd Mode of the puzzle (\q{Unequal} or \q{Adjacent}) \dt \e{Size (s*s)} \dd Size of grid. \dt \e{Difficulty} \dd Controls the difficulty of the generated puzzle. At Trivial level, there are no greater-than signs; the puzzle is to solve the Latin square only. At Recursive level (only available via the \q{Custom} game type selector) backtracking will be required, but the solution should still be unique. The levels in between require increasingly complex reasoning to avoid having to backtrack. \C{galaxies} \i{Galaxies} \cfg{winhelp-topic}{games.galaxies} You have a rectangular grid containing a number of dots. Your aim is to draw edges along the grid lines which divide the rectangle into regions in such a way that every region is 180\u00b0{-degree} rotationally symmetric, and contains exactly one dot which is located at its centre of symmetry. This puzzle was invented by \i{Nikoli} \k{nikoli-galaxies}, under the name \q{Tentai Show}; its name is commonly translated into English as \q{Spiral Galaxies}. Galaxies was contributed to this collection by James Harvey. \B{nikoli-galaxies} \W{http://www.nikoli.co.jp/en/puzzles/astronomical_show/}\cw{http://www.nikoli.co.jp/en/puzzles/astronomical_show/} \H{galaxies-controls} \i{Galaxies controls} \IM{Galaxies controls} controls, for Galaxies Left-click on any grid line to draw an edge if there isn't one already, or to remove one if there is. When you create a valid region (one which is closed, contains exactly one dot, is 180\u00b0{-degree} symmetric about that dot, and contains no extraneous edges inside it) it will be highlighted automatically; so your aim is to have the whole grid highlighted in that way. During solving, you might know that a particular grid square belongs to a specific dot, but not be sure of where the edges go and which other squares are connected to the dot. In order to mark this so you don't forget, you can right-click on the dot and drag, which will create an arrow marker pointing at the dot. Drop that in a square of your choice and it will remind you which dot it's associated with. You can also right-click on existing arrows to pick them up and move them, or destroy them by dropping them off the edge of the grid. (Also, if you're not sure which dot an arrow is pointing at, you can pick it up and move it around to make it clearer. It will swivel constantly as you drag it, to stay pointed at its parent dot.) You can also use the cursor keys to move around the grid squares and lines. Pressing the return key when over a grid line will draw or clear its edge, as above. Pressing the return key when over a dot will pick up an arrow, to be dropped the next time the return key is pressed; this can also be used to move existing arrows around, removing them by dropping them on a dot or another arrow. (All the actions described in \k{common-actions} are also available.) \H{galaxies-parameters} \I{parameters, for Galaxies}Galaxies parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{Difficulty} \dd Controls the difficulty of the generated puzzle. More difficult puzzles require more complex deductions, and the \q{Unreasonable} difficulty level may require backtracking. \C{filling} \i{Filling} \cfg{winhelp-topic}{games.filling} You have a grid of squares, some of which contain digits, and the rest of which are empty. Your job is to fill in digits in the empty squares, in such a way that each connected region of squares all containing the same digit has an area equal to that digit. (\q{Connected region}, for the purposes of this game, does not count diagonally separated squares as adjacent.) For example, it follows that no square can contain a zero, and that two adjacent squares can not both contain a one. No region has an area greater than 9 (because then its area would not be a single digit). Credit for this puzzle goes to \i{Nikoli} \k{nikoli-fillomino}. Filling was contributed to this collection by Jonas K\u00F6{oe}lker. \B{nikoli-fillomino} \W{http://www.nikoli.co.jp/en/puzzles/fillomino/}\cw{http://www.nikoli.co.jp/en/puzzles/fillomino/} \H{filling-controls} \I{controls, for Filling}Filling controls To play Filling, simply click the mouse in any empty square and then type a digit on the keyboard to fill that square. By dragging the mouse, you can select multiple squares to fill with a single keypress. If you make a mistake, click the mouse in the incorrect square and press 0, Space, Backspace or Enter to clear it again (or use the Undo feature). You can also move around the grid with the cursor keys; typing a digit will fill the square containing the cursor with that number, or typing 0, Space, or Enter will clear it. You can also select multiple squares for numbering or clearing by using the return key, before typing a digit to fill in the highlighted squares (as above). (All the actions described in \k{common-actions} are also available.) \H{filling-parameters} \I{parameters, for Filling}Filling parameters Filling allows you to configure the number of rows and columns of the grid, through the \q{Type} menu. \C{keen} \i{Keen} \cfg{winhelp-topic}{games.keen} You have a square grid; each square may contain a digit from 1 to the size of the grid. The grid is divided into blocks of varying shape and size, with arithmetic clues written in them. Your aim is to fully populate the grid with digits such that: \b Each row contains only one occurrence of each digit \b Each column contains only one occurrence of each digit \b The digits in each block can be combined to form the number stated in the clue, using the arithmetic operation given in the clue. That is: \lcont{ \b An addition clue means that the sum of the digits in the block must be the given number. For example, \q{15+} means the contents of the block adds up to fifteen. \b A multiplication clue (e.g. \q{60\times}), similarly, means that the product of the digits in the block must be the given number. \b A subtraction clue will always be written in a block of size two, and it means that one of the digits in the block is greater than the other by the given amount. For example, \q{2\minus} means that one of the digits in the block is 2 more than the other, or equivalently that one digit minus the other one is 2. The two digits could be either way round, though. \b A division clue (e.g. \q{3\divide}), similarly, is always in a block of size two and means that one digit divided by the other is equal to the given amount. Note that a block may contain the same digit more than once (provided the identical ones are not in the same row and column). This rule is precisely the opposite of the rule in Solo's \q{Killer} mode (see \k{solo}). } This puzzle appears in the Times under the name \q{\i{KenKen}}. \H{keen-controls} \i{Keen controls} \IM{Keen controls} controls, for Keen Keen shares much of its control system with Solo (and Unequal). To play Keen, simply click the mouse in any empty square and then type a digit on the keyboard to fill that square. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature). If you \e{right}-click in a square and then type a number, that number will be entered in the square as a \q{pencil mark}. You can have pencil marks for multiple numbers in the same square. Squares containing filled-in numbers cannot also contain pencil marks. The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular number, or you can use them as lists of the possible numbers in a given square, or anything else you feel like. To erase a single pencil mark, right-click in the square and type the same number again. All pencil marks in a square are erased when you left-click and type a number, or when you left-click and press space. Right-clicking and pressing space will also erase pencil marks. As for Solo, the cursor keys can be used in conjunction with the digit keys to set numbers or pencil marks. Use the cursor keys to move a highlight around the grid, and type a digit to enter it in the highlighted square. Pressing return toggles the highlight into a mode in which you can enter or remove pencil marks. Pressing M will fill in a full set of pencil marks in every square that does not have a main digit in it. (All the actions described in \k{common-actions} are also available.) \H{keen-parameters} \I{parameters, for Keen}Keen parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Grid size} \dd Specifies the size of the grid. Lower limit is 3; upper limit is 9 (because the user interface would become more difficult with \q{digits} bigger than 9!). \dt \e{Difficulty} \dd Controls the difficulty of the generated puzzle. At Unreasonable level, some backtracking will be required, but the solution should still be unique. The remaining levels require increasingly complex reasoning to avoid having to backtrack. \C{towers} \i{Towers} \cfg{winhelp-topic}{games.towers} You have a square grid. On each square of the grid you can build a tower, with its height ranging from 1 to the size of the grid. Around the edge of the grid are some numeric clues. Your task is to build a tower on every square, in such a way that: \b Each row contains every possible height of tower once \b Each column contains every possible height of tower once \b Each numeric clue describes the number of towers that can be seen if you look into the square from that direction, assuming that shorter towers are hidden behind taller ones. For example, in a 5\by\.5 grid, a clue marked \q{5} indicates that the five tower heights must appear in increasing order (otherwise you would not be able to see all five towers), whereas a clue marked \q{1} indicates that the tallest tower (the one marked 5) must come first. In harder or larger puzzles, some towers will be specified for you as well as the clues round the edge, and some edge clues may be missing. This puzzle appears on the web under various names, particularly \q{\i{Skyscrapers}}, but I don't know who first invented it. \H{towers-controls} \i{Towers controls} \IM{Towers controls} controls, for Towers Towers shares much of its control system with Solo, Unequal and Keen. To play Towers, simply click the mouse in any empty square and then type a digit on the keyboard to fill that square with a tower of the given height. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature). If you \e{right}-click in a square and then type a number, that number will be entered in the square as a \q{pencil mark}. You can have pencil marks for multiple numbers in the same square. A square containing a tower cannot also contain pencil marks. The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular number, or you can use them as lists of the possible numbers in a given square, or anything else you feel like. To erase a single pencil mark, right-click in the square and type the same number again. All pencil marks in a square are erased when you left-click and type a number, or when you left-click and press space. Right-clicking and pressing space will also erase pencil marks. As for Solo, the cursor keys can be used in conjunction with the digit keys to set numbers or pencil marks. Use the cursor keys to move a highlight around the grid, and type a digit to enter it in the highlighted square. Pressing return toggles the highlight into a mode in which you can enter or remove pencil marks. Pressing M will fill in a full set of pencil marks in every square that does not have a main digit in it. (All the actions described in \k{common-actions} are also available.) \H{towers-parameters} \I{parameters, for Towers}Towers parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Grid size} \dd Specifies the size of the grid. Lower limit is 3; upper limit is 9 (because the user interface would become more difficult with \q{digits} bigger than 9!). \dt \e{Difficulty} \dd Controls the difficulty of the generated puzzle. At Unreasonable level, some backtracking will be required, but the solution should still be unique. The remaining levels require increasingly complex reasoning to avoid having to backtrack. \C{singles} \i{Singles} \cfg{winhelp-topic}{games.singles} You have a grid of white squares, all of which contain numbers. Your task is to colour some of the squares black (removing the number) so as to satisfy all of the following conditions: \b No number occurs more than once in any row or column. \b No black square is horizontally or vertically adjacent to any other black square. \b The remaining white squares must all form one contiguous region (connected by edges, not just touching at corners). Credit for this puzzle goes to \i{Nikoli} \k{nikoli-hitori} who call it \i{Hitori}. Singles was contributed to this collection by James Harvey. \B{nikoli-hitori} \W{http://www.nikoli.com/en/puzzles/hitori/index.html}\cw{http://www.nikoli.com/en/puzzles/hitori/index.html} (beware of Flash) \H{singles-controls} \i{Singles controls} \IM{Singles controls} controls, for Singles Left-clicking on an empty square will colour it black; left-clicking again will restore the number. Right-clicking will add a circle (useful for indicating that a cell is definitely not black). You can also use the cursor keys to move around the grid. Pressing the return or space keys will turn a square black or add a circle respectively, and pressing the key again will restore the number or remove the circle. (All the actions described in \k{common-actions} are also available.) \H{singles-parameters} \I{parameters, for Singles}Singles parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{Difficulty} \dd Controls the difficulty of the generated puzzle. \C{magnets} \i{Magnets} \cfg{winhelp-topic}{games.magnets} A rectangular grid has been filled with a mixture of magnets (that is, dominoes with one positive end and one negative end) and blank dominoes (that is, dominoes with two neutral poles). These dominoes are initially only seen in silhouette. Around the grid are placed a number of clues indicating the number of positive and negative poles contained in certain columns and rows. Your aim is to correctly place the magnets and blank dominoes such that all the clues are satisfied, with the additional constraint that no two similar magnetic poles may be orthogonally adjacent (since they repel). Neutral poles do not repel, and can be adjacent to any other pole. Credit for this puzzle goes to \i{Janko} \k{janko-magnets}. Magnets was contributed to this collection by James Harvey. \B{janko-magnets} \W{http://www.janko.at/Raetsel/Magnete/index.htm}\cw{http://www.janko.at/Raetsel/Magnete/index.htm} \H{magnets-controls} \i{Magnets controls} \IM{Magnets controls} controls, for Magnets Left-clicking on an empty square places a magnet at that position with the positive pole on the square and the negative pole on the other half of the magnet; left-clicking again reverses the polarity, and a third click removes the magnet. Right-clicking on an empty square places a blank domino there. Right-clicking again places two question marks on the domino, signifying \q{this cannot be blank} (which can be useful to note deductions while solving), and right-clicking again empties the domino. You can also use the cursor keys to move a cursor around the grid. Pressing the return key will lay a domino with a positive pole at that position; pressing again reverses the polarity and then removes the domino, as with left-clicking. Using the space bar allows placement of blank dominoes and cannot-be-blank hints, as for right-clicking. (All the actions described in \k{common-actions} are also available.) \H{magnets-parameters} \I{parameters, for Magnets}Magnets parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. There will be half \e{Width} \by \e{Height} dominoes in the grid: if this number is odd then one square will be blank. (Grids with at least one odd dimension tend to be easier to solve.) \dt \e{Difficulty} \dd Controls the difficulty of the generated puzzle. At Tricky level, you are required to make more deductions about empty dominoes and row/column counts. \dt \e{Strip clues} \dd If true, some of the clues around the grid are removed at generation time, making the puzzle more difficult. \C{signpost} \i{Signpost} \cfg{winhelp-topic}{games.signpost} You have a grid of squares; each square (except the last one) contains an arrow, and some squares also contain numbers. Your job is to connect the squares to form a continuous list of numbers starting at 1 and linked in the direction of the arrows \dash so the arrow inside the square with the number 1 will point to the square containing the number 2, which will point to the square containing the number 3, etc. Each square can be any distance away from the previous one, as long as it is somewhere in the direction of the arrow. By convention the first and last numbers are shown; one or more interim numbers may also appear at the beginning. Credit for this puzzle goes to \i{Janko} \k{janko-arrowpath}, who call it \q{Pfeilpfad} (\q{arrow path}). Signpost was contributed to this collection by James Harvey. \B{janko-arrowpath} \W{http://janko.at/Raetsel/Pfeilpfad/index.htm}\cw{http://janko.at/Raetsel/Pfeilpfad/index.htm} \H{signpost-controls} \I{controls, for Signpost}Signpost controls To play Signpost, you connect squares together by dragging from one square to another, indicating that they are adjacent in the sequence. Drag with the left button from a square to its successor, or with the right button from a square to its predecessor. If you connect together two squares in this way and one of them has a number in it, the appropriate number will appear in the other square. If you connect two non-numbered squares, they will be assigned temporary algebraic labels: on the first occasion, they will be labelled \cq{a} and \cq{a+1}, and then \cq{b} and \cq{b+1}, and so on. Connecting more squares on to the ends of such a chain will cause them all to be labelled with the same letter. When you left-click or right-click in a square, the legal squares to connect it to will be shown. The arrow in each square starts off black, and goes grey once you connect the square to its successor. Also, each square which needs a predecessor has a small dot in the bottom left corner, which vanishes once you link a square to it. So your aim is always to connect a square with a black arrow to a square with a dot. To remove any links for a particular square (both incoming and outgoing), left-drag it off the grid. To remove a whole chain, right-drag any square in the chain off the grid. You can also use the cursor keys to move around the grid squares and lines. Pressing the return key when over a square starts a link operation, and pressing the return key again over a square will finish the link, if allowable. Pressing the space bar over a square will show the other squares pointing to it, and allow you to form a backward link, and pressing the space bar again cancels this. (All the actions described in \k{common-actions} are also available.) \H{signpost-parameters} \I{parameters, for Signpost}Signpost parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{Force start/end to corners} \dd If true, the start and end squares are always placed in opposite corners (the start at the top left, and the end at the bottom right). If false the start and end squares are placed randomly (although always both shown). \C{range} \i{Range} \cfg{winhelp-topic}{games.range} You have a grid of squares; some squares contain numbers. Your job is to colour some of the squares black, such that several criteria are satisfied: \b no square with a number is coloured black. \b no two black squares are adjacent (horizontally or vertically). \b for any two white squares, there is a path between them using only white squares. \b for each square with a number, that number denotes the total number of white squares reachable from that square going in a straight line in any horizontal or vertical direction until hitting a wall or a black square; the square with the number is included in the total (once). For instance, a square containing the number one must have four black squares as its neighbours by the last criterion; but then it's impossible for it to be connected to any outside white square, which violates the second to last criterion. So no square will contain the number one. Credit for this puzzle goes to \i{Nikoli}, who have variously called it \q{Kurodoko}, \q{Kuromasu} or \q{Where is Black Cells}. \k{nikoli-range}. Range was contributed to this collection by Jonas K\u00F6{oe}lker. \B{nikoli-range} \W{http://www.nikoli.co.jp/en/puzzles/where_is_black_cells/}\cw{http://www.nikoli.co.jp/en/puzzles/where_is_black_cells/} \H{range-controls} \I{controls, for Range}Range controls Click with the left button to paint a square black, or with the right button to mark a square with a dot to indicate that you are sure it should \e{not} be painted black. Repeated clicking with either button will cycle the square through the three possible states (filled, dotted or empty) in opposite directions. You can also use the cursor keys to move around the grid squares. Pressing Return does the same as clicking with the left button, while pressing Space does the same as a right button click. (All the actions described in \k{common-actions} are also available.) \H{range-parameters} \I{parameters, for Range}Range parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \C{pearl} \i{Pearl} \cfg{winhelp-topic}{games.pearl} You have a grid of squares. Your job is to draw lines between the centres of horizontally or vertically adjacent squares, so that the lines form a single closed loop. In the resulting grid, some of the squares that the loop passes through will contain corners, and some will be straight horizontal or vertical lines. (And some squares can be completely empty \dash the loop doesn't have to pass through every square.) Some of the squares contain black and white circles, which are clues that the loop must satisfy. A black circle in a square indicates that that square is a corner, but neither of the squares adjacent to it in the loop is also a corner. A white circle indicates that the square is a straight edge, but \e{at least one} of the squares adjacent to it in the loop is a corner. (In both cases, the clue only constrains the two squares adjacent \e{in the loop}, that is, the squares that the loop passes into after leaving the clue square. The squares that are only adjacent \e{in the grid} are not constrained.) Credit for this puzzle goes to \i{Nikoli}, who call it \q{Masyu}. \k{nikoli-pearl}. Thanks to James Harvey for assistance with the implementation. \B{nikoli-pearl} \W{http://www.nikoli.co.jp/en/puzzles/masyu/}\cw{http://www.nikoli.co.jp/en/puzzles/masyu/} \H{pearl-controls} \I{controls, for Pearl}Pearl controls Click with the left button on a grid edge to draw a segment of the loop through that edge, or to remove a segment once it is drawn. Drag with the left button through a series of squares to draw more than one segment of the loop in one go. Alternatively, drag over an existing part of the loop to undraw it, or to undraw part of it and then go in a different direction. Click with the right button on a grid edge to mark it with a cross, indicating that you are sure the loop does not go through that edge. (For instance, if you have decided which of the squares adjacent to a white clue has to be a corner, but don't yet know which way the corner turns, you might mark the one way it \e{can't} go with a cross.) Alternatively, use the cursor keys to move the cursor. Use the Enter key to begin and end keyboard `drag' operations. Use the Space key to cancel the drag. Use Ctrl-arrowkey and Shift-arrowkey to simulate a left or right click, respectively, on the edge in the given direction relative to the cursor, i.e. to draw a segment or a cross. (All the actions described in \k{common-actions} are also available.) \H{pearl-parameters} \I{parameters, for Pearl}Pearl parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \C{undead} \i{Undead} \cfg{winhelp-topic}{games.undead} You are given a grid of squares, some of which contain diagonal mirrors. Every square which is not a mirror must be filled with one of three types of undead monster: a ghost, a vampire, or a zombie. Vampires can be seen directly, but are invisible when reflected in mirrors. Ghosts are the opposite way round: they can be seen in mirrors, but are invisible when looked at directly. Zombies are visible by any means. You are also told the total number of each type of monster in the grid. Also around the edge of the grid are written numbers, which indicate how many monsters can be seen if you look into the grid along a row or column starting from that position. (The diagonal mirrors are reflective on both sides. If your reflected line of sight crosses the same monster more than once, the number will count it each time it is visible, not just once.) This puzzle type was invented by David Millar, under the name \q{Haunted Mirror Maze}. See \k{janko-undead} for more details. Undead was contributed to this collection by Steffen Bauer. \B{janko-undead} \W{http://www.janko.at/Raetsel/Spukschloss/index.htm}\cw{http://www.janko.at/Raetsel/Spukschloss/index.htm} \H{undead-controls} \I{controls, for Undead}Undead controls Undead has a similar control system to Solo, Unequal and Keen. To play Undead, click the mouse in any empty square and then type a letter on the keyboard indicating the type of monster: \q{G} for a ghost, \q{V} for a vampire, or \q{Z} for a zombie. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature). If you \e{right}-click in a square and then type a letter, the corresponding monster will be shown in reduced size in that square, as a \q{pencil mark}. You can have pencil marks for multiple monsters in the same square. A square containing a full-size monster cannot also contain pencil marks. The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular monster, or you can use them as lists of the possible monster in a given square, or anything else you feel like. To erase a single pencil mark, right-click in the square and type the same letter again. All pencil marks in a square are erased when you left-click and type a monster letter, or when you left-click and press Space. Right-clicking and pressing space will also erase pencil marks. As for Solo, the cursor keys can be used in conjunction with the letter keys to place monsters or pencil marks. Use the cursor keys to move a highlight around the grid, and type a monster letter to enter it in the highlighted square. Pressing return toggles the highlight into a mode in which you can enter or remove pencil marks. If you prefer plain letters of the alphabet to cute monster pictures, you can press \q{A} to toggle between showing the monsters as monsters or showing them as letters. (All the actions described in \k{common-actions} are also available.) \H{undead-parameters} \I{parameters, for Undead}Undead parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. \dt \e{Difficulty} \dd Controls the difficulty of the generated puzzle. \C{unruly} \i{Unruly} \cfg{winhelp-topic}{games.unruly} You are given a grid of squares, which you must colour either black or white. Some squares are provided as clues; the rest are left for you to fill in. Each row and column must contain the same number of black and white squares, and no row or column may contain three consecutive squares of the same colour. This puzzle type was invented by Adolfo Zanellati, under the name \q{Tohu wa Vohu}. See \k{janko-unruly} for more details. Unruly was contributed to this collection by Lennard Sprong. \B{janko-unruly} \W{http://www.janko.at/Raetsel/Tohu-Wa-Vohu/index.htm}\cw{http://www.janko.at/Raetsel/Tohu-Wa-Vohu/index.htm} \H{unruly-controls} \I{controls, for Unruly}Unruly controls To play Unruly, click the mouse in a square to change its colour. Left-clicking an empty square will turn it black, and right-clicking will turn it white. Keep clicking the same button to cycle through the three possible states for the square. If you middle-click in a square it will be reset to empty. You can also use the cursor keys to move around the grid. Pressing the return or space keys will turn an empty square black or white respectively (and then cycle the colours in the same way as the mouse buttons), and pressing Backspace will reset a square to empty. (All the actions described in \k{common-actions} are also available.) \H{unruly-parameters} \I{parameters, for Unruly}Unruly parameters These parameters are available from the \q{Custom...} option on the \q{Type} menu. \dt \e{Width}, \e{Height} \dd Size of grid in squares. (Note that the rules of the game require both the width and height to be even numbers.) \dt \e{Difficulty} \dd Controls the difficulty of the generated puzzle. \A{licence} \I{MIT licence}\ii{Licence} This software is \i{copyright} 2004-2012 Simon Tatham. Portions copyright Richard Boulton, James Harvey, Mike Pinna, Jonas K\u00F6{oe}lker, Dariusz Olszewski, Michael Schierl, Lambros Lambrou, Bernd Schmidt, Steffen Bauer, Lennard Sprong and Rogier Goossens. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \q{Software}), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED \q{AS IS}, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \IM{command-line}{command line} command line \IM{default parameters, specifying} default parameters, specifying \IM{default parameters, specifying} preferences, specifying default \IM{Unix} Unix \IM{Unix} Linux \IM{generating game IDs} generating game IDs \IM{generating game IDs} game ID, generating \IM{specific} \q{Specific}, menu option \IM{custom} \q{Custom}, menu option \IM{game ID} game ID \IM{game ID} ID, game \IM{ID format} ID format \IM{ID format} format, ID \IM{ID format} game ID, format \IM{keys} keys \IM{keys} shortcuts (keyboard) \IM{initial state} initial state \IM{initial state} state, initial \IM{MIT licence} MIT licence \IM{MIT licence} licence, MIT puzzles-r9872/osx-info.plist0000644000175300017530000000057310175162201015244 0ustar simonsimon CFBundleIconFile Puzzles.icns CFBundleHelpBookFolder Help CFBundleHelpBookName Puzzles Help puzzles-r9872/osx.icns0000644000175300017530000013671510174704177014141 0ustar simonsimonicnsics#His326,,.,,-,@JJA?C=Y,ȧfCv;,`z:T@- \i2v=,ŞZ9*{W;RoGIqdq[M@A/-.$+:>hq_fgs=v1:=cl;`llw@Îy1:Sy~uB}`[]Q_b\*ֶy]7#Ǿÿȟ"ʴø…ŝÅÿ ŜȩÁ®…ĜLн&ŜIϾÅŜIϾÄŜIоsĿŜIϽÀÃŜIϽÀʅˡIнqIо/=I2;MĮ 2CA=98)J8 e xrusstwc8  uŐz~w8 uŽ~yu8  )DBW~zu8  b7~yu8   6YUaAxy~u8 |Ľ<Í}it8 x¨i~y|sgx~i8 x~lt8x Îlt8yϹzlt;nyxxzX8;7_|}|~|~~mjkktt>hq_fgs{|lt=cl;`llw~ÎltSy~uq~`[]UJ]bRֶp6Ǿȡ"ʴø…ŜÅÿ ŜȩÁ®…ĜLн&ŜIϾÅŜIϾÄŜIоsĿŜIϽÀÃŜIϽÀʅˡIнqIо/=I2;MĮ 2CA=98)J8 e xrusstwc8  uŐz~w8 uŽ~yu8  )DBW~zu8  b7~yu8   6YUaAxy~u8 |Ľ<Í}it8 x¨i~y|sgx~i8 x~lt8x Îlt8yϹzlt;nyxxzX8;7_|}|~|~~mjkktt>hq_fgs{{kt=cl;`llw~ÏlsSy~uy~ֶx< Ǿ Ǟaʴø…ŜaÅÿ ŜƯ7Á®¿ĜIн&¯ŜIϾ®…ŜIϾ¯„ŜIоsĿŜIϽÀ¯ƒŜIϽÀ(±ȞIн˜ Iо ϭ 旰I ݆LĮ >d^>baeV l8mkich#Hih329;<;;<;<<;;98:=@?Bs;&#$/V~lstA<#fs?<#d}]WXXW\s?<#c¼~ s?< %eƀ¼} s?<!('$X} s?<?dy{\} s?<!d͜} s?;# &0/.4H|Ɩ}C<% ;As?<  HƖ}Ys?<Gɘľ~Vt?< Gé8nv zIxvvxh@<G{Rq?<G¼~Us?< G}Us?<Gý~ Vs?< GĺyRs?9AG?**)* )&FzB"  s?0co p[acbhm~tbj nAs?0am$lmmn[jmlp|˙Ws?/`lkklmYjkkoyƖĽVt?4gnq)r_pqqt}ƙt~~O~~k;N}s!muuspINQ/UVH)jـݶ=Rx~~}lB2'++&ȽɰĻÀ*ǽ/ ۾Ɣ‚ôĀŔˢӨ̤ œǢՆՂȹ„+œǥóp€ Ɠ@sĂ Ɠ= sœ>sĀŔ=¿sƓ=܀sĉ Ɠ=c;s Ŕ=Ǧn¿9²sƓ=csƓ>YsƓ=sǂ˘=ÿt o= y= /= y?+=³y@ += y@+BΦƸ|B3.ޕQ9;<;;<;<7$Z;&#$$A_poonoonolHONR-<$hʀĄR<#d}fhfN<#c¼~wuN< %eƀ¼}wsN<!('$X}wtN<?dy{\}wtN<!d͜}wsN;# &0/.4H|Ɩ}bsfdpbN<  HƖ}nhN<Gɘľ~qjO< Gé8nv xdfpvv{D<G{miL<G¼~pjN< G}pjN<Gý~ qjN< Gֿs~}}gjN9AG?**)*)&Fwpoprnpohi huwN0co p[acbhnslt vflN0amlmmn[jmlp|ʣprjN/`lkklmYjkkoyšpĽqkO4gnq)r_pqqt}Ţot~~hbz~~IN}s`iuuspIN P@>PUUY1jـݶ=vx~~}v_/'+,ȽɰĽ€*/  ۾œ‚ôĀŔˢӨ ̤ œǢՆՂȹ„+œǥóp€ Ɠ@sĂ Ɠ= sœ>sĀŔ=¿sƓ=܀sĉ Ɠ=c;s Ŕ=Ǧn¿9²sƓ=csƓ>YsƓ=sǂ˘=ÿt o= y= /= y?+=³y@ += y@+BΦƸ|B3.ޕQ9;<;;<;<7$Z;&#$$A_poonoonolHONR-<$hʀĄR<#d}fhfN<#c¼~wuN< %eƀ¼}wsN<!('$X}wtN<?dy{\}wtN<!d͜}wsN;# &0/.4H|Ɩ}bsfdpbN<  HƖ}nhN<Gɘľ~qjO< Gé8nv xdfpvv{D<G{miL<G¼~pjN< G}pjN<Gý~ qjN< Gֿs~}}gjN9AG?**)**+)&Fwpopropohi huwN0cop[acbhnslt vflN0amlmmn[jmlp|ʣprjN/`lkklmYjkkoyšo»}oiM4gnq)r_pqqt}ŢpwpUN}!sj~ jـݶ=wfw˯'ȽɰĽ¿*B۾ œR‚ôĀŔɚSӨ ̤ œƚJՆՂȹ„+œť#Ž€ Ɠ>ŠÂ Ɠ= ġœ>ĠÀŔ=¿ĠƓ=܀ ġÉ Ɠ=c;Ġ Ŕ=Ǧn¿9ġƓ=cġƓ>YĠƓ=,ģʗ=ÿŊ p=ӓхҁ y=ӌ s=͌ r= ό!sBΦ׌) 䲆_PSI'RQV?h8mk it32G91++ 9J\o91+#999 9Qg{Q999 >Qg{Q999 >QgɇɇQ999 9QgY999 >Qg{ɃɅQ999 9QgɄQ999 9Qg{ɇQ999 >Qg{ɏQ999 9Qg{ɇɀQ999 9Qg{Q999 >Qg{ɇɇQ999 >Qg{ɀQ999 9Qg{ɃɅQ999ɈQ999Q999ɆQ999 >QgɃɀQ999 9QgQ999 9Qg{Q999 >Qg{ɆQ999 9Qg{ɀɈQ999 9Qg{Q9GQ>g{ɃQ999 ɂɄɀQ999ɅQ999ɀɀɀɀɀɀQ999݀ɅQ999ɅQ999ɆQ999ɀɀɀɃQ999ɮmgQQYQYQQ999ɀɖQ999ɖQ999ɓY999ɀɋɇQ999ɄɎQ999݀ɃɅɆQ999ɖQ9>9ɏɃQ9>9݀ɀɐQ999ɅɀɄɁQ999ɆɀɈQ999ɏɃQ999ɀɒQ999ɑɁQ999ɈgY9#9>9>9>Q# 99mmgmgmggm9mgm{{_91# 99mgmgmgmgmm9m gmmgmmgQ99ggmgmgmgm9mgmgmmgm{gQ99mgmgmm9m {gɃQ99m9m gmgɁɀQ99mgmgmgmgm9mgm{gɈQ99gmgm9gmgm {{gɂɀQ99mgmgmgmmgmgm9m{_ɆQ99mgmgm9mgmgm g{{gQ99mgmgmgmmgmgmmgg9mgmgɈQG>>G>m{mmgm{{m{m{m{{mm{m{m{{>{{g{m{{mm{{g GvvGQQQ{{{{{{{{mQ{{{{m>gg QQg_{Q+>|5WW_Y݄݄g{Y>>g|gff,GG_g{{gmmgYYQYQ#919 mmӀӁ݁ӈmYg|gg|gR{m{Ɉg{|{ɇɀ{|{+-+9{ӂӝ{ӀY" ɮݷY QQɇɊɋQɁɀɍQɊɗQɗɄQɊɄɃɂQɉɀɎɀ񮪒񦃒>񝪒񝂒>ɆɀɀɀQɇɈɍɀɂ睇{_QɅɖ|fQɀɈɏ|fQɈɀɀɀɀɃ|fQɌɑɊ|fQɅ|fQɄ|fQɘɀɃ|fQɑ|fQɀɈɀɄɂ|fQɃ|fQɄɔ|fQɄɀɈɀɀɀ|fQɓɆ|fQɀɁɀɀɀɈɃ|fQɈɀ|fQɄɀ|fQɆɅQGQGQGQ|fQɃ_m|fQɧɃ1#|fQɈɊ|fQɀɀɀɀɀɆɆɅ Q|fQɄɄɆɃQ |fQɈɀɀɃ_|fQ_|fQɄɄɊɆ 1|fQɀɀɄɀɂɄ{|fQ_|fQɀɀɀ1|fQɊɄɂ|fQɌɑ|fQ|fQɀɀɀɄɀ|fQɀɀɄɐɅ |fQ9+ |fQ@+ _ɀɁ|fQ9- _ |fQ>- _Ʉ |fQ>+ _ɀ|fQ>+ _|fQ>+ _ |fQ>+ _|fQ>+ _|fQ>+ _|fQ>+ _Ɂ |fQ>+  _񦒈|fQ>+ _񝪒|fQ>+ _ݶӷgM6 ӷgQ1#91++ 93,% 91+#999 9Qg{Q999 >Qg{Q999 >QgɇɇQ999 9QgY999 >Qg{ɃɅQ999 9QgɄQ999 9Qg{ɇQ999 >Qg{ɏQ999 9Qg{ɇɀQ999 9Qg{Q999 >Qg{ɇɇQ999 >Qg{ɀQ999 9Qg{ɃɅQ999ɈQ999Q999ɆQ999 >QgɃɀQ999 9QgQ999 9Qg{Q999 >Qg{ɆQ999 9Qg{ɀɈQ999 9Qg{Q9GQ>g{ɃQ999 ɂɄɀQ999ɅQ999ɀɀɀɀɀɀQ999݀ɅQ999ɅQ999ɆQ999ɀɀɀɃQ999ɮmgQQYQYQQ999ɀɖQ999ɖQ999ɓY999ɀɋɇQ999ɄɎQ999݀ɃɅɆQ999ɖQ9>9ɏɃQ9>9݀ɀɐQ999ɅɀɄɁQ999ɆɀɈQ999ɏɃQ999ɀɒQ999ɑɁQ999ɈgY9#9>9>9>Q#99mmgmgmggm9mgm{{_91# 99mgmgmgmgmm9m gmmgmmgQ99ggmgmgmgm9mgmgmmgm{gQ99mgmgmm9m {gɃQ99m9m gmgɁɀQ99mgmgmgmgm9mgm{gɈQ99gmgm9gmgm {{gɂɀQ99mgmgmgmmgmgm9m{_ɆQ99mgmgm9mgmgm g{{gQ99mgmgmgmmgmgmmgg9mgmgǷɈQD>>G>m{mmgm{{m{m{m{{mm{m{m{{>{{g{m{{mm{{g GvvGQQQ{{{{{{{{mQ{{{{m>gg QQg_{Q+|5WW_Y݄݄g{Y>g|gff,GG_g{{gmmgYYQYlQ#919 mmӀӁ݁ӈmYgggg{m{Ɉg{{ɇɀ{{+-+{ӂӝ{ӀǮY͒͒ ɮݷYǮǮ QQɇɊɋQɁɀɍQɊɗQɗɄQɊɄɃɂQɉɀɎɀ񮪒񦃒>Ǯ񝪒񝂒>ɆɀɀɀQɇɈɍɀɂ睇{_QɅɖ|fQɀɈɏ|fQɈɀɀɀɀɃ|fQɌɑɊ|fQɅ|fQɄ|fQɘɀɃ|fQɑ|fQɀɈɀɄɂ|fQɃ|fQɄɔ|fQɄɀɈɀɀɀ|fQɓɆ|fQɀɁɀɀɀɈɃ|fQɈɀ|fQɄɀ|fQɆɅQGQGQGQ|fQɃ_m|fQɧɃ1#|fQɈɊ|fQɀɀɀɀɀɆɆɅ Q|fQɄɄɆɃQ |fQɈɀɀɃ_|fQ_|fQɄɄɊɆ 1|fQɀɀɄɀɂɄ{|fQ_|fQɀɀɀ1|fQɊɄɂ|fQɌɑ|fQ|fQɀɀɀɄɀ|fQɀɀɄɐɅ |fQ9+ |fQ@+ _ɀɁ|fQ9- _ |fQ>- _Ʉ |fQ>+ _ɀ|fQ>+ _|fQ>+ _ |fQ>+ _|fQ>+ _|fQ>+ _|fQ>+ _Ɂ |fQ>+  _񦒈|fQ>+ _񝪒|fQ>+ _ݶӷgM6 ӷgQ1#91++ 93,% 91+#999 9Qg{Q999 >Qg{Q999 >QgɇɇQ999 9QgY999 >Qg{ɃɅQ999 9QgɄQ999 9Qg{ɇQ999 >Qg{ɏQ999 9Qg{ɇɀQ999 9Qg{Q999 >Qg{ɇɇQ999 >Qg{ɀQ999 9Qg{ɃɅQ999ɈQ999Q999ɆQ999 >QgɃɀQ999 9QgQ999 9Qg{Q999 >Qg{ɆQ999 9Qg{ɀɈQ999 9Qg{Q9GQ>g{ɃQ999 ɂɄɀQ999ɅQ999ɀɀɀɀɀɀQ999݀ɅQ999ɅQ999ɆQ999ɀɀɀɃQ999ɮmgQQYQYQQ999ɀɖQ999ɖQ999ɓY999ɀɋɇQ999ɄɎQ999݀ɃɅɆQ999ɖQ9>9ɏɃQ9>9݀ɀɐQ999ɅɀɄɁQ999ɆɀɈQ999ɏɃQ999ɀɒQ999ɑɁQ999ɈgY9#9>9>9>Q#99mmgmgmggm9mgm{{_91# 99mgmgmgmgmm9m gmmgmmgQ99ggmgmgmgm9mgmgmmgm{gQ99mgmgmm9m {gɃQ99m9m gmgɁɀQ99mgmgmgmgm9mgm{gɈQ99gmgm9gmgm {{gɂɀQ99mgmgmgmmgmgm9m{_ɆQ99mgmgm9mgmgm g{{gQ99mgmgmgmmgmgmmgg9mgmgǷɈQD>>G>m{mmgm{{m{m{m{{mm{m{m{{>{{g{m{{mm{{g aGQQQ{{{{{{{{mQ{{{{mq## ##QQg_{Q+9999_Y݄݄g{Y>҃ؓRRRR_g{{gmmgYYQYlQ#91 lRmmӀӁ݁ӈmYgggg%{m{Ɉg{{ɇɀ{{+6{ӂӝ{ӀǮY͒͒% ɮݷYǮǮ Q QɇɊɋ QɁɀɍ QɊɗ QɗɄ QɊɄɃɂ QɉɀɎɀ 񮪒񦃒>Ǯ 񝪒񝂒>ɆɀɀɀQɇɈɍɀɂ 睇{_QɅɖQɀɈɏQɈɀɀɀɀɃQɌɑɊQɅQɄQɘɀɃQɑQɀɈɀɄɂQɃQɄɔQɄɀɈɀɀɀQɓɆQɀɁɀɀɀɈɃQɈɀQɄɀQɆɅQGQGQGQQɃ_mQɧɃ1#QɈɊQɀɀɀɀɀɆɆɅ QQɄɄɆɃQ QɈɀɀɃ_Q_QɄɄɊɆ 1QɀɀɄɀɂɄ{Q_Qɀɀɀ1QɊɄɂQɌɑQQɀɀɀɄɀQɀɀɄɐɅ Q9+ عH _ɀɁ6 _ 6_Ʉ _ɀ _ _ _  _ _ _Ɂ  _񦒈  _񝪒  _ݶ ӷgQ1#t8mk@puzzles-r9872/LICENCE0000644000175300017530000000237312132033107013407 0ustar simonsimonThis software is copyright (c) 2004-2012 Simon Tatham. Portions copyright Richard Boulton, James Harvey, Mike Pinna, Jonas Klker, Dariusz Olszewski, Michael Schierl, Lambros Lambrou, Bernd Schmidt, Steffen Bauer, Lennard Sprong and Rogier Goossens. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. puzzles-r9872/README0000644000175300017530000000474510571552521013322 0ustar simonsimonThis is the README accompanying the source code to Simon Tatham's puzzle collection. The collection's web site is at . If you've obtained the source code by downloading a .tar.gz archive from the Puzzles web site, you should find several Makefiles in the source code. However, if you've checked the source code out from the Puzzles Subversion repository, you won't find the Makefiles: they're automatically generated by `mkfiles.pl', so run that to create them. The Makefiles include: - `Makefile' should work under GNU make on Linux, provided you have GTK installed to compile and link against. It builds GTK binaries of the puzzle games. - `Makefile.vc' should work under MS Visual C++ on Windows. - `Makefile.cyg' should work under Cygwin / MinGW. With appropriate tweaks and setting of TOOLPATH, it should work for both compiling on Windows and cross-compiling on Unix. - `Makefile.osx' should work under Mac OS X, provided the Xcode tools are installed. It builds a single monolithic OS X application capable of running any of the puzzles, or even more than one of them at a time. - `Makefile.wce' should work under MS eMbedded Visual C++ on Windows and the Pocket PC SDK; it builds Pocket PC binaries. Many of these Makefiles build a program called `nullgame' in addition to the actual game binaries. This program doesn't do anything; it's just a template for people to start from when adding a new game to the collection, and it's compiled every time to ensure that it _does_ compile and link successfully (because otherwise it wouldn't be much use as a template). Once it's built, you can run it if you really want to (but it's very boring), and then you should ignore it. DO NOT EDIT THE MAKEFILES DIRECTLY, if you plan to send any changes back to the maintainer. The makefiles are generated automatically by the Perl script `mkfiles.pl' from the file `Recipe' and the various .R files. If you need to change the makefiles as part of a patch, you should change Recipe, *.R, and/or mkfiles.pl. The manual is provided in Windows Help format for the Windows build; in text format for anyone who needs it; and in HTML for the Mac OS X application and for the web site. It is generated from a Halibut source file (puzzles.but), which is the preferred form for modification. To generate the manual in other formats, rebuild it, or learn about Halibut, visit the Halibut website at . puzzles-r9872/Recipe0000644000175300017530000001634012160402300013547 0ustar simonsimon# -*- 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 !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 !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 # 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 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 # Version management. !begin vc version.obj: *.c *.h cl $(VER) $(CFLAGS) /c version.c !end !specialobj vc version !begin wce version.obj: *.c *.h $(CC) $(VER) $(CFLAGS) /c version.c !end !specialobj wce version !begin cygwin version.o: FORCE; FORCE: $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) $(VER) -c version.c !end !specialobj cygwin version # For Unix, we also need the gross MD5 hack that causes automatic # version number selection in release source archives. !begin gtk version.o: version.c version2.def $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) `cat version2.def` -c version.c version2.def: FORCE if test -z "$(VER)" && test -f manifest && md5sum -c manifest; then \ cat version.def > version2.def.new; \ elif test -z "$(VER)" && test -d .svn && svnversion . >/dev/null 2>&1; then \ echo "-DREVISION=`svnversion .`" >version2.def.new; \ else \ echo "$(VER)" >version2.def.new; \ fi && \ if diff -q version2.def.new version2.def; then \ rm version2.def.new; \ else \ mv version2.def.new version2.def; \ fi .PHONY: FORCE !end !specialobj gtk version !begin nestedvm version.o: version.c version2.def $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) `cat version2.def` -c version.c version2.def: FORCE if test -z "$(VER)" && test -f manifest && md5sum -c manifest; then \ cat version.def > version2.def.new; \ elif test -z "$(VER)" && test -d .svn && svnversion . >/dev/null 2>&1; then \ echo "-DREVISION=`svnversion .`" >version2.def.new; \ else \ echo "$(VER)" >version2.def.new; \ fi && \ if diff -q version2.def.new version2.def; then \ rm version2.def.new; \ else \ mv version2.def.new version2.def; \ fi .PHONY: FORCE !end !specialobj nestedvm version # For OS X, this is made more fiddly by the fact that we don't have # md5sum readily available. We do, however, have `md5 -r' which # generates _nearly_ the same output, but it has no check function. !begin osx version.ppc.o: version.c version2.def $(CC) -arch ppc $(COMPAT) $(XFLAGS) $(CFLAGS) `cat version2.def` -c version.c -o $@ version.i386.o: version.c version2.def $(CC) -arch i386 $(COMPAT) $(XFLAGS) $(CFLAGS) `cat version2.def` -c version.c -o $@ version2.def: FORCE if test -z "$(VER)" && test -f manifest && (md5 -r `awk '{print $$2}' manifest` | diff -w manifest -); then \ cat version.def > version2.def.new; \ elif test -z "$(VER)" && test -d .svn && svnversion . >/dev/null 2>&1; then \ echo "-DREVISION=`svnversion .`" >version2.def.new; \ else \ echo "$(VER)" >version2.def.new; \ fi && \ if diff -q version2.def.new version2.def; then \ rm version2.def.new; \ else \ mv version2.def.new version2.def; \ fi .PHONY: FORCE !end !specialobj osx version # 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 .PRECIOUS: %.class %.class: %.mips java -cp $(NESTEDVM)/build:$(NESTEDVM)/upstream/build/classgen/build \ org.ibex.nestedvm.Compiler -outformat class -d . \ PuzzleEngine $< mv PuzzleEngine.class $@ 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 echo "Main-Class: PuzzleApplet" >applet.manifest PuzzleApplet.class: PuzzleApplet.java org javac -source 1.3 -target 1.3 PuzzleApplet.java %.jar: %.class PuzzleApplet.class org mv $< PuzzleEngine.class jar cfm $@ applet.manifest PuzzleEngine.class PuzzleApplet*.class org echo '' >$*.html mv PuzzleEngine.class $< !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: $(GAMES) for i in $(GAMES); do \ for params in $$(env -i ./$(BINPREFIX)$$i --list-presets | cut -f1 -d' '); do \ env -i ./$(BINPREFIX)$$i --test-solve --time-generation --generate 100 $$params \ || exit 1; \ done; \ done > $@ !end puzzles-r9872/puzzles.rc20000644000175300017530000000312710570642243014557 0ustar simonsimon/* 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 */ puzzles-r9872/mkfiles.pl0000755000175300017530000016151712125644025014433 0ustar simonsimon#!/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; @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 scalar 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 { ${$divert} .= "$_\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 "!begin") { if ($_[1] =~ /^>(.*)/) { $divert = \$auxfiles{$1}; } elsif (&mfval($_[1])) { $divert = \$makefile_extra{$_[1]}; } 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","mpw","nestedvm","osx","wce","gnustep","emcc")) { 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 (); } # Now we're ready to output the actual Makefiles. 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'; $orig_dir = cwd; ##-- 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{'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 = ('i386'); ##-- 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=10.4 \$(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=10.4 \$(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=10.4 -x objective-c \$(COMPAT) \$(FWHACK) \$(CFLAGS)". " \$(XFLAGS)$deflist -c \$< -o \$\@\n"; } else { print "\t\$(CC) -arch $arch -mmacosx-version-min=10.4 \$(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; } puzzles-r9872/Makefile0000644000175300017530000010564712161170560014101 0ustar simonsimon# Makefile for puzzles under X/GTK and Unix. # # This file was created by `mkfiles.pl' from the `Recipe' file. # DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead. # You can define this path to point at your tools if you need to # TOOLPATH = /opt/gcc/bin CC := $(TOOLPATH)$(CC) # You can manually set this to `gtk-config' or `pkg-config gtk+-1.2' # (depending on what works on your system) if you want to enforce # building with GTK 1.2, or you can set it to `pkg-config gtk+-2.0' # if you want to enforce 2.0. The default is to try 2.0 and fall back # to 1.2 if it isn't found. GTK_CONFIG = sh -c 'pkg-config gtk+-2.0 $$0 2>/dev/null || gtk-config $$0' CFLAGS := -O2 -Wall -Werror -ansi -pedantic -g -I./ -Iicons/ `$(GTK_CONFIG) \ --cflags` $(CFLAGS) XLIBS = `$(GTK_CONFIG) --libs` -lm ULIBS = -lm# INSTALL=install INSTALL_PROGRAM=$(INSTALL) INSTALL_DATA=$(INSTALL) prefix=/usr/local exec_prefix=$(prefix) bindir=$(exec_prefix)/bin gamesdir=$(exec_prefix)/games mandir=$(prefix)/man man1dir=$(mandir)/man1 all: $(BINPREFIX)blackbox $(BINPREFIX)bridges $(BINPREFIX)cube \ $(BINPREFIX)dominosa $(BINPREFIX)fifteen $(BINPREFIX)filling \ $(BINPREFIX)fillingsolver $(BINPREFIX)flip \ $(BINPREFIX)galaxies $(BINPREFIX)galaxiespicture \ $(BINPREFIX)galaxiessolver $(BINPREFIX)guess \ $(BINPREFIX)inertia $(BINPREFIX)keen $(BINPREFIX)keensolver \ $(BINPREFIX)latincheck $(BINPREFIX)lightup \ $(BINPREFIX)lightupsolver $(BINPREFIX)loopy \ $(BINPREFIX)loopysolver $(BINPREFIX)magnets \ $(BINPREFIX)magnetssolver $(BINPREFIX)map \ $(BINPREFIX)mapsolver $(BINPREFIX)mineobfusc \ $(BINPREFIX)mines $(BINPREFIX)net $(BINPREFIX)netslide \ $(BINPREFIX)nullgame $(BINPREFIX)obfusc $(BINPREFIX)pattern \ $(BINPREFIX)patternsolver $(BINPREFIX)pearl \ $(BINPREFIX)pearlbench $(BINPREFIX)pegs $(BINPREFIX)range \ $(BINPREFIX)rect $(BINPREFIX)samegame $(BINPREFIX)signpost \ $(BINPREFIX)signpostsolver $(BINPREFIX)singles \ $(BINPREFIX)singlessolver $(BINPREFIX)sixteen \ $(BINPREFIX)slant $(BINPREFIX)slantsolver $(BINPREFIX)solo \ $(BINPREFIX)solosolver $(BINPREFIX)tents \ $(BINPREFIX)tentssolver $(BINPREFIX)towers \ $(BINPREFIX)towerssolver $(BINPREFIX)twiddle \ $(BINPREFIX)undead $(BINPREFIX)unequal \ $(BINPREFIX)unequalsolver $(BINPREFIX)unruly \ $(BINPREFIX)unrulysolver $(BINPREFIX)untangle $(BINPREFIX)blackbox: blackbox.o blackbox-icon.o drawing.o gtk.o malloc.o \ midend.o misc.o printing.o ps.o random.o version.o $(CC) -o $@ blackbox.o blackbox-icon.o drawing.o gtk.o malloc.o \ midend.o misc.o printing.o ps.o random.o version.o \ $(XLFLAGS) $(XLIBS) $(BINPREFIX)bridges: bridges.o bridges-icon.o drawing.o dsf.o gtk.o malloc.o \ midend.o misc.o printing.o ps.o random.o version.o $(CC) -o $@ bridges.o bridges-icon.o drawing.o dsf.o gtk.o malloc.o \ midend.o misc.o printing.o ps.o random.o version.o \ $(XLFLAGS) $(XLIBS) $(BINPREFIX)cube: cube.o cube-icon.o drawing.o gtk.o malloc.o midend.o \ misc.o printing.o ps.o random.o version.o $(CC) -o $@ cube.o cube-icon.o drawing.o gtk.o malloc.o midend.o \ misc.o printing.o ps.o random.o version.o $(XLFLAGS) \ $(XLIBS) $(BINPREFIX)dominosa: dominosa.o dominosa-icon.o drawing.o gtk.o laydomino.o \ malloc.o midend.o misc.o printing.o ps.o random.o version.o $(CC) -o $@ dominosa.o dominosa-icon.o drawing.o gtk.o laydomino.o \ malloc.o midend.o misc.o printing.o ps.o random.o version.o \ $(XLFLAGS) $(XLIBS) $(BINPREFIX)fifteen: drawing.o fifteen.o fifteen-icon.o gtk.o malloc.o \ midend.o misc.o printing.o ps.o random.o version.o $(CC) -o $@ drawing.o fifteen.o fifteen-icon.o gtk.o malloc.o \ midend.o misc.o printing.o ps.o random.o version.o \ $(XLFLAGS) $(XLIBS) $(BINPREFIX)filling: drawing.o dsf.o filling.o filling-icon.o gtk.o malloc.o \ midend.o misc.o printing.o ps.o random.o version.o $(CC) -o $@ drawing.o dsf.o filling.o filling-icon.o gtk.o malloc.o \ midend.o misc.o printing.o ps.o random.o version.o \ $(XLFLAGS) $(XLIBS) $(BINPREFIX)fillingsolver: dsf.o filling2.o malloc.o misc.o nullfe.o \ random.o $(CC) -o $@ dsf.o filling2.o malloc.o misc.o nullfe.o random.o \ $(XLFLAGS) $(ULIBS) $(BINPREFIX)flip: drawing.o flip.o flip-icon.o gtk.o malloc.o midend.o \ misc.o printing.o ps.o random.o tree234.o version.o $(CC) -o $@ drawing.o flip.o flip-icon.o gtk.o malloc.o midend.o \ misc.o printing.o ps.o random.o tree234.o version.o \ $(XLFLAGS) $(XLIBS) $(BINPREFIX)galaxies: drawing.o dsf.o galaxies.o galaxies-icon.o gtk.o \ malloc.o midend.o misc.o printing.o ps.o random.o version.o $(CC) -o $@ drawing.o dsf.o galaxies.o galaxies-icon.o gtk.o \ malloc.o midend.o misc.o printing.o ps.o random.o version.o \ $(XLFLAGS) $(XLIBS) $(BINPREFIX)galaxiespicture: dsf.o galaxie4.o malloc.o misc.o nullfe.o \ random.o $(CC) -o $@ dsf.o galaxie4.o malloc.o misc.o nullfe.o random.o -lm \ $(XLFLAGS) $(ULIBS) $(BINPREFIX)galaxiessolver: dsf.o galaxie2.o malloc.o misc.o nullfe.o \ random.o $(CC) -o $@ dsf.o galaxie2.o malloc.o misc.o nullfe.o random.o -lm \ $(XLFLAGS) $(ULIBS) $(BINPREFIX)guess: drawing.o gtk.o guess.o guess-icon.o malloc.o midend.o \ misc.o printing.o ps.o random.o version.o $(CC) -o $@ drawing.o gtk.o guess.o guess-icon.o malloc.o midend.o \ misc.o printing.o ps.o random.o version.o $(XLFLAGS) \ $(XLIBS) $(BINPREFIX)inertia: drawing.o gtk.o inertia.o inertia-icon.o malloc.o \ midend.o misc.o printing.o ps.o random.o version.o $(CC) -o $@ drawing.o gtk.o inertia.o inertia-icon.o malloc.o \ midend.o misc.o printing.o ps.o random.o version.o \ $(XLFLAGS) $(XLIBS) $(BINPREFIX)keen: drawing.o dsf.o gtk.o keen.o keen-icon.o latin.o malloc.o \ maxflow.o midend.o misc.o printing.o ps.o random.o tree234.o \ version.o $(CC) -o $@ drawing.o dsf.o gtk.o keen.o keen-icon.o latin.o \ malloc.o maxflow.o midend.o misc.o printing.o ps.o random.o \ tree234.o version.o $(XLFLAGS) $(XLIBS) $(BINPREFIX)keensolver: dsf.o keen2.o latin6.o malloc.o maxflow.o misc.o \ nullfe.o random.o tree234.o $(CC) -o $@ dsf.o keen2.o latin6.o malloc.o maxflow.o misc.o \ nullfe.o random.o tree234.o $(XLFLAGS) $(ULIBS) $(BINPREFIX)latincheck: latin8.o malloc.o maxflow.o misc.o nullfe.o random.o \ tree234.o $(CC) -o $@ latin8.o malloc.o maxflow.o misc.o nullfe.o random.o \ tree234.o $(XLFLAGS) $(ULIBS) $(BINPREFIX)lightup: combi.o drawing.o gtk.o lightup.o lightup-icon.o \ malloc.o midend.o misc.o printing.o ps.o random.o version.o $(CC) -o $@ combi.o drawing.o gtk.o lightup.o lightup-icon.o \ malloc.o midend.o misc.o printing.o ps.o random.o version.o \ $(XLFLAGS) $(XLIBS) $(BINPREFIX)lightupsolver: combi.o lightup2.o malloc.o misc.o nullfe.o \ random.o $(CC) -o $@ combi.o lightup2.o malloc.o misc.o nullfe.o random.o \ $(XLFLAGS) $(ULIBS) $(BINPREFIX)loopy: drawing.o dsf.o grid.o gtk.o loopgen.o loopy.o \ loopy-icon.o malloc.o midend.o misc.o penrose.o printing.o \ ps.o random.o tree234.o version.o $(CC) -o $@ drawing.o dsf.o grid.o gtk.o loopgen.o loopy.o \ loopy-icon.o malloc.o midend.o misc.o penrose.o printing.o \ ps.o random.o tree234.o version.o $(XLFLAGS) $(XLIBS) $(BINPREFIX)loopysolver: dsf.o grid.o loopgen.o loopy2.o malloc.o misc.o \ nullfe.o penrose.o random.o tree234.o $(CC) -o $@ dsf.o grid.o loopgen.o loopy2.o malloc.o misc.o nullfe.o \ penrose.o random.o tree234.o -lm $(XLFLAGS) $(ULIBS) $(BINPREFIX)magnets: drawing.o gtk.o laydomino.o magnets.o magnets-icon.o \ malloc.o midend.o misc.o printing.o ps.o random.o version.o $(CC) -o $@ drawing.o gtk.o laydomino.o magnets.o magnets-icon.o \ malloc.o midend.o misc.o printing.o ps.o random.o version.o \ $(XLFLAGS) $(XLIBS) $(BINPREFIX)magnetssolver: laydomino.o magnets2.o malloc.o misc.o nullfe.o \ random.o $(CC) -o $@ laydomino.o magnets2.o malloc.o misc.o nullfe.o random.o \ -lm $(XLFLAGS) $(ULIBS) $(BINPREFIX)map: drawing.o dsf.o gtk.o malloc.o map.o map-icon.o midend.o \ misc.o printing.o ps.o random.o version.o $(CC) -o $@ drawing.o dsf.o gtk.o malloc.o map.o map-icon.o midend.o \ misc.o printing.o ps.o random.o version.o $(XLFLAGS) \ $(XLIBS) $(BINPREFIX)mapsolver: dsf.o malloc.o map2.o misc.o nullfe.o random.o $(CC) -o $@ dsf.o malloc.o map2.o misc.o nullfe.o random.o -lm \ $(XLFLAGS) $(ULIBS) $(BINPREFIX)mineobfusc: malloc.o mines2.o misc.o nullfe.o random.o tree234.o $(CC) -o $@ malloc.o mines2.o misc.o nullfe.o random.o tree234.o \ $(XLFLAGS) $(ULIBS) $(BINPREFIX)mines: drawing.o gtk.o malloc.o midend.o mines.o mines-icon.o \ misc.o printing.o ps.o random.o tree234.o version.o $(CC) -o $@ drawing.o gtk.o malloc.o midend.o mines.o mines-icon.o \ misc.o printing.o ps.o random.o tree234.o version.o \ $(XLFLAGS) $(XLIBS) $(BINPREFIX)net: drawing.o dsf.o gtk.o malloc.o midend.o misc.o net.o \ net-icon.o printing.o ps.o random.o tree234.o version.o $(CC) -o $@ drawing.o dsf.o gtk.o malloc.o midend.o misc.o net.o \ net-icon.o printing.o ps.o random.o tree234.o version.o \ $(XLFLAGS) $(XLIBS) $(BINPREFIX)netslide: drawing.o gtk.o malloc.o midend.o misc.o netslide.o \ netslide-icon.o printing.o ps.o random.o tree234.o version.o $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o netslide.o \ netslide-icon.o printing.o ps.o random.o tree234.o version.o \ $(XLFLAGS) $(XLIBS) $(BINPREFIX)nullgame: drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \ nullgame.o printing.o ps.o random.o version.o $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o no-icon.o \ nullgame.o printing.o ps.o random.o version.o $(XLFLAGS) \ $(XLIBS) $(BINPREFIX)obfusc: malloc.o misc.o nullfe.o obfusc.o random.o $(CC) -o $@ malloc.o misc.o nullfe.o obfusc.o random.o $(XLFLAGS) \ $(ULIBS) $(BINPREFIX)pattern: drawing.o gtk.o malloc.o midend.o misc.o pattern.o \ pattern-icon.o printing.o ps.o random.o version.o $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o pattern.o \ pattern-icon.o printing.o ps.o random.o version.o \ $(XLFLAGS) $(XLIBS) $(BINPREFIX)patternsolver: malloc.o misc.o nullfe.o pattern2.o random.o $(CC) -o $@ malloc.o misc.o nullfe.o pattern2.o random.o $(XLFLAGS) \ $(ULIBS) $(BINPREFIX)pearl: drawing.o dsf.o grid.o gtk.o loopgen.o malloc.o midend.o \ misc.o pearl.o pearl-icon.o penrose.o printing.o ps.o \ random.o tdq.o tree234.o version.o $(CC) -o $@ drawing.o dsf.o grid.o gtk.o loopgen.o malloc.o midend.o \ misc.o pearl.o pearl-icon.o penrose.o printing.o ps.o \ random.o tdq.o tree234.o version.o $(XLFLAGS) $(XLIBS) $(BINPREFIX)pearlbench: dsf.o grid.o loopgen.o malloc.o misc.o nullfe.o \ pearl2.o penrose.o random.o tdq.o tree234.o $(CC) -o $@ dsf.o grid.o loopgen.o malloc.o misc.o nullfe.o pearl2.o \ penrose.o random.o tdq.o tree234.o -lm $(XLFLAGS) $(ULIBS) $(BINPREFIX)pegs: drawing.o gtk.o malloc.o midend.o misc.o pegs.o \ pegs-icon.o printing.o ps.o random.o tree234.o version.o $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o pegs.o \ pegs-icon.o printing.o ps.o random.o tree234.o version.o \ $(XLFLAGS) $(XLIBS) $(BINPREFIX)range: drawing.o gtk.o malloc.o midend.o misc.o printing.o ps.o \ random.o range.o range-icon.o version.o $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o printing.o ps.o \ random.o range.o range-icon.o version.o $(XLFLAGS) $(XLIBS) $(BINPREFIX)rect: drawing.o gtk.o malloc.o midend.o misc.o printing.o ps.o \ random.o rect.o rect-icon.o version.o $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o printing.o ps.o \ random.o rect.o rect-icon.o version.o $(XLFLAGS) $(XLIBS) $(BINPREFIX)samegame: drawing.o gtk.o malloc.o midend.o misc.o printing.o \ ps.o random.o samegame.o samegame-icon.o version.o $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o printing.o ps.o \ random.o samegame.o samegame-icon.o version.o $(XLFLAGS) \ $(XLIBS) $(BINPREFIX)signpost: drawing.o dsf.o gtk.o malloc.o midend.o misc.o \ printing.o ps.o random.o signpost.o signpost-icon.o \ version.o $(CC) -o $@ drawing.o dsf.o gtk.o malloc.o midend.o misc.o \ printing.o ps.o random.o signpost.o signpost-icon.o \ version.o $(XLFLAGS) $(XLIBS) $(BINPREFIX)signpostsolver: dsf.o malloc.o misc.o nullfe.o random.o \ signpos2.o $(CC) -o $@ dsf.o malloc.o misc.o nullfe.o random.o signpos2.o -lm \ $(XLFLAGS) $(ULIBS) $(BINPREFIX)singles: drawing.o dsf.o gtk.o latin.o malloc.o maxflow.o \ midend.o misc.o printing.o ps.o random.o singles.o \ singles-icon.o tree234.o version.o $(CC) -o $@ drawing.o dsf.o gtk.o latin.o malloc.o maxflow.o \ midend.o misc.o printing.o ps.o random.o singles.o \ singles-icon.o tree234.o version.o $(XLFLAGS) $(XLIBS) $(BINPREFIX)singlessolver: dsf.o latin.o malloc.o maxflow.o misc.o nullfe.o \ random.o singles3.o tree234.o $(CC) -o $@ dsf.o latin.o malloc.o maxflow.o misc.o nullfe.o \ random.o singles3.o tree234.o $(XLFLAGS) $(ULIBS) $(BINPREFIX)sixteen: drawing.o gtk.o malloc.o midend.o misc.o printing.o \ ps.o random.o sixteen.o sixteen-icon.o version.o $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o printing.o ps.o \ random.o sixteen.o sixteen-icon.o version.o $(XLFLAGS) \ $(XLIBS) $(BINPREFIX)slant: drawing.o dsf.o gtk.o malloc.o midend.o misc.o printing.o \ ps.o random.o slant.o slant-icon.o version.o $(CC) -o $@ drawing.o dsf.o gtk.o malloc.o midend.o misc.o \ printing.o ps.o random.o slant.o slant-icon.o version.o \ $(XLFLAGS) $(XLIBS) $(BINPREFIX)slantsolver: dsf.o malloc.o misc.o nullfe.o random.o slant2.o $(CC) -o $@ dsf.o malloc.o misc.o nullfe.o random.o slant2.o \ $(XLFLAGS) $(ULIBS) $(BINPREFIX)solo: divvy.o drawing.o dsf.o gtk.o malloc.o midend.o misc.o \ printing.o ps.o random.o solo.o solo-icon.o version.o $(CC) -o $@ divvy.o drawing.o dsf.o gtk.o malloc.o midend.o misc.o \ printing.o ps.o random.o solo.o solo-icon.o version.o \ $(XLFLAGS) $(XLIBS) $(BINPREFIX)solosolver: divvy.o dsf.o malloc.o misc.o nullfe.o random.o \ solo2.o $(CC) -o $@ divvy.o dsf.o malloc.o misc.o nullfe.o random.o solo2.o \ $(XLFLAGS) $(ULIBS) $(BINPREFIX)tents: drawing.o dsf.o gtk.o malloc.o maxflow.o midend.o misc.o \ printing.o ps.o random.o tents.o tents-icon.o version.o $(CC) -o $@ drawing.o dsf.o gtk.o malloc.o maxflow.o midend.o misc.o \ printing.o ps.o random.o tents.o tents-icon.o version.o \ $(XLFLAGS) $(XLIBS) $(BINPREFIX)tentssolver: dsf.o malloc.o maxflow.o misc.o nullfe.o random.o \ tents3.o $(CC) -o $@ dsf.o malloc.o maxflow.o misc.o nullfe.o random.o \ tents3.o $(XLFLAGS) $(ULIBS) $(BINPREFIX)towers: drawing.o gtk.o latin.o malloc.o maxflow.o midend.o \ misc.o printing.o ps.o random.o towers.o towers-icon.o \ tree234.o version.o $(CC) -o $@ drawing.o gtk.o latin.o malloc.o maxflow.o midend.o \ misc.o printing.o ps.o random.o towers.o towers-icon.o \ tree234.o version.o $(XLFLAGS) $(XLIBS) $(BINPREFIX)towerssolver: latin6.o malloc.o maxflow.o misc.o nullfe.o \ random.o towers2.o tree234.o $(CC) -o $@ latin6.o malloc.o maxflow.o misc.o nullfe.o random.o \ towers2.o tree234.o $(XLFLAGS) $(ULIBS) $(BINPREFIX)twiddle: drawing.o gtk.o malloc.o midend.o misc.o printing.o \ ps.o random.o twiddle.o twiddle-icon.o version.o $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o printing.o ps.o \ random.o twiddle.o twiddle-icon.o version.o $(XLFLAGS) \ $(XLIBS) $(BINPREFIX)undead: drawing.o gtk.o malloc.o midend.o misc.o printing.o ps.o \ random.o undead.o undead-icon.o version.o $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o printing.o ps.o \ random.o undead.o undead-icon.o version.o $(XLFLAGS) \ $(XLIBS) $(BINPREFIX)unequal: drawing.o gtk.o latin.o malloc.o maxflow.o midend.o \ misc.o printing.o ps.o random.o tree234.o unequal.o \ unequal-icon.o version.o $(CC) -o $@ drawing.o gtk.o latin.o malloc.o maxflow.o midend.o \ misc.o printing.o ps.o random.o tree234.o unequal.o \ unequal-icon.o version.o $(XLFLAGS) $(XLIBS) $(BINPREFIX)unequalsolver: latin6.o malloc.o maxflow.o misc.o nullfe.o \ random.o tree234.o unequal2.o $(CC) -o $@ latin6.o malloc.o maxflow.o misc.o nullfe.o random.o \ tree234.o unequal2.o $(XLFLAGS) $(ULIBS) $(BINPREFIX)unruly: drawing.o gtk.o malloc.o midend.o misc.o printing.o ps.o \ random.o unruly.o unruly-icon.o version.o $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o printing.o ps.o \ random.o unruly.o unruly-icon.o version.o $(XLFLAGS) \ $(XLIBS) $(BINPREFIX)unrulysolver: malloc.o misc.o nullfe.o random.o unruly2.o $(CC) -o $@ malloc.o misc.o nullfe.o random.o unruly2.o $(XLFLAGS) \ $(ULIBS) $(BINPREFIX)untangle: drawing.o gtk.o malloc.o midend.o misc.o printing.o \ ps.o random.o tree234.o untangle.o untangle-icon.o version.o $(CC) -o $@ drawing.o gtk.o malloc.o midend.o misc.o printing.o ps.o \ random.o tree234.o untangle.o untangle-icon.o version.o \ $(XLFLAGS) $(XLIBS) blackbox.o: ./blackbox.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ blackbox-icon.o: icons/blackbox-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ blackbo3.o: ./blackbox.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ bridges.o: ./bridges.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ bridges-icon.o: icons/bridges-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ bridges3.o: ./bridges.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ combi.o: ./combi.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ cube.o: ./cube.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ cube-icon.o: icons/cube-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ cube3.o: ./cube.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ divvy.o: ./divvy.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ dominosa.o: ./dominosa.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ dominosa-icon.o: icons/dominosa-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ dominos3.o: ./dominosa.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ drawing.o: ./drawing.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ dsf.o: ./dsf.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ fifteen.o: ./fifteen.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ fifteen-icon.o: icons/fifteen-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ fifteen3.o: ./fifteen.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ filling.o: ./filling.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ filling-icon.o: icons/filling-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ filling5.o: ./filling.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ filling2.o: ./filling.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ flip.o: ./flip.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ flip-icon.o: icons/flip-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ flip3.o: ./flip.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ galaxies.o: ./galaxies.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ galaxies-icon.o: icons/galaxies-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ galaxie7.o: ./galaxies.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ galaxie4.o: ./galaxies.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_PICTURE_GENERATOR -c $< -o $@ galaxie2.o: ./galaxies.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ grid.o: ./grid.c ./puzzles.h ./tree234.h ./grid.h ./penrose.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ gtk.o: ./gtk.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ guess.o: ./guess.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ guess-icon.o: icons/guess-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ guess3.o: ./guess.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ inertia.o: ./inertia.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ inertia-icon.o: icons/inertia-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ inertia3.o: ./inertia.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ keen.o: ./keen.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ keen-icon.o: icons/keen-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ keen5.o: ./keen.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ keen2.o: ./keen.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ latin.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ latin8.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_LATIN_TEST -c $< -o $@ latin6.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ laydomino.o: ./laydomino.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ lightup.o: ./lightup.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ lightup-icon.o: icons/lightup-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ lightup5.o: ./lightup.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ lightup2.o: ./lightup.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ list.o: ./list.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopgen.o: ./loopgen.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopy.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopy-icon.o: icons/loopy-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopy5.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ loopy2.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ magnets.o: ./magnets.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ magnets-icon.o: icons/magnets-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ magnets5.o: ./magnets.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ magnets2.o: ./magnets.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ malloc.o: ./malloc.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ map.o: ./map.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ map-icon.o: icons/map-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ map5.o: ./map.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ map2.o: ./map.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ maxflow.o: ./maxflow.c ./maxflow.h ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ midend.o: ./midend.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ mines.o: ./mines.c ./tree234.h ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ mines-icon.o: icons/mines-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ mines5.o: ./mines.c ./tree234.h ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ mines2.o: ./mines.c ./tree234.h ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_OBFUSCATOR -c $< -o $@ misc.o: ./misc.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ net.o: ./net.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ net-icon.o: icons/net-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ net3.o: ./net.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ netslide.o: ./netslide.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ netslide-icon.o: icons/netslide-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ netslid3.o: ./netslide.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ no-icon.o: ./no-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ nullfe.o: ./nullfe.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ nullgame.o: ./nullgame.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ obfusc.o: ./obfusc.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ osx.o: ./osx.m ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pattern.o: ./pattern.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pattern-icon.o: icons/pattern-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pattern5.o: ./pattern.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ pattern2.o: ./pattern.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ pearl.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pearl-icon.o: icons/pearl-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pearl5.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ pearl2.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ pegs.o: ./pegs.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pegs-icon.o: icons/pegs-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pegs3.o: ./pegs.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ penrose.o: ./penrose.c ./puzzles.h ./penrose.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ printing.o: ./printing.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ ps.o: ./ps.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ random.o: ./random.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ range.o: ./range.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ range-icon.o: icons/range-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ range3.o: ./range.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ rect.o: ./rect.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ rect-icon.o: icons/rect-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ rect3.o: ./rect.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ samegame.o: ./samegame.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ samegame-icon.o: icons/samegame-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ samegam3.o: ./samegame.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ signpost.o: ./signpost.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ signpost-icon.o: icons/signpost-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ signpos5.o: ./signpost.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ signpos2.o: ./signpost.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ singles.o: ./singles.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ singles-icon.o: icons/singles-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ singles5.o: ./singles.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ singles3.o: ./singles.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ sixteen.o: ./sixteen.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ sixteen-icon.o: icons/sixteen-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ sixteen3.o: ./sixteen.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ slant.o: ./slant.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ slant-icon.o: icons/slant-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ slant5.o: ./slant.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ slant2.o: ./slant.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ solo.o: ./solo.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ solo-icon.o: icons/solo-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ solo5.o: ./solo.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ solo2.o: ./solo.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ tdq.o: ./tdq.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ tents.o: ./tents.c ./puzzles.h ./maxflow.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ tents-icon.o: icons/tents-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ tents5.o: ./tents.c ./puzzles.h ./maxflow.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ tents3.o: ./tents.c ./puzzles.h ./maxflow.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ towers.o: ./towers.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ towers-icon.o: icons/towers-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ towers5.o: ./towers.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ towers2.o: ./towers.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ tree234.o: ./tree234.c ./tree234.h ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ twiddle.o: ./twiddle.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ twiddle-icon.o: icons/twiddle-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ twiddle3.o: ./twiddle.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ undead.o: ./undead.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ undead-icon.o: icons/undead-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ undead3.o: ./undead.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ unequal.o: ./unequal.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ unequal-icon.o: icons/unequal-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ unequal5.o: ./unequal.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ unequal2.o: ./unequal.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ unruly.o: ./unruly.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ unruly-icon.o: icons/unruly-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ unruly5.o: ./unruly.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ unruly2.o: ./unruly.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ untangle.o: ./untangle.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ untangle-icon.o: icons/untangle-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ untangl3.o: ./untangle.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ windows.o: ./windows.c ./puzzles.h ./resource.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ windows1.o: ./windows.c ./puzzles.h ./resource.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ GAMES += blackbox GAMES += bridges GAMES += cube GAMES += dominosa GAMES += fifteen GAMES += filling GAMES += flip GAMES += galaxies GAMES += guess GAMES += inertia GAMES += keen GAMES += lightup GAMES += loopy GAMES += magnets GAMES += map GAMES += mines GAMES += net GAMES += netslide GAMES += pattern GAMES += pearl GAMES += pegs GAMES += range GAMES += rect GAMES += samegame GAMES += signpost GAMES += singles GAMES += sixteen GAMES += slant GAMES += solo GAMES += tents GAMES += towers GAMES += twiddle GAMES += undead GAMES += unequal GAMES += unruly GAMES += untangle version.o: version.c version2.def $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) `cat version2.def` -c version.c version2.def: FORCE if test -z "$(VER)" && test -f manifest && md5sum -c manifest; then \ cat version.def > version2.def.new; \ elif test -z "$(VER)" && test -d .svn && svnversion . >/dev/null 2>&1; then \ echo "-DREVISION=`svnversion .`" >version2.def.new; \ else \ echo "$(VER)" >version2.def.new; \ fi && \ if diff -q version2.def.new version2.def; then \ rm version2.def.new; \ else \ mv version2.def.new version2.def; \ fi .PHONY: FORCE install: for i in $(GAMES); do \ $(INSTALL_PROGRAM) -m 755 $(BINPREFIX)$$i $(DESTDIR)$(gamesdir)/$(BINPREFIX)$$i \ || exit 1; \ done test: benchmark.html benchmark.txt benchmark.html: benchmark.txt benchmark.pl ./benchmark.pl benchmark.txt > $@ benchmark.txt: $(GAMES) for i in $(GAMES); do \ for params in $$(env -i ./$(BINPREFIX)$$i --list-presets | cut -f1 -d' '); do \ env -i ./$(BINPREFIX)$$i --test-solve --time-generation --generate 100 $$params \ || exit 1; \ done; \ done > $@ clean: rm -f *.o $(BINPREFIX)blackbox $(BINPREFIX)bridges $(BINPREFIX)cube $(BINPREFIX)dominosa $(BINPREFIX)fifteen $(BINPREFIX)filling $(BINPREFIX)fillingsolver $(BINPREFIX)flip $(BINPREFIX)galaxies $(BINPREFIX)galaxiespicture $(BINPREFIX)galaxiessolver $(BINPREFIX)guess $(BINPREFIX)inertia $(BINPREFIX)keen $(BINPREFIX)keensolver $(BINPREFIX)latincheck $(BINPREFIX)lightup $(BINPREFIX)lightupsolver $(BINPREFIX)loopy $(BINPREFIX)loopysolver $(BINPREFIX)magnets $(BINPREFIX)magnetssolver $(BINPREFIX)map $(BINPREFIX)mapsolver $(BINPREFIX)mineobfusc $(BINPREFIX)mines $(BINPREFIX)net $(BINPREFIX)netslide $(BINPREFIX)nullgame $(BINPREFIX)obfusc $(BINPREFIX)pattern $(BINPREFIX)patternsolver $(BINPREFIX)pearl $(BINPREFIX)pearlbench $(BINPREFIX)pegs $(BINPREFIX)range $(BINPREFIX)rect $(BINPREFIX)samegame $(BINPREFIX)signpost $(BINPREFIX)signpostsolver $(BINPREFIX)singles $(BINPREFIX)singlessolver $(BINPREFIX)sixteen $(BINPREFIX)slant $(BINPREFIX)slantsolver $(BINPREFIX)solo $(BINPREFIX)solosolver $(BINPREFIX)tents $(BINPREFIX)tentssolver $(BINPREFIX)towers $(BINPREFIX)towerssolver $(BINPREFIX)twiddle $(BINPREFIX)undead $(BINPREFIX)unequal $(BINPREFIX)unequalsolver $(BINPREFIX)unruly $(BINPREFIX)unrulysolver $(BINPREFIX)untangle puzzles-r9872/Makefile.cyg0000644000175300017530000011645712161170560014663 0ustar simonsimon# Makefile for puzzles under cygwin. # # This file was created by `mkfiles.pl' from the `Recipe' file. # DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead. # You can define this path to point at your tools if you need to # TOOLPATH = c:\cygwin\bin\ # or similar, if you're running Windows # TOOLPATH = /pkg/mingw32msvc/i386-mingw32msvc/bin/ CC = $(TOOLPATH)gcc RC = $(TOOLPATH)windres # Uncomment the following two lines to compile under Winelib # CC = winegcc # RC = wrc # You may also need to tell windres where to find include files: # RCINC = --include-dir c:\cygwin\include\ CFLAGS = -mno-cygwin -Wall -O2 -D_WINDOWS -DDEBUG -DWIN32S_COMPAT \ -D_NO_OLDNAMES -DNO_MULTIMON -DNO_HTMLHELP -I./ -Iicons/ LDFLAGS = -mno-cygwin -s RCFLAGS = $(RCINC) --define WIN32=1 --define _WIN32=1 --define WINVER=0x0400 \ --define MINGW32_FIX=1 --include ./ --include icons/ all: blackbox.exe bridges.exe cube.exe dominosa.exe fifteen.exe filling.exe \ fillingsolver.exe flip.exe galaxies.exe galaxiespicture.exe \ galaxiessolver.exe guess.exe inertia.exe keen.exe \ keensolver.exe latincheck.exe lightup.exe lightupsolver.exe \ loopy.exe loopysolver.exe magnets.exe magnetssolver.exe \ map.exe mapsolver.exe mineobfusc.exe mines.exe netgame.exe \ netslide.exe nullgame.exe pattern.exe patternsolver.exe \ pearl.exe pearlbench.exe pegs.exe puzzles.exe range.exe \ rect.exe samegame.exe signpost.exe signpostsolver.exe \ singles.exe singlessolver.exe sixteen.exe slant.exe \ slantsolver.exe solo.exe solosolver.exe tents.exe \ tentssolver.exe towers.exe towerssolver.exe twiddle.exe \ undead.exe unequal.exe unequalsolver.exe unruly.exe \ unrulysolver.exe untangle.exe blackbox.exe: blackbox.o blackbox.res.o drawing.o malloc.o midend.o misc.o \ printing.o random.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,blackbox.map blackbox.o \ blackbox.res.o drawing.o malloc.o midend.o misc.o printing.o \ random.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \ -luser32 -lwinspool bridges.exe: bridges.o bridges.res.o drawing.o dsf.o malloc.o midend.o \ misc.o printing.o random.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,bridges.map bridges.o \ bridges.res.o drawing.o dsf.o malloc.o midend.o misc.o \ printing.o random.o version.o windows.o -lcomctl32 \ -lcomdlg32 -lgdi32 -luser32 -lwinspool cube.exe: cube.o cube.res.o drawing.o malloc.o midend.o misc.o printing.o \ random.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,cube.map cube.o cube.res.o \ drawing.o malloc.o midend.o misc.o printing.o random.o \ version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 -luser32 \ -lwinspool dominosa.exe: dominosa.o dominosa.res.o drawing.o laydomino.o malloc.o \ midend.o misc.o printing.o random.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,dominosa.map dominosa.o \ dominosa.res.o drawing.o laydomino.o malloc.o midend.o \ misc.o printing.o random.o version.o windows.o -lcomctl32 \ -lcomdlg32 -lgdi32 -luser32 -lwinspool fifteen.exe: drawing.o fifteen.o fifteen.res.o malloc.o midend.o misc.o \ printing.o random.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,fifteen.map drawing.o \ fifteen.o fifteen.res.o malloc.o midend.o misc.o printing.o \ random.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \ -luser32 -lwinspool filling.exe: drawing.o dsf.o filling.o filling.res.o malloc.o midend.o \ misc.o printing.o random.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,filling.map drawing.o \ dsf.o filling.o filling.res.o malloc.o midend.o misc.o \ printing.o random.o version.o windows.o -lcomctl32 \ -lcomdlg32 -lgdi32 -luser32 -lwinspool fillingsolver.exe: dsf.o filling2.o malloc.o misc.o nullfe.o random.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,fillingsolver.map dsf.o filling2.o \ malloc.o misc.o nullfe.o random.o flip.exe: drawing.o flip.o flip.res.o malloc.o midend.o misc.o printing.o \ random.o tree234.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,flip.map drawing.o flip.o \ flip.res.o malloc.o midend.o misc.o printing.o random.o \ tree234.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \ -luser32 -lwinspool galaxies.exe: drawing.o dsf.o galaxies.o galaxies.res.o malloc.o midend.o \ misc.o printing.o random.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,galaxies.map drawing.o \ dsf.o galaxies.o galaxies.res.o malloc.o midend.o misc.o \ printing.o random.o version.o windows.o -lcomctl32 \ -lcomdlg32 -lgdi32 -luser32 -lwinspool galaxiespicture.exe: dsf.o galaxie4.o malloc.o misc.o nullfe.o random.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,galaxiespicture.map dsf.o galaxie4.o \ malloc.o misc.o nullfe.o random.o galaxiessolver.exe: dsf.o galaxie2.o malloc.o misc.o nullfe.o random.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,galaxiessolver.map dsf.o galaxie2.o \ malloc.o misc.o nullfe.o random.o guess.exe: drawing.o guess.o guess.res.o malloc.o midend.o misc.o printing.o \ random.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,guess.map drawing.o \ guess.o guess.res.o malloc.o midend.o misc.o printing.o \ random.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \ -luser32 -lwinspool inertia.exe: drawing.o inertia.o inertia.res.o malloc.o midend.o misc.o \ printing.o random.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,inertia.map drawing.o \ inertia.o inertia.res.o malloc.o midend.o misc.o printing.o \ random.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \ -luser32 -lwinspool keen.exe: drawing.o dsf.o keen.o keen.res.o latin.o malloc.o maxflow.o \ midend.o misc.o printing.o random.o tree234.o version.o \ windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,keen.map drawing.o dsf.o \ keen.o keen.res.o latin.o malloc.o maxflow.o midend.o misc.o \ printing.o random.o tree234.o version.o windows.o -lcomctl32 \ -lcomdlg32 -lgdi32 -luser32 -lwinspool keensolver.exe: dsf.o keen2.o latin6.o malloc.o maxflow.o misc.o nullfe.o \ random.o tree234.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,keensolver.map dsf.o keen2.o \ latin6.o malloc.o maxflow.o misc.o nullfe.o random.o \ tree234.o latincheck.exe: latin8.o malloc.o maxflow.o misc.o nullfe.o random.o \ tree234.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,latincheck.map latin8.o malloc.o \ maxflow.o misc.o nullfe.o random.o tree234.o lightup.exe: combi.o drawing.o lightup.o lightup.res.o malloc.o midend.o \ misc.o printing.o random.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,lightup.map combi.o \ drawing.o lightup.o lightup.res.o malloc.o midend.o misc.o \ printing.o random.o version.o windows.o -lcomctl32 \ -lcomdlg32 -lgdi32 -luser32 -lwinspool lightupsolver.exe: combi.o lightup2.o malloc.o misc.o nullfe.o random.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,lightupsolver.map combi.o lightup2.o \ malloc.o misc.o nullfe.o random.o loopy.exe: drawing.o dsf.o grid.o loopgen.o loopy.o loopy.res.o malloc.o \ midend.o misc.o penrose.o printing.o random.o tree234.o \ version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,loopy.map drawing.o dsf.o \ grid.o loopgen.o loopy.o loopy.res.o malloc.o midend.o \ misc.o penrose.o printing.o random.o tree234.o version.o \ windows.o -lcomctl32 -lcomdlg32 -lgdi32 -luser32 -lwinspool loopysolver.exe: dsf.o grid.o loopgen.o loopy2.o malloc.o misc.o nullfe.o \ penrose.o random.o tree234.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,loopysolver.map dsf.o grid.o \ loopgen.o loopy2.o malloc.o misc.o nullfe.o penrose.o \ random.o tree234.o magnets.exe: drawing.o laydomino.o magnets.o magnets.res.o malloc.o midend.o \ misc.o printing.o random.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,magnets.map drawing.o \ laydomino.o magnets.o magnets.res.o malloc.o midend.o misc.o \ printing.o random.o version.o windows.o -lcomctl32 \ -lcomdlg32 -lgdi32 -luser32 -lwinspool magnetssolver.exe: laydomino.o magnets2.o malloc.o misc.o nullfe.o random.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,magnetssolver.map laydomino.o \ magnets2.o malloc.o misc.o nullfe.o random.o map.exe: drawing.o dsf.o malloc.o map.o map.res.o midend.o misc.o printing.o \ random.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,map.map drawing.o dsf.o \ malloc.o map.o map.res.o midend.o misc.o printing.o random.o \ version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 -luser32 \ -lwinspool mapsolver.exe: dsf.o malloc.o map2.o misc.o nullfe.o random.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,mapsolver.map dsf.o malloc.o map2.o \ misc.o nullfe.o random.o mineobfusc.exe: malloc.o mines2.o misc.o nullfe.o random.o tree234.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,mineobfusc.map malloc.o mines2.o \ misc.o nullfe.o random.o tree234.o mines.exe: drawing.o malloc.o midend.o mines.o mines.res.o misc.o printing.o \ random.o tree234.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,mines.map drawing.o \ malloc.o midend.o mines.o mines.res.o misc.o printing.o \ random.o tree234.o version.o windows.o -lcomctl32 -lcomdlg32 \ -lgdi32 -luser32 -lwinspool netgame.exe: drawing.o dsf.o malloc.o midend.o misc.o net.o net.res.o \ printing.o random.o tree234.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,netgame.map drawing.o \ dsf.o malloc.o midend.o misc.o net.o net.res.o printing.o \ random.o tree234.o version.o windows.o -lcomctl32 -lcomdlg32 \ -lgdi32 -luser32 -lwinspool netslide.exe: drawing.o malloc.o midend.o misc.o netslide.o netslide.res.o \ printing.o random.o tree234.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,netslide.map drawing.o \ malloc.o midend.o misc.o netslide.o netslide.res.o \ printing.o random.o tree234.o version.o windows.o -lcomctl32 \ -lcomdlg32 -lgdi32 -luser32 -lwinspool nullgame.exe: drawing.o malloc.o midend.o misc.o noicon.res.o nullgame.o \ printing.o random.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,nullgame.map drawing.o \ malloc.o midend.o misc.o noicon.res.o nullgame.o printing.o \ random.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \ -luser32 -lwinspool pattern.exe: drawing.o malloc.o midend.o misc.o pattern.o pattern.res.o \ printing.o random.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,pattern.map drawing.o \ malloc.o midend.o misc.o pattern.o pattern.res.o printing.o \ random.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \ -luser32 -lwinspool patternsolver.exe: malloc.o misc.o nullfe.o pattern2.o random.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,patternsolver.map malloc.o misc.o \ nullfe.o pattern2.o random.o pearl.exe: drawing.o dsf.o grid.o loopgen.o malloc.o midend.o misc.o pearl.o \ pearl.res.o penrose.o printing.o random.o tdq.o tree234.o \ version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,pearl.map drawing.o dsf.o \ grid.o loopgen.o malloc.o midend.o misc.o pearl.o \ pearl.res.o penrose.o printing.o random.o tdq.o tree234.o \ version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 -luser32 \ -lwinspool pearlbench.exe: dsf.o grid.o loopgen.o malloc.o misc.o nullfe.o pearl2.o \ penrose.o random.o tdq.o tree234.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,pearlbench.map dsf.o grid.o \ loopgen.o malloc.o misc.o nullfe.o pearl2.o penrose.o \ random.o tdq.o tree234.o pegs.exe: drawing.o malloc.o midend.o misc.o pegs.o pegs.res.o printing.o \ random.o tree234.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,pegs.map drawing.o \ malloc.o midend.o misc.o pegs.o pegs.res.o printing.o \ random.o tree234.o version.o windows.o -lcomctl32 -lcomdlg32 \ -lgdi32 -luser32 -lwinspool puzzles.exe: blackbo3.o bridges3.o combi.o cube3.o divvy.o dominos3.o \ drawing.o dsf.o fifteen3.o filling5.o flip3.o galaxie7.o \ grid.o guess3.o inertia3.o keen5.o latin.o laydomino.o \ lightup5.o list.o loopgen.o loopy5.o magnets5.o malloc.o \ map5.o maxflow.o midend.o mines5.o misc.o net3.o netslid3.o \ noicon.res.o pattern5.o pearl5.o pegs3.o penrose.o \ printing.o random.o range3.o rect3.o samegam3.o signpos5.o \ singles5.o sixteen3.o slant5.o solo5.o tdq.o tents5.o \ towers5.o tree234.o twiddle3.o undead3.o unequal5.o \ unruly5.o untangl3.o version.o windows1.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,puzzles.map blackbo3.o \ bridges3.o combi.o cube3.o divvy.o dominos3.o drawing.o \ dsf.o fifteen3.o filling5.o flip3.o galaxie7.o grid.o \ guess3.o inertia3.o keen5.o latin.o laydomino.o lightup5.o \ list.o loopgen.o loopy5.o magnets5.o malloc.o map5.o \ maxflow.o midend.o mines5.o misc.o net3.o netslid3.o \ noicon.res.o pattern5.o pearl5.o pegs3.o penrose.o \ printing.o random.o range3.o rect3.o samegam3.o signpos5.o \ singles5.o sixteen3.o slant5.o solo5.o tdq.o tents5.o \ towers5.o tree234.o twiddle3.o undead3.o unequal5.o \ unruly5.o untangl3.o version.o windows1.o -lcomctl32 \ -lcomdlg32 -lgdi32 -luser32 -lwinspool range.exe: drawing.o malloc.o midend.o misc.o printing.o random.o range.o \ range.res.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,range.map drawing.o \ malloc.o midend.o misc.o printing.o random.o range.o \ range.res.o version.o windows.o -lcomctl32 -lcomdlg32 \ -lgdi32 -luser32 -lwinspool rect.exe: drawing.o malloc.o midend.o misc.o printing.o random.o rect.o \ rect.res.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,rect.map drawing.o \ malloc.o midend.o misc.o printing.o random.o rect.o \ rect.res.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \ -luser32 -lwinspool samegame.exe: drawing.o malloc.o midend.o misc.o printing.o random.o \ samegame.o samegame.res.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,samegame.map drawing.o \ malloc.o midend.o misc.o printing.o random.o samegame.o \ samegame.res.o version.o windows.o -lcomctl32 -lcomdlg32 \ -lgdi32 -luser32 -lwinspool signpost.exe: drawing.o dsf.o malloc.o midend.o misc.o printing.o random.o \ signpost.o signpost.res.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,signpost.map drawing.o \ dsf.o malloc.o midend.o misc.o printing.o random.o \ signpost.o signpost.res.o version.o windows.o -lcomctl32 \ -lcomdlg32 -lgdi32 -luser32 -lwinspool signpostsolver.exe: dsf.o malloc.o misc.o nullfe.o random.o signpos2.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,signpostsolver.map dsf.o malloc.o \ misc.o nullfe.o random.o signpos2.o singles.exe: drawing.o dsf.o latin.o malloc.o maxflow.o midend.o misc.o \ printing.o random.o singles.o singles.res.o tree234.o \ version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,singles.map drawing.o \ dsf.o latin.o malloc.o maxflow.o midend.o misc.o printing.o \ random.o singles.o singles.res.o tree234.o version.o \ windows.o -lcomctl32 -lcomdlg32 -lgdi32 -luser32 -lwinspool singlessolver.exe: dsf.o latin.o malloc.o maxflow.o misc.o nullfe.o random.o \ singles3.o tree234.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,singlessolver.map dsf.o latin.o \ malloc.o maxflow.o misc.o nullfe.o random.o singles3.o \ tree234.o sixteen.exe: drawing.o malloc.o midend.o misc.o printing.o random.o \ sixteen.o sixteen.res.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,sixteen.map drawing.o \ malloc.o midend.o misc.o printing.o random.o sixteen.o \ sixteen.res.o version.o windows.o -lcomctl32 -lcomdlg32 \ -lgdi32 -luser32 -lwinspool slant.exe: drawing.o dsf.o malloc.o midend.o misc.o printing.o random.o \ slant.o slant.res.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,slant.map drawing.o dsf.o \ malloc.o midend.o misc.o printing.o random.o slant.o \ slant.res.o version.o windows.o -lcomctl32 -lcomdlg32 \ -lgdi32 -luser32 -lwinspool slantsolver.exe: dsf.o malloc.o misc.o nullfe.o random.o slant2.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,slantsolver.map dsf.o malloc.o \ misc.o nullfe.o random.o slant2.o solo.exe: divvy.o drawing.o dsf.o malloc.o midend.o misc.o printing.o \ random.o solo.o solo.res.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,solo.map divvy.o drawing.o \ dsf.o malloc.o midend.o misc.o printing.o random.o solo.o \ solo.res.o version.o windows.o -lcomctl32 -lcomdlg32 -lgdi32 \ -luser32 -lwinspool solosolver.exe: divvy.o dsf.o malloc.o misc.o nullfe.o random.o solo2.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,solosolver.map divvy.o dsf.o \ malloc.o misc.o nullfe.o random.o solo2.o tents.exe: drawing.o dsf.o malloc.o maxflow.o midend.o misc.o printing.o \ random.o tents.o tents.res.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,tents.map drawing.o dsf.o \ malloc.o maxflow.o midend.o misc.o printing.o random.o \ tents.o tents.res.o version.o windows.o -lcomctl32 \ -lcomdlg32 -lgdi32 -luser32 -lwinspool tentssolver.exe: dsf.o malloc.o maxflow.o misc.o nullfe.o random.o tents3.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,tentssolver.map dsf.o malloc.o \ maxflow.o misc.o nullfe.o random.o tents3.o towers.exe: drawing.o latin.o malloc.o maxflow.o midend.o misc.o printing.o \ random.o towers.o towers.res.o tree234.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,towers.map drawing.o \ latin.o malloc.o maxflow.o midend.o misc.o printing.o \ random.o towers.o towers.res.o tree234.o version.o windows.o \ -lcomctl32 -lcomdlg32 -lgdi32 -luser32 -lwinspool towerssolver.exe: latin6.o malloc.o maxflow.o misc.o nullfe.o random.o \ towers2.o tree234.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,towerssolver.map latin6.o malloc.o \ maxflow.o misc.o nullfe.o random.o towers2.o tree234.o twiddle.exe: drawing.o malloc.o midend.o misc.o printing.o random.o \ twiddle.o twiddle.res.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,twiddle.map drawing.o \ malloc.o midend.o misc.o printing.o random.o twiddle.o \ twiddle.res.o version.o windows.o -lcomctl32 -lcomdlg32 \ -lgdi32 -luser32 -lwinspool undead.exe: drawing.o malloc.o midend.o misc.o printing.o random.o undead.o \ undead.res.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,undead.map drawing.o \ malloc.o midend.o misc.o printing.o random.o undead.o \ undead.res.o version.o windows.o -lcomctl32 -lcomdlg32 \ -lgdi32 -luser32 -lwinspool unequal.exe: drawing.o latin.o malloc.o maxflow.o midend.o misc.o printing.o \ random.o tree234.o unequal.o unequal.res.o version.o \ windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,unequal.map drawing.o \ latin.o malloc.o maxflow.o midend.o misc.o printing.o \ random.o tree234.o unequal.o unequal.res.o version.o \ windows.o -lcomctl32 -lcomdlg32 -lgdi32 -luser32 -lwinspool unequalsolver.exe: latin6.o malloc.o maxflow.o misc.o nullfe.o random.o \ tree234.o unequal2.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,unequalsolver.map latin6.o malloc.o \ maxflow.o misc.o nullfe.o random.o tree234.o unequal2.o unruly.exe: drawing.o malloc.o midend.o misc.o printing.o random.o unruly.o \ unruly.res.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,unruly.map drawing.o \ malloc.o midend.o misc.o printing.o random.o unruly.o \ unruly.res.o version.o windows.o -lcomctl32 -lcomdlg32 \ -lgdi32 -luser32 -lwinspool unrulysolver.exe: malloc.o misc.o nullfe.o random.o unruly2.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,unrulysolver.map malloc.o misc.o \ nullfe.o random.o unruly2.o untangle.exe: drawing.o malloc.o midend.o misc.o printing.o random.o \ tree234.o untangle.o untangle.res.o version.o windows.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,untangle.map drawing.o \ malloc.o midend.o misc.o printing.o random.o tree234.o \ untangle.o untangle.res.o version.o windows.o -lcomctl32 \ -lcomdlg32 -lgdi32 -luser32 -lwinspool blackbox.o: ./blackbox.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ blackbox-icon.o: icons/blackbox-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ blackbox.res.o: icons/blackbox.rc ./puzzles.rc2 icons/blackbox.ico \ ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ blackbo3.o: ./blackbox.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ bridges.o: ./bridges.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ bridges-icon.o: icons/bridges-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ bridges.res.o: icons/bridges.rc ./puzzles.rc2 icons/bridges.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ bridges3.o: ./bridges.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ combi.o: ./combi.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ cube.o: ./cube.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ cube-icon.o: icons/cube-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ cube.res.o: icons/cube.rc ./puzzles.rc2 icons/cube.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ cube3.o: ./cube.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ divvy.o: ./divvy.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ dominosa.o: ./dominosa.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ dominosa-icon.o: icons/dominosa-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ dominosa.res.o: icons/dominosa.rc ./puzzles.rc2 icons/dominosa.ico \ ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ dominos3.o: ./dominosa.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ drawing.o: ./drawing.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ dsf.o: ./dsf.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ fifteen.o: ./fifteen.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ fifteen-icon.o: icons/fifteen-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ fifteen.res.o: icons/fifteen.rc ./puzzles.rc2 icons/fifteen.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ fifteen3.o: ./fifteen.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ filling.o: ./filling.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ filling-icon.o: icons/filling-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ filling.res.o: icons/filling.rc ./puzzles.rc2 icons/filling.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ filling5.o: ./filling.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ filling2.o: ./filling.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ flip.o: ./flip.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ flip-icon.o: icons/flip-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ flip.res.o: icons/flip.rc ./puzzles.rc2 icons/flip.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ flip3.o: ./flip.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ galaxies.o: ./galaxies.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ galaxies-icon.o: icons/galaxies-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ galaxies.res.o: icons/galaxies.rc ./puzzles.rc2 icons/galaxies.ico \ ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ galaxie7.o: ./galaxies.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ galaxie4.o: ./galaxies.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_PICTURE_GENERATOR -c $< -o $@ galaxie2.o: ./galaxies.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ grid.o: ./grid.c ./puzzles.h ./tree234.h ./grid.h ./penrose.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ gtk.o: ./gtk.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ guess.o: ./guess.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ guess-icon.o: icons/guess-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ guess.res.o: icons/guess.rc ./puzzles.rc2 icons/guess.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ guess3.o: ./guess.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ inertia.o: ./inertia.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ inertia-icon.o: icons/inertia-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ inertia.res.o: icons/inertia.rc ./puzzles.rc2 icons/inertia.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ inertia3.o: ./inertia.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ keen.o: ./keen.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ keen-icon.o: icons/keen-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ keen.res.o: icons/keen.rc ./puzzles.rc2 icons/keen.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ keen5.o: ./keen.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ keen2.o: ./keen.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ latin.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ latin8.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_LATIN_TEST -c $< -o $@ latin6.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ laydomino.o: ./laydomino.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ lightup.o: ./lightup.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ lightup-icon.o: icons/lightup-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ lightup.res.o: icons/lightup.rc ./puzzles.rc2 icons/lightup.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ lightup5.o: ./lightup.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ lightup2.o: ./lightup.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ list.o: ./list.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopgen.o: ./loopgen.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopy.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopy-icon.o: icons/loopy-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopy.res.o: icons/loopy.rc ./puzzles.rc2 icons/loopy.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ loopy5.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ loopy2.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ magnets.o: ./magnets.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ magnets-icon.o: icons/magnets-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ magnets.res.o: icons/magnets.rc ./puzzles.rc2 icons/magnets.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ magnets5.o: ./magnets.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ magnets2.o: ./magnets.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ malloc.o: ./malloc.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ map.o: ./map.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ map-icon.o: icons/map-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ map.res.o: icons/map.rc ./puzzles.rc2 icons/map.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ map5.o: ./map.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ map2.o: ./map.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ maxflow.o: ./maxflow.c ./maxflow.h ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ midend.o: ./midend.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ mines.o: ./mines.c ./tree234.h ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ mines-icon.o: icons/mines-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ mines.res.o: icons/mines.rc ./puzzles.rc2 icons/mines.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ mines5.o: ./mines.c ./tree234.h ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ mines2.o: ./mines.c ./tree234.h ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_OBFUSCATOR -c $< -o $@ misc.o: ./misc.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ net.o: ./net.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ net-icon.o: icons/net-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ net.res.o: icons/net.rc ./puzzles.rc2 icons/net.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ net3.o: ./net.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ netslide.o: ./netslide.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ netslide-icon.o: icons/netslide-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ netslide.res.o: icons/netslide.rc ./puzzles.rc2 icons/netslide.ico \ ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ netslid3.o: ./netslide.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ no-icon.o: ./no-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ noicon.res.o: ./noicon.rc ./puzzles.rc2 ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ nullfe.o: ./nullfe.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ nullgame.o: ./nullgame.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ obfusc.o: ./obfusc.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ osx.o: ./osx.m ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pattern.o: ./pattern.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pattern-icon.o: icons/pattern-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pattern.res.o: icons/pattern.rc ./puzzles.rc2 icons/pattern.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ pattern5.o: ./pattern.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ pattern2.o: ./pattern.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ pearl.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pearl-icon.o: icons/pearl-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pearl.res.o: icons/pearl.rc ./puzzles.rc2 icons/pearl.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ pearl5.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ pearl2.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ pegs.o: ./pegs.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pegs-icon.o: icons/pegs-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pegs.res.o: icons/pegs.rc ./puzzles.rc2 icons/pegs.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ pegs3.o: ./pegs.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ penrose.o: ./penrose.c ./puzzles.h ./penrose.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ printing.o: ./printing.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ ps.o: ./ps.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ random.o: ./random.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ range.o: ./range.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ range-icon.o: icons/range-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ range.res.o: icons/range.rc ./puzzles.rc2 icons/range.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ range3.o: ./range.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ rect.o: ./rect.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ rect-icon.o: icons/rect-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ rect.res.o: icons/rect.rc ./puzzles.rc2 icons/rect.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ rect3.o: ./rect.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ samegame.o: ./samegame.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ samegame-icon.o: icons/samegame-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ samegame.res.o: icons/samegame.rc ./puzzles.rc2 icons/samegame.ico \ ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ samegam3.o: ./samegame.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ signpost.o: ./signpost.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ signpost-icon.o: icons/signpost-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ signpost.res.o: icons/signpost.rc ./puzzles.rc2 icons/signpost.ico \ ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ signpos5.o: ./signpost.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ signpos2.o: ./signpost.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ singles.o: ./singles.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ singles-icon.o: icons/singles-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ singles.res.o: icons/singles.rc ./puzzles.rc2 icons/singles.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ singles5.o: ./singles.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ singles3.o: ./singles.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ sixteen.o: ./sixteen.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ sixteen-icon.o: icons/sixteen-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ sixteen.res.o: icons/sixteen.rc ./puzzles.rc2 icons/sixteen.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ sixteen3.o: ./sixteen.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ slant.o: ./slant.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ slant-icon.o: icons/slant-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ slant.res.o: icons/slant.rc ./puzzles.rc2 icons/slant.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ slant5.o: ./slant.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ slant2.o: ./slant.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ solo.o: ./solo.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ solo-icon.o: icons/solo-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ solo.res.o: icons/solo.rc ./puzzles.rc2 icons/solo.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ solo5.o: ./solo.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ solo2.o: ./solo.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ tdq.o: ./tdq.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ tents.o: ./tents.c ./puzzles.h ./maxflow.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ tents-icon.o: icons/tents-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ tents.res.o: icons/tents.rc ./puzzles.rc2 icons/tents.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ tents5.o: ./tents.c ./puzzles.h ./maxflow.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ tents3.o: ./tents.c ./puzzles.h ./maxflow.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ towers.o: ./towers.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ towers-icon.o: icons/towers-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ towers.res.o: icons/towers.rc ./puzzles.rc2 icons/towers.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ towers5.o: ./towers.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ towers2.o: ./towers.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ tree234.o: ./tree234.c ./tree234.h ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ twiddle.o: ./twiddle.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ twiddle-icon.o: icons/twiddle-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ twiddle.res.o: icons/twiddle.rc ./puzzles.rc2 icons/twiddle.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ twiddle3.o: ./twiddle.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ undead.o: ./undead.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ undead-icon.o: icons/undead-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ undead.res.o: icons/undead.rc ./puzzles.rc2 icons/undead.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ undead3.o: ./undead.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ unequal.o: ./unequal.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ unequal-icon.o: icons/unequal-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ unequal.res.o: icons/unequal.rc ./puzzles.rc2 icons/unequal.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ unequal5.o: ./unequal.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ unequal2.o: ./unequal.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ unruly.o: ./unruly.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ unruly-icon.o: icons/unruly-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ unruly.res.o: icons/unruly.rc ./puzzles.rc2 icons/unruly.ico ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ unruly5.o: ./unruly.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ unruly2.o: ./unruly.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ untangle.o: ./untangle.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ untangle-icon.o: icons/untangle-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ untangle.res.o: icons/untangle.rc ./puzzles.rc2 icons/untangle.ico \ ./resource.h $(RC) $(FWHACK) $(RCFL) $(RCFLAGS) $< $@ untangl3.o: ./untangle.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ windows.o: ./windows.c ./puzzles.h ./resource.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ windows1.o: ./windows.c ./puzzles.h ./resource.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ version.o: FORCE; FORCE: $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) $(VER) -c version.c clean: rm -f *.o *.exe *.res.o *.map puzzles-r9872/Makefile.doc0000644000175300017530000000071712013521175014633 0ustar simonsimonall: puzzles.hlp puzzles.txt HACKING preprocessed.but: puzzles.but sed 's/PREFIX-/$(BINPREFIX)/g' puzzles.but > preprocessed.but puzzles.hlp puzzles.txt: preprocessed.but halibut --winhelp=puzzles.hlp --text=puzzles.txt preprocessed.but HACKING: devel.but halibut --text=HACKING devel.but chm: puzzles.hhp puzzles.hhp: puzzles.but chm.but halibut --html puzzles.but chm.but clean: rm -f puzzles.hlp puzzles.txt preprocessed.but HACKING *.html *.hh[pck] puzzles-r9872/Makefile.emcc0000644000175300017530000007610512161170560015003 0ustar simonsimon# Makefile for puzzles using Emscripten. Requires GNU make. # # This file was created by `mkfiles.pl' from the `Recipe' file. # DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead. # This can be set on the command line to point at the emcc command, # if it is not on your PATH. EMCC = emcc CFLAGS = -DSLOW_SYSTEM -I./ -Iicons/ all: $(OUTPREFIX)blackbox.js $(OUTPREFIX)bridges.js $(OUTPREFIX)cube.js \ $(OUTPREFIX)dominosa.js $(OUTPREFIX)fifteen.js \ $(OUTPREFIX)filling.js $(OUTPREFIX)flip.js \ $(OUTPREFIX)galaxies.js $(OUTPREFIX)guess.js \ $(OUTPREFIX)inertia.js $(OUTPREFIX)keen.js \ $(OUTPREFIX)lightup.js $(OUTPREFIX)loopy.js \ $(OUTPREFIX)magnets.js $(OUTPREFIX)map.js \ $(OUTPREFIX)mines.js $(OUTPREFIX)net.js \ $(OUTPREFIX)netslide.js $(OUTPREFIX)nullgame.js \ $(OUTPREFIX)pattern.js $(OUTPREFIX)pearl.js \ $(OUTPREFIX)pegs.js $(OUTPREFIX)range.js $(OUTPREFIX)rect.js \ $(OUTPREFIX)samegame.js $(OUTPREFIX)signpost.js \ $(OUTPREFIX)singles.js $(OUTPREFIX)sixteen.js \ $(OUTPREFIX)slant.js $(OUTPREFIX)solo.js \ $(OUTPREFIX)tents.js $(OUTPREFIX)towers.js \ $(OUTPREFIX)twiddle.js $(OUTPREFIX)undead.js \ $(OUTPREFIX)unequal.js $(OUTPREFIX)unruly.js \ $(OUTPREFIX)untangle.js $(OUTPREFIX)blackbox.js: blackbox.o blackbox-icon.o drawing.o emcc.o \ malloc.o midend.o misc.o printing.o ps.o random.o version.o \ emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)blackbox.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" blackbox.o blackbox-icon.o drawing.o emcc.o malloc.o midend.o misc.o printing.o ps.o random.o version.o $(OUTPREFIX)bridges.js: bridges.o bridges-icon.o drawing.o dsf.o emcc.o \ malloc.o midend.o misc.o printing.o ps.o random.o version.o \ emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)bridges.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" bridges.o bridges-icon.o drawing.o dsf.o emcc.o malloc.o midend.o misc.o printing.o ps.o random.o version.o $(OUTPREFIX)cube.js: cube.o cube-icon.o drawing.o emcc.o malloc.o midend.o \ misc.o printing.o ps.o random.o version.o emccpre.js \ emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)cube.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" cube.o cube-icon.o drawing.o emcc.o malloc.o midend.o misc.o printing.o ps.o random.o version.o $(OUTPREFIX)dominosa.js: dominosa.o dominosa-icon.o drawing.o emcc.o \ laydomino.o malloc.o midend.o misc.o printing.o ps.o \ random.o version.o emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)dominosa.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" dominosa.o dominosa-icon.o drawing.o emcc.o laydomino.o malloc.o midend.o misc.o printing.o ps.o random.o version.o $(OUTPREFIX)fifteen.js: drawing.o fifteen.o fifteen-icon.o emcc.o malloc.o \ midend.o misc.o printing.o ps.o random.o version.o \ emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)fifteen.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o fifteen.o fifteen-icon.o emcc.o malloc.o midend.o misc.o printing.o ps.o random.o version.o $(OUTPREFIX)filling.js: drawing.o dsf.o filling.o filling-icon.o emcc.o \ malloc.o midend.o misc.o printing.o ps.o random.o version.o \ emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)filling.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o filling.o filling-icon.o emcc.o malloc.o midend.o misc.o printing.o ps.o random.o version.o $(OUTPREFIX)flip.js: drawing.o flip.o flip-icon.o emcc.o malloc.o midend.o \ misc.o printing.o ps.o random.o tree234.o version.o \ emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)flip.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o flip.o flip-icon.o emcc.o malloc.o midend.o misc.o printing.o ps.o random.o tree234.o version.o $(OUTPREFIX)galaxies.js: drawing.o dsf.o galaxies.o galaxies-icon.o emcc.o \ malloc.o midend.o misc.o printing.o ps.o random.o version.o \ emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)galaxies.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o galaxies.o galaxies-icon.o emcc.o malloc.o midend.o misc.o printing.o ps.o random.o version.o $(OUTPREFIX)guess.js: drawing.o emcc.o guess.o guess-icon.o malloc.o \ midend.o misc.o printing.o ps.o random.o version.o \ emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)guess.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o guess.o guess-icon.o malloc.o midend.o misc.o printing.o ps.o random.o version.o $(OUTPREFIX)inertia.js: drawing.o emcc.o inertia.o inertia-icon.o malloc.o \ midend.o misc.o printing.o ps.o random.o version.o \ emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)inertia.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o inertia.o inertia-icon.o malloc.o midend.o misc.o printing.o ps.o random.o version.o $(OUTPREFIX)keen.js: drawing.o dsf.o emcc.o keen.o keen-icon.o latin.o \ malloc.o maxflow.o midend.o misc.o printing.o ps.o random.o \ tree234.o version.o emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)keen.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o emcc.o keen.o keen-icon.o latin.o malloc.o maxflow.o midend.o misc.o printing.o ps.o random.o tree234.o version.o $(OUTPREFIX)lightup.js: combi.o drawing.o emcc.o lightup.o lightup-icon.o \ malloc.o midend.o misc.o printing.o ps.o random.o version.o \ emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)lightup.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" combi.o drawing.o emcc.o lightup.o lightup-icon.o malloc.o midend.o misc.o printing.o ps.o random.o version.o $(OUTPREFIX)loopy.js: drawing.o dsf.o grid.o emcc.o loopgen.o loopy.o \ loopy-icon.o malloc.o midend.o misc.o penrose.o printing.o \ ps.o random.o tree234.o version.o emccpre.js emcclib.js \ emccx.json $(EMCC) -o $(OUTPREFIX)loopy.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o grid.o emcc.o loopgen.o loopy.o loopy-icon.o malloc.o midend.o misc.o penrose.o printing.o ps.o random.o tree234.o version.o $(OUTPREFIX)magnets.js: drawing.o emcc.o laydomino.o magnets.o \ magnets-icon.o malloc.o midend.o misc.o printing.o ps.o \ random.o version.o emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)magnets.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o laydomino.o magnets.o magnets-icon.o malloc.o midend.o misc.o printing.o ps.o random.o version.o $(OUTPREFIX)map.js: drawing.o dsf.o emcc.o malloc.o map.o map-icon.o \ midend.o misc.o printing.o ps.o random.o version.o \ emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)map.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o emcc.o malloc.o map.o map-icon.o midend.o misc.o printing.o ps.o random.o version.o $(OUTPREFIX)mines.js: drawing.o emcc.o malloc.o midend.o mines.o \ mines-icon.o misc.o printing.o ps.o random.o tree234.o \ version.o emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)mines.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o mines.o mines-icon.o misc.o printing.o ps.o random.o tree234.o version.o $(OUTPREFIX)net.js: drawing.o dsf.o emcc.o malloc.o midend.o misc.o net.o \ net-icon.o printing.o ps.o random.o tree234.o version.o \ emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)net.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o emcc.o malloc.o midend.o misc.o net.o net-icon.o printing.o ps.o random.o tree234.o version.o $(OUTPREFIX)netslide.js: drawing.o emcc.o malloc.o midend.o misc.o \ netslide.o netslide-icon.o printing.o ps.o random.o \ tree234.o version.o emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)netslide.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o netslide.o netslide-icon.o printing.o ps.o random.o tree234.o version.o $(OUTPREFIX)nullgame.js: drawing.o emcc.o malloc.o midend.o misc.o no-icon.o \ nullgame.o printing.o ps.o random.o version.o emccpre.js \ emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)nullgame.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o no-icon.o nullgame.o printing.o ps.o random.o version.o $(OUTPREFIX)pattern.js: drawing.o emcc.o malloc.o midend.o misc.o pattern.o \ pattern-icon.o printing.o ps.o random.o version.o emccpre.js \ emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)pattern.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o pattern.o pattern-icon.o printing.o ps.o random.o version.o $(OUTPREFIX)pearl.js: drawing.o dsf.o grid.o emcc.o loopgen.o malloc.o \ midend.o misc.o pearl.o pearl-icon.o penrose.o printing.o \ ps.o random.o tdq.o tree234.o version.o emccpre.js \ emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)pearl.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o grid.o emcc.o loopgen.o malloc.o midend.o misc.o pearl.o pearl-icon.o penrose.o printing.o ps.o random.o tdq.o tree234.o version.o $(OUTPREFIX)pegs.js: drawing.o emcc.o malloc.o midend.o misc.o pegs.o \ pegs-icon.o printing.o ps.o random.o tree234.o version.o \ emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)pegs.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o pegs.o pegs-icon.o printing.o ps.o random.o tree234.o version.o $(OUTPREFIX)range.js: drawing.o emcc.o malloc.o midend.o misc.o printing.o \ ps.o random.o range.o range-icon.o version.o emccpre.js \ emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)range.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o printing.o ps.o random.o range.o range-icon.o version.o $(OUTPREFIX)rect.js: drawing.o emcc.o malloc.o midend.o misc.o printing.o \ ps.o random.o rect.o rect-icon.o version.o emccpre.js \ emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)rect.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o printing.o ps.o random.o rect.o rect-icon.o version.o $(OUTPREFIX)samegame.js: drawing.o emcc.o malloc.o midend.o misc.o \ printing.o ps.o random.o samegame.o samegame-icon.o \ version.o emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)samegame.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o printing.o ps.o random.o samegame.o samegame-icon.o version.o $(OUTPREFIX)signpost.js: drawing.o dsf.o emcc.o malloc.o midend.o misc.o \ printing.o ps.o random.o signpost.o signpost-icon.o \ version.o emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)signpost.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o emcc.o malloc.o midend.o misc.o printing.o ps.o random.o signpost.o signpost-icon.o version.o $(OUTPREFIX)singles.js: drawing.o dsf.o emcc.o latin.o malloc.o maxflow.o \ midend.o misc.o printing.o ps.o random.o singles.o \ singles-icon.o tree234.o version.o emccpre.js emcclib.js \ emccx.json $(EMCC) -o $(OUTPREFIX)singles.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o emcc.o latin.o malloc.o maxflow.o midend.o misc.o printing.o ps.o random.o singles.o singles-icon.o tree234.o version.o $(OUTPREFIX)sixteen.js: drawing.o emcc.o malloc.o midend.o misc.o printing.o \ ps.o random.o sixteen.o sixteen-icon.o version.o emccpre.js \ emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)sixteen.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o printing.o ps.o random.o sixteen.o sixteen-icon.o version.o $(OUTPREFIX)slant.js: drawing.o dsf.o emcc.o malloc.o midend.o misc.o \ printing.o ps.o random.o slant.o slant-icon.o version.o \ emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)slant.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o emcc.o malloc.o midend.o misc.o printing.o ps.o random.o slant.o slant-icon.o version.o $(OUTPREFIX)solo.js: divvy.o drawing.o dsf.o emcc.o malloc.o midend.o misc.o \ printing.o ps.o random.o solo.o solo-icon.o version.o \ emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)solo.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" divvy.o drawing.o dsf.o emcc.o malloc.o midend.o misc.o printing.o ps.o random.o solo.o solo-icon.o version.o $(OUTPREFIX)tents.js: drawing.o dsf.o emcc.o malloc.o maxflow.o midend.o \ misc.o printing.o ps.o random.o tents.o tents-icon.o \ version.o emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)tents.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o dsf.o emcc.o malloc.o maxflow.o midend.o misc.o printing.o ps.o random.o tents.o tents-icon.o version.o $(OUTPREFIX)towers.js: drawing.o emcc.o latin.o malloc.o maxflow.o midend.o \ misc.o printing.o ps.o random.o towers.o towers-icon.o \ tree234.o version.o emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)towers.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o latin.o malloc.o maxflow.o midend.o misc.o printing.o ps.o random.o towers.o towers-icon.o tree234.o version.o $(OUTPREFIX)twiddle.js: drawing.o emcc.o malloc.o midend.o misc.o printing.o \ ps.o random.o twiddle.o twiddle-icon.o version.o emccpre.js \ emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)twiddle.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o printing.o ps.o random.o twiddle.o twiddle-icon.o version.o $(OUTPREFIX)undead.js: drawing.o emcc.o malloc.o midend.o misc.o printing.o \ ps.o random.o undead.o undead-icon.o version.o emccpre.js \ emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)undead.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o printing.o ps.o random.o undead.o undead-icon.o version.o $(OUTPREFIX)unequal.js: drawing.o emcc.o latin.o malloc.o maxflow.o midend.o \ misc.o printing.o ps.o random.o tree234.o unequal.o \ unequal-icon.o version.o emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)unequal.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o latin.o malloc.o maxflow.o midend.o misc.o printing.o ps.o random.o tree234.o unequal.o unequal-icon.o version.o $(OUTPREFIX)unruly.js: drawing.o emcc.o malloc.o midend.o misc.o printing.o \ ps.o random.o unruly.o unruly-icon.o version.o emccpre.js \ emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)unruly.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o printing.o ps.o random.o unruly.o unruly-icon.o version.o $(OUTPREFIX)untangle.js: drawing.o emcc.o malloc.o midend.o misc.o \ printing.o ps.o random.o tree234.o untangle.o \ untangle-icon.o version.o emccpre.js emcclib.js emccx.json $(EMCC) -o $(OUTPREFIX)untangle.js -O2 -s ASM_JS=1 --pre-js emccpre.js --js-library emcclib.js -s EXPORTED_FUNCTIONS="`sed 's://.*::' emccx.json | tr -d ' \n'`" drawing.o emcc.o malloc.o midend.o misc.o printing.o ps.o random.o tree234.o untangle.o untangle-icon.o version.o blackbox.o: ./blackbox.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ blackbox-icon.o: icons/blackbox-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ blackbo3.o: ./blackbox.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ bridges.o: ./bridges.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ bridges-icon.o: icons/bridges-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ bridges3.o: ./bridges.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ combi.o: ./combi.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ cube.o: ./cube.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ cube-icon.o: icons/cube-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ cube3.o: ./cube.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ divvy.o: ./divvy.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ dominosa.o: ./dominosa.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ dominosa-icon.o: icons/dominosa-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ dominos3.o: ./dominosa.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ drawing.o: ./drawing.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ dsf.o: ./dsf.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ fifteen.o: ./fifteen.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ fifteen-icon.o: icons/fifteen-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ fifteen3.o: ./fifteen.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ filling.o: ./filling.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ filling-icon.o: icons/filling-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ filling5.o: ./filling.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ filling2.o: ./filling.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ flip.o: ./flip.c ./puzzles.h ./tree234.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ flip-icon.o: icons/flip-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ flip3.o: ./flip.c ./puzzles.h ./tree234.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ galaxies.o: ./galaxies.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ galaxies-icon.o: icons/galaxies-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ galaxie7.o: ./galaxies.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ galaxie4.o: ./galaxies.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_PICTURE_GENERATOR -c $< -o $@ galaxie2.o: ./galaxies.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ grid.o: ./grid.c ./puzzles.h ./tree234.h ./grid.h ./penrose.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ emcc.o: ./emcc.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ guess.o: ./guess.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ guess-icon.o: icons/guess-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ guess3.o: ./guess.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ inertia.o: ./inertia.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ inertia-icon.o: icons/inertia-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ inertia3.o: ./inertia.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ keen.o: ./keen.c ./puzzles.h ./latin.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ keen-icon.o: icons/keen-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ keen5.o: ./keen.c ./puzzles.h ./latin.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ keen2.o: ./keen.c ./puzzles.h ./latin.h $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ latin.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ latin8.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_LATIN_TEST -c $< -o $@ latin6.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ laydomino.o: ./laydomino.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ lightup.o: ./lightup.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ lightup-icon.o: icons/lightup-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ lightup5.o: ./lightup.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ lightup2.o: ./lightup.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ list.o: ./list.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopgen.o: ./loopgen.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopy.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopy-icon.o: icons/loopy-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopy5.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ loopy2.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ magnets.o: ./magnets.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ magnets-icon.o: icons/magnets-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ magnets5.o: ./magnets.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ magnets2.o: ./magnets.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ malloc.o: ./malloc.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ map.o: ./map.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ map-icon.o: icons/map-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ map5.o: ./map.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ map2.o: ./map.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ maxflow.o: ./maxflow.c ./maxflow.h ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ midend.o: ./midend.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ mines.o: ./mines.c ./tree234.h ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ mines-icon.o: icons/mines-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ mines5.o: ./mines.c ./tree234.h ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ mines2.o: ./mines.c ./tree234.h ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_OBFUSCATOR -c $< -o $@ misc.o: ./misc.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ net.o: ./net.c ./puzzles.h ./tree234.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ net-icon.o: icons/net-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ net3.o: ./net.c ./puzzles.h ./tree234.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ netslide.o: ./netslide.c ./puzzles.h ./tree234.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ netslide-icon.o: icons/netslide-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ netslid3.o: ./netslide.c ./puzzles.h ./tree234.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ no-icon.o: ./no-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ nullfe.o: ./nullfe.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ nullgame.o: ./nullgame.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ obfusc.o: ./obfusc.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ osx.o: ./osx.m ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ pattern.o: ./pattern.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ pattern-icon.o: icons/pattern-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ pattern5.o: ./pattern.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ pattern2.o: ./pattern.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ pearl.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ pearl-icon.o: icons/pearl-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ pearl5.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ pearl2.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ pegs.o: ./pegs.c ./puzzles.h ./tree234.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ pegs-icon.o: icons/pegs-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ pegs3.o: ./pegs.c ./puzzles.h ./tree234.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ penrose.o: ./penrose.c ./puzzles.h ./penrose.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ printing.o: ./printing.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ ps.o: ./ps.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ random.o: ./random.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ range.o: ./range.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ range-icon.o: icons/range-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ range3.o: ./range.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ rect.o: ./rect.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ rect-icon.o: icons/rect-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ rect3.o: ./rect.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ samegame.o: ./samegame.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ samegame-icon.o: icons/samegame-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ samegam3.o: ./samegame.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ signpost.o: ./signpost.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ signpost-icon.o: icons/signpost-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ signpos5.o: ./signpost.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ signpos2.o: ./signpost.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ singles.o: ./singles.c ./puzzles.h ./latin.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ singles-icon.o: icons/singles-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ singles5.o: ./singles.c ./puzzles.h ./latin.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ singles3.o: ./singles.c ./puzzles.h ./latin.h $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ sixteen.o: ./sixteen.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ sixteen-icon.o: icons/sixteen-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ sixteen3.o: ./sixteen.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ slant.o: ./slant.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ slant-icon.o: icons/slant-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ slant5.o: ./slant.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ slant2.o: ./slant.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ solo.o: ./solo.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ solo-icon.o: icons/solo-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ solo5.o: ./solo.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ solo2.o: ./solo.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ tdq.o: ./tdq.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ tents.o: ./tents.c ./puzzles.h ./maxflow.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ tents-icon.o: icons/tents-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ tents5.o: ./tents.c ./puzzles.h ./maxflow.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ tents3.o: ./tents.c ./puzzles.h ./maxflow.h $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ towers.o: ./towers.c ./puzzles.h ./latin.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ towers-icon.o: icons/towers-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ towers5.o: ./towers.c ./puzzles.h ./latin.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ towers2.o: ./towers.c ./puzzles.h ./latin.h $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ tree234.o: ./tree234.c ./tree234.h ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ twiddle.o: ./twiddle.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ twiddle-icon.o: icons/twiddle-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ twiddle3.o: ./twiddle.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ undead.o: ./undead.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ undead-icon.o: icons/undead-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ undead3.o: ./undead.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ unequal.o: ./unequal.c ./puzzles.h ./latin.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ unequal-icon.o: icons/unequal-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ unequal5.o: ./unequal.c ./puzzles.h ./latin.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ unequal2.o: ./unequal.c ./puzzles.h ./latin.h $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ unruly.o: ./unruly.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ unruly-icon.o: icons/unruly-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ unruly5.o: ./unruly.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ unruly2.o: ./unruly.c ./puzzles.h $(EMCC) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ untangle.o: ./untangle.c ./puzzles.h ./tree234.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ untangle-icon.o: icons/untangle-icon.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ untangl3.o: ./untangle.c ./puzzles.h ./tree234.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ version.o: ./version.c $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ windows.o: ./windows.c ./puzzles.h ./resource.h $(EMCC) $(CFLAGS) $(XFLAGS) -c $< -o $@ windows1.o: ./windows.c ./puzzles.h ./resource.h $(EMCC) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ clean: rm -rf *.o $(OUTPREFIX)blackbox.js $(OUTPREFIX)bridges.js $(OUTPREFIX)cube.js $(OUTPREFIX)dominosa.js $(OUTPREFIX)fifteen.js $(OUTPREFIX)filling.js $(OUTPREFIX)flip.js $(OUTPREFIX)galaxies.js $(OUTPREFIX)guess.js $(OUTPREFIX)inertia.js $(OUTPREFIX)keen.js $(OUTPREFIX)lightup.js $(OUTPREFIX)loopy.js $(OUTPREFIX)magnets.js $(OUTPREFIX)map.js $(OUTPREFIX)mines.js $(OUTPREFIX)net.js $(OUTPREFIX)netslide.js $(OUTPREFIX)nullgame.js $(OUTPREFIX)pattern.js $(OUTPREFIX)pearl.js $(OUTPREFIX)pegs.js $(OUTPREFIX)range.js $(OUTPREFIX)rect.js $(OUTPREFIX)samegame.js $(OUTPREFIX)signpost.js $(OUTPREFIX)singles.js $(OUTPREFIX)sixteen.js $(OUTPREFIX)slant.js $(OUTPREFIX)solo.js $(OUTPREFIX)tents.js $(OUTPREFIX)towers.js $(OUTPREFIX)twiddle.js $(OUTPREFIX)undead.js $(OUTPREFIX)unequal.js $(OUTPREFIX)unruly.js $(OUTPREFIX)untangle.js puzzles-r9872/Makefile.gnustep0000644000175300017530000005724412161170560015564 0ustar simonsimon# Makefile for puzzles under GNUstep. # # This file was created by `mkfiles.pl' from the `Recipe' file. # DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead. NEEDS_GUI=yes include $(GNUSTEP_MAKEFILES)/common.make include $(GNUSTEP_MAKEFILES)/rules.make include $(GNUSTEP_MAKEFILES)/Instance/rules.make all:: Puzzles fillingsolver galaxiespicture galaxiessolver keensolver \ latincheck lightupsolver loopysolver magnetssolver mapsolver \ mineobfusc obfusc patternsolver pearlbench signpostsolver \ singlessolver slantsolver solosolver tentssolver \ towerssolver unequalsolver unrulysolver .SUFFIXES: .o .c .m Puzzles.app: mkdir -p $@ Puzzles.app/Resources: Puzzles.app mkdir -p $@ Puzzles.app/Resources/Puzzles.icns: Puzzles.app/Resources osx.icns cp osx.icns $@ Puzzles.app/Info.plist: Puzzles.app osx-info.plist cp osx-info.plist $@ Puzzles: Puzzles.app Puzzles.app/Puzzles \ Puzzles.app/Resources/Puzzles.icns Puzzles.app/Info.plist \ $(Puzzles_extra) Puzzles.app/Puzzles: blackbo3.o bridges3.o combi.o cube3.o divvy.o \ dominos3.o drawing.o dsf.o fifteen3.o filling5.o flip3.o \ galaxie7.o grid.o guess3.o inertia3.o keen5.o latin.o \ laydomino.o lightup5.o list.o loopgen.o loopy5.o magnets5.o \ malloc.o map5.o maxflow.o midend.o mines5.o misc.o net3.o \ netslid3.o osx.o pattern5.o pearl5.o pegs3.o penrose.o \ random.o range3.o rect3.o samegam3.o signpos5.o singles5.o \ sixteen3.o slant5.o solo5.o tdq.o tents5.o towers5.o \ tree234.o twiddle3.o undead3.o unequal5.o unruly5.o \ untangl3.o version.o $(CC) $(ALL_LDFLAGS) -o $@ blackbo3.o bridges3.o combi.o cube3.o \ divvy.o dominos3.o drawing.o dsf.o fifteen3.o filling5.o \ flip3.o galaxie7.o grid.o guess3.o inertia3.o keen5.o \ latin.o laydomino.o lightup5.o list.o loopgen.o loopy5.o \ magnets5.o malloc.o map5.o maxflow.o midend.o mines5.o \ misc.o net3.o netslid3.o osx.o pattern5.o pearl5.o pegs3.o \ penrose.o random.o range3.o rect3.o samegam3.o signpos5.o \ singles5.o sixteen3.o slant5.o solo5.o tdq.o tents5.o \ towers5.o tree234.o twiddle3.o undead3.o unequal5.o \ unruly5.o untangl3.o version.o $(ALL_LIB_DIRS) $(ALL_LIBS) fillingsolver: dsf.o filling2.o malloc.o misc.o nullfe.o random.o $(CC) $(ULDFLAGS) -o $@ dsf.o filling2.o malloc.o misc.o nullfe.o \ random.o galaxiespicture: dsf.o galaxie4.o malloc.o misc.o nullfe.o random.o $(CC) $(ULDFLAGS) -o $@ dsf.o galaxie4.o malloc.o misc.o nullfe.o \ random.o -lm galaxiessolver: dsf.o galaxie2.o malloc.o misc.o nullfe.o random.o $(CC) $(ULDFLAGS) -o $@ dsf.o galaxie2.o malloc.o misc.o nullfe.o \ random.o -lm keensolver: dsf.o keen2.o latin6.o malloc.o maxflow.o misc.o nullfe.o \ random.o tree234.o $(CC) $(ULDFLAGS) -o $@ dsf.o keen2.o latin6.o malloc.o maxflow.o \ misc.o nullfe.o random.o tree234.o latincheck: latin8.o malloc.o maxflow.o misc.o nullfe.o random.o tree234.o $(CC) $(ULDFLAGS) -o $@ latin8.o malloc.o maxflow.o misc.o nullfe.o \ random.o tree234.o lightupsolver: combi.o lightup2.o malloc.o misc.o nullfe.o random.o $(CC) $(ULDFLAGS) -o $@ combi.o lightup2.o malloc.o misc.o nullfe.o \ random.o loopysolver: dsf.o grid.o loopgen.o loopy2.o malloc.o misc.o nullfe.o \ penrose.o random.o tree234.o $(CC) $(ULDFLAGS) -o $@ dsf.o grid.o loopgen.o loopy2.o malloc.o \ misc.o nullfe.o penrose.o random.o tree234.o -lm magnetssolver: laydomino.o magnets2.o malloc.o misc.o nullfe.o random.o $(CC) $(ULDFLAGS) -o $@ laydomino.o magnets2.o malloc.o misc.o \ nullfe.o random.o -lm mapsolver: dsf.o malloc.o map2.o misc.o nullfe.o random.o $(CC) $(ULDFLAGS) -o $@ dsf.o malloc.o map2.o misc.o nullfe.o \ random.o -lm mineobfusc: malloc.o mines2.o misc.o nullfe.o random.o tree234.o $(CC) $(ULDFLAGS) -o $@ malloc.o mines2.o misc.o nullfe.o random.o \ tree234.o obfusc: malloc.o misc.o nullfe.o obfusc.o random.o $(CC) $(ULDFLAGS) -o $@ malloc.o misc.o nullfe.o obfusc.o random.o patternsolver: malloc.o misc.o nullfe.o pattern2.o random.o $(CC) $(ULDFLAGS) -o $@ malloc.o misc.o nullfe.o pattern2.o random.o pearlbench: dsf.o grid.o loopgen.o malloc.o misc.o nullfe.o pearl2.o \ penrose.o random.o tdq.o tree234.o $(CC) $(ULDFLAGS) -o $@ dsf.o grid.o loopgen.o malloc.o misc.o \ nullfe.o pearl2.o penrose.o random.o tdq.o tree234.o -lm signpostsolver: dsf.o malloc.o misc.o nullfe.o random.o signpos2.o $(CC) $(ULDFLAGS) -o $@ dsf.o malloc.o misc.o nullfe.o random.o \ signpos2.o -lm singlessolver: dsf.o latin.o malloc.o maxflow.o misc.o nullfe.o random.o \ singles3.o tree234.o $(CC) $(ULDFLAGS) -o $@ dsf.o latin.o malloc.o maxflow.o misc.o \ nullfe.o random.o singles3.o tree234.o slantsolver: dsf.o malloc.o misc.o nullfe.o random.o slant2.o $(CC) $(ULDFLAGS) -o $@ dsf.o malloc.o misc.o nullfe.o random.o \ slant2.o solosolver: divvy.o dsf.o malloc.o misc.o nullfe.o random.o solo2.o $(CC) $(ULDFLAGS) -o $@ divvy.o dsf.o malloc.o misc.o nullfe.o \ random.o solo2.o tentssolver: dsf.o malloc.o maxflow.o misc.o nullfe.o random.o tents3.o $(CC) $(ULDFLAGS) -o $@ dsf.o malloc.o maxflow.o misc.o nullfe.o \ random.o tents3.o towerssolver: latin6.o malloc.o maxflow.o misc.o nullfe.o random.o towers2.o \ tree234.o $(CC) $(ULDFLAGS) -o $@ latin6.o malloc.o maxflow.o misc.o nullfe.o \ random.o towers2.o tree234.o unequalsolver: latin6.o malloc.o maxflow.o misc.o nullfe.o random.o \ tree234.o unequal2.o $(CC) $(ULDFLAGS) -o $@ latin6.o malloc.o maxflow.o misc.o nullfe.o \ random.o tree234.o unequal2.o unrulysolver: malloc.o misc.o nullfe.o random.o unruly2.o $(CC) $(ULDFLAGS) -o $@ malloc.o misc.o nullfe.o random.o unruly2.o blackbox.o: ./blackbox.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ blackbox-icon.o: icons/blackbox-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ blackbo3.o: ./blackbox.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ bridges.o: ./bridges.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ bridges-icon.o: icons/bridges-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ bridges3.o: ./bridges.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ combi.o: ./combi.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ cube.o: ./cube.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ cube-icon.o: icons/cube-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ cube3.o: ./cube.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ divvy.o: ./divvy.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ dominosa.o: ./dominosa.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ dominosa-icon.o: icons/dominosa-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ dominos3.o: ./dominosa.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ drawing.o: ./drawing.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ dsf.o: ./dsf.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ fifteen.o: ./fifteen.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ fifteen-icon.o: icons/fifteen-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ fifteen3.o: ./fifteen.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ filling.o: ./filling.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ filling-icon.o: icons/filling-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ filling5.o: ./filling.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ filling2.o: ./filling.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ flip.o: ./flip.c ./puzzles.h ./tree234.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ flip-icon.o: icons/flip-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ flip3.o: ./flip.c ./puzzles.h ./tree234.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ galaxies.o: ./galaxies.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ galaxies-icon.o: icons/galaxies-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ galaxie7.o: ./galaxies.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ galaxie4.o: ./galaxies.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_PICTURE_GENERATOR -c $< -o $@ galaxie2.o: ./galaxies.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ grid.o: ./grid.c ./puzzles.h ./tree234.h ./grid.h ./penrose.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ gtk.o: ./gtk.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ guess.o: ./guess.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ guess-icon.o: icons/guess-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ guess3.o: ./guess.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ inertia.o: ./inertia.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ inertia-icon.o: icons/inertia-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ inertia3.o: ./inertia.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ keen.o: ./keen.c ./puzzles.h ./latin.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ keen-icon.o: icons/keen-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ keen5.o: ./keen.c ./puzzles.h ./latin.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ keen2.o: ./keen.c ./puzzles.h ./latin.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ latin.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ latin8.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_LATIN_TEST -c $< -o $@ latin6.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ laydomino.o: ./laydomino.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ lightup.o: ./lightup.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ lightup-icon.o: icons/lightup-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ lightup5.o: ./lightup.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ lightup2.o: ./lightup.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ list.o: ./list.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopgen.o: ./loopgen.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopy.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopy-icon.o: icons/loopy-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopy5.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ loopy2.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ magnets.o: ./magnets.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ magnets-icon.o: icons/magnets-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ magnets5.o: ./magnets.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ magnets2.o: ./magnets.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ malloc.o: ./malloc.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ map.o: ./map.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ map-icon.o: icons/map-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ map5.o: ./map.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ map2.o: ./map.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ maxflow.o: ./maxflow.c ./maxflow.h ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ midend.o: ./midend.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ mines.o: ./mines.c ./tree234.h ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ mines-icon.o: icons/mines-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ mines5.o: ./mines.c ./tree234.h ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ mines2.o: ./mines.c ./tree234.h ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_OBFUSCATOR -c $< -o $@ misc.o: ./misc.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ net.o: ./net.c ./puzzles.h ./tree234.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ net-icon.o: icons/net-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ net3.o: ./net.c ./puzzles.h ./tree234.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ netslide.o: ./netslide.c ./puzzles.h ./tree234.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ netslide-icon.o: icons/netslide-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ netslid3.o: ./netslide.c ./puzzles.h ./tree234.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ no-icon.o: ./no-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ nullfe.o: ./nullfe.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ nullgame.o: ./nullgame.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ obfusc.o: ./obfusc.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ osx.o: ./osx.m ./puzzles.h $(CC) -DGNUSTEP $(ALL_OBJCFLAGS) $(COMPAT) $(FWHACK) $(OBJCFLAGS) $(XFLAGS) -c $< -o $@ pattern.o: ./pattern.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pattern-icon.o: icons/pattern-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pattern5.o: ./pattern.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ pattern2.o: ./pattern.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ pearl.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pearl-icon.o: icons/pearl-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pearl5.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ pearl2.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ pegs.o: ./pegs.c ./puzzles.h ./tree234.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pegs-icon.o: icons/pegs-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pegs3.o: ./pegs.c ./puzzles.h ./tree234.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ penrose.o: ./penrose.c ./puzzles.h ./penrose.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ printing.o: ./printing.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ ps.o: ./ps.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ random.o: ./random.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ range.o: ./range.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ range-icon.o: icons/range-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ range3.o: ./range.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ rect.o: ./rect.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ rect-icon.o: icons/rect-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ rect3.o: ./rect.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ samegame.o: ./samegame.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ samegame-icon.o: icons/samegame-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ samegam3.o: ./samegame.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ signpost.o: ./signpost.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ signpost-icon.o: icons/signpost-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ signpos5.o: ./signpost.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ signpos2.o: ./signpost.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ singles.o: ./singles.c ./puzzles.h ./latin.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ singles-icon.o: icons/singles-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ singles5.o: ./singles.c ./puzzles.h ./latin.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ singles3.o: ./singles.c ./puzzles.h ./latin.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ sixteen.o: ./sixteen.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ sixteen-icon.o: icons/sixteen-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ sixteen3.o: ./sixteen.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ slant.o: ./slant.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ slant-icon.o: icons/slant-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ slant5.o: ./slant.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ slant2.o: ./slant.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ solo.o: ./solo.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ solo-icon.o: icons/solo-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ solo5.o: ./solo.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ solo2.o: ./solo.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ tdq.o: ./tdq.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ tents.o: ./tents.c ./puzzles.h ./maxflow.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ tents-icon.o: icons/tents-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ tents5.o: ./tents.c ./puzzles.h ./maxflow.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ tents3.o: ./tents.c ./puzzles.h ./maxflow.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ towers.o: ./towers.c ./puzzles.h ./latin.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ towers-icon.o: icons/towers-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ towers5.o: ./towers.c ./puzzles.h ./latin.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ towers2.o: ./towers.c ./puzzles.h ./latin.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ tree234.o: ./tree234.c ./tree234.h ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ twiddle.o: ./twiddle.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ twiddle-icon.o: icons/twiddle-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ twiddle3.o: ./twiddle.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ undead.o: ./undead.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ undead-icon.o: icons/undead-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ undead3.o: ./undead.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ unequal.o: ./unequal.c ./puzzles.h ./latin.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ unequal-icon.o: icons/unequal-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ unequal5.o: ./unequal.c ./puzzles.h ./latin.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ unequal2.o: ./unequal.c ./puzzles.h ./latin.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ unruly.o: ./unruly.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ unruly-icon.o: icons/unruly-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ unruly5.o: ./unruly.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ unruly2.o: ./unruly.c ./puzzles.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ untangle.o: ./untangle.c ./puzzles.h ./tree234.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ untangle-icon.o: icons/untangle-icon.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ untangl3.o: ./untangle.c ./puzzles.h ./tree234.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ version.o: ./version.c $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ windows.o: ./windows.c ./puzzles.h ./resource.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ windows1.o: ./windows.c ./puzzles.h ./resource.h $(CC) $(ALL_CFLAGS) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ clean:: rm -f *.o fillingsolver galaxiespicture galaxiessolver keensolver latincheck lightupsolver loopysolver magnetssolver mapsolver mineobfusc obfusc patternsolver pearlbench signpostsolver singlessolver slantsolver solosolver tentssolver towerssolver unequalsolver unrulysolver rm -rf *.app puzzles-r9872/Makefile.nestedvm0000644000175300017530000007067512161170560015727 0ustar simonsimon# Makefile for puzzles under NestedVM. # # This file was created by `mkfiles.pl' from the `Recipe' file. # DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead. # This path points at the nestedvm root directory NESTEDVM = /opt/nestedvm # You can define this path to point at your tools if you need to TOOLPATH = $(NESTEDVM)/upstream/install/bin CC = $(TOOLPATH)/mips-unknown-elf-gcc CFLAGS = -O2 -Wall -Werror -DSLOW_SYSTEM -g -I./ -Iicons/ all: blackbox.jar bridges.jar cube.jar dominosa.jar fifteen.jar filling.jar \ flip.jar galaxies.jar guess.jar inertia.jar keen.jar \ lightup.jar loopy.jar magnets.jar map.jar mines.jar net.jar \ netslide.jar nullgame.jar pattern.jar pearl.jar pegs.jar \ range.jar rect.jar samegame.jar signpost.jar singles.jar \ sixteen.jar slant.jar solo.jar tents.jar towers.jar \ twiddle.jar undead.jar unequal.jar unruly.jar untangle.jar blackbox.mips: blackbox.o blackbox-icon.o drawing.o nestedvm.o malloc.o \ midend.o misc.o printing.o ps.o random.o version.o $(CC) $(XLDFLAGS) -o $@ blackbox.o blackbox-icon.o drawing.o \ nestedvm.o malloc.o midend.o misc.o printing.o ps.o random.o \ version.o -lm bridges.mips: bridges.o bridges-icon.o drawing.o dsf.o nestedvm.o malloc.o \ midend.o misc.o printing.o ps.o random.o version.o $(CC) $(XLDFLAGS) -o $@ bridges.o bridges-icon.o drawing.o dsf.o \ nestedvm.o malloc.o midend.o misc.o printing.o ps.o random.o \ version.o -lm cube.mips: cube.o cube-icon.o drawing.o nestedvm.o malloc.o midend.o misc.o \ printing.o ps.o random.o version.o $(CC) $(XLDFLAGS) -o $@ cube.o cube-icon.o drawing.o nestedvm.o \ malloc.o midend.o misc.o printing.o ps.o random.o version.o \ -lm dominosa.mips: dominosa.o dominosa-icon.o drawing.o nestedvm.o laydomino.o \ malloc.o midend.o misc.o printing.o ps.o random.o version.o $(CC) $(XLDFLAGS) -o $@ dominosa.o dominosa-icon.o drawing.o \ nestedvm.o laydomino.o malloc.o midend.o misc.o printing.o \ ps.o random.o version.o -lm fifteen.mips: drawing.o fifteen.o fifteen-icon.o nestedvm.o malloc.o \ midend.o misc.o printing.o ps.o random.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o fifteen.o fifteen-icon.o \ nestedvm.o malloc.o midend.o misc.o printing.o ps.o random.o \ version.o -lm filling.mips: drawing.o dsf.o filling.o filling-icon.o nestedvm.o malloc.o \ midend.o misc.o printing.o ps.o random.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o filling.o filling-icon.o \ nestedvm.o malloc.o midend.o misc.o printing.o ps.o random.o \ version.o -lm flip.mips: drawing.o flip.o flip-icon.o nestedvm.o malloc.o midend.o misc.o \ printing.o ps.o random.o tree234.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o flip.o flip-icon.o nestedvm.o \ malloc.o midend.o misc.o printing.o ps.o random.o tree234.o \ version.o -lm galaxies.mips: drawing.o dsf.o galaxies.o galaxies-icon.o nestedvm.o \ malloc.o midend.o misc.o printing.o ps.o random.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o galaxies.o galaxies-icon.o \ nestedvm.o malloc.o midend.o misc.o printing.o ps.o random.o \ version.o -lm guess.mips: drawing.o nestedvm.o guess.o guess-icon.o malloc.o midend.o \ misc.o printing.o ps.o random.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o guess.o guess-icon.o \ malloc.o midend.o misc.o printing.o ps.o random.o version.o \ -lm inertia.mips: drawing.o nestedvm.o inertia.o inertia-icon.o malloc.o \ midend.o misc.o printing.o ps.o random.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o inertia.o \ inertia-icon.o malloc.o midend.o misc.o printing.o ps.o \ random.o version.o -lm keen.mips: drawing.o dsf.o nestedvm.o keen.o keen-icon.o latin.o malloc.o \ maxflow.o midend.o misc.o printing.o ps.o random.o tree234.o \ version.o $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o nestedvm.o keen.o \ keen-icon.o latin.o malloc.o maxflow.o midend.o misc.o \ printing.o ps.o random.o tree234.o version.o -lm lightup.mips: combi.o drawing.o nestedvm.o lightup.o lightup-icon.o malloc.o \ midend.o misc.o printing.o ps.o random.o version.o $(CC) $(XLDFLAGS) -o $@ combi.o drawing.o nestedvm.o lightup.o \ lightup-icon.o malloc.o midend.o misc.o printing.o ps.o \ random.o version.o -lm loopy.mips: drawing.o dsf.o grid.o nestedvm.o loopgen.o loopy.o loopy-icon.o \ malloc.o midend.o misc.o penrose.o printing.o ps.o random.o \ tree234.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o grid.o nestedvm.o loopgen.o \ loopy.o loopy-icon.o malloc.o midend.o misc.o penrose.o \ printing.o ps.o random.o tree234.o version.o -lm magnets.mips: drawing.o nestedvm.o laydomino.o magnets.o magnets-icon.o \ malloc.o midend.o misc.o printing.o ps.o random.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o laydomino.o magnets.o \ magnets-icon.o malloc.o midend.o misc.o printing.o ps.o \ random.o version.o -lm map.mips: drawing.o dsf.o nestedvm.o malloc.o map.o map-icon.o midend.o \ misc.o printing.o ps.o random.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o nestedvm.o malloc.o map.o \ map-icon.o midend.o misc.o printing.o ps.o random.o \ version.o -lm mines.mips: drawing.o nestedvm.o malloc.o midend.o mines.o mines-icon.o \ misc.o printing.o ps.o random.o tree234.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \ mines.o mines-icon.o misc.o printing.o ps.o random.o \ tree234.o version.o -lm net.mips: drawing.o dsf.o nestedvm.o malloc.o midend.o misc.o net.o \ net-icon.o printing.o ps.o random.o tree234.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o nestedvm.o malloc.o midend.o \ misc.o net.o net-icon.o printing.o ps.o random.o tree234.o \ version.o -lm netslide.mips: drawing.o nestedvm.o malloc.o midend.o misc.o netslide.o \ netslide-icon.o printing.o ps.o random.o tree234.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \ misc.o netslide.o netslide-icon.o printing.o ps.o random.o \ tree234.o version.o -lm nullgame.mips: drawing.o nestedvm.o malloc.o midend.o misc.o no-icon.o \ nullgame.o printing.o ps.o random.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \ misc.o no-icon.o nullgame.o printing.o ps.o random.o \ version.o -lm pattern.mips: drawing.o nestedvm.o malloc.o midend.o misc.o pattern.o \ pattern-icon.o printing.o ps.o random.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \ misc.o pattern.o pattern-icon.o printing.o ps.o random.o \ version.o -lm pearl.mips: drawing.o dsf.o grid.o nestedvm.o loopgen.o malloc.o midend.o \ misc.o pearl.o pearl-icon.o penrose.o printing.o ps.o \ random.o tdq.o tree234.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o grid.o nestedvm.o loopgen.o \ malloc.o midend.o misc.o pearl.o pearl-icon.o penrose.o \ printing.o ps.o random.o tdq.o tree234.o version.o -lm pegs.mips: drawing.o nestedvm.o malloc.o midend.o misc.o pegs.o pegs-icon.o \ printing.o ps.o random.o tree234.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \ misc.o pegs.o pegs-icon.o printing.o ps.o random.o tree234.o \ version.o -lm range.mips: drawing.o nestedvm.o malloc.o midend.o misc.o printing.o ps.o \ random.o range.o range-icon.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \ misc.o printing.o ps.o random.o range.o range-icon.o \ version.o -lm rect.mips: drawing.o nestedvm.o malloc.o midend.o misc.o printing.o ps.o \ random.o rect.o rect-icon.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \ misc.o printing.o ps.o random.o rect.o rect-icon.o version.o \ -lm samegame.mips: drawing.o nestedvm.o malloc.o midend.o misc.o printing.o ps.o \ random.o samegame.o samegame-icon.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \ misc.o printing.o ps.o random.o samegame.o samegame-icon.o \ version.o -lm signpost.mips: drawing.o dsf.o nestedvm.o malloc.o midend.o misc.o \ printing.o ps.o random.o signpost.o signpost-icon.o \ version.o $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o nestedvm.o malloc.o midend.o \ misc.o printing.o ps.o random.o signpost.o signpost-icon.o \ version.o -lm singles.mips: drawing.o dsf.o nestedvm.o latin.o malloc.o maxflow.o midend.o \ misc.o printing.o ps.o random.o singles.o singles-icon.o \ tree234.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o nestedvm.o latin.o malloc.o \ maxflow.o midend.o misc.o printing.o ps.o random.o singles.o \ singles-icon.o tree234.o version.o -lm sixteen.mips: drawing.o nestedvm.o malloc.o midend.o misc.o printing.o ps.o \ random.o sixteen.o sixteen-icon.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \ misc.o printing.o ps.o random.o sixteen.o sixteen-icon.o \ version.o -lm slant.mips: drawing.o dsf.o nestedvm.o malloc.o midend.o misc.o printing.o \ ps.o random.o slant.o slant-icon.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o nestedvm.o malloc.o midend.o \ misc.o printing.o ps.o random.o slant.o slant-icon.o \ version.o -lm solo.mips: divvy.o drawing.o dsf.o nestedvm.o malloc.o midend.o misc.o \ printing.o ps.o random.o solo.o solo-icon.o version.o $(CC) $(XLDFLAGS) -o $@ divvy.o drawing.o dsf.o nestedvm.o malloc.o \ midend.o misc.o printing.o ps.o random.o solo.o solo-icon.o \ version.o -lm tents.mips: drawing.o dsf.o nestedvm.o malloc.o maxflow.o midend.o misc.o \ printing.o ps.o random.o tents.o tents-icon.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o dsf.o nestedvm.o malloc.o \ maxflow.o midend.o misc.o printing.o ps.o random.o tents.o \ tents-icon.o version.o -lm towers.mips: drawing.o nestedvm.o latin.o malloc.o maxflow.o midend.o misc.o \ printing.o ps.o random.o towers.o towers-icon.o tree234.o \ version.o $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o latin.o malloc.o \ maxflow.o midend.o misc.o printing.o ps.o random.o towers.o \ towers-icon.o tree234.o version.o -lm twiddle.mips: drawing.o nestedvm.o malloc.o midend.o misc.o printing.o ps.o \ random.o twiddle.o twiddle-icon.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \ misc.o printing.o ps.o random.o twiddle.o twiddle-icon.o \ version.o -lm undead.mips: drawing.o nestedvm.o malloc.o midend.o misc.o printing.o ps.o \ random.o undead.o undead-icon.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \ misc.o printing.o ps.o random.o undead.o undead-icon.o \ version.o -lm unequal.mips: drawing.o nestedvm.o latin.o malloc.o maxflow.o midend.o \ misc.o printing.o ps.o random.o tree234.o unequal.o \ unequal-icon.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o latin.o malloc.o \ maxflow.o midend.o misc.o printing.o ps.o random.o tree234.o \ unequal.o unequal-icon.o version.o -lm unruly.mips: drawing.o nestedvm.o malloc.o midend.o misc.o printing.o ps.o \ random.o unruly.o unruly-icon.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \ misc.o printing.o ps.o random.o unruly.o unruly-icon.o \ version.o -lm untangle.mips: drawing.o nestedvm.o malloc.o midend.o misc.o printing.o ps.o \ random.o tree234.o untangle.o untangle-icon.o version.o $(CC) $(XLDFLAGS) -o $@ drawing.o nestedvm.o malloc.o midend.o \ misc.o printing.o ps.o random.o tree234.o untangle.o \ untangle-icon.o version.o -lm blackbox.o: ./blackbox.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ blackbox-icon.o: icons/blackbox-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ blackbo3.o: ./blackbox.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ bridges.o: ./bridges.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ bridges-icon.o: icons/bridges-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ bridges3.o: ./bridges.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ combi.o: ./combi.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ cube.o: ./cube.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ cube-icon.o: icons/cube-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ cube3.o: ./cube.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ divvy.o: ./divvy.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ dominosa.o: ./dominosa.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ dominosa-icon.o: icons/dominosa-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ dominos3.o: ./dominosa.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ drawing.o: ./drawing.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ dsf.o: ./dsf.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ fifteen.o: ./fifteen.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ fifteen-icon.o: icons/fifteen-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ fifteen3.o: ./fifteen.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ filling.o: ./filling.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ filling-icon.o: icons/filling-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ filling5.o: ./filling.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ filling2.o: ./filling.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ flip.o: ./flip.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ flip-icon.o: icons/flip-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ flip3.o: ./flip.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ galaxies.o: ./galaxies.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ galaxies-icon.o: icons/galaxies-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ galaxie7.o: ./galaxies.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ galaxie4.o: ./galaxies.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_PICTURE_GENERATOR -c $< -o $@ galaxie2.o: ./galaxies.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ grid.o: ./grid.c ./puzzles.h ./tree234.h ./grid.h ./penrose.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ nestedvm.o: ./nestedvm.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ guess.o: ./guess.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ guess-icon.o: icons/guess-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ guess3.o: ./guess.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ inertia.o: ./inertia.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ inertia-icon.o: icons/inertia-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ inertia3.o: ./inertia.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ keen.o: ./keen.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ keen-icon.o: icons/keen-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ keen5.o: ./keen.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ keen2.o: ./keen.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ latin.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ latin8.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_LATIN_TEST -c $< -o $@ latin6.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ laydomino.o: ./laydomino.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ lightup.o: ./lightup.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ lightup-icon.o: icons/lightup-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ lightup5.o: ./lightup.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ lightup2.o: ./lightup.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ list.o: ./list.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopgen.o: ./loopgen.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopy.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopy-icon.o: icons/loopy-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopy5.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ loopy2.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ magnets.o: ./magnets.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ magnets-icon.o: icons/magnets-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ magnets5.o: ./magnets.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ magnets2.o: ./magnets.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ malloc.o: ./malloc.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ map.o: ./map.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ map-icon.o: icons/map-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ map5.o: ./map.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ map2.o: ./map.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ maxflow.o: ./maxflow.c ./maxflow.h ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ midend.o: ./midend.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ mines.o: ./mines.c ./tree234.h ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ mines-icon.o: icons/mines-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ mines5.o: ./mines.c ./tree234.h ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ mines2.o: ./mines.c ./tree234.h ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_OBFUSCATOR -c $< -o $@ misc.o: ./misc.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ net.o: ./net.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ net-icon.o: icons/net-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ net3.o: ./net.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ netslide.o: ./netslide.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ netslide-icon.o: icons/netslide-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ netslid3.o: ./netslide.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ no-icon.o: ./no-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ nullfe.o: ./nullfe.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ nullgame.o: ./nullgame.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ obfusc.o: ./obfusc.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ osx.o: ./osx.m ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pattern.o: ./pattern.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pattern-icon.o: icons/pattern-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pattern5.o: ./pattern.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ pattern2.o: ./pattern.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ pearl.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pearl-icon.o: icons/pearl-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pearl5.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ pearl2.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ pegs.o: ./pegs.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pegs-icon.o: icons/pegs-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pegs3.o: ./pegs.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ penrose.o: ./penrose.c ./puzzles.h ./penrose.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ printing.o: ./printing.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ ps.o: ./ps.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ random.o: ./random.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ range.o: ./range.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ range-icon.o: icons/range-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ range3.o: ./range.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ rect.o: ./rect.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ rect-icon.o: icons/rect-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ rect3.o: ./rect.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ samegame.o: ./samegame.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ samegame-icon.o: icons/samegame-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ samegam3.o: ./samegame.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ signpost.o: ./signpost.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ signpost-icon.o: icons/signpost-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ signpos5.o: ./signpost.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ signpos2.o: ./signpost.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ singles.o: ./singles.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ singles-icon.o: icons/singles-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ singles5.o: ./singles.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ singles3.o: ./singles.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ sixteen.o: ./sixteen.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ sixteen-icon.o: icons/sixteen-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ sixteen3.o: ./sixteen.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ slant.o: ./slant.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ slant-icon.o: icons/slant-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ slant5.o: ./slant.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ slant2.o: ./slant.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ solo.o: ./solo.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ solo-icon.o: icons/solo-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ solo5.o: ./solo.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ solo2.o: ./solo.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ tdq.o: ./tdq.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ tents.o: ./tents.c ./puzzles.h ./maxflow.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ tents-icon.o: icons/tents-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ tents5.o: ./tents.c ./puzzles.h ./maxflow.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ tents3.o: ./tents.c ./puzzles.h ./maxflow.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ towers.o: ./towers.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ towers-icon.o: icons/towers-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ towers5.o: ./towers.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ towers2.o: ./towers.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ tree234.o: ./tree234.c ./tree234.h ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ twiddle.o: ./twiddle.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ twiddle-icon.o: icons/twiddle-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ twiddle3.o: ./twiddle.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ undead.o: ./undead.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ undead-icon.o: icons/undead-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ undead3.o: ./undead.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ unequal.o: ./unequal.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ unequal-icon.o: icons/unequal-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ unequal5.o: ./unequal.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ unequal2.o: ./unequal.c ./puzzles.h ./latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ unruly.o: ./unruly.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ unruly-icon.o: icons/unruly-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ unruly5.o: ./unruly.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ unruly2.o: ./unruly.c ./puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ untangle.o: ./untangle.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ untangle-icon.o: icons/untangle-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ untangl3.o: ./untangle.c ./puzzles.h ./tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ windows.o: ./windows.c ./puzzles.h ./resource.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ windows1.o: ./windows.c ./puzzles.h ./resource.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ version.o: version.c version2.def $(CC) $(COMPAT) $(XFLAGS) $(CFLAGS) `cat version2.def` -c version.c version2.def: FORCE if test -z "$(VER)" && test -f manifest && md5sum -c manifest; then \ cat version.def > version2.def.new; \ elif test -z "$(VER)" && test -d .svn && svnversion . >/dev/null 2>&1; then \ echo "-DREVISION=`svnversion .`" >version2.def.new; \ else \ echo "$(VER)" >version2.def.new; \ fi && \ if diff -q version2.def.new version2.def; then \ rm version2.def.new; \ else \ mv version2.def.new version2.def; \ fi .PHONY: FORCE .PRECIOUS: %.class %.class: %.mips java -cp $(NESTEDVM)/build:$(NESTEDVM)/upstream/build/classgen/build \ org.ibex.nestedvm.Compiler -outformat class -d . \ PuzzleEngine $< mv PuzzleEngine.class $@ 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 echo "Main-Class: PuzzleApplet" >applet.manifest PuzzleApplet.class: PuzzleApplet.java org javac -source 1.3 -target 1.3 PuzzleApplet.java %.jar: %.class PuzzleApplet.class org mv $< PuzzleEngine.class jar cfm $@ applet.manifest PuzzleEngine.class PuzzleApplet*.class org echo '' >$*.html mv PuzzleEngine.class $< clean: rm -rf *.o *.mips *.class *.html *.jar org applet.manifest puzzles-r9872/Makefile.osx0000644000175300017530000010535112161170560014701 0ustar simonsimon# Makefile for puzzles under Mac OS X. # # This file was created by `mkfiles.pl' from the `Recipe' file. # DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead. CC = $(TOOLPATH)gcc LIPO = $(TOOLPATH)lipo CFLAGS = -O2 -Wall -Werror -g -I./ -Iicons/ LDFLAGS = -framework Cocoa all: Puzzles fillingsolver galaxiespicture galaxiessolver keensolver \ latincheck lightupsolver loopysolver magnetssolver mapsolver \ mineobfusc obfusc patternsolver pearlbench signpostsolver \ singlessolver slantsolver solosolver tentssolver \ towerssolver unequalsolver unrulysolver 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 version.ppc.o: version.c version2.def $(CC) -arch ppc $(COMPAT) $(XFLAGS) $(CFLAGS) `cat version2.def` -c version.c -o $@ version.i386.o: version.c version2.def $(CC) -arch i386 $(COMPAT) $(XFLAGS) $(CFLAGS) `cat version2.def` -c version.c -o $@ version2.def: FORCE if test -z "$(VER)" && test -f manifest && (md5 -r `awk '{print $$2}' manifest` | diff -w manifest -); then \ cat version.def > version2.def.new; \ elif test -z "$(VER)" && test -d .svn && svnversion . >/dev/null 2>&1; then \ echo "-DREVISION=`svnversion .`" >version2.def.new; \ else \ echo "$(VER)" >version2.def.new; \ fi && \ if diff -q version2.def.new version2.def; then \ rm version2.def.new; \ else \ mv version2.def.new version2.def; \ fi .PHONY: FORCE .SUFFIXES: .o .c .m Puzzles.app: mkdir -p $@ Puzzles.app/Contents: Puzzles.app mkdir -p $@ Puzzles.app/Contents/MacOS: Puzzles.app/Contents mkdir -p $@ Puzzles.app/Contents/Resources: Puzzles.app/Contents mkdir -p $@ Puzzles.app/Contents/Resources/Puzzles.icns: Puzzles.app/Contents/Resources osx.icns cp osx.icns $@ Puzzles.app/Contents/Info.plist: Puzzles.app/Contents/Resources osx-info.plist cp osx-info.plist $@ Puzzles: Puzzles.app/Contents/MacOS/Puzzles \ Puzzles.app/Contents/Resources/Puzzles.icns \ Puzzles.app/Contents/Info.plist $(Puzzles_extra) Puzzles.i386.bin: blackbo3.i386.o bridges3.i386.o combi.i386.o cube3.i386.o \ divvy.i386.o dominos3.i386.o drawing.i386.o dsf.i386.o \ fifteen3.i386.o filling5.i386.o flip3.i386.o galaxie7.i386.o \ grid.i386.o guess3.i386.o inertia3.i386.o keen5.i386.o \ latin.i386.o laydomino.i386.o lightup5.i386.o list.i386.o \ loopgen.i386.o loopy5.i386.o magnets5.i386.o malloc.i386.o \ map5.i386.o maxflow.i386.o midend.i386.o mines5.i386.o \ misc.i386.o net3.i386.o netslid3.i386.o osx.i386.o \ pattern5.i386.o pearl5.i386.o pegs3.i386.o penrose.i386.o \ random.i386.o range3.i386.o rect3.i386.o samegam3.i386.o \ signpos5.i386.o singles5.i386.o sixteen3.i386.o \ slant5.i386.o solo5.i386.o tdq.i386.o tents5.i386.o \ towers5.i386.o tree234.i386.o twiddle3.i386.o undead3.i386.o \ unequal5.i386.o unruly5.i386.o untangl3.i386.o \ version.i386.o $(CC) -arch i386 -mmacosx-version-min=10.4 $(LDFLAGS) -o $@ \ blackbo3.i386.o bridges3.i386.o combi.i386.o cube3.i386.o \ divvy.i386.o dominos3.i386.o drawing.i386.o dsf.i386.o \ fifteen3.i386.o filling5.i386.o flip3.i386.o galaxie7.i386.o \ grid.i386.o guess3.i386.o inertia3.i386.o keen5.i386.o \ latin.i386.o laydomino.i386.o lightup5.i386.o list.i386.o \ loopgen.i386.o loopy5.i386.o magnets5.i386.o malloc.i386.o \ map5.i386.o maxflow.i386.o midend.i386.o mines5.i386.o \ misc.i386.o net3.i386.o netslid3.i386.o osx.i386.o \ pattern5.i386.o pearl5.i386.o pegs3.i386.o penrose.i386.o \ random.i386.o range3.i386.o rect3.i386.o samegam3.i386.o \ signpos5.i386.o singles5.i386.o sixteen3.i386.o \ slant5.i386.o solo5.i386.o tdq.i386.o tents5.i386.o \ towers5.i386.o tree234.i386.o twiddle3.i386.o undead3.i386.o \ unequal5.i386.o unruly5.i386.o untangl3.i386.o \ version.i386.o Puzzles.app/Contents/MacOS/Puzzles: Puzzles.app/Contents/MacOS \ Puzzles.i386.bin $(LIPO) -create Puzzles.i386.bin -output $@ fillingsolver.i386: dsf.i386.o filling2.i386.o malloc.i386.o misc.i386.o \ nullfe.i386.o random.i386.o $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \ dsf.i386.o filling2.i386.o malloc.i386.o misc.i386.o \ nullfe.i386.o random.i386.o fillingsolver: fillingsolver.i386 $(LIPO) -create fillingsolver.i386 -output $@ galaxiespicture.i386: dsf.i386.o galaxie4.i386.o malloc.i386.o misc.i386.o \ nullfe.i386.o random.i386.o $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \ dsf.i386.o galaxie4.i386.o malloc.i386.o misc.i386.o \ nullfe.i386.o random.i386.o -lm galaxiespicture: galaxiespicture.i386 $(LIPO) -create galaxiespicture.i386 -output $@ galaxiessolver.i386: dsf.i386.o galaxie2.i386.o malloc.i386.o misc.i386.o \ nullfe.i386.o random.i386.o $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \ dsf.i386.o galaxie2.i386.o malloc.i386.o misc.i386.o \ nullfe.i386.o random.i386.o -lm galaxiessolver: galaxiessolver.i386 $(LIPO) -create galaxiessolver.i386 -output $@ keensolver.i386: dsf.i386.o keen2.i386.o latin6.i386.o malloc.i386.o \ maxflow.i386.o misc.i386.o nullfe.i386.o random.i386.o \ tree234.i386.o $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \ dsf.i386.o keen2.i386.o latin6.i386.o malloc.i386.o \ maxflow.i386.o misc.i386.o nullfe.i386.o random.i386.o \ tree234.i386.o keensolver: keensolver.i386 $(LIPO) -create keensolver.i386 -output $@ latincheck.i386: latin8.i386.o malloc.i386.o maxflow.i386.o misc.i386.o \ nullfe.i386.o random.i386.o tree234.i386.o $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \ latin8.i386.o malloc.i386.o maxflow.i386.o misc.i386.o \ nullfe.i386.o random.i386.o tree234.i386.o latincheck: latincheck.i386 $(LIPO) -create latincheck.i386 -output $@ lightupsolver.i386: combi.i386.o lightup2.i386.o malloc.i386.o misc.i386.o \ nullfe.i386.o random.i386.o $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \ combi.i386.o lightup2.i386.o malloc.i386.o misc.i386.o \ nullfe.i386.o random.i386.o lightupsolver: lightupsolver.i386 $(LIPO) -create lightupsolver.i386 -output $@ loopysolver.i386: dsf.i386.o grid.i386.o loopgen.i386.o loopy2.i386.o \ malloc.i386.o misc.i386.o nullfe.i386.o penrose.i386.o \ random.i386.o tree234.i386.o $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \ dsf.i386.o grid.i386.o loopgen.i386.o loopy2.i386.o \ malloc.i386.o misc.i386.o nullfe.i386.o penrose.i386.o \ random.i386.o tree234.i386.o -lm loopysolver: loopysolver.i386 $(LIPO) -create loopysolver.i386 -output $@ magnetssolver.i386: laydomino.i386.o magnets2.i386.o malloc.i386.o \ misc.i386.o nullfe.i386.o random.i386.o $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \ laydomino.i386.o magnets2.i386.o malloc.i386.o misc.i386.o \ nullfe.i386.o random.i386.o -lm magnetssolver: magnetssolver.i386 $(LIPO) -create magnetssolver.i386 -output $@ mapsolver.i386: dsf.i386.o malloc.i386.o map2.i386.o misc.i386.o \ nullfe.i386.o random.i386.o $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \ dsf.i386.o malloc.i386.o map2.i386.o misc.i386.o \ nullfe.i386.o random.i386.o -lm mapsolver: mapsolver.i386 $(LIPO) -create mapsolver.i386 -output $@ mineobfusc.i386: malloc.i386.o mines2.i386.o misc.i386.o nullfe.i386.o \ random.i386.o tree234.i386.o $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \ malloc.i386.o mines2.i386.o misc.i386.o nullfe.i386.o \ random.i386.o tree234.i386.o mineobfusc: mineobfusc.i386 $(LIPO) -create mineobfusc.i386 -output $@ obfusc.i386: malloc.i386.o misc.i386.o nullfe.i386.o obfusc.i386.o \ random.i386.o $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \ malloc.i386.o misc.i386.o nullfe.i386.o obfusc.i386.o \ random.i386.o obfusc: obfusc.i386 $(LIPO) -create obfusc.i386 -output $@ patternsolver.i386: malloc.i386.o misc.i386.o nullfe.i386.o pattern2.i386.o \ random.i386.o $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \ malloc.i386.o misc.i386.o nullfe.i386.o pattern2.i386.o \ random.i386.o patternsolver: patternsolver.i386 $(LIPO) -create patternsolver.i386 -output $@ pearlbench.i386: dsf.i386.o grid.i386.o loopgen.i386.o malloc.i386.o \ misc.i386.o nullfe.i386.o pearl2.i386.o penrose.i386.o \ random.i386.o tdq.i386.o tree234.i386.o $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \ dsf.i386.o grid.i386.o loopgen.i386.o malloc.i386.o \ misc.i386.o nullfe.i386.o pearl2.i386.o penrose.i386.o \ random.i386.o tdq.i386.o tree234.i386.o -lm pearlbench: pearlbench.i386 $(LIPO) -create pearlbench.i386 -output $@ signpostsolver.i386: dsf.i386.o malloc.i386.o misc.i386.o nullfe.i386.o \ random.i386.o signpos2.i386.o $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \ dsf.i386.o malloc.i386.o misc.i386.o nullfe.i386.o \ random.i386.o signpos2.i386.o -lm signpostsolver: signpostsolver.i386 $(LIPO) -create signpostsolver.i386 -output $@ singlessolver.i386: dsf.i386.o latin.i386.o malloc.i386.o maxflow.i386.o \ misc.i386.o nullfe.i386.o random.i386.o singles3.i386.o \ tree234.i386.o $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \ dsf.i386.o latin.i386.o malloc.i386.o maxflow.i386.o \ misc.i386.o nullfe.i386.o random.i386.o singles3.i386.o \ tree234.i386.o singlessolver: singlessolver.i386 $(LIPO) -create singlessolver.i386 -output $@ slantsolver.i386: dsf.i386.o malloc.i386.o misc.i386.o nullfe.i386.o \ random.i386.o slant2.i386.o $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \ dsf.i386.o malloc.i386.o misc.i386.o nullfe.i386.o \ random.i386.o slant2.i386.o slantsolver: slantsolver.i386 $(LIPO) -create slantsolver.i386 -output $@ solosolver.i386: divvy.i386.o dsf.i386.o malloc.i386.o misc.i386.o \ nullfe.i386.o random.i386.o solo2.i386.o $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \ divvy.i386.o dsf.i386.o malloc.i386.o misc.i386.o \ nullfe.i386.o random.i386.o solo2.i386.o solosolver: solosolver.i386 $(LIPO) -create solosolver.i386 -output $@ tentssolver.i386: dsf.i386.o malloc.i386.o maxflow.i386.o misc.i386.o \ nullfe.i386.o random.i386.o tents3.i386.o $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \ dsf.i386.o malloc.i386.o maxflow.i386.o misc.i386.o \ nullfe.i386.o random.i386.o tents3.i386.o tentssolver: tentssolver.i386 $(LIPO) -create tentssolver.i386 -output $@ towerssolver.i386: latin6.i386.o malloc.i386.o maxflow.i386.o misc.i386.o \ nullfe.i386.o random.i386.o towers2.i386.o tree234.i386.o $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \ latin6.i386.o malloc.i386.o maxflow.i386.o misc.i386.o \ nullfe.i386.o random.i386.o towers2.i386.o tree234.i386.o towerssolver: towerssolver.i386 $(LIPO) -create towerssolver.i386 -output $@ unequalsolver.i386: latin6.i386.o malloc.i386.o maxflow.i386.o misc.i386.o \ nullfe.i386.o random.i386.o tree234.i386.o unequal2.i386.o $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \ latin6.i386.o malloc.i386.o maxflow.i386.o misc.i386.o \ nullfe.i386.o random.i386.o tree234.i386.o unequal2.i386.o unequalsolver: unequalsolver.i386 $(LIPO) -create unequalsolver.i386 -output $@ unrulysolver.i386: malloc.i386.o misc.i386.o nullfe.i386.o random.i386.o \ unruly2.i386.o $(CC) -arch i386 -mmacosx-version-min=10.4 $(ULDFLAGS) -o $@ \ malloc.i386.o misc.i386.o nullfe.i386.o random.i386.o \ unruly2.i386.o unrulysolver: unrulysolver.i386 $(LIPO) -create unrulysolver.i386 -output $@ blackbox.i386.o: ./blackbox.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ blackbox-icon.i386.o: icons/blackbox-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ blackbo3.i386.o: ./blackbox.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ bridges.i386.o: ./bridges.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ bridges-icon.i386.o: icons/bridges-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ bridges3.i386.o: ./bridges.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ combi.i386.o: ./combi.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ cube.i386.o: ./cube.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ cube-icon.i386.o: icons/cube-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ cube3.i386.o: ./cube.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ divvy.i386.o: ./divvy.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ dominosa.i386.o: ./dominosa.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ dominosa-icon.i386.o: icons/dominosa-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ dominos3.i386.o: ./dominosa.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ drawing.i386.o: ./drawing.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ dsf.i386.o: ./dsf.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ fifteen.i386.o: ./fifteen.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ fifteen-icon.i386.o: icons/fifteen-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ fifteen3.i386.o: ./fifteen.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ filling.i386.o: ./filling.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ filling-icon.i386.o: icons/filling-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ filling5.i386.o: ./filling.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ filling2.i386.o: ./filling.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ flip.i386.o: ./flip.c ./puzzles.h ./tree234.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ flip-icon.i386.o: icons/flip-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ flip3.i386.o: ./flip.c ./puzzles.h ./tree234.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ galaxies.i386.o: ./galaxies.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ galaxies-icon.i386.o: icons/galaxies-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ galaxie7.i386.o: ./galaxies.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ galaxie4.i386.o: ./galaxies.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_PICTURE_GENERATOR -c $< -o $@ galaxie2.i386.o: ./galaxies.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ grid.i386.o: ./grid.c ./puzzles.h ./tree234.h ./grid.h ./penrose.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ gtk.i386.o: ./gtk.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ guess.i386.o: ./guess.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ guess-icon.i386.o: icons/guess-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ guess3.i386.o: ./guess.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ inertia.i386.o: ./inertia.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ inertia-icon.i386.o: icons/inertia-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ inertia3.i386.o: ./inertia.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ keen.i386.o: ./keen.c ./puzzles.h ./latin.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ keen-icon.i386.o: icons/keen-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ keen5.i386.o: ./keen.c ./puzzles.h ./latin.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ keen2.i386.o: ./keen.c ./puzzles.h ./latin.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ latin.i386.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ latin8.i386.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_LATIN_TEST -c $< -o $@ latin6.i386.o: ./latin.c ./puzzles.h ./tree234.h ./maxflow.h ./latin.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ laydomino.i386.o: ./laydomino.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ lightup.i386.o: ./lightup.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ lightup-icon.i386.o: icons/lightup-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ lightup5.i386.o: ./lightup.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ lightup2.i386.o: ./lightup.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ list.i386.o: ./list.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopgen.i386.o: ./loopgen.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopy.i386.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopy-icon.i386.o: icons/loopy-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ loopy5.i386.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ loopy2.i386.o: ./loopy.c ./puzzles.h ./tree234.h ./grid.h ./loopgen.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ magnets.i386.o: ./magnets.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ magnets-icon.i386.o: icons/magnets-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ magnets5.i386.o: ./magnets.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ magnets2.i386.o: ./magnets.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ malloc.i386.o: ./malloc.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ map.i386.o: ./map.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ map-icon.i386.o: icons/map-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ map5.i386.o: ./map.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ map2.i386.o: ./map.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ maxflow.i386.o: ./maxflow.c ./maxflow.h ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ midend.i386.o: ./midend.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ mines.i386.o: ./mines.c ./tree234.h ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ mines-icon.i386.o: icons/mines-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ mines5.i386.o: ./mines.c ./tree234.h ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ mines2.i386.o: ./mines.c ./tree234.h ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_OBFUSCATOR -c $< -o $@ misc.i386.o: ./misc.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ net.i386.o: ./net.c ./puzzles.h ./tree234.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ net-icon.i386.o: icons/net-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ net3.i386.o: ./net.c ./puzzles.h ./tree234.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ netslide.i386.o: ./netslide.c ./puzzles.h ./tree234.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ netslide-icon.i386.o: icons/netslide-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ netslid3.i386.o: ./netslide.c ./puzzles.h ./tree234.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ no-icon.i386.o: ./no-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ nullfe.i386.o: ./nullfe.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ nullgame.i386.o: ./nullgame.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ obfusc.i386.o: ./obfusc.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ osx.i386.o: ./osx.m ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 -x objective-c $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pattern.i386.o: ./pattern.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pattern-icon.i386.o: icons/pattern-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pattern5.i386.o: ./pattern.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ pattern2.i386.o: ./pattern.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ pearl.i386.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pearl-icon.i386.o: icons/pearl-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pearl5.i386.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ pearl2.i386.o: ./pearl.c ./puzzles.h ./grid.h ./loopgen.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ pegs.i386.o: ./pegs.c ./puzzles.h ./tree234.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pegs-icon.i386.o: icons/pegs-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ pegs3.i386.o: ./pegs.c ./puzzles.h ./tree234.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ penrose.i386.o: ./penrose.c ./puzzles.h ./penrose.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ printing.i386.o: ./printing.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ ps.i386.o: ./ps.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ random.i386.o: ./random.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ range.i386.o: ./range.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ range-icon.i386.o: icons/range-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ range3.i386.o: ./range.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ rect.i386.o: ./rect.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ rect-icon.i386.o: icons/rect-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ rect3.i386.o: ./rect.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ samegame.i386.o: ./samegame.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ samegame-icon.i386.o: icons/samegame-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ samegam3.i386.o: ./samegame.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ signpost.i386.o: ./signpost.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ signpost-icon.i386.o: icons/signpost-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ signpos5.i386.o: ./signpost.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ signpos2.i386.o: ./signpost.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ singles.i386.o: ./singles.c ./puzzles.h ./latin.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ singles-icon.i386.o: icons/singles-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ singles5.i386.o: ./singles.c ./puzzles.h ./latin.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ singles3.i386.o: ./singles.c ./puzzles.h ./latin.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ sixteen.i386.o: ./sixteen.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ sixteen-icon.i386.o: icons/sixteen-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ sixteen3.i386.o: ./sixteen.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ slant.i386.o: ./slant.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ slant-icon.i386.o: icons/slant-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ slant5.i386.o: ./slant.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ slant2.i386.o: ./slant.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ solo.i386.o: ./solo.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ solo-icon.i386.o: icons/solo-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ solo5.i386.o: ./solo.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ solo2.i386.o: ./solo.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ tdq.i386.o: ./tdq.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ tents.i386.o: ./tents.c ./puzzles.h ./maxflow.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ tents-icon.i386.o: icons/tents-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ tents5.i386.o: ./tents.c ./puzzles.h ./maxflow.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ tents3.i386.o: ./tents.c ./puzzles.h ./maxflow.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ towers.i386.o: ./towers.c ./puzzles.h ./latin.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ towers-icon.i386.o: icons/towers-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ towers5.i386.o: ./towers.c ./puzzles.h ./latin.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ towers2.i386.o: ./towers.c ./puzzles.h ./latin.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ tree234.i386.o: ./tree234.c ./tree234.h ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ twiddle.i386.o: ./twiddle.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ twiddle-icon.i386.o: icons/twiddle-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ twiddle3.i386.o: ./twiddle.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ undead.i386.o: ./undead.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ undead-icon.i386.o: icons/undead-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ undead3.i386.o: ./undead.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ unequal.i386.o: ./unequal.c ./puzzles.h ./latin.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ unequal-icon.i386.o: icons/unequal-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ unequal5.i386.o: ./unequal.c ./puzzles.h ./latin.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ unequal2.i386.o: ./unequal.c ./puzzles.h ./latin.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ unruly.i386.o: ./unruly.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ unruly-icon.i386.o: icons/unruly-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ unruly5.i386.o: ./unruly.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ unruly2.i386.o: ./unruly.c ./puzzles.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DSTANDALONE_SOLVER -c $< -o $@ untangle.i386.o: ./untangle.c ./puzzles.h ./tree234.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ untangle-icon.i386.o: icons/untangle-icon.c $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ untangl3.i386.o: ./untangle.c ./puzzles.h ./tree234.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ windows.i386.o: ./windows.c ./puzzles.h ./resource.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -c $< -o $@ windows1.i386.o: ./windows.c ./puzzles.h ./resource.h $(CC) -arch i386 -mmacosx-version-min=10.4 $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) -DCOMBINED -c $< -o $@ clean: rm -f *.o *.dmg fillingsolver fillingsolver.i386 galaxiespicture galaxiespicture.i386 galaxiessolver galaxiessolver.i386 keensolver keensolver.i386 latincheck latincheck.i386 lightupsolver lightupsolver.i386 loopysolver loopysolver.i386 magnetssolver magnetssolver.i386 mapsolver mapsolver.i386 mineobfusc mineobfusc.i386 obfusc obfusc.i386 patternsolver patternsolver.i386 pearlbench pearlbench.i386 signpostsolver signpostsolver.i386 singlessolver singlessolver.i386 slantsolver slantsolver.i386 solosolver solosolver.i386 tentssolver tentssolver.i386 towerssolver towerssolver.i386 unequalsolver unequalsolver.i386 unrulysolver unrulysolver.i386 rm -rf *.app puzzles-r9872/Makefile.vc0000644000175300017530000015741112161170560014504 0ustar simonsimon# Makefile for puzzles under Visual C. # # This file was created by `mkfiles.pl' from the `Recipe' file. # DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead. # If you rename this file to `Makefile', you should change this line, # so that the .rsp files still depend on the correct makefile. MAKEFILE = Makefile.vc # C compilation flags CFLAGS = /nologo /W3 /O1 /D_WINDOWS /D_WIN32_WINDOWS=0x401 /DWINVER=0x401 /I. LFLAGS = /incremental:no /fixed all: blackbox.exe bridges.exe cube.exe dominosa.exe fifteen.exe filling.exe \ fillingsolver.exe flip.exe galaxies.exe galaxiespicture.exe \ galaxiessolver.exe guess.exe inertia.exe keen.exe \ keensolver.exe latincheck.exe lightup.exe lightupsolver.exe \ loopy.exe loopysolver.exe magnets.exe magnetssolver.exe \ map.exe mapsolver.exe mineobfusc.exe mines.exe netgame.exe \ netslide.exe nullgame.exe pattern.exe patternsolver.exe \ pearl.exe pearlbench.exe pegs.exe puzzles.exe range.exe \ rect.exe samegame.exe signpost.exe signpostsolver.exe \ singles.exe singlessolver.exe sixteen.exe slant.exe \ slantsolver.exe solo.exe solosolver.exe tents.exe \ tentssolver.exe towers.exe towerssolver.exe twiddle.exe \ undead.exe unequal.exe unequalsolver.exe unruly.exe \ unrulysolver.exe untangle.exe blackbox.exe: blackbox.obj blackbox.res drawing.obj malloc.obj midend.obj \ misc.obj printing.obj random.obj version.obj windows.obj \ blackbox.rsp link $(LFLAGS) -out:blackbox.exe -map:blackbox.map @blackbox.rsp bridges.exe: bridges.obj bridges.res drawing.obj dsf.obj malloc.obj \ midend.obj misc.obj printing.obj random.obj version.obj \ windows.obj bridges.rsp link $(LFLAGS) -out:bridges.exe -map:bridges.map @bridges.rsp cube.exe: cube.obj cube.res drawing.obj malloc.obj midend.obj misc.obj \ printing.obj random.obj version.obj windows.obj cube.rsp link $(LFLAGS) -out:cube.exe -map:cube.map @cube.rsp dominosa.exe: dominosa.obj dominosa.res drawing.obj laydomino.obj malloc.obj \ midend.obj misc.obj printing.obj random.obj version.obj \ windows.obj dominosa.rsp link $(LFLAGS) -out:dominosa.exe -map:dominosa.map @dominosa.rsp fifteen.exe: drawing.obj fifteen.obj fifteen.res malloc.obj midend.obj \ misc.obj printing.obj random.obj version.obj windows.obj \ fifteen.rsp link $(LFLAGS) -out:fifteen.exe -map:fifteen.map @fifteen.rsp filling.exe: drawing.obj dsf.obj filling.obj filling.res malloc.obj \ midend.obj misc.obj printing.obj random.obj version.obj \ windows.obj filling.rsp link $(LFLAGS) -out:filling.exe -map:filling.map @filling.rsp fillingsolver.exe: dsf.obj filling2.obj malloc.obj misc.obj nullfe.obj \ random.obj fillingsolver.rsp link $(LFLAGS) -out:fillingsolver.exe -map:fillingsolver.map @fillingsolver.rsp flip.exe: drawing.obj flip.obj flip.res malloc.obj midend.obj misc.obj \ printing.obj random.obj tree234.obj version.obj windows.obj \ flip.rsp link $(LFLAGS) -out:flip.exe -map:flip.map @flip.rsp galaxies.exe: drawing.obj dsf.obj galaxies.obj galaxies.res malloc.obj \ midend.obj misc.obj printing.obj random.obj version.obj \ windows.obj galaxies.rsp link $(LFLAGS) -out:galaxies.exe -map:galaxies.map @galaxies.rsp galaxiespicture.exe: dsf.obj galaxie4.obj malloc.obj misc.obj nullfe.obj \ random.obj galaxiespicture.rsp link $(LFLAGS) -out:galaxiespicture.exe -map:galaxiespicture.map @galaxiespicture.rsp galaxiessolver.exe: dsf.obj galaxie2.obj malloc.obj misc.obj nullfe.obj \ random.obj galaxiessolver.rsp link $(LFLAGS) -out:galaxiessolver.exe -map:galaxiessolver.map @galaxiessolver.rsp guess.exe: drawing.obj guess.obj guess.res malloc.obj midend.obj misc.obj \ printing.obj random.obj version.obj windows.obj guess.rsp link $(LFLAGS) -out:guess.exe -map:guess.map @guess.rsp inertia.exe: drawing.obj inertia.obj inertia.res malloc.obj midend.obj \ misc.obj printing.obj random.obj version.obj windows.obj \ inertia.rsp link $(LFLAGS) -out:inertia.exe -map:inertia.map @inertia.rsp keen.exe: drawing.obj dsf.obj keen.obj keen.res latin.obj malloc.obj \ maxflow.obj midend.obj misc.obj printing.obj random.obj \ tree234.obj version.obj windows.obj keen.rsp link $(LFLAGS) -out:keen.exe -map:keen.map @keen.rsp keensolver.exe: dsf.obj keen2.obj latin6.obj malloc.obj maxflow.obj misc.obj \ nullfe.obj random.obj tree234.obj keensolver.rsp link $(LFLAGS) -out:keensolver.exe -map:keensolver.map @keensolver.rsp latincheck.exe: latin8.obj malloc.obj maxflow.obj misc.obj nullfe.obj \ random.obj tree234.obj latincheck.rsp link $(LFLAGS) -out:latincheck.exe -map:latincheck.map @latincheck.rsp lightup.exe: combi.obj drawing.obj lightup.obj lightup.res malloc.obj \ midend.obj misc.obj printing.obj random.obj version.obj \ windows.obj lightup.rsp link $(LFLAGS) -out:lightup.exe -map:lightup.map @lightup.rsp lightupsolver.exe: combi.obj lightup2.obj malloc.obj misc.obj nullfe.obj \ random.obj lightupsolver.rsp link $(LFLAGS) -out:lightupsolver.exe -map:lightupsolver.map @lightupsolver.rsp loopy.exe: drawing.obj dsf.obj grid.obj loopgen.obj loopy.obj loopy.res \ malloc.obj midend.obj misc.obj penrose.obj printing.obj \ random.obj tree234.obj version.obj windows.obj loopy.rsp link $(LFLAGS) -out:loopy.exe -map:loopy.map @loopy.rsp loopysolver.exe: dsf.obj grid.obj loopgen.obj loopy2.obj malloc.obj misc.obj \ nullfe.obj penrose.obj random.obj tree234.obj \ loopysolver.rsp link $(LFLAGS) -out:loopysolver.exe -map:loopysolver.map @loopysolver.rsp magnets.exe: drawing.obj laydomino.obj magnets.obj magnets.res malloc.obj \ midend.obj misc.obj printing.obj random.obj version.obj \ windows.obj magnets.rsp link $(LFLAGS) -out:magnets.exe -map:magnets.map @magnets.rsp magnetssolver.exe: laydomino.obj magnets2.obj malloc.obj misc.obj nullfe.obj \ random.obj magnetssolver.rsp link $(LFLAGS) -out:magnetssolver.exe -map:magnetssolver.map @magnetssolver.rsp map.exe: drawing.obj dsf.obj malloc.obj map.obj map.res midend.obj misc.obj \ printing.obj random.obj version.obj windows.obj map.rsp link $(LFLAGS) -out:map.exe -map:map.map @map.rsp mapsolver.exe: dsf.obj malloc.obj map2.obj misc.obj nullfe.obj random.obj \ mapsolver.rsp link $(LFLAGS) -out:mapsolver.exe -map:mapsolver.map @mapsolver.rsp mineobfusc.exe: malloc.obj mines2.obj misc.obj nullfe.obj random.obj \ tree234.obj mineobfusc.rsp link $(LFLAGS) -out:mineobfusc.exe -map:mineobfusc.map @mineobfusc.rsp mines.exe: drawing.obj malloc.obj midend.obj mines.obj mines.res misc.obj \ printing.obj random.obj tree234.obj version.obj windows.obj \ mines.rsp link $(LFLAGS) -out:mines.exe -map:mines.map @mines.rsp netgame.exe: drawing.obj dsf.obj malloc.obj midend.obj misc.obj net.obj \ net.res printing.obj random.obj tree234.obj version.obj \ windows.obj netgame.rsp link $(LFLAGS) -out:netgame.exe -map:netgame.map @netgame.rsp netslide.exe: drawing.obj malloc.obj midend.obj misc.obj netslide.obj \ netslide.res printing.obj random.obj tree234.obj version.obj \ windows.obj netslide.rsp link $(LFLAGS) -out:netslide.exe -map:netslide.map @netslide.rsp nullgame.exe: drawing.obj malloc.obj midend.obj misc.obj noicon.res \ nullgame.obj printing.obj random.obj version.obj windows.obj \ nullgame.rsp link $(LFLAGS) -out:nullgame.exe -map:nullgame.map @nullgame.rsp pattern.exe: drawing.obj malloc.obj midend.obj misc.obj pattern.obj \ pattern.res printing.obj random.obj version.obj windows.obj \ pattern.rsp link $(LFLAGS) -out:pattern.exe -map:pattern.map @pattern.rsp patternsolver.exe: malloc.obj misc.obj nullfe.obj pattern2.obj random.obj \ patternsolver.rsp link $(LFLAGS) -out:patternsolver.exe -map:patternsolver.map @patternsolver.rsp pearl.exe: drawing.obj dsf.obj grid.obj loopgen.obj malloc.obj midend.obj \ misc.obj pearl.obj pearl.res penrose.obj printing.obj \ random.obj tdq.obj tree234.obj version.obj windows.obj \ pearl.rsp link $(LFLAGS) -out:pearl.exe -map:pearl.map @pearl.rsp pearlbench.exe: dsf.obj grid.obj loopgen.obj malloc.obj misc.obj nullfe.obj \ pearl2.obj penrose.obj random.obj tdq.obj tree234.obj \ pearlbench.rsp link $(LFLAGS) -out:pearlbench.exe -map:pearlbench.map @pearlbench.rsp pegs.exe: drawing.obj malloc.obj midend.obj misc.obj pegs.obj pegs.res \ printing.obj random.obj tree234.obj version.obj windows.obj \ pegs.rsp link $(LFLAGS) -out:pegs.exe -map:pegs.map @pegs.rsp puzzles.exe: blackbo3.obj bridges3.obj combi.obj cube3.obj divvy.obj \ dominos3.obj drawing.obj dsf.obj fifteen3.obj filling5.obj \ flip3.obj galaxie7.obj grid.obj guess3.obj inertia3.obj \ keen5.obj latin.obj laydomino.obj lightup5.obj list.obj \ loopgen.obj loopy5.obj magnets5.obj malloc.obj map5.obj \ maxflow.obj midend.obj mines5.obj misc.obj net3.obj \ netslid3.obj noicon.res pattern5.obj pearl5.obj pegs3.obj \ penrose.obj printing.obj random.obj range3.obj rect3.obj \ samegam3.obj signpos5.obj singles5.obj sixteen3.obj \ slant5.obj solo5.obj tdq.obj tents5.obj towers5.obj \ tree234.obj twiddle3.obj undead3.obj unequal5.obj \ unruly5.obj untangl3.obj version.obj windows1.obj \ puzzles.rsp link $(LFLAGS) -out:puzzles.exe -map:puzzles.map @puzzles.rsp range.exe: drawing.obj malloc.obj midend.obj misc.obj printing.obj \ random.obj range.obj range.res version.obj windows.obj \ range.rsp link $(LFLAGS) -out:range.exe -map:range.map @range.rsp rect.exe: drawing.obj malloc.obj midend.obj misc.obj printing.obj random.obj \ rect.obj rect.res version.obj windows.obj rect.rsp link $(LFLAGS) -out:rect.exe -map:rect.map @rect.rsp samegame.exe: drawing.obj malloc.obj midend.obj misc.obj printing.obj \ random.obj samegame.obj samegame.res version.obj windows.obj \ samegame.rsp link $(LFLAGS) -out:samegame.exe -map:samegame.map @samegame.rsp signpost.exe: drawing.obj dsf.obj malloc.obj midend.obj misc.obj \ printing.obj random.obj signpost.obj signpost.res \ version.obj windows.obj signpost.rsp link $(LFLAGS) -out:signpost.exe -map:signpost.map @signpost.rsp signpostsolver.exe: dsf.obj malloc.obj misc.obj nullfe.obj random.obj \ signpos2.obj signpostsolver.rsp link $(LFLAGS) -out:signpostsolver.exe -map:signpostsolver.map @signpostsolver.rsp singles.exe: drawing.obj dsf.obj latin.obj malloc.obj maxflow.obj midend.obj \ misc.obj printing.obj random.obj singles.obj singles.res \ tree234.obj version.obj windows.obj singles.rsp link $(LFLAGS) -out:singles.exe -map:singles.map @singles.rsp singlessolver.exe: dsf.obj latin.obj malloc.obj maxflow.obj misc.obj \ nullfe.obj random.obj singles3.obj tree234.obj \ singlessolver.rsp link $(LFLAGS) -out:singlessolver.exe -map:singlessolver.map @singlessolver.rsp sixteen.exe: drawing.obj malloc.obj midend.obj misc.obj printing.obj \ random.obj sixteen.obj sixteen.res version.obj windows.obj \ sixteen.rsp link $(LFLAGS) -out:sixteen.exe -map:sixteen.map @sixteen.rsp slant.exe: drawing.obj dsf.obj malloc.obj midend.obj misc.obj printing.obj \ random.obj slant.obj slant.res version.obj windows.obj \ slant.rsp link $(LFLAGS) -out:slant.exe -map:slant.map @slant.rsp slantsolver.exe: dsf.obj malloc.obj misc.obj nullfe.obj random.obj \ slant2.obj slantsolver.rsp link $(LFLAGS) -out:slantsolver.exe -map:slantsolver.map @slantsolver.rsp solo.exe: divvy.obj drawing.obj dsf.obj malloc.obj midend.obj misc.obj \ printing.obj random.obj solo.obj solo.res version.obj \ windows.obj solo.rsp link $(LFLAGS) -out:solo.exe -map:solo.map @solo.rsp solosolver.exe: divvy.obj dsf.obj malloc.obj misc.obj nullfe.obj random.obj \ solo2.obj solosolver.rsp link $(LFLAGS) -out:solosolver.exe -map:solosolver.map @solosolver.rsp tents.exe: drawing.obj dsf.obj malloc.obj maxflow.obj midend.obj misc.obj \ printing.obj random.obj tents.obj tents.res version.obj \ windows.obj tents.rsp link $(LFLAGS) -out:tents.exe -map:tents.map @tents.rsp tentssolver.exe: dsf.obj malloc.obj maxflow.obj misc.obj nullfe.obj \ random.obj tents3.obj tentssolver.rsp link $(LFLAGS) -out:tentssolver.exe -map:tentssolver.map @tentssolver.rsp towers.exe: drawing.obj latin.obj malloc.obj maxflow.obj midend.obj misc.obj \ printing.obj random.obj towers.obj towers.res tree234.obj \ version.obj windows.obj towers.rsp link $(LFLAGS) -out:towers.exe -map:towers.map @towers.rsp towerssolver.exe: latin6.obj malloc.obj maxflow.obj misc.obj nullfe.obj \ random.obj towers2.obj tree234.obj towerssolver.rsp link $(LFLAGS) -out:towerssolver.exe -map:towerssolver.map @towerssolver.rsp twiddle.exe: drawing.obj malloc.obj midend.obj misc.obj printing.obj \ random.obj twiddle.obj twiddle.res version.obj windows.obj \ twiddle.rsp link $(LFLAGS) -out:twiddle.exe -map:twiddle.map @twiddle.rsp undead.exe: drawing.obj malloc.obj midend.obj misc.obj printing.obj \ random.obj undead.obj undead.res version.obj windows.obj \ undead.rsp link $(LFLAGS) -out:undead.exe -map:undead.map @undead.rsp unequal.exe: drawing.obj latin.obj malloc.obj maxflow.obj midend.obj \ misc.obj printing.obj random.obj tree234.obj unequal.obj \ unequal.res version.obj windows.obj unequal.rsp link $(LFLAGS) -out:unequal.exe -map:unequal.map @unequal.rsp unequalsolver.exe: latin6.obj malloc.obj maxflow.obj misc.obj nullfe.obj \ random.obj tree234.obj unequal2.obj unequalsolver.rsp link $(LFLAGS) -out:unequalsolver.exe -map:unequalsolver.map @unequalsolver.rsp unruly.exe: drawing.obj malloc.obj midend.obj misc.obj printing.obj \ random.obj unruly.obj unruly.res version.obj windows.obj \ unruly.rsp link $(LFLAGS) -out:unruly.exe -map:unruly.map @unruly.rsp unrulysolver.exe: malloc.obj misc.obj nullfe.obj random.obj unruly2.obj \ unrulysolver.rsp link $(LFLAGS) -out:unrulysolver.exe -map:unrulysolver.map @unrulysolver.rsp untangle.exe: drawing.obj malloc.obj midend.obj misc.obj printing.obj \ random.obj tree234.obj untangle.obj untangle.res version.obj \ windows.obj untangle.rsp link $(LFLAGS) -out:untangle.exe -map:untangle.map @untangle.rsp blackbox.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > blackbox.rsp echo blackbox.obj blackbox.res comctl32.lib >> blackbox.rsp echo comdlg32.lib drawing.obj gdi32.lib malloc.obj >> blackbox.rsp echo midend.obj misc.obj printing.obj random.obj >> blackbox.rsp echo user32.lib version.obj windows.obj winspool.lib >> blackbox.rsp bridges.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > bridges.rsp echo bridges.obj bridges.res comctl32.lib comdlg32.lib >> bridges.rsp echo drawing.obj dsf.obj gdi32.lib malloc.obj >> bridges.rsp echo midend.obj misc.obj printing.obj random.obj >> bridges.rsp echo user32.lib version.obj windows.obj winspool.lib >> bridges.rsp cube.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > cube.rsp echo comctl32.lib comdlg32.lib cube.obj cube.res >> cube.rsp echo drawing.obj gdi32.lib malloc.obj midend.obj >> cube.rsp echo misc.obj printing.obj random.obj user32.lib >> cube.rsp echo version.obj windows.obj winspool.lib >> cube.rsp dominosa.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > dominosa.rsp echo comctl32.lib comdlg32.lib dominosa.obj >> dominosa.rsp echo dominosa.res drawing.obj gdi32.lib laydomino.obj >> dominosa.rsp echo malloc.obj midend.obj misc.obj printing.obj >> dominosa.rsp echo random.obj user32.lib version.obj windows.obj >> dominosa.rsp echo winspool.lib >> dominosa.rsp fifteen.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > fifteen.rsp echo comctl32.lib comdlg32.lib drawing.obj fifteen.obj >> fifteen.rsp echo fifteen.res gdi32.lib malloc.obj midend.obj >> fifteen.rsp echo misc.obj printing.obj random.obj user32.lib >> fifteen.rsp echo version.obj windows.obj winspool.lib >> fifteen.rsp filling.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > filling.rsp echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> filling.rsp echo filling.obj filling.res gdi32.lib malloc.obj >> filling.rsp echo midend.obj misc.obj printing.obj random.obj >> filling.rsp echo user32.lib version.obj windows.obj winspool.lib >> filling.rsp fillingsolver.rsp: $(MAKEFILE) echo /nologo /subsystem:console > fillingsolver.rsp echo dsf.obj filling2.obj malloc.obj misc.obj >> fillingsolver.rsp echo nullfe.obj random.obj >> fillingsolver.rsp flip.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > flip.rsp echo comctl32.lib comdlg32.lib drawing.obj flip.obj >> flip.rsp echo flip.res gdi32.lib malloc.obj midend.obj misc.obj >> flip.rsp echo printing.obj random.obj tree234.obj user32.lib >> flip.rsp echo version.obj windows.obj winspool.lib >> flip.rsp galaxies.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > galaxies.rsp echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> galaxies.rsp echo galaxies.obj galaxies.res gdi32.lib malloc.obj >> galaxies.rsp echo midend.obj misc.obj printing.obj random.obj >> galaxies.rsp echo user32.lib version.obj windows.obj winspool.lib >> galaxies.rsp galaxiespicture.rsp: $(MAKEFILE) echo /nologo /subsystem:console > galaxiespicture.rsp echo dsf.obj galaxie4.obj malloc.obj misc.obj >> galaxiespicture.rsp echo nullfe.obj random.obj >> galaxiespicture.rsp galaxiessolver.rsp: $(MAKEFILE) echo /nologo /subsystem:console > galaxiessolver.rsp echo dsf.obj galaxie2.obj malloc.obj misc.obj >> galaxiessolver.rsp echo nullfe.obj random.obj >> galaxiessolver.rsp guess.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > guess.rsp echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> guess.rsp echo guess.obj guess.res malloc.obj midend.obj >> guess.rsp echo misc.obj printing.obj random.obj user32.lib >> guess.rsp echo version.obj windows.obj winspool.lib >> guess.rsp inertia.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > inertia.rsp echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> inertia.rsp echo inertia.obj inertia.res malloc.obj midend.obj >> inertia.rsp echo misc.obj printing.obj random.obj user32.lib >> inertia.rsp echo version.obj windows.obj winspool.lib >> inertia.rsp keen.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > keen.rsp echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> keen.rsp echo gdi32.lib keen.obj keen.res latin.obj malloc.obj >> keen.rsp echo maxflow.obj midend.obj misc.obj printing.obj >> keen.rsp echo random.obj tree234.obj user32.lib version.obj >> keen.rsp echo windows.obj winspool.lib >> keen.rsp keensolver.rsp: $(MAKEFILE) echo /nologo /subsystem:console > keensolver.rsp echo dsf.obj keen2.obj latin6.obj malloc.obj >> keensolver.rsp echo maxflow.obj misc.obj nullfe.obj random.obj >> keensolver.rsp echo tree234.obj >> keensolver.rsp latincheck.rsp: $(MAKEFILE) echo /nologo /subsystem:console > latincheck.rsp echo latin8.obj malloc.obj maxflow.obj misc.obj >> latincheck.rsp echo nullfe.obj random.obj tree234.obj >> latincheck.rsp lightup.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > lightup.rsp echo combi.obj comctl32.lib comdlg32.lib drawing.obj >> lightup.rsp echo gdi32.lib lightup.obj lightup.res malloc.obj >> lightup.rsp echo midend.obj misc.obj printing.obj random.obj >> lightup.rsp echo user32.lib version.obj windows.obj winspool.lib >> lightup.rsp lightupsolver.rsp: $(MAKEFILE) echo /nologo /subsystem:console > lightupsolver.rsp echo combi.obj lightup2.obj malloc.obj misc.obj >> lightupsolver.rsp echo nullfe.obj random.obj >> lightupsolver.rsp loopy.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > loopy.rsp echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> loopy.rsp echo gdi32.lib grid.obj loopgen.obj loopy.obj >> loopy.rsp echo loopy.res malloc.obj midend.obj misc.obj >> loopy.rsp echo penrose.obj printing.obj random.obj tree234.obj >> loopy.rsp echo user32.lib version.obj windows.obj winspool.lib >> loopy.rsp loopysolver.rsp: $(MAKEFILE) echo /nologo /subsystem:console > loopysolver.rsp echo dsf.obj grid.obj loopgen.obj loopy2.obj >> loopysolver.rsp echo malloc.obj misc.obj nullfe.obj penrose.obj >> loopysolver.rsp echo random.obj tree234.obj >> loopysolver.rsp magnets.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > magnets.rsp echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> magnets.rsp echo laydomino.obj magnets.obj magnets.res malloc.obj >> magnets.rsp echo midend.obj misc.obj printing.obj random.obj >> magnets.rsp echo user32.lib version.obj windows.obj winspool.lib >> magnets.rsp magnetssolver.rsp: $(MAKEFILE) echo /nologo /subsystem:console > magnetssolver.rsp echo laydomino.obj magnets2.obj malloc.obj misc.obj >> magnetssolver.rsp echo nullfe.obj random.obj >> magnetssolver.rsp map.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > map.rsp echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> map.rsp echo gdi32.lib malloc.obj map.obj map.res midend.obj >> map.rsp echo misc.obj printing.obj random.obj user32.lib >> map.rsp echo version.obj windows.obj winspool.lib >> map.rsp mapsolver.rsp: $(MAKEFILE) echo /nologo /subsystem:console > mapsolver.rsp echo dsf.obj malloc.obj map2.obj misc.obj nullfe.obj >> mapsolver.rsp echo random.obj >> mapsolver.rsp mineobfusc.rsp: $(MAKEFILE) echo /nologo /subsystem:console > mineobfusc.rsp echo malloc.obj mines2.obj misc.obj nullfe.obj >> mineobfusc.rsp echo random.obj tree234.obj >> mineobfusc.rsp mines.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > mines.rsp echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> mines.rsp echo malloc.obj midend.obj mines.obj mines.res >> mines.rsp echo misc.obj printing.obj random.obj tree234.obj >> mines.rsp echo user32.lib version.obj windows.obj winspool.lib >> mines.rsp netgame.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > netgame.rsp echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> netgame.rsp echo gdi32.lib malloc.obj midend.obj misc.obj net.obj >> netgame.rsp echo net.res printing.obj random.obj tree234.obj >> netgame.rsp echo user32.lib version.obj windows.obj winspool.lib >> netgame.rsp netslide.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > netslide.rsp echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> netslide.rsp echo malloc.obj midend.obj misc.obj netslide.obj >> netslide.rsp echo netslide.res printing.obj random.obj tree234.obj >> netslide.rsp echo user32.lib version.obj windows.obj winspool.lib >> netslide.rsp nullgame.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > nullgame.rsp echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> nullgame.rsp echo malloc.obj midend.obj misc.obj noicon.res >> nullgame.rsp echo nullgame.obj printing.obj random.obj user32.lib >> nullgame.rsp echo version.obj windows.obj winspool.lib >> nullgame.rsp pattern.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > pattern.rsp echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> pattern.rsp echo malloc.obj midend.obj misc.obj pattern.obj >> pattern.rsp echo pattern.res printing.obj random.obj user32.lib >> pattern.rsp echo version.obj windows.obj winspool.lib >> pattern.rsp patternsolver.rsp: $(MAKEFILE) echo /nologo /subsystem:console > patternsolver.rsp echo malloc.obj misc.obj nullfe.obj pattern2.obj >> patternsolver.rsp echo random.obj >> patternsolver.rsp pearl.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > pearl.rsp echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> pearl.rsp echo gdi32.lib grid.obj loopgen.obj malloc.obj >> pearl.rsp echo midend.obj misc.obj pearl.obj pearl.res >> pearl.rsp echo penrose.obj printing.obj random.obj tdq.obj >> pearl.rsp echo tree234.obj user32.lib version.obj windows.obj >> pearl.rsp echo winspool.lib >> pearl.rsp pearlbench.rsp: $(MAKEFILE) echo /nologo /subsystem:console > pearlbench.rsp echo dsf.obj grid.obj loopgen.obj malloc.obj misc.obj >> pearlbench.rsp echo nullfe.obj pearl2.obj penrose.obj random.obj >> pearlbench.rsp echo tdq.obj tree234.obj >> pearlbench.rsp pegs.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > pegs.rsp echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> pegs.rsp echo malloc.obj midend.obj misc.obj pegs.obj pegs.res >> pegs.rsp echo printing.obj random.obj tree234.obj user32.lib >> pegs.rsp echo version.obj windows.obj winspool.lib >> pegs.rsp puzzles.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > puzzles.rsp echo blackbo3.obj bridges3.obj combi.obj comctl32.lib >> puzzles.rsp echo comdlg32.lib cube3.obj divvy.obj dominos3.obj >> puzzles.rsp echo drawing.obj dsf.obj fifteen3.obj filling5.obj >> puzzles.rsp echo flip3.obj galaxie7.obj gdi32.lib grid.obj >> puzzles.rsp echo guess3.obj inertia3.obj keen5.obj latin.obj >> puzzles.rsp echo laydomino.obj lightup5.obj list.obj loopgen.obj >> puzzles.rsp echo loopy5.obj magnets5.obj malloc.obj map5.obj >> puzzles.rsp echo maxflow.obj midend.obj mines5.obj misc.obj >> puzzles.rsp echo net3.obj netslid3.obj noicon.res pattern5.obj >> puzzles.rsp echo pearl5.obj pegs3.obj penrose.obj printing.obj >> puzzles.rsp echo random.obj range3.obj rect3.obj samegam3.obj >> puzzles.rsp echo signpos5.obj singles5.obj sixteen3.obj slant5.obj >> puzzles.rsp echo solo5.obj tdq.obj tents5.obj towers5.obj >> puzzles.rsp echo tree234.obj twiddle3.obj undead3.obj unequal5.obj >> puzzles.rsp echo unruly5.obj untangl3.obj user32.lib version.obj >> puzzles.rsp echo windows1.obj winspool.lib >> puzzles.rsp range.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > range.rsp echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> range.rsp echo malloc.obj midend.obj misc.obj printing.obj >> range.rsp echo random.obj range.obj range.res user32.lib >> range.rsp echo version.obj windows.obj winspool.lib >> range.rsp rect.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > rect.rsp echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> rect.rsp echo malloc.obj midend.obj misc.obj printing.obj >> rect.rsp echo random.obj rect.obj rect.res user32.lib >> rect.rsp echo version.obj windows.obj winspool.lib >> rect.rsp samegame.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > samegame.rsp echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> samegame.rsp echo malloc.obj midend.obj misc.obj printing.obj >> samegame.rsp echo random.obj samegame.obj samegame.res user32.lib >> samegame.rsp echo version.obj windows.obj winspool.lib >> samegame.rsp signpost.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > signpost.rsp echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> signpost.rsp echo gdi32.lib malloc.obj midend.obj misc.obj >> signpost.rsp echo printing.obj random.obj signpost.obj signpost.res >> signpost.rsp echo user32.lib version.obj windows.obj winspool.lib >> signpost.rsp signpostsolver.rsp: $(MAKEFILE) echo /nologo /subsystem:console > signpostsolver.rsp echo dsf.obj malloc.obj misc.obj nullfe.obj random.obj >> signpostsolver.rsp echo signpos2.obj >> signpostsolver.rsp singles.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > singles.rsp echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> singles.rsp echo gdi32.lib latin.obj malloc.obj maxflow.obj >> singles.rsp echo midend.obj misc.obj printing.obj random.obj >> singles.rsp echo singles.obj singles.res tree234.obj user32.lib >> singles.rsp echo version.obj windows.obj winspool.lib >> singles.rsp singlessolver.rsp: $(MAKEFILE) echo /nologo /subsystem:console > singlessolver.rsp echo dsf.obj latin.obj malloc.obj maxflow.obj misc.obj >> singlessolver.rsp echo nullfe.obj random.obj singles3.obj tree234.obj >> singlessolver.rsp sixteen.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > sixteen.rsp echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> sixteen.rsp echo malloc.obj midend.obj misc.obj printing.obj >> sixteen.rsp echo random.obj sixteen.obj sixteen.res user32.lib >> sixteen.rsp echo version.obj windows.obj winspool.lib >> sixteen.rsp slant.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > slant.rsp echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> slant.rsp echo gdi32.lib malloc.obj midend.obj misc.obj >> slant.rsp echo printing.obj random.obj slant.obj slant.res >> slant.rsp echo user32.lib version.obj windows.obj winspool.lib >> slant.rsp slantsolver.rsp: $(MAKEFILE) echo /nologo /subsystem:console > slantsolver.rsp echo dsf.obj malloc.obj misc.obj nullfe.obj random.obj >> slantsolver.rsp echo slant2.obj >> slantsolver.rsp solo.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > solo.rsp echo comctl32.lib comdlg32.lib divvy.obj drawing.obj >> solo.rsp echo dsf.obj gdi32.lib malloc.obj midend.obj misc.obj >> solo.rsp echo printing.obj random.obj solo.obj solo.res >> solo.rsp echo user32.lib version.obj windows.obj winspool.lib >> solo.rsp solosolver.rsp: $(MAKEFILE) echo /nologo /subsystem:console > solosolver.rsp echo divvy.obj dsf.obj malloc.obj misc.obj nullfe.obj >> solosolver.rsp echo random.obj solo2.obj >> solosolver.rsp tents.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > tents.rsp echo comctl32.lib comdlg32.lib drawing.obj dsf.obj >> tents.rsp echo gdi32.lib malloc.obj maxflow.obj midend.obj >> tents.rsp echo misc.obj printing.obj random.obj tents.obj >> tents.rsp echo tents.res user32.lib version.obj windows.obj >> tents.rsp echo winspool.lib >> tents.rsp tentssolver.rsp: $(MAKEFILE) echo /nologo /subsystem:console > tentssolver.rsp echo dsf.obj malloc.obj maxflow.obj misc.obj >> tentssolver.rsp echo nullfe.obj random.obj tents3.obj >> tentssolver.rsp towers.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > towers.rsp echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> towers.rsp echo latin.obj malloc.obj maxflow.obj midend.obj >> towers.rsp echo misc.obj printing.obj random.obj towers.obj >> towers.rsp echo towers.res tree234.obj user32.lib version.obj >> towers.rsp echo windows.obj winspool.lib >> towers.rsp towerssolver.rsp: $(MAKEFILE) echo /nologo /subsystem:console > towerssolver.rsp echo latin6.obj malloc.obj maxflow.obj misc.obj >> towerssolver.rsp echo nullfe.obj random.obj towers2.obj tree234.obj >> towerssolver.rsp twiddle.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > twiddle.rsp echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> twiddle.rsp echo malloc.obj midend.obj misc.obj printing.obj >> twiddle.rsp echo random.obj twiddle.obj twiddle.res user32.lib >> twiddle.rsp echo version.obj windows.obj winspool.lib >> twiddle.rsp undead.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > undead.rsp echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> undead.rsp echo malloc.obj midend.obj misc.obj printing.obj >> undead.rsp echo random.obj undead.obj undead.res user32.lib >> undead.rsp echo version.obj windows.obj winspool.lib >> undead.rsp unequal.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > unequal.rsp echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> unequal.rsp echo latin.obj malloc.obj maxflow.obj midend.obj >> unequal.rsp echo misc.obj printing.obj random.obj tree234.obj >> unequal.rsp echo unequal.obj unequal.res user32.lib version.obj >> unequal.rsp echo windows.obj winspool.lib >> unequal.rsp unequalsolver.rsp: $(MAKEFILE) echo /nologo /subsystem:console > unequalsolver.rsp echo latin6.obj malloc.obj maxflow.obj misc.obj >> unequalsolver.rsp echo nullfe.obj random.obj tree234.obj unequal2.obj >> unequalsolver.rsp unruly.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > unruly.rsp echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> unruly.rsp echo malloc.obj midend.obj misc.obj printing.obj >> unruly.rsp echo random.obj unruly.obj unruly.res user32.lib >> unruly.rsp echo version.obj windows.obj winspool.lib >> unruly.rsp unrulysolver.rsp: $(MAKEFILE) echo /nologo /subsystem:console > unrulysolver.rsp echo malloc.obj misc.obj nullfe.obj random.obj >> unrulysolver.rsp echo unruly2.obj >> unrulysolver.rsp untangle.rsp: $(MAKEFILE) echo /nologo /subsystem:windows > untangle.rsp echo comctl32.lib comdlg32.lib drawing.obj gdi32.lib >> untangle.rsp echo malloc.obj midend.obj misc.obj printing.obj >> untangle.rsp echo random.obj tree234.obj untangle.obj untangle.res >> untangle.rsp echo user32.lib version.obj windows.obj winspool.lib >> untangle.rsp blackbox.obj: .\blackbox.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\blackbox.c /Foblackbox.obj blackbox-icon.obj: icons\blackbox-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\blackbox-icon.c /Foblackbox-icon.obj blackbox.res: icons\blackbox.rc .\puzzles.rc2 icons\blackbox.ico \ .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -foblackbox.res icons\blackbox.rc blackbo3.obj: .\blackbox.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\blackbox.c /Foblackbo3.obj bridges.obj: .\bridges.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\bridges.c /Fobridges.obj bridges-icon.obj: icons\bridges-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\bridges-icon.c /Fobridges-icon.obj bridges.res: icons\bridges.rc .\puzzles.rc2 icons\bridges.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fobridges.res icons\bridges.rc bridges3.obj: .\bridges.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\bridges.c /Fobridges3.obj combi.obj: .\combi.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\combi.c /Focombi.obj cube.obj: .\cube.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\cube.c /Focube.obj cube-icon.obj: icons\cube-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\cube-icon.c /Focube-icon.obj cube.res: icons\cube.rc .\puzzles.rc2 icons\cube.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -focube.res icons\cube.rc cube3.obj: .\cube.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\cube.c /Focube3.obj divvy.obj: .\divvy.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\divvy.c /Fodivvy.obj dominosa.obj: .\dominosa.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\dominosa.c /Fodominosa.obj dominosa-icon.obj: icons\dominosa-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\dominosa-icon.c /Fodominosa-icon.obj dominosa.res: icons\dominosa.rc .\puzzles.rc2 icons\dominosa.ico \ .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fodominosa.res icons\dominosa.rc dominos3.obj: .\dominosa.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\dominosa.c /Fodominos3.obj drawing.obj: .\drawing.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\drawing.c /Fodrawing.obj dsf.obj: .\dsf.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\dsf.c /Fodsf.obj fifteen.obj: .\fifteen.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\fifteen.c /Fofifteen.obj fifteen-icon.obj: icons\fifteen-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\fifteen-icon.c /Fofifteen-icon.obj fifteen.res: icons\fifteen.rc .\puzzles.rc2 icons\fifteen.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fofifteen.res icons\fifteen.rc fifteen3.obj: .\fifteen.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\fifteen.c /Fofifteen3.obj filling.obj: .\filling.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\filling.c /Fofilling.obj filling-icon.obj: icons\filling-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\filling-icon.c /Fofilling-icon.obj filling.res: icons\filling.rc .\puzzles.rc2 icons\filling.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fofilling.res icons\filling.rc filling5.obj: .\filling.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\filling.c /Fofilling5.obj filling2.obj: .\filling.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\filling.c /Fofilling2.obj flip.obj: .\flip.c .\puzzles.h .\tree234.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\flip.c /Foflip.obj flip-icon.obj: icons\flip-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\flip-icon.c /Foflip-icon.obj flip.res: icons\flip.rc .\puzzles.rc2 icons\flip.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -foflip.res icons\flip.rc flip3.obj: .\flip.c .\puzzles.h .\tree234.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\flip.c /Foflip3.obj galaxies.obj: .\galaxies.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\galaxies.c /Fogalaxies.obj galaxies-icon.obj: icons\galaxies-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\galaxies-icon.c /Fogalaxies-icon.obj galaxies.res: icons\galaxies.rc .\puzzles.rc2 icons\galaxies.ico \ .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fogalaxies.res icons\galaxies.rc galaxie7.obj: .\galaxies.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\galaxies.c /Fogalaxie7.obj galaxie4.obj: .\galaxies.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_PICTURE_GENERATOR /c .\galaxies.c /Fogalaxie4.obj galaxie2.obj: .\galaxies.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\galaxies.c /Fogalaxie2.obj grid.obj: .\grid.c .\puzzles.h .\tree234.h .\grid.h .\penrose.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\grid.c /Fogrid.obj gtk.obj: .\gtk.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\gtk.c /Fogtk.obj guess.obj: .\guess.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\guess.c /Foguess.obj guess-icon.obj: icons\guess-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\guess-icon.c /Foguess-icon.obj guess.res: icons\guess.rc .\puzzles.rc2 icons\guess.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -foguess.res icons\guess.rc guess3.obj: .\guess.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\guess.c /Foguess3.obj inertia.obj: .\inertia.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\inertia.c /Foinertia.obj inertia-icon.obj: icons\inertia-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\inertia-icon.c /Foinertia-icon.obj inertia.res: icons\inertia.rc .\puzzles.rc2 icons\inertia.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -foinertia.res icons\inertia.rc inertia3.obj: .\inertia.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\inertia.c /Foinertia3.obj keen.obj: .\keen.c .\puzzles.h .\latin.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\keen.c /Fokeen.obj keen-icon.obj: icons\keen-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\keen-icon.c /Fokeen-icon.obj keen.res: icons\keen.rc .\puzzles.rc2 icons\keen.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fokeen.res icons\keen.rc keen5.obj: .\keen.c .\puzzles.h .\latin.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\keen.c /Fokeen5.obj keen2.obj: .\keen.c .\puzzles.h .\latin.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\keen.c /Fokeen2.obj latin.obj: .\latin.c .\puzzles.h .\tree234.h .\maxflow.h .\latin.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\latin.c /Folatin.obj latin8.obj: .\latin.c .\puzzles.h .\tree234.h .\maxflow.h .\latin.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_LATIN_TEST /c .\latin.c /Folatin8.obj latin6.obj: .\latin.c .\puzzles.h .\tree234.h .\maxflow.h .\latin.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\latin.c /Folatin6.obj laydomino.obj: .\laydomino.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\laydomino.c /Folaydomino.obj lightup.obj: .\lightup.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\lightup.c /Folightup.obj lightup-icon.obj: icons\lightup-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\lightup-icon.c /Folightup-icon.obj lightup.res: icons\lightup.rc .\puzzles.rc2 icons\lightup.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -folightup.res icons\lightup.rc lightup5.obj: .\lightup.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\lightup.c /Folightup5.obj lightup2.obj: .\lightup.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\lightup.c /Folightup2.obj list.obj: .\list.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\list.c /Folist.obj loopgen.obj: .\loopgen.c .\puzzles.h .\tree234.h .\grid.h .\loopgen.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\loopgen.c /Foloopgen.obj loopy.obj: .\loopy.c .\puzzles.h .\tree234.h .\grid.h .\loopgen.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\loopy.c /Foloopy.obj loopy-icon.obj: icons\loopy-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\loopy-icon.c /Foloopy-icon.obj loopy.res: icons\loopy.rc .\puzzles.rc2 icons\loopy.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -foloopy.res icons\loopy.rc loopy5.obj: .\loopy.c .\puzzles.h .\tree234.h .\grid.h .\loopgen.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\loopy.c /Foloopy5.obj loopy2.obj: .\loopy.c .\puzzles.h .\tree234.h .\grid.h .\loopgen.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\loopy.c /Foloopy2.obj magnets.obj: .\magnets.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\magnets.c /Fomagnets.obj magnets-icon.obj: icons\magnets-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\magnets-icon.c /Fomagnets-icon.obj magnets.res: icons\magnets.rc .\puzzles.rc2 icons\magnets.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fomagnets.res icons\magnets.rc magnets5.obj: .\magnets.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\magnets.c /Fomagnets5.obj magnets2.obj: .\magnets.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\magnets.c /Fomagnets2.obj malloc.obj: .\malloc.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\malloc.c /Fomalloc.obj map.obj: .\map.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\map.c /Fomap.obj map-icon.obj: icons\map-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\map-icon.c /Fomap-icon.obj map.res: icons\map.rc .\puzzles.rc2 icons\map.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fomap.res icons\map.rc map5.obj: .\map.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\map.c /Fomap5.obj map2.obj: .\map.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\map.c /Fomap2.obj maxflow.obj: .\maxflow.c .\maxflow.h .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\maxflow.c /Fomaxflow.obj midend.obj: .\midend.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\midend.c /Fomidend.obj mines.obj: .\mines.c .\tree234.h .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\mines.c /Fomines.obj mines-icon.obj: icons\mines-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\mines-icon.c /Fomines-icon.obj mines.res: icons\mines.rc .\puzzles.rc2 icons\mines.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fomines.res icons\mines.rc mines5.obj: .\mines.c .\tree234.h .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\mines.c /Fomines5.obj mines2.obj: .\mines.c .\tree234.h .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_OBFUSCATOR /c .\mines.c /Fomines2.obj misc.obj: .\misc.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\misc.c /Fomisc.obj net.obj: .\net.c .\puzzles.h .\tree234.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\net.c /Fonet.obj net-icon.obj: icons\net-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\net-icon.c /Fonet-icon.obj net.res: icons\net.rc .\puzzles.rc2 icons\net.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fonet.res icons\net.rc net3.obj: .\net.c .\puzzles.h .\tree234.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\net.c /Fonet3.obj netslide.obj: .\netslide.c .\puzzles.h .\tree234.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\netslide.c /Fonetslide.obj netslide-icon.obj: icons\netslide-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\netslide-icon.c /Fonetslide-icon.obj netslide.res: icons\netslide.rc .\puzzles.rc2 icons\netslide.ico \ .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fonetslide.res icons\netslide.rc netslid3.obj: .\netslide.c .\puzzles.h .\tree234.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\netslide.c /Fonetslid3.obj no-icon.obj: .\no-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\no-icon.c /Fono-icon.obj noicon.res: .\noicon.rc .\puzzles.rc2 .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fonoicon.res .\noicon.rc nullfe.obj: .\nullfe.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\nullfe.c /Fonullfe.obj nullgame.obj: .\nullgame.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\nullgame.c /Fonullgame.obj obfusc.obj: .\obfusc.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\obfusc.c /Foobfusc.obj osx.obj: .\osx.m .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\osx.m /Foosx.obj pattern.obj: .\pattern.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\pattern.c /Fopattern.obj pattern-icon.obj: icons\pattern-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\pattern-icon.c /Fopattern-icon.obj pattern.res: icons\pattern.rc .\puzzles.rc2 icons\pattern.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fopattern.res icons\pattern.rc pattern5.obj: .\pattern.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\pattern.c /Fopattern5.obj pattern2.obj: .\pattern.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\pattern.c /Fopattern2.obj pearl.obj: .\pearl.c .\puzzles.h .\grid.h .\loopgen.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\pearl.c /Fopearl.obj pearl-icon.obj: icons\pearl-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\pearl-icon.c /Fopearl-icon.obj pearl.res: icons\pearl.rc .\puzzles.rc2 icons\pearl.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fopearl.res icons\pearl.rc pearl5.obj: .\pearl.c .\puzzles.h .\grid.h .\loopgen.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\pearl.c /Fopearl5.obj pearl2.obj: .\pearl.c .\puzzles.h .\grid.h .\loopgen.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\pearl.c /Fopearl2.obj pegs.obj: .\pegs.c .\puzzles.h .\tree234.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\pegs.c /Fopegs.obj pegs-icon.obj: icons\pegs-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\pegs-icon.c /Fopegs-icon.obj pegs.res: icons\pegs.rc .\puzzles.rc2 icons\pegs.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fopegs.res icons\pegs.rc pegs3.obj: .\pegs.c .\puzzles.h .\tree234.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\pegs.c /Fopegs3.obj penrose.obj: .\penrose.c .\puzzles.h .\penrose.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\penrose.c /Fopenrose.obj printing.obj: .\printing.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\printing.c /Foprinting.obj ps.obj: .\ps.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\ps.c /Fops.obj random.obj: .\random.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\random.c /Forandom.obj range.obj: .\range.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\range.c /Forange.obj range-icon.obj: icons\range-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\range-icon.c /Forange-icon.obj range.res: icons\range.rc .\puzzles.rc2 icons\range.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -forange.res icons\range.rc range3.obj: .\range.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\range.c /Forange3.obj rect.obj: .\rect.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\rect.c /Forect.obj rect-icon.obj: icons\rect-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\rect-icon.c /Forect-icon.obj rect.res: icons\rect.rc .\puzzles.rc2 icons\rect.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -forect.res icons\rect.rc rect3.obj: .\rect.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\rect.c /Forect3.obj samegame.obj: .\samegame.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\samegame.c /Fosamegame.obj samegame-icon.obj: icons\samegame-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\samegame-icon.c /Fosamegame-icon.obj samegame.res: icons\samegame.rc .\puzzles.rc2 icons\samegame.ico \ .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fosamegame.res icons\samegame.rc samegam3.obj: .\samegame.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\samegame.c /Fosamegam3.obj signpost.obj: .\signpost.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\signpost.c /Fosignpost.obj signpost-icon.obj: icons\signpost-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\signpost-icon.c /Fosignpost-icon.obj signpost.res: icons\signpost.rc .\puzzles.rc2 icons\signpost.ico \ .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fosignpost.res icons\signpost.rc signpos5.obj: .\signpost.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\signpost.c /Fosignpos5.obj signpos2.obj: .\signpost.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\signpost.c /Fosignpos2.obj singles.obj: .\singles.c .\puzzles.h .\latin.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\singles.c /Fosingles.obj singles-icon.obj: icons\singles-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\singles-icon.c /Fosingles-icon.obj singles.res: icons\singles.rc .\puzzles.rc2 icons\singles.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fosingles.res icons\singles.rc singles5.obj: .\singles.c .\puzzles.h .\latin.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\singles.c /Fosingles5.obj singles3.obj: .\singles.c .\puzzles.h .\latin.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\singles.c /Fosingles3.obj sixteen.obj: .\sixteen.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\sixteen.c /Fosixteen.obj sixteen-icon.obj: icons\sixteen-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\sixteen-icon.c /Fosixteen-icon.obj sixteen.res: icons\sixteen.rc .\puzzles.rc2 icons\sixteen.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fosixteen.res icons\sixteen.rc sixteen3.obj: .\sixteen.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\sixteen.c /Fosixteen3.obj slant.obj: .\slant.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\slant.c /Foslant.obj slant-icon.obj: icons\slant-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\slant-icon.c /Foslant-icon.obj slant.res: icons\slant.rc .\puzzles.rc2 icons\slant.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -foslant.res icons\slant.rc slant5.obj: .\slant.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\slant.c /Foslant5.obj slant2.obj: .\slant.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\slant.c /Foslant2.obj solo.obj: .\solo.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\solo.c /Fosolo.obj solo-icon.obj: icons\solo-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\solo-icon.c /Fosolo-icon.obj solo.res: icons\solo.rc .\puzzles.rc2 icons\solo.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fosolo.res icons\solo.rc solo5.obj: .\solo.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\solo.c /Fosolo5.obj solo2.obj: .\solo.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\solo.c /Fosolo2.obj tdq.obj: .\tdq.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\tdq.c /Fotdq.obj tents.obj: .\tents.c .\puzzles.h .\maxflow.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\tents.c /Fotents.obj tents-icon.obj: icons\tents-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\tents-icon.c /Fotents-icon.obj tents.res: icons\tents.rc .\puzzles.rc2 icons\tents.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fotents.res icons\tents.rc tents5.obj: .\tents.c .\puzzles.h .\maxflow.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\tents.c /Fotents5.obj tents3.obj: .\tents.c .\puzzles.h .\maxflow.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\tents.c /Fotents3.obj towers.obj: .\towers.c .\puzzles.h .\latin.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\towers.c /Fotowers.obj towers-icon.obj: icons\towers-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\towers-icon.c /Fotowers-icon.obj towers.res: icons\towers.rc .\puzzles.rc2 icons\towers.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fotowers.res icons\towers.rc towers5.obj: .\towers.c .\puzzles.h .\latin.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\towers.c /Fotowers5.obj towers2.obj: .\towers.c .\puzzles.h .\latin.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\towers.c /Fotowers2.obj tree234.obj: .\tree234.c .\tree234.h .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\tree234.c /Fotree234.obj twiddle.obj: .\twiddle.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\twiddle.c /Fotwiddle.obj twiddle-icon.obj: icons\twiddle-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\twiddle-icon.c /Fotwiddle-icon.obj twiddle.res: icons\twiddle.rc .\puzzles.rc2 icons\twiddle.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fotwiddle.res icons\twiddle.rc twiddle3.obj: .\twiddle.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\twiddle.c /Fotwiddle3.obj undead.obj: .\undead.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\undead.c /Foundead.obj undead-icon.obj: icons\undead-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\undead-icon.c /Foundead-icon.obj undead.res: icons\undead.rc .\puzzles.rc2 icons\undead.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -foundead.res icons\undead.rc undead3.obj: .\undead.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\undead.c /Foundead3.obj unequal.obj: .\unequal.c .\puzzles.h .\latin.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\unequal.c /Founequal.obj unequal-icon.obj: icons\unequal-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\unequal-icon.c /Founequal-icon.obj unequal.res: icons\unequal.rc .\puzzles.rc2 icons\unequal.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -founequal.res icons\unequal.rc unequal5.obj: .\unequal.c .\puzzles.h .\latin.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\unequal.c /Founequal5.obj unequal2.obj: .\unequal.c .\puzzles.h .\latin.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\unequal.c /Founequal2.obj unruly.obj: .\unruly.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\unruly.c /Founruly.obj unruly-icon.obj: icons\unruly-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\unruly-icon.c /Founruly-icon.obj unruly.res: icons\unruly.rc .\puzzles.rc2 icons\unruly.ico .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -founruly.res icons\unruly.rc unruly5.obj: .\unruly.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\unruly.c /Founruly5.obj unruly2.obj: .\unruly.c .\puzzles.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\unruly.c /Founruly2.obj untangle.obj: .\untangle.c .\puzzles.h .\tree234.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\untangle.c /Fountangle.obj untangle-icon.obj: icons\untangle-icon.c cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\untangle-icon.c /Fountangle-icon.obj untangle.res: icons\untangle.rc .\puzzles.rc2 icons\untangle.ico \ .\resource.h rc $(FWHACK) $(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 -fountangle.res icons\untangle.rc untangl3.obj: .\untangle.c .\puzzles.h .\tree234.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\untangle.c /Fountangl3.obj windows.obj: .\windows.c .\puzzles.h .\resource.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\windows.c /Fowindows.obj windows1.obj: .\windows.c .\puzzles.h .\resource.h cl $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\windows.c /Fowindows1.obj version.obj: *.c *.h cl $(VER) $(CFLAGS) /c version.c clean: tidy -del *.exe tidy: -del *.obj -del *.res -del *.pch -del *.aps -del *.ilk -del *.pdb -del *.rsp -del *.dsp -del *.dsw -del *.ncb -del *.opt -del *.plg -del *.map -del *.idb -del debug.log puzzles-r9872/Makefile.wce0000644000175300017530000013033612161170560014647 0ustar simonsimon# Makefile for puzzles on PocketPC using eMbedded Visual C. # # This file was created by `mkfiles.pl' from the `Recipe' file. # DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead. # If you rename this file to `Makefile', you should change this line, # so that the .rsp files still depend on the correct makefile. MAKEFILE = Makefile.wce # This makefile expects the environment to have been set up by one # of the PocketPC batch files wcearmv4.bat and wceemulator.bat. No # other build targets are currently supported, because they would # need a section in this if statement. !if "$(TARGETCPU)" == "emulator" PLATFORM_DEFS=/D "_i386_" /D "i_386_" /D "_X86_" /D "x86" CC=cl BASELIBS=commctrl.lib coredll.lib corelibc.lib aygshell.lib MACHINE=IX86 !else PLATFORM_DEFS=/D "ARM" /D "_ARM_" /D "ARMV4" CC=clarm BASELIBS=commctrl.lib coredll.lib aygshell.lib MACHINE=ARM !endif # C compilation flags CFLAGS = /nologo /W3 /O1 /MC /D _WIN32_WCE=420 /D "WIN32_PLATFORM_PSPC=400" /D UNDER_CE=420 \ $(PLATFORM_DEFS) \ /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "NO_HTMLHELP" LFLAGS = /nologo /incremental:no \ /base:0x00010000 /stack:0x10000,0x1000 /entry:WinMainCRTStartup \ /nodefaultlib:libc.lib /nodefaultlib:libcmt.lib /nodefaultlib:msvcrt.lib /nodefaultlib:OLDNAMES.lib \ /subsystem:windowsce,4.20 /align:4096 /MACHINE:$(MACHINE) RCFL = /d UNDER_CE=420 /d _WIN32_WCE=420 /d "WIN32_PLATFORM_PSPC=400" \ $(PLATFORM_DEFS) \ /d "NDEBUG" /d "UNICODE" /d "_UNICODE" all: blackbox.exe bridges.exe cube.exe dominosa.exe fifteen.exe filling.exe \ flip.exe galaxies.exe guess.exe inertia.exe keen.exe \ lightup.exe loopy.exe magnets.exe map.exe mines.exe \ netgame.exe netslide.exe nullgame.exe pattern.exe pearl.exe \ pegs.exe puzzles.exe range.exe rect.exe samegame.exe \ signpost.exe singles.exe sixteen.exe slant.exe solo.exe \ tents.exe towers.exe twiddle.exe undead.exe unequal.exe \ unruly.exe untangle.exe blackbox.exe: blackbox.obj blackbox.res drawing.obj malloc.obj midend.obj \ misc.obj printing.obj random.obj version.obj windows.obj \ blackbox.rsp link $(LFLAGS) -out:blackbox.exe -map:blackbox.map @blackbox.rsp bridges.exe: bridges.obj bridges.res drawing.obj dsf.obj malloc.obj \ midend.obj misc.obj printing.obj random.obj version.obj \ windows.obj bridges.rsp link $(LFLAGS) -out:bridges.exe -map:bridges.map @bridges.rsp cube.exe: cube.obj cube.res drawing.obj malloc.obj midend.obj misc.obj \ printing.obj random.obj version.obj windows.obj cube.rsp link $(LFLAGS) -out:cube.exe -map:cube.map @cube.rsp dominosa.exe: dominosa.obj dominosa.res drawing.obj laydomino.obj malloc.obj \ midend.obj misc.obj printing.obj random.obj version.obj \ windows.obj dominosa.rsp link $(LFLAGS) -out:dominosa.exe -map:dominosa.map @dominosa.rsp fifteen.exe: drawing.obj fifteen.obj fifteen.res malloc.obj midend.obj \ misc.obj printing.obj random.obj version.obj windows.obj \ fifteen.rsp link $(LFLAGS) -out:fifteen.exe -map:fifteen.map @fifteen.rsp filling.exe: drawing.obj dsf.obj filling.obj filling.res malloc.obj \ midend.obj misc.obj printing.obj random.obj version.obj \ windows.obj filling.rsp link $(LFLAGS) -out:filling.exe -map:filling.map @filling.rsp flip.exe: drawing.obj flip.obj flip.res malloc.obj midend.obj misc.obj \ printing.obj random.obj tree234.obj version.obj windows.obj \ flip.rsp link $(LFLAGS) -out:flip.exe -map:flip.map @flip.rsp galaxies.exe: drawing.obj dsf.obj galaxies.obj galaxies.res malloc.obj \ midend.obj misc.obj printing.obj random.obj version.obj \ windows.obj galaxies.rsp link $(LFLAGS) -out:galaxies.exe -map:galaxies.map @galaxies.rsp guess.exe: drawing.obj guess.obj guess.res malloc.obj midend.obj misc.obj \ printing.obj random.obj version.obj windows.obj guess.rsp link $(LFLAGS) -out:guess.exe -map:guess.map @guess.rsp inertia.exe: drawing.obj inertia.obj inertia.res malloc.obj midend.obj \ misc.obj printing.obj random.obj version.obj windows.obj \ inertia.rsp link $(LFLAGS) -out:inertia.exe -map:inertia.map @inertia.rsp keen.exe: drawing.obj dsf.obj keen.obj keen.res latin.obj malloc.obj \ maxflow.obj midend.obj misc.obj printing.obj random.obj \ tree234.obj version.obj windows.obj keen.rsp link $(LFLAGS) -out:keen.exe -map:keen.map @keen.rsp lightup.exe: combi.obj drawing.obj lightup.obj lightup.res malloc.obj \ midend.obj misc.obj printing.obj random.obj version.obj \ windows.obj lightup.rsp link $(LFLAGS) -out:lightup.exe -map:lightup.map @lightup.rsp loopy.exe: drawing.obj dsf.obj grid.obj loopgen.obj loopy.obj loopy.res \ malloc.obj midend.obj misc.obj penrose.obj printing.obj \ random.obj tree234.obj version.obj windows.obj loopy.rsp link $(LFLAGS) -out:loopy.exe -map:loopy.map @loopy.rsp magnets.exe: drawing.obj laydomino.obj magnets.obj magnets.res malloc.obj \ midend.obj misc.obj printing.obj random.obj version.obj \ windows.obj magnets.rsp link $(LFLAGS) -out:magnets.exe -map:magnets.map @magnets.rsp map.exe: drawing.obj dsf.obj malloc.obj map.obj map.res midend.obj misc.obj \ printing.obj random.obj version.obj windows.obj map.rsp link $(LFLAGS) -out:map.exe -map:map.map @map.rsp mines.exe: drawing.obj malloc.obj midend.obj mines.obj mines.res misc.obj \ printing.obj random.obj tree234.obj version.obj windows.obj \ mines.rsp link $(LFLAGS) -out:mines.exe -map:mines.map @mines.rsp netgame.exe: drawing.obj dsf.obj malloc.obj midend.obj misc.obj net.obj \ net.res printing.obj random.obj tree234.obj version.obj \ windows.obj netgame.rsp link $(LFLAGS) -out:netgame.exe -map:netgame.map @netgame.rsp netslide.exe: drawing.obj malloc.obj midend.obj misc.obj netslide.obj \ netslide.res printing.obj random.obj tree234.obj version.obj \ windows.obj netslide.rsp link $(LFLAGS) -out:netslide.exe -map:netslide.map @netslide.rsp nullgame.exe: drawing.obj malloc.obj midend.obj misc.obj noicon.res \ nullgame.obj printing.obj random.obj version.obj windows.obj \ nullgame.rsp link $(LFLAGS) -out:nullgame.exe -map:nullgame.map @nullgame.rsp pattern.exe: drawing.obj malloc.obj midend.obj misc.obj pattern.obj \ pattern.res printing.obj random.obj version.obj windows.obj \ pattern.rsp link $(LFLAGS) -out:pattern.exe -map:pattern.map @pattern.rsp pearl.exe: drawing.obj dsf.obj grid.obj loopgen.obj malloc.obj midend.obj \ misc.obj pearl.obj pearl.res penrose.obj printing.obj \ random.obj tdq.obj tree234.obj version.obj windows.obj \ pearl.rsp link $(LFLAGS) -out:pearl.exe -map:pearl.map @pearl.rsp pegs.exe: drawing.obj malloc.obj midend.obj misc.obj pegs.obj pegs.res \ printing.obj random.obj tree234.obj version.obj windows.obj \ pegs.rsp link $(LFLAGS) -out:pegs.exe -map:pegs.map @pegs.rsp puzzles.exe: blackbo3.obj bridges3.obj combi.obj cube3.obj divvy.obj \ dominos3.obj drawing.obj dsf.obj fifteen3.obj filling5.obj \ flip3.obj galaxie7.obj grid.obj guess3.obj inertia3.obj \ keen5.obj latin.obj laydomino.obj lightup5.obj list.obj \ loopgen.obj loopy5.obj magnets5.obj malloc.obj map5.obj \ maxflow.obj midend.obj mines5.obj misc.obj net3.obj \ netslid3.obj noicon.res pattern5.obj pearl5.obj pegs3.obj \ penrose.obj printing.obj random.obj range3.obj rect3.obj \ samegam3.obj signpos5.obj singles5.obj sixteen3.obj \ slant5.obj solo5.obj tdq.obj tents5.obj towers5.obj \ tree234.obj twiddle3.obj undead3.obj unequal5.obj \ unruly5.obj untangl3.obj version.obj windows1.obj \ puzzles.rsp link $(LFLAGS) -out:puzzles.exe -map:puzzles.map @puzzles.rsp range.exe: drawing.obj malloc.obj midend.obj misc.obj printing.obj \ random.obj range.obj range.res version.obj windows.obj \ range.rsp link $(LFLAGS) -out:range.exe -map:range.map @range.rsp rect.exe: drawing.obj malloc.obj midend.obj misc.obj printing.obj random.obj \ rect.obj rect.res version.obj windows.obj rect.rsp link $(LFLAGS) -out:rect.exe -map:rect.map @rect.rsp samegame.exe: drawing.obj malloc.obj midend.obj misc.obj printing.obj \ random.obj samegame.obj samegame.res version.obj windows.obj \ samegame.rsp link $(LFLAGS) -out:samegame.exe -map:samegame.map @samegame.rsp signpost.exe: drawing.obj dsf.obj malloc.obj midend.obj misc.obj \ printing.obj random.obj signpost.obj signpost.res \ version.obj windows.obj signpost.rsp link $(LFLAGS) -out:signpost.exe -map:signpost.map @signpost.rsp singles.exe: drawing.obj dsf.obj latin.obj malloc.obj maxflow.obj midend.obj \ misc.obj printing.obj random.obj singles.obj singles.res \ tree234.obj version.obj windows.obj singles.rsp link $(LFLAGS) -out:singles.exe -map:singles.map @singles.rsp sixteen.exe: drawing.obj malloc.obj midend.obj misc.obj printing.obj \ random.obj sixteen.obj sixteen.res version.obj windows.obj \ sixteen.rsp link $(LFLAGS) -out:sixteen.exe -map:sixteen.map @sixteen.rsp slant.exe: drawing.obj dsf.obj malloc.obj midend.obj misc.obj printing.obj \ random.obj slant.obj slant.res version.obj windows.obj \ slant.rsp link $(LFLAGS) -out:slant.exe -map:slant.map @slant.rsp solo.exe: divvy.obj drawing.obj dsf.obj malloc.obj midend.obj misc.obj \ printing.obj random.obj solo.obj solo.res version.obj \ windows.obj solo.rsp link $(LFLAGS) -out:solo.exe -map:solo.map @solo.rsp tents.exe: drawing.obj dsf.obj malloc.obj maxflow.obj midend.obj misc.obj \ printing.obj random.obj tents.obj tents.res version.obj \ windows.obj tents.rsp link $(LFLAGS) -out:tents.exe -map:tents.map @tents.rsp towers.exe: drawing.obj latin.obj malloc.obj maxflow.obj midend.obj misc.obj \ printing.obj random.obj towers.obj towers.res tree234.obj \ version.obj windows.obj towers.rsp link $(LFLAGS) -out:towers.exe -map:towers.map @towers.rsp twiddle.exe: drawing.obj malloc.obj midend.obj misc.obj printing.obj \ random.obj twiddle.obj twiddle.res version.obj windows.obj \ twiddle.rsp link $(LFLAGS) -out:twiddle.exe -map:twiddle.map @twiddle.rsp undead.exe: drawing.obj malloc.obj midend.obj misc.obj printing.obj \ random.obj undead.obj undead.res version.obj windows.obj \ undead.rsp link $(LFLAGS) -out:undead.exe -map:undead.map @undead.rsp unequal.exe: drawing.obj latin.obj malloc.obj maxflow.obj midend.obj \ misc.obj printing.obj random.obj tree234.obj unequal.obj \ unequal.res version.obj windows.obj unequal.rsp link $(LFLAGS) -out:unequal.exe -map:unequal.map @unequal.rsp unruly.exe: drawing.obj malloc.obj midend.obj misc.obj printing.obj \ random.obj unruly.obj unruly.res version.obj windows.obj \ unruly.rsp link $(LFLAGS) -out:unruly.exe -map:unruly.map @unruly.rsp untangle.exe: drawing.obj malloc.obj midend.obj misc.obj printing.obj \ random.obj tree234.obj untangle.obj untangle.res version.obj \ windows.obj untangle.rsp link $(LFLAGS) -out:untangle.exe -map:untangle.map @untangle.rsp blackbox.rsp: $(MAKEFILE) echo $(BASELIBS) > blackbox.rsp echo blackbox.obj blackbox.res drawing.obj malloc.obj >> blackbox.rsp echo midend.obj misc.obj printing.obj random.obj >> blackbox.rsp echo version.obj windows.obj >> blackbox.rsp bridges.rsp: $(MAKEFILE) echo $(BASELIBS) > bridges.rsp echo bridges.obj bridges.res drawing.obj dsf.obj >> bridges.rsp echo malloc.obj midend.obj misc.obj printing.obj >> bridges.rsp echo random.obj version.obj windows.obj >> bridges.rsp cube.rsp: $(MAKEFILE) echo $(BASELIBS) > cube.rsp echo cube.obj cube.res drawing.obj malloc.obj >> cube.rsp echo midend.obj misc.obj printing.obj random.obj >> cube.rsp echo version.obj windows.obj >> cube.rsp dominosa.rsp: $(MAKEFILE) echo $(BASELIBS) > dominosa.rsp echo dominosa.obj dominosa.res drawing.obj >> dominosa.rsp echo laydomino.obj malloc.obj midend.obj misc.obj >> dominosa.rsp echo printing.obj random.obj version.obj windows.obj >> dominosa.rsp fifteen.rsp: $(MAKEFILE) echo $(BASELIBS) > fifteen.rsp echo drawing.obj fifteen.obj fifteen.res malloc.obj >> fifteen.rsp echo midend.obj misc.obj printing.obj random.obj >> fifteen.rsp echo version.obj windows.obj >> fifteen.rsp filling.rsp: $(MAKEFILE) echo $(BASELIBS) > filling.rsp echo drawing.obj dsf.obj filling.obj filling.res >> filling.rsp echo malloc.obj midend.obj misc.obj printing.obj >> filling.rsp echo random.obj version.obj windows.obj >> filling.rsp flip.rsp: $(MAKEFILE) echo $(BASELIBS) > flip.rsp echo drawing.obj flip.obj flip.res malloc.obj >> flip.rsp echo midend.obj misc.obj printing.obj random.obj >> flip.rsp echo tree234.obj version.obj windows.obj >> flip.rsp galaxies.rsp: $(MAKEFILE) echo $(BASELIBS) > galaxies.rsp echo drawing.obj dsf.obj galaxies.obj galaxies.res >> galaxies.rsp echo malloc.obj midend.obj misc.obj printing.obj >> galaxies.rsp echo random.obj version.obj windows.obj >> galaxies.rsp guess.rsp: $(MAKEFILE) echo $(BASELIBS) > guess.rsp echo drawing.obj guess.obj guess.res malloc.obj >> guess.rsp echo midend.obj misc.obj printing.obj random.obj >> guess.rsp echo version.obj windows.obj >> guess.rsp inertia.rsp: $(MAKEFILE) echo $(BASELIBS) > inertia.rsp echo drawing.obj inertia.obj inertia.res malloc.obj >> inertia.rsp echo midend.obj misc.obj printing.obj random.obj >> inertia.rsp echo version.obj windows.obj >> inertia.rsp keen.rsp: $(MAKEFILE) echo $(BASELIBS) > keen.rsp echo drawing.obj dsf.obj keen.obj keen.res latin.obj >> keen.rsp echo malloc.obj maxflow.obj midend.obj misc.obj >> keen.rsp echo printing.obj random.obj tree234.obj version.obj >> keen.rsp echo windows.obj >> keen.rsp lightup.rsp: $(MAKEFILE) echo $(BASELIBS) > lightup.rsp echo combi.obj drawing.obj lightup.obj lightup.res >> lightup.rsp echo malloc.obj midend.obj misc.obj printing.obj >> lightup.rsp echo random.obj version.obj windows.obj >> lightup.rsp loopy.rsp: $(MAKEFILE) echo $(BASELIBS) > loopy.rsp echo drawing.obj dsf.obj grid.obj loopgen.obj >> loopy.rsp echo loopy.obj loopy.res malloc.obj midend.obj >> loopy.rsp echo misc.obj penrose.obj printing.obj random.obj >> loopy.rsp echo tree234.obj version.obj windows.obj >> loopy.rsp magnets.rsp: $(MAKEFILE) echo $(BASELIBS) > magnets.rsp echo drawing.obj laydomino.obj magnets.obj magnets.res >> magnets.rsp echo malloc.obj midend.obj misc.obj printing.obj >> magnets.rsp echo random.obj version.obj windows.obj >> magnets.rsp map.rsp: $(MAKEFILE) echo $(BASELIBS) > map.rsp echo drawing.obj dsf.obj malloc.obj map.obj map.res >> map.rsp echo midend.obj misc.obj printing.obj random.obj >> map.rsp echo version.obj windows.obj >> map.rsp mines.rsp: $(MAKEFILE) echo $(BASELIBS) > mines.rsp echo drawing.obj malloc.obj midend.obj mines.obj >> mines.rsp echo mines.res misc.obj printing.obj random.obj >> mines.rsp echo tree234.obj version.obj windows.obj >> mines.rsp netgame.rsp: $(MAKEFILE) echo $(BASELIBS) > netgame.rsp echo drawing.obj dsf.obj malloc.obj midend.obj >> netgame.rsp echo misc.obj net.obj net.res printing.obj random.obj >> netgame.rsp echo tree234.obj version.obj windows.obj >> netgame.rsp netslide.rsp: $(MAKEFILE) echo $(BASELIBS) > netslide.rsp echo drawing.obj malloc.obj midend.obj misc.obj >> netslide.rsp echo netslide.obj netslide.res printing.obj random.obj >> netslide.rsp echo tree234.obj version.obj windows.obj >> netslide.rsp nullgame.rsp: $(MAKEFILE) echo $(BASELIBS) > nullgame.rsp echo drawing.obj malloc.obj midend.obj misc.obj >> nullgame.rsp echo noicon.res nullgame.obj printing.obj random.obj >> nullgame.rsp echo version.obj windows.obj >> nullgame.rsp pattern.rsp: $(MAKEFILE) echo $(BASELIBS) > pattern.rsp echo drawing.obj malloc.obj midend.obj misc.obj >> pattern.rsp echo pattern.obj pattern.res printing.obj random.obj >> pattern.rsp echo version.obj windows.obj >> pattern.rsp pearl.rsp: $(MAKEFILE) echo $(BASELIBS) > pearl.rsp echo drawing.obj dsf.obj grid.obj loopgen.obj >> pearl.rsp echo malloc.obj midend.obj misc.obj pearl.obj >> pearl.rsp echo pearl.res penrose.obj printing.obj random.obj >> pearl.rsp echo tdq.obj tree234.obj version.obj windows.obj >> pearl.rsp pegs.rsp: $(MAKEFILE) echo $(BASELIBS) > pegs.rsp echo drawing.obj malloc.obj midend.obj misc.obj >> pegs.rsp echo pegs.obj pegs.res printing.obj random.obj >> pegs.rsp echo tree234.obj version.obj windows.obj >> pegs.rsp puzzles.rsp: $(MAKEFILE) echo $(BASELIBS) > puzzles.rsp echo blackbo3.obj bridges3.obj combi.obj cube3.obj >> puzzles.rsp echo divvy.obj dominos3.obj drawing.obj dsf.obj >> puzzles.rsp echo fifteen3.obj filling5.obj flip3.obj galaxie7.obj >> puzzles.rsp echo grid.obj guess3.obj inertia3.obj keen5.obj >> puzzles.rsp echo latin.obj laydomino.obj lightup5.obj list.obj >> puzzles.rsp echo loopgen.obj loopy5.obj magnets5.obj malloc.obj >> puzzles.rsp echo map5.obj maxflow.obj midend.obj mines5.obj >> puzzles.rsp echo misc.obj net3.obj netslid3.obj noicon.res >> puzzles.rsp echo pattern5.obj pearl5.obj pegs3.obj penrose.obj >> puzzles.rsp echo printing.obj random.obj range3.obj rect3.obj >> puzzles.rsp echo samegam3.obj signpos5.obj singles5.obj >> puzzles.rsp echo sixteen3.obj slant5.obj solo5.obj tdq.obj >> puzzles.rsp echo tents5.obj towers5.obj tree234.obj twiddle3.obj >> puzzles.rsp echo undead3.obj unequal5.obj unruly5.obj untangl3.obj >> puzzles.rsp echo version.obj windows1.obj >> puzzles.rsp range.rsp: $(MAKEFILE) echo $(BASELIBS) > range.rsp echo drawing.obj malloc.obj midend.obj misc.obj >> range.rsp echo printing.obj random.obj range.obj range.res >> range.rsp echo version.obj windows.obj >> range.rsp rect.rsp: $(MAKEFILE) echo $(BASELIBS) > rect.rsp echo drawing.obj malloc.obj midend.obj misc.obj >> rect.rsp echo printing.obj random.obj rect.obj rect.res >> rect.rsp echo version.obj windows.obj >> rect.rsp samegame.rsp: $(MAKEFILE) echo $(BASELIBS) > samegame.rsp echo drawing.obj malloc.obj midend.obj misc.obj >> samegame.rsp echo printing.obj random.obj samegame.obj samegame.res >> samegame.rsp echo version.obj windows.obj >> samegame.rsp signpost.rsp: $(MAKEFILE) echo $(BASELIBS) > signpost.rsp echo drawing.obj dsf.obj malloc.obj midend.obj >> signpost.rsp echo misc.obj printing.obj random.obj signpost.obj >> signpost.rsp echo signpost.res version.obj windows.obj >> signpost.rsp singles.rsp: $(MAKEFILE) echo $(BASELIBS) > singles.rsp echo drawing.obj dsf.obj latin.obj malloc.obj >> singles.rsp echo maxflow.obj midend.obj misc.obj printing.obj >> singles.rsp echo random.obj singles.obj singles.res tree234.obj >> singles.rsp echo version.obj windows.obj >> singles.rsp sixteen.rsp: $(MAKEFILE) echo $(BASELIBS) > sixteen.rsp echo drawing.obj malloc.obj midend.obj misc.obj >> sixteen.rsp echo printing.obj random.obj sixteen.obj sixteen.res >> sixteen.rsp echo version.obj windows.obj >> sixteen.rsp slant.rsp: $(MAKEFILE) echo $(BASELIBS) > slant.rsp echo drawing.obj dsf.obj malloc.obj midend.obj >> slant.rsp echo misc.obj printing.obj random.obj slant.obj >> slant.rsp echo slant.res version.obj windows.obj >> slant.rsp solo.rsp: $(MAKEFILE) echo $(BASELIBS) > solo.rsp echo divvy.obj drawing.obj dsf.obj malloc.obj >> solo.rsp echo midend.obj misc.obj printing.obj random.obj >> solo.rsp echo solo.obj solo.res version.obj windows.obj >> solo.rsp tents.rsp: $(MAKEFILE) echo $(BASELIBS) > tents.rsp echo drawing.obj dsf.obj malloc.obj maxflow.obj >> tents.rsp echo midend.obj misc.obj printing.obj random.obj >> tents.rsp echo tents.obj tents.res version.obj windows.obj >> tents.rsp towers.rsp: $(MAKEFILE) echo $(BASELIBS) > towers.rsp echo drawing.obj latin.obj malloc.obj maxflow.obj >> towers.rsp echo midend.obj misc.obj printing.obj random.obj >> towers.rsp echo towers.obj towers.res tree234.obj version.obj >> towers.rsp echo windows.obj >> towers.rsp twiddle.rsp: $(MAKEFILE) echo $(BASELIBS) > twiddle.rsp echo drawing.obj malloc.obj midend.obj misc.obj >> twiddle.rsp echo printing.obj random.obj twiddle.obj twiddle.res >> twiddle.rsp echo version.obj windows.obj >> twiddle.rsp undead.rsp: $(MAKEFILE) echo $(BASELIBS) > undead.rsp echo drawing.obj malloc.obj midend.obj misc.obj >> undead.rsp echo printing.obj random.obj undead.obj undead.res >> undead.rsp echo version.obj windows.obj >> undead.rsp unequal.rsp: $(MAKEFILE) echo $(BASELIBS) > unequal.rsp echo drawing.obj latin.obj malloc.obj maxflow.obj >> unequal.rsp echo midend.obj misc.obj printing.obj random.obj >> unequal.rsp echo tree234.obj unequal.obj unequal.res version.obj >> unequal.rsp echo windows.obj >> unequal.rsp unruly.rsp: $(MAKEFILE) echo $(BASELIBS) > unruly.rsp echo drawing.obj malloc.obj midend.obj misc.obj >> unruly.rsp echo printing.obj random.obj unruly.obj unruly.res >> unruly.rsp echo version.obj windows.obj >> unruly.rsp untangle.rsp: $(MAKEFILE) echo $(BASELIBS) > untangle.rsp echo drawing.obj malloc.obj midend.obj misc.obj >> untangle.rsp echo printing.obj random.obj tree234.obj untangle.obj >> untangle.rsp echo untangle.res version.obj windows.obj >> untangle.rsp blackbox.obj: .\blackbox.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\blackbox.c /Foblackbox.obj blackbox-icon.obj: icons\blackbox-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\blackbox-icon.c /Foblackbox-icon.obj blackbox.res: icons\blackbox.rc .\puzzles.rc2 icons\blackbox.ico \ .\resource.h rc $(FWHACK) $(RCFL) -r -foblackbox.res icons\blackbox.rc blackbo3.obj: .\blackbox.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\blackbox.c /Foblackbo3.obj bridges.obj: .\bridges.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\bridges.c /Fobridges.obj bridges-icon.obj: icons\bridges-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\bridges-icon.c /Fobridges-icon.obj bridges.res: icons\bridges.rc .\puzzles.rc2 icons\bridges.ico .\resource.h rc $(FWHACK) $(RCFL) -r -fobridges.res icons\bridges.rc bridges3.obj: .\bridges.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\bridges.c /Fobridges3.obj combi.obj: .\combi.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\combi.c /Focombi.obj cube.obj: .\cube.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\cube.c /Focube.obj cube-icon.obj: icons\cube-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\cube-icon.c /Focube-icon.obj cube.res: icons\cube.rc .\puzzles.rc2 icons\cube.ico .\resource.h rc $(FWHACK) $(RCFL) -r -focube.res icons\cube.rc cube3.obj: .\cube.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\cube.c /Focube3.obj divvy.obj: .\divvy.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\divvy.c /Fodivvy.obj dominosa.obj: .\dominosa.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\dominosa.c /Fodominosa.obj dominosa-icon.obj: icons\dominosa-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\dominosa-icon.c /Fodominosa-icon.obj dominosa.res: icons\dominosa.rc .\puzzles.rc2 icons\dominosa.ico \ .\resource.h rc $(FWHACK) $(RCFL) -r -fodominosa.res icons\dominosa.rc dominos3.obj: .\dominosa.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\dominosa.c /Fodominos3.obj drawing.obj: .\drawing.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\drawing.c /Fodrawing.obj dsf.obj: .\dsf.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\dsf.c /Fodsf.obj fifteen.obj: .\fifteen.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\fifteen.c /Fofifteen.obj fifteen-icon.obj: icons\fifteen-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\fifteen-icon.c /Fofifteen-icon.obj fifteen.res: icons\fifteen.rc .\puzzles.rc2 icons\fifteen.ico .\resource.h rc $(FWHACK) $(RCFL) -r -fofifteen.res icons\fifteen.rc fifteen3.obj: .\fifteen.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\fifteen.c /Fofifteen3.obj filling.obj: .\filling.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\filling.c /Fofilling.obj filling-icon.obj: icons\filling-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\filling-icon.c /Fofilling-icon.obj filling.res: icons\filling.rc .\puzzles.rc2 icons\filling.ico .\resource.h rc $(FWHACK) $(RCFL) -r -fofilling.res icons\filling.rc filling5.obj: .\filling.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\filling.c /Fofilling5.obj filling2.obj: .\filling.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\filling.c /Fofilling2.obj flip.obj: .\flip.c .\puzzles.h .\tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\flip.c /Foflip.obj flip-icon.obj: icons\flip-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\flip-icon.c /Foflip-icon.obj flip.res: icons\flip.rc .\puzzles.rc2 icons\flip.ico .\resource.h rc $(FWHACK) $(RCFL) -r -foflip.res icons\flip.rc flip3.obj: .\flip.c .\puzzles.h .\tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\flip.c /Foflip3.obj galaxies.obj: .\galaxies.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\galaxies.c /Fogalaxies.obj galaxies-icon.obj: icons\galaxies-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\galaxies-icon.c /Fogalaxies-icon.obj galaxies.res: icons\galaxies.rc .\puzzles.rc2 icons\galaxies.ico \ .\resource.h rc $(FWHACK) $(RCFL) -r -fogalaxies.res icons\galaxies.rc galaxie7.obj: .\galaxies.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\galaxies.c /Fogalaxie7.obj galaxie4.obj: .\galaxies.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_PICTURE_GENERATOR /c .\galaxies.c /Fogalaxie4.obj galaxie2.obj: .\galaxies.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\galaxies.c /Fogalaxie2.obj grid.obj: .\grid.c .\puzzles.h .\tree234.h .\grid.h .\penrose.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\grid.c /Fogrid.obj gtk.obj: .\gtk.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\gtk.c /Fogtk.obj guess.obj: .\guess.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\guess.c /Foguess.obj guess-icon.obj: icons\guess-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\guess-icon.c /Foguess-icon.obj guess.res: icons\guess.rc .\puzzles.rc2 icons\guess.ico .\resource.h rc $(FWHACK) $(RCFL) -r -foguess.res icons\guess.rc guess3.obj: .\guess.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\guess.c /Foguess3.obj inertia.obj: .\inertia.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\inertia.c /Foinertia.obj inertia-icon.obj: icons\inertia-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\inertia-icon.c /Foinertia-icon.obj inertia.res: icons\inertia.rc .\puzzles.rc2 icons\inertia.ico .\resource.h rc $(FWHACK) $(RCFL) -r -foinertia.res icons\inertia.rc inertia3.obj: .\inertia.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\inertia.c /Foinertia3.obj keen.obj: .\keen.c .\puzzles.h .\latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\keen.c /Fokeen.obj keen-icon.obj: icons\keen-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\keen-icon.c /Fokeen-icon.obj keen.res: icons\keen.rc .\puzzles.rc2 icons\keen.ico .\resource.h rc $(FWHACK) $(RCFL) -r -fokeen.res icons\keen.rc keen5.obj: .\keen.c .\puzzles.h .\latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\keen.c /Fokeen5.obj keen2.obj: .\keen.c .\puzzles.h .\latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\keen.c /Fokeen2.obj latin.obj: .\latin.c .\puzzles.h .\tree234.h .\maxflow.h .\latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\latin.c /Folatin.obj latin8.obj: .\latin.c .\puzzles.h .\tree234.h .\maxflow.h .\latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_LATIN_TEST /c .\latin.c /Folatin8.obj latin6.obj: .\latin.c .\puzzles.h .\tree234.h .\maxflow.h .\latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\latin.c /Folatin6.obj laydomino.obj: .\laydomino.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\laydomino.c /Folaydomino.obj lightup.obj: .\lightup.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\lightup.c /Folightup.obj lightup-icon.obj: icons\lightup-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\lightup-icon.c /Folightup-icon.obj lightup.res: icons\lightup.rc .\puzzles.rc2 icons\lightup.ico .\resource.h rc $(FWHACK) $(RCFL) -r -folightup.res icons\lightup.rc lightup5.obj: .\lightup.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\lightup.c /Folightup5.obj lightup2.obj: .\lightup.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\lightup.c /Folightup2.obj list.obj: .\list.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\list.c /Folist.obj loopgen.obj: .\loopgen.c .\puzzles.h .\tree234.h .\grid.h .\loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\loopgen.c /Foloopgen.obj loopy.obj: .\loopy.c .\puzzles.h .\tree234.h .\grid.h .\loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\loopy.c /Foloopy.obj loopy-icon.obj: icons\loopy-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\loopy-icon.c /Foloopy-icon.obj loopy.res: icons\loopy.rc .\puzzles.rc2 icons\loopy.ico .\resource.h rc $(FWHACK) $(RCFL) -r -foloopy.res icons\loopy.rc loopy5.obj: .\loopy.c .\puzzles.h .\tree234.h .\grid.h .\loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\loopy.c /Foloopy5.obj loopy2.obj: .\loopy.c .\puzzles.h .\tree234.h .\grid.h .\loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\loopy.c /Foloopy2.obj magnets.obj: .\magnets.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\magnets.c /Fomagnets.obj magnets-icon.obj: icons\magnets-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\magnets-icon.c /Fomagnets-icon.obj magnets.res: icons\magnets.rc .\puzzles.rc2 icons\magnets.ico .\resource.h rc $(FWHACK) $(RCFL) -r -fomagnets.res icons\magnets.rc magnets5.obj: .\magnets.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\magnets.c /Fomagnets5.obj magnets2.obj: .\magnets.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\magnets.c /Fomagnets2.obj malloc.obj: .\malloc.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\malloc.c /Fomalloc.obj map.obj: .\map.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\map.c /Fomap.obj map-icon.obj: icons\map-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\map-icon.c /Fomap-icon.obj map.res: icons\map.rc .\puzzles.rc2 icons\map.ico .\resource.h rc $(FWHACK) $(RCFL) -r -fomap.res icons\map.rc map5.obj: .\map.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\map.c /Fomap5.obj map2.obj: .\map.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\map.c /Fomap2.obj maxflow.obj: .\maxflow.c .\maxflow.h .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\maxflow.c /Fomaxflow.obj midend.obj: .\midend.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\midend.c /Fomidend.obj mines.obj: .\mines.c .\tree234.h .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\mines.c /Fomines.obj mines-icon.obj: icons\mines-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\mines-icon.c /Fomines-icon.obj mines.res: icons\mines.rc .\puzzles.rc2 icons\mines.ico .\resource.h rc $(FWHACK) $(RCFL) -r -fomines.res icons\mines.rc mines5.obj: .\mines.c .\tree234.h .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\mines.c /Fomines5.obj mines2.obj: .\mines.c .\tree234.h .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_OBFUSCATOR /c .\mines.c /Fomines2.obj misc.obj: .\misc.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\misc.c /Fomisc.obj net.obj: .\net.c .\puzzles.h .\tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\net.c /Fonet.obj net-icon.obj: icons\net-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\net-icon.c /Fonet-icon.obj net.res: icons\net.rc .\puzzles.rc2 icons\net.ico .\resource.h rc $(FWHACK) $(RCFL) -r -fonet.res icons\net.rc net3.obj: .\net.c .\puzzles.h .\tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\net.c /Fonet3.obj netslide.obj: .\netslide.c .\puzzles.h .\tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\netslide.c /Fonetslide.obj netslide-icon.obj: icons\netslide-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\netslide-icon.c /Fonetslide-icon.obj netslide.res: icons\netslide.rc .\puzzles.rc2 icons\netslide.ico \ .\resource.h rc $(FWHACK) $(RCFL) -r -fonetslide.res icons\netslide.rc netslid3.obj: .\netslide.c .\puzzles.h .\tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\netslide.c /Fonetslid3.obj no-icon.obj: .\no-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\no-icon.c /Fono-icon.obj noicon.res: .\noicon.rc .\puzzles.rc2 .\resource.h rc $(FWHACK) $(RCFL) -r -fonoicon.res .\noicon.rc nullfe.obj: .\nullfe.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\nullfe.c /Fonullfe.obj nullgame.obj: .\nullgame.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\nullgame.c /Fonullgame.obj obfusc.obj: .\obfusc.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\obfusc.c /Foobfusc.obj osx.obj: .\osx.m .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\osx.m /Foosx.obj pattern.obj: .\pattern.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\pattern.c /Fopattern.obj pattern-icon.obj: icons\pattern-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\pattern-icon.c /Fopattern-icon.obj pattern.res: icons\pattern.rc .\puzzles.rc2 icons\pattern.ico .\resource.h rc $(FWHACK) $(RCFL) -r -fopattern.res icons\pattern.rc pattern5.obj: .\pattern.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\pattern.c /Fopattern5.obj pattern2.obj: .\pattern.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\pattern.c /Fopattern2.obj pearl.obj: .\pearl.c .\puzzles.h .\grid.h .\loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\pearl.c /Fopearl.obj pearl-icon.obj: icons\pearl-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\pearl-icon.c /Fopearl-icon.obj pearl.res: icons\pearl.rc .\puzzles.rc2 icons\pearl.ico .\resource.h rc $(FWHACK) $(RCFL) -r -fopearl.res icons\pearl.rc pearl5.obj: .\pearl.c .\puzzles.h .\grid.h .\loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\pearl.c /Fopearl5.obj pearl2.obj: .\pearl.c .\puzzles.h .\grid.h .\loopgen.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\pearl.c /Fopearl2.obj pegs.obj: .\pegs.c .\puzzles.h .\tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\pegs.c /Fopegs.obj pegs-icon.obj: icons\pegs-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\pegs-icon.c /Fopegs-icon.obj pegs.res: icons\pegs.rc .\puzzles.rc2 icons\pegs.ico .\resource.h rc $(FWHACK) $(RCFL) -r -fopegs.res icons\pegs.rc pegs3.obj: .\pegs.c .\puzzles.h .\tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\pegs.c /Fopegs3.obj penrose.obj: .\penrose.c .\puzzles.h .\penrose.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\penrose.c /Fopenrose.obj printing.obj: .\printing.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\printing.c /Foprinting.obj ps.obj: .\ps.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\ps.c /Fops.obj random.obj: .\random.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\random.c /Forandom.obj range.obj: .\range.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\range.c /Forange.obj range-icon.obj: icons\range-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\range-icon.c /Forange-icon.obj range.res: icons\range.rc .\puzzles.rc2 icons\range.ico .\resource.h rc $(FWHACK) $(RCFL) -r -forange.res icons\range.rc range3.obj: .\range.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\range.c /Forange3.obj rect.obj: .\rect.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\rect.c /Forect.obj rect-icon.obj: icons\rect-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\rect-icon.c /Forect-icon.obj rect.res: icons\rect.rc .\puzzles.rc2 icons\rect.ico .\resource.h rc $(FWHACK) $(RCFL) -r -forect.res icons\rect.rc rect3.obj: .\rect.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\rect.c /Forect3.obj samegame.obj: .\samegame.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\samegame.c /Fosamegame.obj samegame-icon.obj: icons\samegame-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\samegame-icon.c /Fosamegame-icon.obj samegame.res: icons\samegame.rc .\puzzles.rc2 icons\samegame.ico \ .\resource.h rc $(FWHACK) $(RCFL) -r -fosamegame.res icons\samegame.rc samegam3.obj: .\samegame.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\samegame.c /Fosamegam3.obj signpost.obj: .\signpost.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\signpost.c /Fosignpost.obj signpost-icon.obj: icons\signpost-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\signpost-icon.c /Fosignpost-icon.obj signpost.res: icons\signpost.rc .\puzzles.rc2 icons\signpost.ico \ .\resource.h rc $(FWHACK) $(RCFL) -r -fosignpost.res icons\signpost.rc signpos5.obj: .\signpost.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\signpost.c /Fosignpos5.obj signpos2.obj: .\signpost.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\signpost.c /Fosignpos2.obj singles.obj: .\singles.c .\puzzles.h .\latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\singles.c /Fosingles.obj singles-icon.obj: icons\singles-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\singles-icon.c /Fosingles-icon.obj singles.res: icons\singles.rc .\puzzles.rc2 icons\singles.ico .\resource.h rc $(FWHACK) $(RCFL) -r -fosingles.res icons\singles.rc singles5.obj: .\singles.c .\puzzles.h .\latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\singles.c /Fosingles5.obj singles3.obj: .\singles.c .\puzzles.h .\latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\singles.c /Fosingles3.obj sixteen.obj: .\sixteen.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\sixteen.c /Fosixteen.obj sixteen-icon.obj: icons\sixteen-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\sixteen-icon.c /Fosixteen-icon.obj sixteen.res: icons\sixteen.rc .\puzzles.rc2 icons\sixteen.ico .\resource.h rc $(FWHACK) $(RCFL) -r -fosixteen.res icons\sixteen.rc sixteen3.obj: .\sixteen.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\sixteen.c /Fosixteen3.obj slant.obj: .\slant.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\slant.c /Foslant.obj slant-icon.obj: icons\slant-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\slant-icon.c /Foslant-icon.obj slant.res: icons\slant.rc .\puzzles.rc2 icons\slant.ico .\resource.h rc $(FWHACK) $(RCFL) -r -foslant.res icons\slant.rc slant5.obj: .\slant.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\slant.c /Foslant5.obj slant2.obj: .\slant.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\slant.c /Foslant2.obj solo.obj: .\solo.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\solo.c /Fosolo.obj solo-icon.obj: icons\solo-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\solo-icon.c /Fosolo-icon.obj solo.res: icons\solo.rc .\puzzles.rc2 icons\solo.ico .\resource.h rc $(FWHACK) $(RCFL) -r -fosolo.res icons\solo.rc solo5.obj: .\solo.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\solo.c /Fosolo5.obj solo2.obj: .\solo.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\solo.c /Fosolo2.obj tdq.obj: .\tdq.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\tdq.c /Fotdq.obj tents.obj: .\tents.c .\puzzles.h .\maxflow.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\tents.c /Fotents.obj tents-icon.obj: icons\tents-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\tents-icon.c /Fotents-icon.obj tents.res: icons\tents.rc .\puzzles.rc2 icons\tents.ico .\resource.h rc $(FWHACK) $(RCFL) -r -fotents.res icons\tents.rc tents5.obj: .\tents.c .\puzzles.h .\maxflow.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\tents.c /Fotents5.obj tents3.obj: .\tents.c .\puzzles.h .\maxflow.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\tents.c /Fotents3.obj towers.obj: .\towers.c .\puzzles.h .\latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\towers.c /Fotowers.obj towers-icon.obj: icons\towers-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\towers-icon.c /Fotowers-icon.obj towers.res: icons\towers.rc .\puzzles.rc2 icons\towers.ico .\resource.h rc $(FWHACK) $(RCFL) -r -fotowers.res icons\towers.rc towers5.obj: .\towers.c .\puzzles.h .\latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\towers.c /Fotowers5.obj towers2.obj: .\towers.c .\puzzles.h .\latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\towers.c /Fotowers2.obj tree234.obj: .\tree234.c .\tree234.h .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\tree234.c /Fotree234.obj twiddle.obj: .\twiddle.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\twiddle.c /Fotwiddle.obj twiddle-icon.obj: icons\twiddle-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\twiddle-icon.c /Fotwiddle-icon.obj twiddle.res: icons\twiddle.rc .\puzzles.rc2 icons\twiddle.ico .\resource.h rc $(FWHACK) $(RCFL) -r -fotwiddle.res icons\twiddle.rc twiddle3.obj: .\twiddle.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\twiddle.c /Fotwiddle3.obj undead.obj: .\undead.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\undead.c /Foundead.obj undead-icon.obj: icons\undead-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\undead-icon.c /Foundead-icon.obj undead.res: icons\undead.rc .\puzzles.rc2 icons\undead.ico .\resource.h rc $(FWHACK) $(RCFL) -r -foundead.res icons\undead.rc undead3.obj: .\undead.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\undead.c /Foundead3.obj unequal.obj: .\unequal.c .\puzzles.h .\latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\unequal.c /Founequal.obj unequal-icon.obj: icons\unequal-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\unequal-icon.c /Founequal-icon.obj unequal.res: icons\unequal.rc .\puzzles.rc2 icons\unequal.ico .\resource.h rc $(FWHACK) $(RCFL) -r -founequal.res icons\unequal.rc unequal5.obj: .\unequal.c .\puzzles.h .\latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\unequal.c /Founequal5.obj unequal2.obj: .\unequal.c .\puzzles.h .\latin.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\unequal.c /Founequal2.obj unruly.obj: .\unruly.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\unruly.c /Founruly.obj unruly-icon.obj: icons\unruly-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\unruly-icon.c /Founruly-icon.obj unruly.res: icons\unruly.rc .\puzzles.rc2 icons\unruly.ico .\resource.h rc $(FWHACK) $(RCFL) -r -founruly.res icons\unruly.rc unruly5.obj: .\unruly.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\unruly.c /Founruly5.obj unruly2.obj: .\unruly.c .\puzzles.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DSTANDALONE_SOLVER /c .\unruly.c /Founruly2.obj untangle.obj: .\untangle.c .\puzzles.h .\tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\untangle.c /Fountangle.obj untangle-icon.obj: icons\untangle-icon.c $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c icons\untangle-icon.c /Fountangle-icon.obj untangle.res: icons\untangle.rc .\puzzles.rc2 icons\untangle.ico \ .\resource.h rc $(FWHACK) $(RCFL) -r -fountangle.res icons\untangle.rc untangl3.obj: .\untangle.c .\puzzles.h .\tree234.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\untangle.c /Fountangl3.obj windows.obj: .\windows.c .\puzzles.h .\resource.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /c .\windows.c /Fowindows.obj windows1.obj: .\windows.c .\puzzles.h .\resource.h $(CC) $(COMPAT) $(FWHACK) $(CFLAGS) $(XFLAGS) /DCOMBINED /c .\windows.c /Fowindows1.obj version.obj: *.c *.h $(CC) $(VER) $(CFLAGS) /c version.c clean: tidy -del *.exe tidy: -del *.obj -del *.res -del *.pch -del *.aps -del *.ilk -del *.pdb -del *.rsp -del *.dsp -del *.dsw -del *.ncb -del *.opt -del *.plg -del *.map -del *.idb -del debug.log puzzles-r9872/HACKING0000644000175300017530000060476712161170561013440 0ustar simonsimonDeveloper documentation for Simon Tatham's puzzle collection ============================================================ This is a guide to the internal structure of Simon Tatham's Portable Puzzle Collection (henceforth referred to simply as `Puzzles'), for use by anyone attempting to implement a new puzzle or port to a new platform. This guide is believed correct as of r6190. Hopefully it will be updated along with the code in future, but if not, I've at least left this version number in here so you can figure out what's changed by tracking commit comments from there onwards. 1. Introduction --------------- The Puzzles code base is divided into four parts: a set of interchangeable front ends, a set of interchangeable back ends, a universal `middle end' which acts as a buffer between the two, and a bunch of miscellaneous utility functions. In the following sections I give some general discussion of each of these parts. 1.1. Front end -------------- The front end is the non-portable part of the code: it's the bit that you replace completely when you port to a different platform. So it's responsible for all system calls, all GUI interaction, and anything else platform-specific. The current front ends in the main code base are for Windows, GTK and MacOS X; I also know of a third-party front end for PalmOS. The front end contains main() or the local platform's equivalent. Top- level control over the application's execution flow belongs to the front end (it isn't, for example, a set of functions called by a universal main() somewhere else). The front end has complete freedom to design the GUI for any given port of Puzzles. There is no centralised mechanism for maintaining the menu layout, for example. This has a cost in consistency (when I _do_ want the same menu layout on more than one platform, I have to edit two pieces of code in parallel every time I make a change), but the advantage is that local GUI conventions can be conformed to and local constraints adapted to. For example, MacOS X has strict human interface guidelines which specify a different menu layout from the one I've used on Windows and GTK; there's nothing stopping the OS X front end from providing a menu layout consistent with those guidelines. Although the front end is mostly caller rather than the callee in its interactions with other parts of the code, it is required to implement a small API for other modules to call, mostly of drawing functions for games to use when drawing their graphics. The drawing API is documented in chapter 3; the other miscellaneous front end API functions are documented in section 4.35. 1.2. Back end ------------- A `back end', in this collection, is synonymous with a `puzzle'. Each back end implements a different game. At the top level, a back end is simply a data structure, containing a few constants (flag words, preferred pixel size) and a large number of function pointers. Back ends are almost invariably callee rather than caller, which means there's a limitation on what a back end can do on its own initiative. The persistent state in a back end is divided into a number of data structures, which are used for different purposes and therefore likely to be switched around, changed without notice, and otherwise updated by the rest of the code. It is important when designing a back end to put the right pieces of data into the right structures, or standard midend- provided features (such as Undo) may fail to work. The functions and variables provided in the back end data structure are documented in chapter 2. 1.3. Middle end --------------- Puzzles has a single and universal `middle end'. This code is common to all platforms and all games; it sits in between the front end and the back end and provides standard functionality everywhere. People adding new back ends or new front ends should generally not need to edit the middle end. On rare occasions there might be a change that can be made to the middle end to permit a new game to do something not currently anticipated by the middle end's present design; however, this is terribly easy to get wrong and should probably not be undertaken without consulting the primary maintainer (me). Patch submissions containing unannounced mid-end changes will be treated on their merits like any other patch; this is just a friendly warning that mid-end changes will need quite a lot of merits to make them acceptable. Functionality provided by the mid-end includes: - Maintaining a list of game state structures and moving back and forth along that list to provide Undo and Redo. - Handling timers (for move animations, flashes on completion, and in some cases actually timing the game). - Handling the container format of game IDs: receiving them, picking them apart into parameters, description and/or random seed, and so on. The game back end need only handle the individual parts of a game ID (encoded parameters and encoded game description); everything else is handled centrally by the mid-end. - Handling standard keystrokes and menu commands, such as `New Game', `Restart Game' and `Quit'. - Pre-processing mouse events so that the game back ends can rely on them arriving in a sensible order (no missing button-release events, no sudden changes of which button is currently pressed, etc). - Handling the dialog boxes which ask the user for a game ID. - Handling serialisation of entire games (for loading and saving a half-finished game to a disk file, or for handling application shutdown and restart on platforms such as PalmOS where state is expected to be saved). Thus, there's a lot of work done once by the mid-end so that individual back ends don't have to worry about it. All the back end has to do is cooperate in ensuring the mid-end can do its work properly. The API of functions provided by the mid-end to be called by the front end is documented in chapter 4. 1.4. Miscellaneous utilities ---------------------------- In addition to these three major structural components, the Puzzles code also contains a variety of utility modules usable by all of the above components. There is a set of functions to provide platform-independent random number generation; functions to make memory allocation easier; functions which implement a balanced tree structure to be used as necessary in complex algorithms; and a few other miscellaneous functions. All of these are documented in chapter 5. 1.5. Structure of this guide ---------------------------- There are a number of function call interfaces within Puzzles, and this guide will discuss each one in a chapter of its own. After that, chapter 6 discusses how to design new games, with some general design thoughts and tips. 2. Interface to the back end ---------------------------- This chapter gives a detailed discussion of the interface that each back end must implement. At the top level, each back end source file exports a single global symbol, which is a `const struct game' containing a large number of function pointers and a small amount of constant data. This structure is called by different names depending on what kind of platform the puzzle set is being compiled on: - On platforms such as Windows and GTK, which build a separate binary for each puzzle, the game structure in every back end has the same name, `thegame'; the front end refers directly to this name, so that compiling the same front end module against a different back end module builds a different puzzle. - On platforms such as MacOS X and PalmOS, which build all the puzzles into a single monolithic binary, the game structure in each back end must have a different name, and there's a helper module `list.c' (constructed automatically by the same Perl script that builds the Makefiles) which contains a complete list of those game structures. On the latter type of platform, source files may assume that the preprocessor symbol `COMBINED' has been defined. Thus, the usual code to declare the game structure looks something like this: #ifdef COMBINED #define thegame net /* or whatever this game is called */ #endif const struct game thegame = { /* lots of structure initialisation in here */ }; Game back ends must also internally define a number of data structures, for storing their various persistent state. This chapter will first discuss the nature and use of those structures, and then go on to give details of every element of the game structure. 2.1. Data structures -------------------- Each game is required to define four separate data structures. This section discusses each one and suggests what sorts of things need to be put in it. 2.1.1. `game_params' -------------------- The `game_params' structure contains anything which affects the automatic generation of new puzzles. So if puzzle generation is parametrised in any way, those parameters need to be stored in `game_params'. Most puzzles currently in this collection are played on a grid of squares, meaning that the most obvious parameter is the grid size. Many puzzles have additional parameters; for example, Mines allows you to control the number of mines in the grid independently of its size, Net can be wrapping or non-wrapping, Solo has difficulty levels and symmetry settings, and so on. A simple rule for deciding whether a data item needs to go in `game_params' is: would the user expect to be able to control this data item from either the preset-game-types menu or the `Custom' game type configuration? If so, it's part of `game_params'. `game_params' structures are permitted to contain pointers to subsidiary data if they need to. The back end is required to provide functions to create and destroy `game_params', and those functions can allocate and free additional memory if necessary. (It has not yet been necessary to do this in any puzzle so far, but the capability is there just in case.) `game_params' is also the only structure which the game's compute_size() function may refer to; this means that any aspect of the game which affects the size of the window it needs to be drawn in must be stored in `game_params'. In particular, this imposes the fundamental limitation that random game generation may not have a random effect on the window size: game generation algorithms are constrained to work by starting from the grid size rather than generating it as an emergent phenomenon. (Although this is a restriction in theory, it has not yet seemed to be a problem.) 2.1.2. `game_state' ------------------- While the user is actually playing a puzzle, the `game_state' structure stores all the data corresponding to the current state of play. The mid-end keeps `game_state's in a list, and adds to the list every time the player makes a move; the Undo and Redo functions step back and forth through that list. Therefore, a good means of deciding whether a data item needs to go in `game_state' is: would a player expect that data item to be restored on undo? If so, put it in `game_state', and this will automatically happen without you having to lift a finger. If not - for example, the deaths counter in Mines is precisely something that does _not_ want to be reset to its previous state on an undo - then you might have found a data item that needs to go in `game_ui' instead. During play, `game_state's are often passed around without an accompanying `game_params' structure. Therefore, any information in `game_params' which is important during play (such as the grid size) must be duplicated within the `game_state'. One simple method of doing this is to have the `game_state' structure _contain_ a `game_params' structure as one of its members, although this isn't obligatory if you prefer to do it another way. 2.1.3. `game_drawstate' ----------------------- `game_drawstate' carries persistent state relating to the current graphical contents of the puzzle window. The same `game_drawstate' is passed to every call to the game redraw function, so that it can remember what it has already drawn and what needs redrawing. A typical use for a `game_drawstate' is to have an array mirroring the array of grid squares in the `game_state'; then every time the redraw function was passed a `game_state', it would loop over all the squares, and physically redraw any whose description in the `game_state' (i.e. what the square needs to look like when the redraw is completed) did not match its description in the `game_drawstate' (i.e. what the square currently looks like). `game_drawstate' is occasionally completely torn down and reconstructed by the mid-end, if the user somehow forces a full redraw. Therefore, no data should be stored in `game_drawstate' which is _not_ related to the state of the puzzle window, because it might be unexpectedly destroyed. The back end provides functions to create and destroy `game_drawstate', which means it can contain pointers to subsidiary allocated data if it needs to. A common thing to want to allocate in a `game_drawstate' is a `blitter'; see section 3.1.13 for more on this subject. 2.1.4. `game_ui' ---------------- `game_ui' contains whatever doesn't fit into the above three structures! A new `game_ui' is created when the user begins playing a new instance of a puzzle (i.e. during `New Game' or after entering a game ID etc). It persists until the user finishes playing that game and begins another one (or closes the window); in particular, `Restart Game' does _not_ destroy the `game_ui'. `game_ui' is useful for implementing user-interface state which is not part of `game_state'. Common examples are keyboard control (you wouldn't want to have to separately Undo through every cursor motion) and mouse dragging. See section 6.3.2 and section 6.3.3, respectively, for more details. Another use for `game_ui' is to store highly persistent data such as the Mines death counter. This is conceptually rather different: where the Net cursor position was _not important enough_ to preserve for the player to restore by Undo, the Mines death counter is _too important_ to permit the player to revert by Undo! A final use for `game_ui' is to pass information to the redraw function about recent changes to the game state. This is used in Mines, for example, to indicate whether a requested `flash' should be a white flash for victory or a red flash for defeat; see section 6.3.5. 2.2. Simple data in the back end -------------------------------- In this section I begin to discuss each individual element in the back end structure. To begin with, here are some simple self-contained data elements. 2.2.1. `name' ------------- const char *name; This is a simple ASCII string giving the name of the puzzle. This name will be used in window titles, in game selection menus on monolithic platforms, and anywhere else that the front end needs to know the name of a game. 2.2.2. `winhelp_topic' ---------------------- const char *winhelp_topic; This member is used on Windows only, to provide online help. Although the Windows front end provides a separate binary for each puzzle, it has a single monolithic help file; so when a user selects `Help' from the menu, the program needs to open the help file and jump to the chapter describing that particular puzzle. Therefore, each chapter in `puzzles.but' is labelled with a _help topic_ name, similar to this: \cfg{winhelp-topic}{games.net} And then the corresponding game back end encodes the topic string (here `games.net') in the `winhelp_topic' element of the game structure. 2.3. Handling game parameter sets --------------------------------- In this section I present the various functions which handle the `game_params' structure. 2.3.1. default_params() ----------------------- game_params *(*default_params)(void); This function allocates a new `game_params' structure, fills it with the default values, and returns a pointer to it. 2.3.2. fetch_preset() --------------------- int (*fetch_preset)(int i, char **name, game_params **params); This function is used to populate the `Type' menu, which provides a list of conveniently accessible preset parameters for most games. The function is called with `i' equal to the index of the preset required (numbering from zero). It returns FALSE if that preset does not exist (if `i' is less than zero or greater than the largest preset index). Otherwise, it sets `*params' to point at a newly allocated `game_params' structure containing the preset information, sets `*name' to point at a newly allocated C string containing the preset title (to go on the `Type' menu), and returns TRUE. If the game does not wish to support any presets at all, this function is permitted to return FALSE always. 2.3.3. encode_params() ---------------------- char *(*encode_params)(const game_params *params, int full); The job of this function is to take a `game_params', and encode it in a string form for use in game IDs. The return value must be a newly allocated C string, and _must_ not contain a colon or a hash (since those characters are used to mark the end of the parameter section in a game ID). Ideally, it should also not contain any other potentially controversial punctuation; bear in mind when designing a string parameter format that it will probably be used on both Windows and Unix command lines under a variety of exciting shell quoting and metacharacter rules. Sticking entirely to alphanumerics is the safest thing; if you really need punctuation, you can probably get away with commas, periods or underscores without causing anybody any major inconvenience. If you venture far beyond that, you're likely to irritate _somebody_. (At the time of writing this, all existing games have purely alphanumeric string parameter formats. Usually these involve a letter denoting a parameter, followed optionally by a number giving the value of that parameter, with a few mandatory parts at the beginning such as numeric width and height separated by `x'.) If the `full' parameter is TRUE, this function should encode absolutely everything in the `game_params', such that a subsequent call to decode_params() (section 2.3.4) will yield an identical structure. If `full' is FALSE, however, you should leave out anything which is not necessary to describe a _specific puzzle instance_, i.e. anything which only takes effect when a new puzzle is _generated_. For example, the Solo `game_params' includes a difficulty rating used when constructing new puzzles; but a Solo game ID need not explicitly include the difficulty, since to describe a puzzle once generated it's sufficient to give the grid dimensions and the location and contents of the clue squares. (Indeed, one might very easily type in a puzzle out of a newspaper without _knowing_ what its difficulty level is in Solo's terminology.) Therefore, Solo's encode_params() only encodes the difficulty level if `full' is set. 2.3.4. decode_params() ---------------------- void (*decode_params)(game_params *params, char const *string); This function is the inverse of encode_params() (section 2.3.3). It parses the supplied string and fills in the supplied `game_params' structure. Note that the structure will _already_ have been allocated: this function is not expected to create a _new_ `game_params', but to modify an existing one. This function can receive a string which only encodes a subset of the parameters. The most obvious way in which this can happen is if the string was constructed by encode_params() with its `full' parameter set to FALSE; however, it could also happen if the user typed in a parameter set manually and missed something out. Be prepared to deal with a wide range of possibilities. When dealing with a parameter which is not specified in the input string, what to do requires a judgment call on the part of the programmer. Sometimes it makes sense to adjust other parameters to bring them into line with the new ones. In Mines, for example, you would probably not want to keep the same mine count if the user dropped the grid size and didn't specify one, since you might easily end up with more mines than would actually fit in the grid! On the other hand, sometimes it makes sense to leave the parameter alone: a Solo player might reasonably expect to be able to configure size and difficulty independently of one another. This function currently has no direct means of returning an error if the string cannot be parsed at all. However, the returned `game_params' is almost always subsequently passed to validate_params() (section 2.3.10), so if you really want to signal parse errors, you could always have a `char *' in your parameters structure which stored an error message, and have validate_params() return it if it is non-NULL. 2.3.5. free_params() -------------------- void (*free_params)(game_params *params); This function frees a `game_params' structure, and any subsidiary allocations contained within it. 2.3.6. dup_params() ------------------- game_params *(*dup_params)(const game_params *params); This function allocates a new `game_params' structure and initialises it with an exact copy of the information in the one provided as input. It returns a pointer to the new duplicate. 2.3.7. `can_configure' ---------------------- int can_configure; This boolean data element is set to TRUE if the back end supports custom parameter configuration via a dialog box. If it is TRUE, then the functions configure() and custom_params() are expected to work. See section 2.3.8 and section 2.3.9 for more details. 2.3.8. configure() ------------------ config_item *(*configure)(const game_params *params); This function is called when the user requests a dialog box for custom parameter configuration. It returns a newly allocated array of config_item structures, describing the GUI elements required in the dialog box. The array should have one more element than the number of controls, since it is terminated with a C_END marker (see below). Each array element describes the control together with its initial value; the front end will modify the value fields and return the updated array to custom_params() (see section 2.3.9). The config_item structure contains the following elements: char *name; int type; char *sval; int ival; `name' is an ASCII string giving the textual label for a GUI control. It is _not_ expected to be dynamically allocated. `type' contains one of a small number of `enum' values defining what type of control is being described. The meaning of the `sval' and `ival' fields depends on the value in `type'. The valid values are: `C_STRING' Describes a text input box. (This is also used for numeric input. The back end does not bother informing the front end that the box is numeric rather than textual; some front ends do have the capacity to take this into account, but I decided it wasn't worth the extra complexity in the interface.) For this type, `ival' is unused, and `sval' contains a dynamically allocated string representing the contents of the input box. `C_BOOLEAN' Describes a simple checkbox. For this type, `sval' is unused, and `ival' is TRUE or FALSE. `C_CHOICES' Describes a drop-down list presenting one of a small number of fixed choices. For this type, `sval' contains a list of strings describing the choices; the very first character of `sval' is used as a delimiter when processing the rest (so that the strings `:zero:one:two', `!zero!one!two' and `xzeroxonextwo' all define a three-element list containing `zero', `one' and `two'). `ival' contains the index of the currently selected element, numbering from zero (so that in the above example, 0 would mean `zero' and 2 would mean `two'). Note that for this control type, `sval' is _not_ dynamically allocated, whereas it was for `C_STRING'. `C_END' Marks the end of the array of `config_item's. All other fields are unused. The array returned from this function is expected to have filled in the initial values of all the controls according to the input `game_params' structure. If the game's `can_configure' flag is set to FALSE, this function is never called and need not do anything at all. 2.3.9. custom_params() ---------------------- game_params *(*custom_params)(const config_item *cfg); This function is the counterpart to configure() (section 2.3.8). It receives as input an array of `config_item's which was originally created by configure(), but in which the control values have since been changed in accordance with user input. Its function is to read the new values out of the controls and return a newly allocated `game_params' structure representing the user's chosen parameter set. (The front end will have modified the controls' _values_, but there will still always be the same set of controls, in the same order, as provided by configure(). It is not necessary to check the `name' and `type' fields, although you could use assert() if you were feeling energetic.) This function is not expected to (and indeed _must not_) free the input `config_item' array. (If the parameters fail to validate, the dialog box will stay open.) If the game's `can_configure' flag is set to FALSE, this function is never called and need not do anything at all. 2.3.10. validate_params() ------------------------- char *(*validate_params)(const game_params *params, int full); This function takes a `game_params' structure as input, and checks that the parameters described in it fall within sensible limits. (At the very least, grid dimensions should almost certainly be strictly positive, for example.) Return value is NULL if no problems were found, or alternatively a (non- dynamically-allocated) ASCII string describing the error in human- readable form. If the `full' parameter is set, full validation should be performed: any set of parameters which would not permit generation of a sensible puzzle should be faulted. If `full' is _not_ set, the implication is that these parameters are not going to be used for _generating_ a puzzle; so parameters which can't even sensibly _describe_ a valid puzzle should still be faulted, but parameters which only affect puzzle generation should not be. (The `full' option makes a difference when parameter combinations are non-orthogonal. For example, Net has a boolean option controlling whether it enforces a unique solution; it turns out that it's impossible to generate a uniquely soluble puzzle with wrapping walls and width 2, so validate_params() will complain if you ask for one. However, if the user had just been playing a unique wrapping puzzle of a more sensible width, and then pastes in a game ID acquired from somebody else which happens to describe a _non_-unique wrapping width-2 puzzle, then validate_params() will be passed a `game_params' containing the width and wrapping settings from the new game ID and the uniqueness setting from the old one. This would be faulted, if it weren't for the fact that `full' is not set during this call, so Net ignores the inconsistency. The resulting `game_params' is never subsequently used to generate a puzzle; this is a promise made by the mid-end when it asks for a non- full validation.) 2.4. Handling game descriptions ------------------------------- In this section I present the functions that deal with a textual description of a puzzle, i.e. the part that comes after the colon in a descriptive-format game ID. 2.4.1. new_desc() ----------------- char *(*new_desc)(const game_params *params, random_state *rs, char **aux, int interactive); This function is where all the really hard work gets done. This is the function whose job is to randomly generate a new puzzle, ensuring solubility and uniqueness as appropriate. As input it is given a `game_params' structure and a random state (see section 5.1 for the random number API). It must invent a puzzle instance, encode it in string form, and return a dynamically allocated C string containing that encoding. Additionally, it may return a second dynamically allocated string in `*aux'. (If it doesn't want to, then it can leave that parameter completely alone; it isn't required to set it to NULL, although doing so is harmless.) That string, if present, will be passed to solve() (section 2.7.4) later on; so if the puzzle is generated in such a way that a solution is known, then information about that solution can be saved in `*aux' for solve() to use. The `interactive' parameter should be ignored by almost all puzzles. Its purpose is to distinguish between generating a puzzle within a GUI context for immediate play, and generating a puzzle in a command-line context for saving to be played later. The only puzzle that currently uses this distinction (and, I fervently hope, the only one which will _ever_ need to use it) is Mines, which chooses a random first-click location when generating puzzles non-interactively, but which waits for the user to place the first click when interactive. If you think you have come up with another puzzle which needs to make use of this parameter, please think for at least ten minutes about whether there is _any_ alternative! Note that game description strings are not required to contain an encoding of parameters such as grid size; a game description is never separated from the `game_params' it was generated with, so any information contained in that structure need not be encoded again in the game description. 2.4.2. validate_desc() ---------------------- char *(*validate_desc)(const game_params *params, const char *desc); This function is given a game description, and its job is to validate that it describes a puzzle which makes sense. To some extent it's up to the user exactly how far they take the phrase `makes sense'; there are no particularly strict rules about how hard the user is permitted to shoot themself in the foot when typing in a bogus game description by hand. (For example, Rectangles will not verify that the sum of all the numbers in the grid equals the grid's area. So a user could enter a puzzle which was provably not soluble, and the program wouldn't complain; there just wouldn't happen to be any sequence of moves which solved it.) The one non-negotiable criterion is that any game description which makes it through validate_desc() _must not_ subsequently cause a crash or an assertion failure when fed to new_game() and thence to the rest of the back end. The return value is NULL on success, or a non-dynamically-allocated C string containing an error message. 2.4.3. new_game() ----------------- game_state *(*new_game)(midend *me, const game_params *params, const char *desc); This function takes a game description as input, together with its accompanying `game_params', and constructs a `game_state' describing the initial state of the puzzle. It returns a newly allocated `game_state' structure. Almost all puzzles should ignore the `me' parameter. It is required by Mines, which needs it for later passing to midend_supersede_game_desc() (see section 2.11.2) once the user has placed the first click. I fervently hope that no other puzzle will be awkward enough to require it, so everybody else should ignore it. As with the `interactive' parameter in new_desc() (section 2.4.1), if you think you have a reason to need this parameter, please try very hard to think of an alternative approach! 2.5. Handling game states ------------------------- This section describes the functions which create and destroy `game_state' structures. (Well, except new_game(), which is in section 2.4.3 instead of under here; but it deals with game descriptions _and_ game states and it had to go in one section or the other.) 2.5.1. dup_game() ----------------- game_state *(*dup_game)(const game_state *state); This function allocates a new `game_state' structure and initialises it with an exact copy of the information in the one provided as input. It returns a pointer to the new duplicate. 2.5.2. free_game() ------------------ void (*free_game)(game_state *state); This function frees a `game_state' structure, and any subsidiary allocations contained within it. 2.6. Handling `game_ui' ----------------------- 2.6.1. new_ui() --------------- game_ui *(*new_ui)(const game_state *state); This function allocates and returns a new `game_ui' structure for playing a particular puzzle. It is passed a pointer to the initial `game_state', in case it needs to refer to that when setting up the initial values for the new game. 2.6.2. free_ui() ---------------- void (*free_ui)(game_ui *ui); This function frees a `game_ui' structure, and any subsidiary allocations contained within it. 2.6.3. encode_ui() ------------------ char *(*encode_ui)(const game_ui *ui); This function encodes any _important_ data in a `game_ui' structure in string form. It is only called when saving a half-finished game to a file. It should be used sparingly. Almost all data in a `game_ui' is not important enough to save. The location of the keyboard-controlled cursor, for example, can be reset to a default position on reloading the game without impacting the user experience. If the user should somehow manage to save a game while a mouse drag was in progress, then discarding that mouse drag would be an outright _feature_. A typical thing that _would_ be worth encoding in this function is the Mines death counter: it's in the `game_ui' rather than the `game_state' because it's too important to allow the user to revert it by using Undo, and therefore it's also too important to allow the user to revert it by saving and reloading. (Of course, the user could edit the save file by hand... But if the user is _that_ determined to cheat, they could just as easily modify the game's source.) 2.6.4. decode_ui() ------------------ void (*decode_ui)(game_ui *ui, const char *encoding); This function parses a string previously output by encode_ui(), and writes the decoded data back into the provided `game_ui' structure. 2.6.5. changed_state() ---------------------- void (*changed_state)(game_ui *ui, const game_state *oldstate, const game_state *newstate); This function is called by the mid-end whenever the current game state changes, for any reason. Those reasons include: - a fresh move being made by interpret_move() and execute_move() - a solve operation being performed by solve() and execute_move() - the user moving back and forth along the undo list by means of the Undo and Redo operations - the user selecting Restart to go back to the initial game state. The job of changed_state() is to update the `game_ui' for consistency with the new game state, if any update is necessary. For example, Same Game stores data about the currently selected tile group in its `game_ui', and this data is intrinsically related to the game state it was derived from. So it's very likely to become invalid when the game state changes; thus, Same Game's changed_state() function clears the current selection whenever it is called. When anim_length() or flash_length() are called, you can be sure that there has been a previous call to changed_state(). So changed_state() can set up data in the `game_ui' which will be read by anim_length() and flash_length(), and those functions will not have to worry about being called without the data having been initialised. 2.7. Making moves ----------------- This section describes the functions which actually make moves in the game: that is, the functions which process user input and end up producing new `game_state's. 2.7.1. interpret_move() ----------------------- char *(*interpret_move)(const game_state *state, game_ui *ui, const game_drawstate *ds, int x, int y, int button); This function receives user input and processes it. Its input parameters are the current `game_state', the current `game_ui' and the current `game_drawstate', plus details of the input event. `button' is either an ASCII value or a special code (listed below) indicating an arrow or function key or a mouse event; when `button' is a mouse event, `x' and `y' contain the pixel coordinates of the mouse pointer relative to the top left of the puzzle's drawing area. (The pointer to the `game_drawstate' is marked `const', because `interpret_move' should not write to it. The normal use of that pointer will be to read the game's tile size parameter in order to divide mouse coordinates by it.) interpret_move() may return in three different ways: - Returning NULL indicates that no action whatsoever occurred in response to the input event; the puzzle was not interested in it at all. - Returning the empty string ("") indicates that the input event has resulted in a change being made to the `game_ui' which will require a redraw of the game window, but that no actual _move_ was made (i.e. no new `game_state' needs to be created). - Returning anything else indicates that a move was made and that a new `game_state' must be created. However, instead of actually constructing a new `game_state' itself, this function is required to return a string description of the details of the move. This string will be passed to execute_move() (section 2.7.2) to actually create the new `game_state'. (Encoding moves as strings in this way means that the mid-end can keep the strings as well as the game states, and the strings can be written to disk when saving the game and fed to execute_move() again on reloading.) The return value from interpret_move() is expected to be dynamically allocated if and only if it is not either NULL _or_ the empty string. After this function is called, the back end is permitted to rely on some subsequent operations happening in sequence: - execute_move() will be called to convert this move description into a new `game_state' - changed_state() will be called with the new `game_state'. This means that if interpret_move() needs to do updates to the `game_ui' which are easier to perform by referring to the new `game_state', it can safely leave them to be done in changed_state() and not worry about them failing to happen. (Note, however, that execute_move() may _also_ be called in other circumstances. It is only interpret_move() which can rely on a subsequent call to changed_state().) The special key codes supported by this function are: LEFT_BUTTON, MIDDLE_BUTTON, RIGHT_BUTTON Indicate that one of the mouse buttons was pressed down. LEFT_DRAG, MIDDLE_DRAG, RIGHT_DRAG Indicate that the mouse was moved while one of the mouse buttons was still down. The mid-end guarantees that when one of these events is received, it will always have been preceded by a button-down event (and possibly other drag events) for the same mouse button, and no event involving another mouse button will have appeared in between. LEFT_RELEASE, MIDDLE_RELEASE, RIGHT_RELEASE Indicate that a mouse button was released. The mid-end guarantees that when one of these events is received, it will always have been preceded by a button-down event (and possibly some drag events) for the same mouse button, and no event involving another mouse button will have appeared in between. CURSOR_UP, CURSOR_DOWN, CURSOR_LEFT, CURSOR_RIGHT Indicate that an arrow key was pressed. CURSOR_SELECT On platforms which have a prominent `select' button alongside their cursor keys, indicates that that button was pressed. In addition, there are some modifiers which can be bitwise-ORed into the `button' parameter: MOD_CTRL, MOD_SHFT These indicate that the Control or Shift key was pressed alongside the key. They only apply to the cursor keys, not to mouse buttons or anything else. MOD_NUM_KEYPAD This applies to some ASCII values, and indicates that the key code was input via the numeric keypad rather than the main keyboard. Some puzzles may wish to treat this differently (for example, a puzzle might want to use the numeric keypad as an eight-way directional pad), whereas others might not (a game involving numeric input probably just wants to treat the numeric keypad as numbers). MOD_MASK This mask is the bitwise OR of all the available modifiers; you can bitwise-AND with ~MOD_MASK to strip all the modifiers off any input value. 2.7.2. execute_move() --------------------- game_state *(*execute_move)(const game_state *state, char *move); This function takes an input `game_state' and a move string as output from interpret_move(). It returns a newly allocated `game_state' which contains the result of applying the specified move to the input game state. This function may return NULL if it cannot parse the move string (and this is definitely preferable to crashing or failing an assertion, since one way this can happen is if loading a corrupt save file). However, it must not return NULL for any move string that really was output from interpret_move(): this is punishable by assertion failure in the mid- end. 2.7.3. `can_solve' ------------------ int can_solve; This boolean field is set to TRUE if the game's solve() function does something. If it's set to FALSE, the game will not even offer the `Solve' menu option. 2.7.4. solve() -------------- char *(*solve)(const game_state *orig, const game_state *curr, const char *aux, char **error); This function is called when the user selects the `Solve' option from the menu. It is passed two input game states: `orig' is the game state from the very start of the puzzle, and `curr' is the current one. (Different games find one or other or both of these convenient.) It is also passed the `aux' string saved by new_desc() (section 2.4.1), in case that encodes important information needed to provide the solution. If this function is unable to produce a solution (perhaps, for example, the game has no in-built solver so it can only solve puzzles it invented internally and has an `aux' string for) then it may return NULL. If it does this, it must also set `*error' to an error message to be presented to the user (such as `Solution not known for this puzzle'); that error message is not expected to be dynamically allocated. If this function _does_ produce a solution, it returns a move string suitable for feeding to execute_move() (section 2.7.2). Like a (non- empty) string returned from interpret_move(), the returned string should be dynamically allocated. 2.8. Drawing the game graphics ------------------------------ This section discusses the back end functions that deal with drawing. 2.8.1. new_drawstate() ---------------------- game_drawstate *(*new_drawstate)(drawing *dr, const game_state *state); This function allocates and returns a new `game_drawstate' structure for drawing a particular puzzle. It is passed a pointer to a `game_state', in case it needs to refer to that when setting up any initial data. This function may not rely on the puzzle having been newly started; a new draw state can be constructed at any time if the front end requests a forced redraw. For games like Pattern, in which initial game states are much simpler than general ones, this might be important to keep in mind. The parameter `dr' is a drawing object (see chapter 3) which the function might need to use to allocate blitters. (However, this isn't recommended; it's usually more sensible to wait to allocate a blitter until set_size() is called, because that way you can tailor it to the scale at which the puzzle is being drawn.) 2.8.2. free_drawstate() ----------------------- void (*free_drawstate)(drawing *dr, game_drawstate *ds); This function frees a `game_drawstate' structure, and any subsidiary allocations contained within it. The parameter `dr' is a drawing object (see chapter 3), which might be required if you are freeing a blitter. 2.8.3. `preferred_tilesize' --------------------------- int preferred_tilesize; Each game is required to define a single integer parameter which expresses, in some sense, the scale at which it is drawn. This is described in the APIs as `tilesize', since most puzzles are on a square (or possibly triangular or hexagonal) grid and hence a sensible interpretation of this parameter is to define it as the size of one grid tile in pixels; however, there's no actual requirement that the `tile size' be proportional to the game window size. Window size is required to increase monotonically with `tile size', however. The data element `preferred_tilesize' indicates the tile size which should be used in the absence of a good reason to do otherwise (such as the screen being too small, or the user explicitly requesting a resize if that ever gets implemented). 2.8.4. compute_size() --------------------- void (*compute_size)(const game_params *params, int tilesize, int *x, int *y); This function is passed a `game_params' structure and a tile size. It returns, in `*x' and `*y', the size in pixels of the drawing area that would be required to render a puzzle with those parameters at that tile size. 2.8.5. set_size() ----------------- void (*set_size)(drawing *dr, game_drawstate *ds, const game_params *params, int tilesize); This function is responsible for setting up a `game_drawstate' to draw at a given tile size. Typically this will simply involve copying the supplied `tilesize' parameter into a `tilesize' field inside the draw state; for some more complex games it might also involve setting up other dimension fields, or possibly allocating a blitter (see section 3.1.13). The parameter `dr' is a drawing object (see chapter 3), which is required if a blitter needs to be allocated. Back ends may assume (and may enforce by assertion) that this function will be called at most once for any `game_drawstate'. If a puzzle needs to be redrawn at a different size, the mid-end will create a fresh drawstate. 2.8.6. colours() ---------------- float *(*colours)(frontend *fe, int *ncolours); This function is responsible for telling the front end what colours the puzzle will need to draw itself. It returns the number of colours required in `*ncolours', and the return value from the function itself is a dynamically allocated array of three times that many `float's, containing the red, green and blue components of each colour respectively as numbers in the range [0,1]. The second parameter passed to this function is a front end handle. The only things it is permitted to do with this handle are to call the front-end function called frontend_default_colour() (see section 4.40) or the utility function called game_mkhighlight() (see section 5.4.7). (The latter is a wrapper on the former, so front end implementors only need to provide frontend_default_colour().) This allows colours() to take local configuration into account when deciding on its own colour allocations. Most games use the front end's default colour as their background, apart from a few which depend on drawing relief highlights so they adjust the background colour if it's too light for highlights to show up against it. Note that the colours returned from this function are for _drawing_, not for printing. Printing has an entirely different colour allocation policy. 2.8.7. anim_length() -------------------- float (*anim_length)(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui); This function is called when a move is made, undone or redone. It is given the old and the new `game_state', and its job is to decide whether the transition between the two needs to be animated or can be instant. `oldstate' is the state that was current until this call; `newstate' is the state that will be current after it. `dir' specifies the chronological order of those states: if it is positive, then the transition is the result of a move or a redo (and so `newstate' is the later of the two moves), whereas if it is negative then the transition is the result of an undo (so that `newstate' is the _earlier_ move). If this function decides the transition should be animated, it returns the desired length of the animation in seconds. If not, it returns zero. State changes as a result of a Restart operation are never animated; the mid-end will handle them internally and never consult this function at all. State changes as a result of Solve operations are also not animated by default, although you can change this for a particular game by setting a flag in `flags' (section 2.10.7). The function is also passed a pointer to the local `game_ui'. It may refer to information in here to help with its decision (see section 6.3.7 for an example of this), and/or it may _write_ information about the nature of the animation which will be read later by redraw(). When this function is called, it may rely on changed_state() having been called previously, so if anim_length() needs to refer to information in the `game_ui', then changed_state() is a reliable place to have set that information up. Move animations do not inhibit further input events. If the user continues playing before a move animation is complete, the animation will be abandoned and the display will jump straight to the final state. 2.8.8. flash_length() --------------------- float (*flash_length)(const game_state *oldstate, const game_state *newstate, int dir, game_ui *ui); This function is called when a move is completed. (`Completed' means that not only has the move been made, but any animation which accompanied it has finished.) It decides whether the transition from `oldstate' to `newstate' merits a `flash'. A flash is much like a move animation, but it is _not_ interrupted by further user interface activity; it runs to completion in parallel with whatever else might be going on on the display. The only thing which will rush a flash to completion is another flash. The purpose of flashes is to indicate that the game has been completed. They were introduced as a separate concept from move animations because of Net: the habit of most Net players (and certainly me) is to rotate a tile into place and immediately lock it, then move on to another tile. When you make your last move, at the instant the final tile is rotated into place the screen starts to flash to indicate victory - but if you then press the lock button out of habit, then the move animation is cancelled, and the victory flash does not complete. (And if you _don't_ press the lock button, the completed grid will look untidy because there will be one unlocked square.) Therefore, I introduced a specific concept of a `flash' which is separate from a move animation and can proceed in parallel with move animations and any other display activity, so that the victory flash in Net is not cancelled by that final locking move. The input parameters to flash_length() are exactly the same as the ones to anim_length(). Just like anim_length(), when this function is called, it may rely on changed_state() having been called previously, so if it needs to refer to information in the `game_ui' then changed_state() is a reliable place to have set that information up. (Some games use flashes to indicate defeat as well as victory; Mines, for example, flashes in a different colour when you tread on a mine from the colour it uses when you complete the game. In order to achieve this, its flash_length() function has to store a flag in the `game_ui' to indicate which flash type is required.) 2.8.9. status() --------------- int (*status)(const game_state *state); This function returns a status value indicating whether the current game is still in play, or has been won, or has been conclusively lost. The mid-end uses this to implement midend_status() (section 4.27). The return value should be +1 if the game has been successfully solved. If the game has been lost in a situation where further play is unlikely, the return value should be -1. If neither is true (so play is still ongoing), return zero. Front ends may wish to use a non-zero status as a cue to proactively offer the option of starting a new game. Therefore, back ends should not return -1 if the game has been _technically_ lost but undoing and continuing is still a realistic possibility. (For instance, games with hidden information such as Guess or Mines might well return a non-zero status whenever they reveal the solution, whether or not the player guessed it correctly, on the grounds that a player would be unlikely to hide the solution and continue playing after the answer was spoiled. On the other hand, games where you can merely get into a dead end such as Same Game or Inertia might choose to return 0 in that situation, on the grounds that the player would quite likely press Undo and carry on playing.) 2.8.10. redraw() ---------------- void (*redraw)(drawing *dr, game_drawstate *ds, const game_state *oldstate, const game_state *newstate, int dir, const game_ui *ui, float anim_time, float flash_time); This function is responsible for actually drawing the contents of the game window, and for redrawing every time the game state or the `game_ui' changes. The parameter `dr' is a drawing object which may be passed to the drawing API functions (see chapter 3 for documentation of the drawing API). This function may not save `dr' and use it elsewhere; it must only use it for calling back to the drawing API functions within its own lifetime. `ds' is the local `game_drawstate', of course, and `ui' is the local `game_ui'. `newstate' is the semantically-current game state, and is always non- NULL. If `oldstate' is also non-NULL, it means that a move has recently been made and the game is still in the process of displaying an animation linking the old and new states; in this situation, `anim_time' will give the length of time (in seconds) that the animation has already been running. If `oldstate' is NULL, then `anim_time' is unused (and will hopefully be set to zero to avoid confusion). `flash_time', if it is is non-zero, denotes that the game is in the middle of a flash, and gives the time since the start of the flash. See section 2.8.8 for general discussion of flashes. The very first time this function is called for a new `game_drawstate', it is expected to redraw the _entire_ drawing area. Since this often involves drawing visual furniture which is never subsequently altered, it is often simplest to arrange this by having a special `first time' flag in the draw state, and resetting it after the first redraw. When this function (or any subfunction) calls the drawing API, it is expected to pass colour indices which were previously defined by the colours() function. 2.9. Printing functions ----------------------- This section discusses the back end functions that deal with printing puzzles out on paper. 2.9.1. `can_print' ------------------ int can_print; This flag is set to TRUE if the puzzle is capable of printing itself on paper. (This makes sense for some puzzles, such as Solo, which can be filled in with a pencil. Other puzzles, such as Twiddle, inherently involve moving things around and so would not make sense to print.) If this flag is FALSE, then the functions print_size() and print() will never be called. 2.9.2. `can_print_in_colour' ---------------------------- int can_print_in_colour; This flag is set to TRUE if the puzzle is capable of printing itself differently when colour is available. For example, Map can actually print coloured regions in different _colours_ rather than resorting to cross-hatching. If the `can_print' flag is FALSE, then this flag will be ignored. 2.9.3. print_size() ------------------- void (*print_size)(const game_params *params, float *x, float *y); This function is passed a `game_params' structure and a tile size. It returns, in `*x' and `*y', the preferred size in _millimetres_ of that puzzle if it were to be printed out on paper. If the `can_print' flag is FALSE, this function will never be called. 2.9.4. print() -------------- void (*print)(drawing *dr, const game_state *state, int tilesize); This function is called when a puzzle is to be printed out on paper. It should use the drawing API functions (see chapter 3) to print itself. This function is separate from redraw() because it is often very different: - The printing function may not depend on pixel accuracy, since printer resolution is variable. Draw as if your canvas had infinite resolution. - The printing function sometimes needs to display things in a completely different style. Net, for example, is very different as an on-screen puzzle and as a printed one. - The printing function is often much simpler since it has no need to deal with repeated partial redraws. However, there's no reason the printing and redraw functions can't share some code if they want to. When this function (or any subfunction) calls the drawing API, the colour indices it passes should be colours which have been allocated by the print_*_colour() functions within this execution of print(). This is very different from the fixed small number of colours used in redraw(), because printers do not have a limitation on the total number of colours that may be used. Some puzzles' printing functions might wish to allocate only one `ink' colour and use it for all drawing; others might wish to allocate _more_ colours than are used on screen. One possible colour policy worth mentioning specifically is that a puzzle's printing function might want to allocate the _same_ colour indices as are used by the redraw function, so that code shared between drawing and printing does not have to keep switching its colour indices. In order to do this, the simplest thing is to make use of the fact that colour indices returned from print_*_colour() are guaranteed to be in increasing order from zero. So if you have declared an `enum' defining three colours COL_BACKGROUND, COL_THIS and COL_THAT, you might then write int c; c = print_mono_colour(dr, 1); assert(c == COL_BACKGROUND); c = print_mono_colour(dr, 0); assert(c == COL_THIS); c = print_mono_colour(dr, 0); assert(c == COL_THAT); If the `can_print' flag is FALSE, this function will never be called. 2.10. Miscellaneous ------------------- 2.10.1. `can_format_as_text_ever' --------------------------------- int can_format_as_text_ever; This boolean field is TRUE if the game supports formatting a game state as ASCII text (typically ASCII art) for copying to the clipboard and pasting into other applications. If it is FALSE, front ends will not offer the `Copy' command at all. If this field is TRUE, the game does not necessarily have to support text formatting for _all_ games: e.g. a game which can be played on a square grid or a triangular one might only support copy and paste for the former, because triangular grids in ASCII art are just too difficult. If this field is FALSE, the functions can_format_as_text_now() (section 2.10.2) and text_format() (section 2.10.3) are never called. 2.10.2. `can_format_as_text_now()' ---------------------------------- int (*can_format_as_text_now)(const game_params *params); This function is passed a `game_params' and returns a boolean, which is TRUE if the game can support ASCII text output for this particular game type. If it returns FALSE, front ends will grey out or otherwise disable the `Copy' command. Games may enable and disable the copy-and-paste function for different game _parameters_, but are currently constrained to return the same answer from this function for all game _states_ sharing the same parameters. In other words, the `Copy' function may enable or disable itself when the player changes game preset, but will never change during play of a single game or when another game of exactly the same type is generated. This function should not take into account aspects of the game parameters which are not encoded by encode_params() (section 2.3.3) when the `full' parameter is set to FALSE. Such parameters will not necessarily match up between a call to this function and a subsequent call to text_format() itself. (For instance, game _difficulty_ should not affect whether the game can be copied to the clipboard. Only the actual visible _shape_ of the game can affect that.) 2.10.3. text_format() --------------------- char *(*text_format)(const game_state *state); This function is passed a `game_state', and returns a newly allocated C string containing an ASCII representation of that game state. It is used to implement the `Copy' operation in many front ends. This function will only ever be called if the back end field `can_format_as_text_ever' (section 2.10.1) is TRUE _and_ the function can_format_as_text_now() (section 2.10.2) has returned TRUE for the currently selected game parameters. The returned string may contain line endings (and will probably want to), using the normal C internal `\n' convention. For consistency between puzzles, all multi-line textual puzzle representations should _end_ with a newline as well as containing them internally. (There are currently no puzzles which have a one-line ASCII representation, so there's no precedent yet for whether that should come with a newline or not.) 2.10.4. wants_statusbar ----------------------- int wants_statusbar; This boolean field is set to TRUE if the puzzle has a use for a textual status line (to display score, completion status, currently active tiles, etc). 2.10.5. `is_timed' ------------------ int is_timed; This boolean field is TRUE if the puzzle is time-critical. If so, the mid-end will maintain a game timer while the user plays. If this field is FALSE, then timing_state() will never be called and need not do anything. 2.10.6. timing_state() ---------------------- int (*timing_state)(const game_state *state, game_ui *ui); This function is passed the current `game_state' and the local `game_ui'; it returns TRUE if the game timer should currently be running. A typical use for the `game_ui' in this function is to note when the game was first completed (by setting a flag in changed_state() - see section 2.6.5), and freeze the timer thereafter so that the user can undo back through their solution process without altering their time. 2.10.7. `flags' --------------- int flags; This field contains miscellaneous per-backend flags. It consists of the bitwise OR of some combination of the following: BUTTON_BEATS(x,y) Given any x and y from the set {LEFT_BUTTON, MIDDLE_BUTTON, RIGHT_BUTTON}, this macro evaluates to a bit flag which indicates that when buttons x and y are both pressed simultaneously, the mid- end should consider x to have priority. (In the absence of any such flags, the mid-end will always consider the most recently pressed button to have priority.) SOLVE_ANIMATES This flag indicates that moves generated by solve() (section 2.7.4) are candidates for animation just like any other move. For most games, solve moves should not be animated, so the mid-end doesn't even bother calling anim_length() (section 2.8.7), thus saving some special-case code in each game. On the rare occasion that animated solve moves are actually required, you can set this flag. REQUIRE_RBUTTON This flag indicates that the puzzle cannot be usefully played without the use of mouse buttons other than the left one. On some PDA platforms, this flag is used by the front end to enable right- button emulation through an appropriate gesture. Note that a puzzle is not required to set this just because it _uses_ the right button, but only if its use of the right button is critical to playing the game. (Slant, for example, uses the right button to cycle through the three square states in the opposite order from the left button, and hence can manage fine without it.) REQUIRE_NUMPAD This flag indicates that the puzzle cannot be usefully played without the use of number-key input. On some PDA platforms it causes an emulated number pad to appear on the screen. Similarly to REQUIRE_RBUTTON, a puzzle need not specify this simply if its use of the number keys is not critical. 2.11. Things a back end may do on its own initiative ---------------------------------------------------- This section describes a couple of things that a back end may choose to do by calling functions elsewhere in the program, which would not otherwise be obvious. 2.11.1. Create a random state ----------------------------- If a back end needs random numbers at some point during normal play, it can create a fresh `random_state' by first calling `get_random_seed' (section 4.36) and then passing the returned seed data to random_new(). This is likely not to be what you want. If a puzzle needs randomness in the middle of play, it's likely to be more sensible to store some sort of random state within the `game_state', so that the random numbers are tied to the particular game state and hence the player can't simply keep undoing their move until they get numbers they like better. This facility is currently used only in Net, to implement the `jumble' command, which sets every unlocked tile to a new random orientation. This randomness _is_ a reasonable use of the feature, because it's non- adversarial - there's no advantage to the user in getting different random numbers. 2.11.2. Supersede its own game description ------------------------------------------ In response to a move, a back end is (reluctantly) permitted to call midend_supersede_game_desc(): void midend_supersede_game_desc(midend *me, char *desc, char *privdesc); When the user selects `New Game', the mid-end calls new_desc() (section 2.4.1) to get a new game description, and (as well as using that to generate an initial game state) stores it for the save file and for telling to the user. The function above overwrites that game description, and also splits it in two. `desc' becomes the new game description which is provided to the user on request, and is also the one used to construct a new initial game state if the user selects `Restart'. `privdesc' is a `private' game description, used to reconstruct the game's initial state when reloading. The distinction between the two, as well as the need for this function at all, comes from Mines. Mines begins with a blank grid and no idea of where the mines actually are; new_desc() does almost no work in interactive mode, and simply returns a string encoding the `random_state'. When the user first clicks to open a tile, _then_ Mines generates the mine positions, in such a way that the game is soluble from that starting point. Then it uses this function to supersede the random-state game description with a proper one. But it needs two: one containing the initial click location (because that's what you want to happen if you restart the game, and also what you want to send to a friend so that they play _the same game_ as you), and one without the initial click location (because when you save and reload the game, you expect to see the same blank initial state as you had before saving). I should stress again that this function is a horrid hack. Nobody should use it if they're not Mines; if you think you need to use it, think again repeatedly in the hope of finding a better way to do whatever it was you needed to do. 3. The drawing API ------------------ The back end function redraw() (section 2.8.10) is required to draw the puzzle's graphics on the window's drawing area, or on paper if the puzzle is printable. To do this portably, it is provided with a drawing API allowing it to talk directly to the front end. In this chapter I document that API, both for the benefit of back end authors trying to use it and for front end authors trying to implement it. The drawing API as seen by the back end is a collection of global functions, each of which takes a pointer to a `drawing' structure (a `drawing object'). These objects are supplied as parameters to the back end's redraw() and print() functions. In fact these global functions are not implemented directly by the front end; instead, they are implemented centrally in `drawing.c' and form a small piece of middleware. The drawing API as supplied by the front end is a structure containing a set of function pointers, plus a `void *' handle which is passed to each of those functions. This enables a single front end to switch between multiple implementations of the drawing API if necessary. For example, the Windows API supplies a printing mechanism integrated into the same GDI which deals with drawing in windows, and therefore the same API implementation can handle both drawing and printing; but on Unix, the most common way for applications to print is by producing PostScript output directly, and although it would be _possible_ to write a single (say) draw_rect() function which checked a global flag to decide whether to do GTK drawing operations or output PostScript to a file, it's much nicer to have two separate functions and switch between them as appropriate. When drawing, the puzzle window is indexed by pixel coordinates, with the top left pixel defined as (0,0) and the bottom right pixel (w-1,h- 1), where `w' and `h' are the width and height values returned by the back end function compute_size() (section 2.8.4). When printing, the puzzle's print area is indexed in exactly the same way (with an arbitrary tile size provided by the printing module `printing.c'), to facilitate sharing of code between the drawing and printing routines. However, when printing, puzzles may no longer assume that the coordinate unit has any relationship to a pixel; the printer's actual resolution might very well not even be known at print time, so the coordinate unit might be smaller or larger than a pixel. Puzzles' print functions should restrict themselves to drawing geometric shapes rather than fiddly pixel manipulation. _Puzzles' redraw functions may assume that the surface they draw on is persistent_. It is the responsibility of every front end to preserve the puzzle's window contents in the face of GUI window expose issues and similar. It is not permissible to request that the back end redraw any part of a window that it has already drawn, unless something has actually changed as a result of making moves in the puzzle. Most front ends accomplish this by having the drawing routines draw on a stored bitmap rather than directly on the window, and copying the bitmap to the window every time a part of the window needs to be redrawn. Therefore, it is vitally important that whenever the back end does any drawing it informs the front end of which parts of the window it has accessed, and hence which parts need repainting. This is done by calling draw_update() (section 3.1.11). Persistence of old drawing is convenient. However, a puzzle should be very careful about how it updates its drawing area. The problem is that some front ends do anti-aliased drawing: rather than simply choosing between leaving each pixel untouched or painting it a specified colour, an antialiased drawing function will _blend_ the original and new colours in pixels at a figure's boundary according to the proportion of the pixel occupied by the figure (probably modified by some heuristic fudge factors). All of this produces a smoother appearance for curves and diagonal lines. An unfortunate effect of drawing an anti-aliased figure repeatedly is that the pixels around the figure's boundary come steadily more saturated with `ink' and the boundary appears to `spread out'. Worse, redrawing a figure in a different colour won't fully paint over the old boundary pixels, so the end result is a rather ugly smudge. A good strategy to avoid unpleasant anti-aliasing artifacts is to identify a number of rectangular areas which need to be redrawn, clear them to the background colour, and then redraw their contents from scratch, being careful all the while not to stray beyond the boundaries of the original rectangles. The clip() function (section 3.1.9) comes in very handy here. Games based on a square grid can often do this fairly easily. Other games may need to be somewhat more careful. For example, Loopy's redraw function first identifies portions of the display which need to be updated. Then, if the changes are fairly well localised, it clears and redraws a rectangle containing each changed area. Otherwise, it gives up and redraws the entire grid from scratch. It is possible to avoid clearing to background and redrawing from scratch if one is very careful about which drawing functions one uses: if a function is documented as not anti-aliasing under some circumstances, you can rely on each pixel in a drawing either being left entirely alone or being set to the requested colour, with no blending being performed. In the following sections I first discuss the drawing API as seen by the back end, and then the _almost_ identical function-pointer form seen by the front end. 3.1. Drawing API as seen by the back end ---------------------------------------- This section documents the back-end drawing API, in the form of functions which take a `drawing' object as an argument. 3.1.1. draw_rect() ------------------ void draw_rect(drawing *dr, int x, int y, int w, int h, int colour); Draws a filled rectangle in the puzzle window. `x' and `y' give the coordinates of the top left pixel of the rectangle. `w' and `h' give its width and height. Thus, the horizontal extent of the rectangle runs from `x' to `x+w-1' inclusive, and the vertical extent from `y' to `y+h-1' inclusive. `colour' is an integer index into the colours array returned by the back end function colours() (section 2.8.6). There is no separate pixel-plotting function. If you want to plot a single pixel, the approved method is to use draw_rect() with width and height set to 1. Unlike many of the other drawing functions, this function is guaranteed to be pixel-perfect: the rectangle will be sharply defined and not anti- aliased or anything like that. This function may be used for both drawing and printing. 3.1.2. draw_rect_outline() -------------------------- void draw_rect_outline(drawing *dr, int x, int y, int w, int h, int colour); Draws an outline rectangle in the puzzle window. `x' and `y' give the coordinates of the top left pixel of the rectangle. `w' and `h' give its width and height. Thus, the horizontal extent of the rectangle runs from `x' to `x+w-1' inclusive, and the vertical extent from `y' to `y+h-1' inclusive. `colour' is an integer index into the colours array returned by the back end function colours() (section 2.8.6). From a back end perspective, this function may be considered to be part of the drawing API. However, front ends are not required to implement it, since it is actually implemented centrally (in misc.c) as a wrapper on draw_polygon(). This function may be used for both drawing and printing. 3.1.3. draw_line() ------------------ void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour); Draws a straight line in the puzzle window. `x1' and `y1' give the coordinates of one end of the line. `x2' and `y2' give the coordinates of the other end. The line drawn includes both those points. `colour' is an integer index into the colours array returned by the back end function colours() (section 2.8.6). Some platforms may perform anti-aliasing on this function. Therefore, do not assume that you can erase a line by drawing the same line over it in the background colour; anti-aliasing might lead to perceptible ghost artefacts around the vanished line. Horizontal and vertical lines, however, are pixel-perfect and not anti-aliased. This function may be used for both drawing and printing. 3.1.4. draw_polygon() --------------------- void draw_polygon(drawing *dr, int *coords, int npoints, int fillcolour, int outlinecolour); Draws an outlined or filled polygon in the puzzle window. `coords' is an array of (2*npoints) integers, containing the `x' and `y' coordinates of `npoints' vertices. `fillcolour' and `outlinecolour' are integer indices into the colours array returned by the back end function colours() (section 2.8.6). `fillcolour' may also be -1 to indicate that the polygon should be outlined only. The polygon defined by the specified list of vertices is first filled in `fillcolour', if specified, and then outlined in `outlinecolour'. `outlinecolour' may _not_ be -1; it must be a valid colour (and front ends are permitted to enforce this by assertion). This is because different platforms disagree on whether a filled polygon should include its boundary line or not, so drawing _only_ a filled polygon would have non-portable effects. If you want your filled polygon not to have a visible outline, you must set `outlinecolour' to the same as `fillcolour'. Some platforms may perform anti-aliasing on this function. Therefore, do not assume that you can erase a polygon by drawing the same polygon over it in the background colour. Also, be prepared for the polygon to extend a pixel beyond its obvious bounding box as a result of this; if you really need it not to do this to avoid interfering with other delicate graphics, you should probably use clip() (section 3.1.9). You can rely on horizontal and vertical lines not being anti-aliased. This function may be used for both drawing and printing. 3.1.5. draw_circle() -------------------- void draw_circle(drawing *dr, int cx, int cy, int radius, int fillcolour, int outlinecolour); Draws an outlined or filled circle in the puzzle window. `cx' and `cy' give the coordinates of the centre of the circle. `radius' gives its radius. The total horizontal pixel extent of the circle is from `cx-radius+1' to `cx+radius-1' inclusive, and the vertical extent similarly around `cy'. `fillcolour' and `outlinecolour' are integer indices into the colours array returned by the back end function colours() (section 2.8.6). `fillcolour' may also be -1 to indicate that the circle should be outlined only. The circle is first filled in `fillcolour', if specified, and then outlined in `outlinecolour'. `outlinecolour' may _not_ be -1; it must be a valid colour (and front ends are permitted to enforce this by assertion). This is because different platforms disagree on whether a filled circle should include its boundary line or not, so drawing _only_ a filled circle would have non-portable effects. If you want your filled circle not to have a visible outline, you must set `outlinecolour' to the same as `fillcolour'. Some platforms may perform anti-aliasing on this function. Therefore, do not assume that you can erase a circle by drawing the same circle over it in the background colour. Also, be prepared for the circle to extend a pixel beyond its obvious bounding box as a result of this; if you really need it not to do this to avoid interfering with other delicate graphics, you should probably use clip() (section 3.1.9). This function may be used for both drawing and printing. 3.1.6. draw_thick_line() ------------------------ void draw_thick_line(drawing *dr, float thickness, float x1, float y1, float x2, float y2, int colour) Draws a line in the puzzle window, giving control over the line's thickness. `x1' and `y1' give the coordinates of one end of the line. `x2' and `y2' give the coordinates of the other end. `thickness' gives the thickness of the line, in pixels. Note that the coordinates and thickness are floating-point: the continuous coordinate system is in effect here. It's important to be able to address points with better-than-pixel precision in this case, because one can't otherwise properly express the endpoints of lines with both odd and even thicknesses. Some platforms may perform anti-aliasing on this function. The precise pixels affected by a thick-line drawing operation may vary between platforms, and no particular guarantees are provided. Indeed, even horizontal or vertical lines may be anti-aliased. This function may be used for both drawing and printing. 3.1.7. draw_text() ------------------ void draw_text(drawing *dr, int x, int y, int fonttype, int fontsize, int align, int colour, char *text); Draws text in the puzzle window. `x' and `y' give the coordinates of a point. The relation of this point to the location of the text is specified by `align', which is a bitwise OR of horizontal and vertical alignment flags: ALIGN_VNORMAL Indicates that `y' is aligned with the baseline of the text. ALIGN_VCENTRE Indicates that `y' is aligned with the vertical centre of the text. (In fact, it's aligned with the vertical centre of normal _capitalised_ text: displaying two pieces of text with ALIGN_VCENTRE at the same y-coordinate will cause their baselines to be aligned with one another, even if one is an ascender and the other a descender.) ALIGN_HLEFT Indicates that `x' is aligned with the left-hand end of the text. ALIGN_HCENTRE Indicates that `x' is aligned with the horizontal centre of the text. ALIGN_HRIGHT Indicates that `x' is aligned with the right-hand end of the text. `fonttype' is either FONT_FIXED or FONT_VARIABLE, for a monospaced or proportional font respectively. (No more detail than that may be specified; it would only lead to portability issues between different platforms.) `fontsize' is the desired size, in pixels, of the text. This size corresponds to the overall point size of the text, not to any internal dimension such as the cap-height. `colour' is an integer index into the colours array returned by the back end function colours() (section 2.8.6). This function may be used for both drawing and printing. The character set used to encode the text passed to this function is specified _by the drawing object_, although it must be a superset of ASCII. If a puzzle wants to display text that is not contained in ASCII, it should use the text_fallback() function (section 3.1.8) to query the drawing object for an appropriate representation of the characters it wants. 3.1.8. text_fallback() ---------------------- char *text_fallback(drawing *dr, const char *const *strings, int nstrings); This function is used to request a translation of UTF-8 text into whatever character encoding is expected by the drawing object's implementation of draw_text(). The input is a list of strings encoded in UTF-8: nstrings gives the number of strings in the list, and strings[0], strings[1], ..., strings[nstrings-1] are the strings themselves. The returned string (which is dynamically allocated and must be freed when finished with) is derived from the first string in the list that the drawing object expects to be able to display reliably; it will consist of that string translated into the character set expected by draw_text(). Drawing implementations are not required to handle anything outside ASCII, but are permitted to assume that _some_ string will be successfully translated. So every call to this function must include a string somewhere in the list (presumably the last element) which consists of nothing but ASCII, to be used by any front end which cannot handle anything else. For example, if a puzzle wished to display a string including a multiplication sign (U+00D7 in Unicode, represented by the bytes C3 97 in UTF-8), it might do something like this: static const char *const times_signs[] = { "\xC3\x97", "x" }; char *times_sign = text_fallback(dr, times_signs, 2); sprintf(buffer, "%d%s%d", width, times_sign, height); draw_text(dr, x, y, font, size, align, colour, buffer); sfree(buffer); which would draw a string with a times sign in the middle on platforms that support it, and fall back to a simple ASCII `x' where there was no alternative. 3.1.9. clip() ------------- void clip(drawing *dr, int x, int y, int w, int h); Establishes a clipping rectangle in the puzzle window. `x' and `y' give the coordinates of the top left pixel of the clipping rectangle. `w' and `h' give its width and height. Thus, the horizontal extent of the rectangle runs from `x' to `x+w-1' inclusive, and the vertical extent from `y' to `y+h-1' inclusive. (These are exactly the same semantics as draw_rect().) After this call, no drawing operation will affect anything outside the specified rectangle. The effect can be reversed by calling unclip() (section 3.1.10). The clipping rectangle is pixel-perfect: pixels within the rectangle are affected as usual by drawing functions; pixels outside are completely untouched. Back ends should not assume that a clipping rectangle will be automatically cleared up by the front end if it's left lying around; that might work on current front ends, but shouldn't be relied upon. Always explicitly call unclip(). This function may be used for both drawing and printing. 3.1.10. unclip() ---------------- void unclip(drawing *dr); Reverts the effect of a previous call to clip(). After this call, all drawing operations will be able to affect the entire puzzle window again. This function may be used for both drawing and printing. 3.1.11. draw_update() --------------------- void draw_update(drawing *dr, int x, int y, int w, int h); Informs the front end that a rectangular portion of the puzzle window has been drawn on and needs to be updated. `x' and `y' give the coordinates of the top left pixel of the update rectangle. `w' and `h' give its width and height. Thus, the horizontal extent of the rectangle runs from `x' to `x+w-1' inclusive, and the vertical extent from `y' to `y+h-1' inclusive. (These are exactly the same semantics as draw_rect().) The back end redraw function _must_ call this function to report any changes it has made to the window. Otherwise, those changes may not become immediately visible, and may then appear at an unpredictable subsequent time such as the next time the window is covered and re- exposed. This function is only important when drawing. It may be called when printing as well, but doing so is not compulsory, and has no effect. (So if you have a shared piece of code between the drawing and printing routines, that code may safely call draw_update().) 3.1.12. status_bar() -------------------- void status_bar(drawing *dr, char *text); Sets the text in the game's status bar to `text'. The text is copied from the supplied buffer, so the caller is free to deallocate or modify the buffer after use. (This function is not exactly a _drawing_ function, but it shares with the drawing API the property that it may only be called from within the back end redraw function, so this is as good a place as any to document it.) The supplied text is filtered through the mid-end for optional rewriting before being passed on to the front end; the mid-end will prepend the current game time if the game is timed (and may in future perform other rewriting if it seems like a good idea). This function is for drawing only; it must never be called during printing. 3.1.13. Blitter functions ------------------------- This section describes a group of related functions which save and restore a section of the puzzle window. This is most commonly used to implement user interfaces involving dragging a puzzle element around the window: at the end of each call to redraw(), if an object is currently being dragged, the back end saves the window contents under that location and then draws the dragged object, and at the start of the next redraw() the first thing it does is to restore the background. The front end defines an opaque type called a `blitter', which is capable of storing a rectangular area of a specified size. Blitter functions are for drawing only; they must never be called during printing. 3.1.13.1. blitter_new() ----------------------- blitter *blitter_new(drawing *dr, int w, int h); Creates a new blitter object which stores a rectangle of size `w' by `h' pixels. Returns a pointer to the blitter object. Blitter objects are best stored in the `game_drawstate'. A good time to create them is in the set_size() function (section 2.8.5), since it is at this point that you first know how big a rectangle they will need to save. 3.1.13.2. blitter_free() ------------------------ void blitter_free(drawing *dr, blitter *bl); Disposes of a blitter object. Best called in free_drawstate(). (However, check that the blitter object is not NULL before attempting to free it; it is possible that a draw state might be created and freed without ever having set_size() called on it in between.) 3.1.13.3. blitter_save() ------------------------ void blitter_save(drawing *dr, blitter *bl, int x, int y); This is a true drawing API function, in that it may only be called from within the game redraw routine. It saves a rectangular portion of the puzzle window into the specified blitter object. `x' and `y' give the coordinates of the top left corner of the saved rectangle. The rectangle's width and height are the ones specified when the blitter object was created. This function is required to cope and do the right thing if `x' and `y' are out of range. (The right thing probably means saving whatever part of the blitter rectangle overlaps with the visible area of the puzzle window.) 3.1.13.4. blitter_load() ------------------------ void blitter_load(drawing *dr, blitter *bl, int x, int y); This is a true drawing API function, in that it may only be called from within the game redraw routine. It restores a rectangular portion of the puzzle window from the specified blitter object. `x' and `y' give the coordinates of the top left corner of the rectangle to be restored. The rectangle's width and height are the ones specified when the blitter object was created. Alternatively, you can specify both `x' and `y' as the special value BLITTER_FROMSAVED, in which case the rectangle will be restored to exactly where it was saved from. (This is probably what you want to do almost all the time, if you're using blitters to implement draggable puzzle elements.) This function is required to cope and do the right thing if `x' and `y' (or the equivalent ones saved in the blitter) are out of range. (The right thing probably means restoring whatever part of the blitter rectangle overlaps with the visible area of the puzzle window.) If this function is called on a blitter which had previously been saved from a partially out-of-range rectangle, then the parts of the saved bitmap which were not visible at save time are undefined. If the blitter is restored to a different position so as to make those parts visible, the effect on the drawing area is undefined. 3.1.14. print_mono_colour() --------------------------- int print_mono_colour(drawing *dr, int grey); This function allocates a colour index for a simple monochrome colour during printing. `grey' must be 0 or 1. If `grey' is 0, the colour returned is black; if `grey' is 1, the colour is white. 3.1.15. print_grey_colour() --------------------------- int print_grey_colour(drawing *dr, float grey); This function allocates a colour index for a grey-scale colour during printing. `grey' may be any number between 0 (black) and 1 (white); for example, 0.5 indicates a medium grey. The chosen colour will be rendered to the limits of the printer's halftoning capability. 3.1.16. print_hatched_colour() ------------------------------ int print_hatched_colour(drawing *dr, int hatch); This function allocates a colour index which does not represent a literal _colour_. Instead, regions shaded in this colour will be hatched with parallel lines. The `hatch' parameter defines what type of hatching should be used in place of this colour: HATCH_SLASH This colour will be hatched by lines slanting to the right at 45 degrees. HATCH_BACKSLASH This colour will be hatched by lines slanting to the left at 45 degrees. HATCH_HORIZ This colour will be hatched by horizontal lines. HATCH_VERT This colour will be hatched by vertical lines. HATCH_PLUS This colour will be hatched by criss-crossing horizontal and vertical lines. HATCH_X This colour will be hatched by criss-crossing diagonal lines. Colours defined to use hatching may not be used for drawing lines or text; they may only be used for filling areas. That is, they may be used as the `fillcolour' parameter to draw_circle() and draw_polygon(), and as the colour parameter to draw_rect(), but may not be used as the `outlinecolour' parameter to draw_circle() or draw_polygon(), or with draw_line() or draw_text(). 3.1.17. print_rgb_mono_colour() ------------------------------- int print_rgb_mono_colour(drawing *dr, float r, float g, float b, float grey); This function allocates a colour index for a fully specified RGB colour during printing. `r', `g' and `b' may each be anywhere in the range from 0 to 1. If printing in black and white only, these values will be ignored, and either pure black or pure white will be used instead, according to the `grey' parameter. (The fallback colour is the same as the one which would be allocated by print_mono_colour(grey).) 3.1.18. print_rgb_grey_colour() ------------------------------- int print_rgb_grey_colour(drawing *dr, float r, float g, float b, float grey); This function allocates a colour index for a fully specified RGB colour during printing. `r', `g' and `b' may each be anywhere in the range from 0 to 1. If printing in black and white only, these values will be ignored, and a shade of grey given by the `grey' parameter will be used instead. (The fallback colour is the same as the one which would be allocated by print_grey_colour(grey).) 3.1.19. print_rgb_hatched_colour() ---------------------------------- int print_rgb_hatched_colour(drawing *dr, float r, float g, float b, float hatched); This function allocates a colour index for a fully specified RGB colour during printing. `r', `g' and `b' may each be anywhere in the range from 0 to 1. If printing in black and white only, these values will be ignored, and a form of cross-hatching given by the `hatch' parameter will be used instead; see section 3.1.16 for the possible values of this parameter. (The fallback colour is the same as the one which would be allocated by print_hatched_colour(hatch).) 3.1.20. print_line_width() -------------------------- void print_line_width(drawing *dr, int width); This function is called to set the thickness of lines drawn during printing. It is meaningless in drawing: all lines drawn by draw_line(), draw_circle and draw_polygon() are one pixel in thickness. However, in printing there is no clear definition of a pixel and so line widths must be explicitly specified. The line width is specified in the usual coordinate system. Note, however, that it is a hint only: the central printing system may choose to vary line thicknesses at user request or due to printer capabilities. 3.1.21. print_line_dotted() --------------------------- void print_line_dotted(drawing *dr, int dotted); This function is called to toggle the drawing of dotted lines during printing. It is not supported during drawing. The parameter `dotted' is a boolean; TRUE means that future lines drawn by draw_line(), draw_circle and draw_polygon() will be dotted, and FALSE means that they will be solid. Some front ends may impose restrictions on the width of dotted lines. Asking for a dotted line via this front end will override any line width request if the front end requires it. 3.2. The drawing API as implemented by the front end ---------------------------------------------------- This section describes the drawing API in the function-pointer form in which it is implemented by a front end. (It isn't only platform-specific front ends which implement this API; the platform-independent module `ps.c' also provides an implementation of it which outputs PostScript. Thus, any platform which wants to do PS printing can do so with minimum fuss.) The following entries all describe function pointer fields in a structure called `drawing_api'. Each of the functions takes a `void *' context pointer, which it should internally cast back to a more useful type. Thus, a drawing _object_ (`drawing *)' suitable for passing to the back end redraw or printing functions is constructed by passing a `drawing_api' and a `void *' to the function drawing_new() (see section 3.3.1). 3.2.1. draw_text() ------------------ void (*draw_text)(void *handle, int x, int y, int fonttype, int fontsize, int align, int colour, char *text); This function behaves exactly like the back end draw_text() function; see section 3.1.7. 3.2.2. draw_rect() ------------------ void (*draw_rect)(void *handle, int x, int y, int w, int h, int colour); This function behaves exactly like the back end draw_rect() function; see section 3.1.1. 3.2.3. draw_line() ------------------ void (*draw_line)(void *handle, int x1, int y1, int x2, int y2, int colour); This function behaves exactly like the back end draw_line() function; see section 3.1.3. 3.2.4. draw_polygon() --------------------- void (*draw_polygon)(void *handle, int *coords, int npoints, int fillcolour, int outlinecolour); This function behaves exactly like the back end draw_polygon() function; see section 3.1.4. 3.2.5. draw_circle() -------------------- void (*draw_circle)(void *handle, int cx, int cy, int radius, int fillcolour, int outlinecolour); This function behaves exactly like the back end draw_circle() function; see section 3.1.5. 3.2.6. draw_thick_line() ------------------------ void draw_thick_line(drawing *dr, float thickness, float x1, float y1, float x2, float y2, int colour) This function behaves exactly like the back end draw_thick_line() function; see section 3.1.6. An implementation of this API which doesn't provide high-quality rendering of thick lines is permitted to define this function pointer to be NULL. The middleware in drawing.c will notice and provide a low- quality alternative using draw_polygon(). 3.2.7. draw_update() -------------------- void (*draw_update)(void *handle, int x, int y, int w, int h); This function behaves exactly like the back end draw_update() function; see section 3.1.11. An implementation of this API which only supports printing is permitted to define this function pointer to be NULL rather than bothering to define an empty function. The middleware in drawing.c will notice and avoid calling it. 3.2.8. clip() ------------- void (*clip)(void *handle, int x, int y, int w, int h); This function behaves exactly like the back end clip() function; see section 3.1.9. 3.2.9. unclip() --------------- void (*unclip)(void *handle); This function behaves exactly like the back end unclip() function; see section 3.1.10. 3.2.10. start_draw() -------------------- void (*start_draw)(void *handle); This function is called at the start of drawing. It allows the front end to initialise any temporary data required to draw with, such as device contexts. Implementations of this API which do not provide drawing services may define this function pointer to be NULL; it will never be called unless drawing is attempted. 3.2.11. end_draw() ------------------ void (*end_draw)(void *handle); This function is called at the end of drawing. It allows the front end to do cleanup tasks such as deallocating device contexts and scheduling appropriate GUI redraw events. Implementations of this API which do not provide drawing services may define this function pointer to be NULL; it will never be called unless drawing is attempted. 3.2.12. status_bar() -------------------- void (*status_bar)(void *handle, char *text); This function behaves exactly like the back end status_bar() function; see section 3.1.12. Front ends implementing this function need not worry about it being called repeatedly with the same text; the middleware code in status_bar() will take care of this. Implementations of this API which do not provide drawing services may define this function pointer to be NULL; it will never be called unless drawing is attempted. 3.2.13. blitter_new() --------------------- blitter *(*blitter_new)(void *handle, int w, int h); This function behaves exactly like the back end blitter_new() function; see section 3.1.13.1. Implementations of this API which do not provide drawing services may define this function pointer to be NULL; it will never be called unless drawing is attempted. 3.2.14. blitter_free() ---------------------- void (*blitter_free)(void *handle, blitter *bl); This function behaves exactly like the back end blitter_free() function; see section 3.1.13.2. Implementations of this API which do not provide drawing services may define this function pointer to be NULL; it will never be called unless drawing is attempted. 3.2.15. blitter_save() ---------------------- void (*blitter_save)(void *handle, blitter *bl, int x, int y); This function behaves exactly like the back end blitter_save() function; see section 3.1.13.3. Implementations of this API which do not provide drawing services may define this function pointer to be NULL; it will never be called unless drawing is attempted. 3.2.16. blitter_load() ---------------------- void (*blitter_load)(void *handle, blitter *bl, int x, int y); This function behaves exactly like the back end blitter_load() function; see section 3.1.13.4. Implementations of this API which do not provide drawing services may define this function pointer to be NULL; it will never be called unless drawing is attempted. 3.2.17. begin_doc() ------------------- void (*begin_doc)(void *handle, int pages); This function is called at the beginning of a printing run. It gives the front end an opportunity to initialise any required printing subsystem. It also provides the number of pages in advance. Implementations of this API which do not provide printing services may define this function pointer to be NULL; it will never be called unless printing is attempted. 3.2.18. begin_page() -------------------- void (*begin_page)(void *handle, int number); This function is called during printing, at the beginning of each page. It gives the page number (numbered from 1 rather than 0, so suitable for use in user-visible contexts). Implementations of this API which do not provide printing services may define this function pointer to be NULL; it will never be called unless printing is attempted. 3.2.19. begin_puzzle() ---------------------- void (*begin_puzzle)(void *handle, float xm, float xc, float ym, float yc, int pw, int ph, float wmm); This function is called during printing, just before printing a single puzzle on a page. It specifies the size and location of the puzzle on the page. `xm' and `xc' specify the horizontal position of the puzzle on the page, as a linear function of the page width. The front end is expected to multiply the page width by `xm', add `xc' (measured in millimetres), and use the resulting x-coordinate as the left edge of the puzzle. Similarly, `ym' and `yc' specify the vertical position of the puzzle as a function of the page height: the page height times `ym', plus `yc' millimetres, equals the desired distance from the top of the page to the top of the puzzle. (This unwieldy mechanism is required because not all printing systems can communicate the page size back to the software. The PostScript back end, for example, writes out PS which determines the page size at print time by means of calling `clippath', and centres the puzzles within that. Thus, exactly the same PS file works on A4 or on US Letter paper without needing local configuration, which simplifies matters.) pw and ph give the size of the puzzle in drawing API coordinates. The printing system will subsequently call the puzzle's own print function, which will in turn call drawing API functions in the expectation that an area pw by ph units is available to draw the puzzle on. Finally, wmm gives the desired width of the puzzle in millimetres. (The aspect ratio is expected to be preserved, so if the desired puzzle height is also needed then it can be computed as wmm*ph/pw.) Implementations of this API which do not provide printing services may define this function pointer to be NULL; it will never be called unless printing is attempted. 3.2.20. end_puzzle() -------------------- void (*end_puzzle)(void *handle); This function is called after the printing of a specific puzzle is complete. Implementations of this API which do not provide printing services may define this function pointer to be NULL; it will never be called unless printing is attempted. 3.2.21. end_page() ------------------ void (*end_page)(void *handle, int number); This function is called after the printing of a page is finished. Implementations of this API which do not provide printing services may define this function pointer to be NULL; it will never be called unless printing is attempted. 3.2.22. end_doc() ----------------- void (*end_doc)(void *handle); This function is called after the printing of the entire document is finished. This is the moment to close files, send things to the print spooler, or whatever the local convention is. Implementations of this API which do not provide printing services may define this function pointer to be NULL; it will never be called unless printing is attempted. 3.2.23. line_width() -------------------- void (*line_width)(void *handle, float width); This function is called to set the line thickness, during printing only. Note that the width is a float here, where it was an int as seen by the back end. This is because drawing.c may have scaled it on the way past. However, the width is still specified in the same coordinate system as the rest of the drawing. Implementations of this API which do not provide printing services may define this function pointer to be NULL; it will never be called unless printing is attempted. 3.2.24. text_fallback() ----------------------- char *(*text_fallback)(void *handle, const char *const *strings, int nstrings); This function behaves exactly like the back end text_fallback() function; see section 3.1.8. Implementations of this API which do not support any characters outside ASCII may define this function pointer to be NULL, in which case the central code in drawing.c will provide a default implementation. 3.3. The drawing API as called by the front end ----------------------------------------------- There are a small number of functions provided in drawing.c which the front end needs to _call_, rather than helping to implement. They are described in this section. 3.3.1. drawing_new() -------------------- drawing *drawing_new(const drawing_api *api, midend *me, void *handle); This function creates a drawing object. It is passed a `drawing_api', which is a structure containing nothing but function pointers; and also a `void *' handle. The handle is passed back to each function pointer when it is called. The `midend' parameter is used for rewriting the status bar contents: status_bar() (see section 3.1.12) has to call a function in the mid- end which might rewrite the status bar text. If the drawing object is to be used only for printing, or if the game is known not to call status_bar(), this parameter may be NULL. 3.3.2. drawing_free() --------------------- void drawing_free(drawing *dr); This function frees a drawing object. Note that the `void *' handle is not freed; if that needs cleaning up it must be done by the front end. 3.3.3. print_get_colour() ------------------------- void print_get_colour(drawing *dr, int colour, int printincolour, int *hatch, float *r, float *g, float *b) This function is called by the implementations of the drawing API functions when they are called in a printing context. It takes a colour index as input, and returns the description of the colour as requested by the back end. `printincolour' is TRUE iff the implementation is printing in colour. This will alter the results returned if the colour in question was specified with a black-and-white fallback value. If the colour should be rendered by hatching, `*hatch' is filled with the type of hatching desired. See section 3.1.15 for details of the values this integer can take. If the colour should be rendered as solid colour, `*hatch' is given a negative value, and `*r', `*g' and `*b' are filled with the RGB values of the desired colour (if printing in colour), or all filled with the grey-scale value (if printing in black and white). 4. The API provided by the mid-end ---------------------------------- This chapter documents the API provided by the mid-end to be called by the front end. You probably only need to read this if you are a front end implementor, i.e. you are porting Puzzles to a new platform. If you're only interested in writing new puzzles, you can safely skip this chapter. All the persistent state in the mid-end is encapsulated within a `midend' structure, to facilitate having multiple mid-ends in any port which supports multiple puzzle windows open simultaneously. Each `midend' is intended to handle the contents of a single puzzle window. 4.1. midend_new() ----------------- midend *midend_new(frontend *fe, const game *ourgame, const drawing_api *drapi, void *drhandle) Allocates and returns a new mid-end structure. The `fe' argument is stored in the mid-end. It will be used when calling back to functions such as activate_timer() (section 4.37), and will be passed on to the back end function colours() (section 2.8.6). The parameters `drapi' and `drhandle' are passed to drawing_new() (section 3.3.1) to construct a drawing object which will be passed to the back end function redraw() (section 2.8.10). Hence, all drawing- related function pointers defined in `drapi' can expect to be called with `drhandle' as their first argument. The `ourgame' argument points to a container structure describing a game back end. The mid-end thus created will only be capable of handling that one game. (So even in a monolithic front end containing all the games, this imposes the constraint that any individual puzzle window is tied to a single game. Unless, of course, you feel brave enough to change the mid-end for the window without closing the window...) 4.2. midend_free() ------------------ void midend_free(midend *me); Frees a mid-end structure and all its associated data. 4.3. midend_tilesize() ---------------------- int midend_tilesize(midend *me); Returns the `tilesize' parameter being used to display the current puzzle (section 2.8.3). 4.4. midend_set_params() ------------------------ void midend_set_params(midend *me, game_params *params); Sets the current game parameters for a mid-end. Subsequent games generated by midend_new_game() (section 4.8) will use these parameters until further notice. The usual way in which the front end will have an actual `game_params' structure to pass to this function is if it had previously got it from midend_fetch_preset() (section 4.16). Thus, this function is usually called in response to the user making a selection from the presets menu. 4.5. midend_get_params() ------------------------ game_params *midend_get_params(midend *me); Returns the current game parameters stored in this mid-end. The returned value is dynamically allocated, and should be freed when finished with by passing it to the game's own free_params() function (see section 2.3.5). 4.6. midend_size() ------------------ void midend_size(midend *me, int *x, int *y, int user_size); Tells the mid-end to figure out its window size. On input, `*x' and `*y' should contain the maximum or requested size for the window. (Typically this will be the size of the screen that the window has to fit on, or similar.) The mid-end will repeatedly call the back end function compute_size() (section 2.8.4), searching for a tile size that best satisfies the requirements. On exit, `*x' and `*y' will contain the size needed for the puzzle window's drawing area. (It is of course up to the front end to adjust this for any additional window furniture such as menu bars and window borders, if necessary. The status bar is also not included in this size.) Use `user_size' to indicate whether `*x' and `*y' are a requested size, or just a maximum size. If `user_size' is set to TRUE, the mid-end will treat the input size as a request, and will pick a tile size which approximates it _as closely as possible_, going over the game's preferred tile size if necessary to achieve this. The mid-end will also use the resulting tile size as its preferred one until further notice, on the assumption that this size was explicitly requested by the user. Use this option if you want your front end to support dynamic resizing of the puzzle window with automatic scaling of the puzzle to fit. If `user_size' is set to FALSE, then the game's tile size will never go over its preferred one, although it may go under in order to fit within the maximum bounds specified by `*x' and `*y'. This is the recommended approach when opening a new window at default size: the game will use its preferred size unless it has to use a smaller one to fit on the screen. If the tile size is shrunk for this reason, the change will not persist; if a smaller grid is subsequently chosen, the tile size will recover. The mid-end will try as hard as it can to return a size which is less than or equal to the input size, in both dimensions. In extreme circumstances it may fail (if even the lowest possible tile size gives window dimensions greater than the input), in which case it will return a size greater than the input size. Front ends should be prepared for this to happen (i.e. don't crash or fail an assertion), but may handle it in any way they see fit: by rejecting the game parameters which caused the problem, by opening a window larger than the screen regardless of inconvenience, by introducing scroll bars on the window, by drawing on a large bitmap and scaling it into a smaller window, or by any other means you can think of. It is likely that when the tile size is that small the game will be unplayable anyway, so don't put _too_ much effort into handling it creatively. If your platform has no limit on window size (or if you're planning to use scroll bars for large puzzles), you can pass dimensions of INT_MAX as input to this function. You should probably not do that _and_ set the `user_size' flag, though! The midend relies on the frontend calling midend_new_game() (section 4.8) before calling midend_size(). 4.7. midend_reset_tilesize() ---------------------------- void midend_reset_tilesize(midend *me); This function resets the midend's preferred tile size to that of the standard puzzle. As discussed in section 4.6, puzzle resizes are typically 'sticky', in that once the user has dragged the puzzle to a different window size, the resulting tile size will be remembered and used when the puzzle configuration changes. If you _don't_ want that, e.g. if you want to provide a command to explicitly reset the puzzle size back to its default, then you can call this just before calling midend_size() (which, in turn, you would probably call with `user_size' set to FALSE). 4.8. midend_new_game() ---------------------- void midend_new_game(midend *me); Causes the mid-end to begin a new game. Normally the game will be a new randomly generated puzzle. However, if you have previously called midend_game_id() or midend_set_config(), the game generated might be dictated by the results of those functions. (In particular, you _must_ call midend_new_game() after calling either of those functions, or else no immediate effect will be visible.) You will probably need to call midend_size() after calling this function, because if the game parameters have been changed since the last new game then the window size might need to change. (If you know the parameters _haven't_ changed, you don't need to do this.) This function will create a new `game_drawstate', but does not actually perform a redraw (since you often need to call midend_size() before the redraw can be done). So after calling this function and after calling midend_size(), you should then call midend_redraw(). (It is not necessary to call midend_force_redraw(); that will discard the draw state and create a fresh one, which is unnecessary in this case since there's a fresh one already. It would work, but it's usually excessive.) 4.9. midend_restart_game() -------------------------- void midend_restart_game(midend *me); This function causes the current game to be restarted. This is done by placing a new copy of the original game state on the end of the undo list (so that an accidental restart can be undone). This function automatically causes a redraw, i.e. the front end can expect its drawing API to be called from _within_ a call to this function. Some back ends require that midend_size() (section 4.6) is called before midend_restart_game(). 4.10. midend_force_redraw() --------------------------- void midend_force_redraw(midend *me); Forces a complete redraw of the puzzle window, by means of discarding the current `game_drawstate' and creating a new one from scratch before calling the game's redraw() function. The front end can expect its drawing API to be called from within a call to this function. Some back ends require that midend_size() (section 4.6) is called before midend_force_redraw(). 4.11. midend_redraw() --------------------- void midend_redraw(midend *me); Causes a partial redraw of the puzzle window, by means of simply calling the game's redraw() function. (That is, the only things redrawn will be things that have changed since the last redraw.) The front end can expect its drawing API to be called from within a call to this function. Some back ends require that midend_size() (section 4.6) is called before midend_redraw(). 4.12. midend_process_key() -------------------------- int midend_process_key(midend *me, int x, int y, int button); The front end calls this function to report a mouse or keyboard event. The parameters `x', `y' and `button' are almost identical to the ones passed to the back end function interpret_move() (section 2.7.1), except that the front end is _not_ required to provide the guarantees about mouse event ordering. The mid-end will sort out multiple simultaneous button presses and changes of button; the front end's responsibility is simply to pass on the mouse events it receives as accurately as possible. (Some platforms may need to emulate absent mouse buttons by means of using a modifier key such as Shift with another mouse button. This tends to mean that if Shift is pressed or released in the middle of a mouse drag, the mid-end will suddenly stop receiving, say, LEFT_DRAG events and start receiving RIGHT_DRAGs, with no intervening button release or press events. This too is something which the mid-end will sort out for you; the front end has no obligation to maintain sanity in this area.) The front end _should_, however, always eventually send some kind of button release. On some platforms this requires special effort: Windows, for example, requires a call to the system API function SetCapture() in order to ensure that your window receives a mouse-up event even if the pointer has left the window by the time the mouse button is released. On any platform that requires this sort of thing, the front end _is_ responsible for doing it. Calling this function is very likely to result in calls back to the front end's drawing API and/or activate_timer() (section 4.37). The return value from midend_process_key() is non-zero, unless the effect of the keypress was to request termination of the program. A front end should shut down the puzzle in response to a zero return. 4.13. midend_colours() ---------------------- float *midend_colours(midend *me, int *ncolours); Returns an array of the colours required by the game, in exactly the same format as that returned by the back end function colours() (section 2.8.6). Front ends should call this function rather than calling the back end's version directly, since the mid-end adds standard customisation facilities. (At the time of writing, those customisation facilities are implemented hackily by means of environment variables, but it's not impossible that they may become more full and formal in future.) 4.14. midend_timer() -------------------- void midend_timer(midend *me, float tplus); If the mid-end has called activate_timer() (section 4.37) to request regular callbacks for purposes of animation or timing, this is the function the front end should call on a regular basis. The argument `tplus' gives the time, in seconds, since the last time either this function was called or activate_timer() was invoked. One of the major purposes of timing in the mid-end is to perform move animation. Therefore, calling this function is very likely to result in calls back to the front end's drawing API. 4.15. midend_num_presets() -------------------------- int midend_num_presets(midend *me); Returns the number of game parameter presets supplied by this game. Front ends should use this function and midend_fetch_preset() to configure their presets menu rather than calling the back end directly, since the mid-end adds standard customisation facilities. (At the time of writing, those customisation facilities are implemented hackily by means of environment variables, but it's not impossible that they may become more full and formal in future.) 4.16. midend_fetch_preset() --------------------------- void midend_fetch_preset(midend *me, int n, char **name, game_params **params); Returns one of the preset game parameter structures for the game. On input `n' must be a non-negative integer and less than the value returned from midend_num_presets(). On output, `*name' is set to an ASCII string suitable for entering in the game's presets menu, and `*params' is set to the corresponding `game_params' structure. Both of the two output values are dynamically allocated, but they are owned by the mid-end structure: the front end should not ever free them directly, because they will be freed automatically during midend_free(). 4.17. midend_which_preset() --------------------------- int midend_which_preset(midend *me); Returns the numeric index of the preset game parameter structure which matches the current game parameters, or a negative number if no preset matches. Front ends could use this to maintain a tick beside one of the items in the menu (or tick the `Custom' option if the return value is less than zero). 4.18. midend_wants_statusbar() ------------------------------ int midend_wants_statusbar(midend *me); This function returns TRUE if the puzzle has a use for a textual status line (to display score, completion status, currently active tiles, time, or anything else). Front ends should call this function rather than talking directly to the back end. 4.19. midend_get_config() ------------------------- config_item *midend_get_config(midend *me, int which, char **wintitle); Returns a dialog box description for user configuration. On input, which should be set to one of three values, which select which of the various dialog box descriptions is returned: CFG_SETTINGS Requests the GUI parameter configuration box generated by the puzzle itself. This should be used when the user selects `Custom' from the game types menu (or equivalent). The mid-end passes this request on to the back end function configure() (section 2.3.8). CFG_DESC Requests a box suitable for entering a descriptive game ID (and viewing the existing one). The mid-end generates this dialog box description itself. This should be used when the user selects `Specific' from the game menu (or equivalent). CFG_SEED Requests a box suitable for entering a random-seed game ID (and viewing the existing one). The mid-end generates this dialog box description itself. This should be used when the user selects `Random Seed' from the game menu (or equivalent). The returned value is an array of config_items, exactly as described in section 2.3.8. Another returned value is an ASCII string giving a suitable title for the configuration window, in `*wintitle'. Both returned values are dynamically allocated and will need to be freed. The window title can be freed in the obvious way; the config_item array is a slightly complex structure, so a utility function free_cfg() is provided to free it for you. See section 5.2.6. (Of course, you will probably not want to free the config_item array until the dialog box is dismissed, because before then you will probably need to pass it to midend_set_config.) 4.20. midend_set_config() ------------------------- char *midend_set_config(midend *me, int which, config_item *cfg); Passes the mid-end the results of a configuration dialog box. `which' should have the same value which it had when midend_get_config() was called; `cfg' should be the array of `config_item's returned from midend_get_config(), modified to contain the results of the user's editing operations. This function returns NULL on success, or otherwise (if the configuration data was in some way invalid) an ASCII string containing an error message suitable for showing to the user. If the function succeeds, it is likely that the game parameters will have been changed and it is certain that a new game will be requested. The front end should therefore call midend_new_game(), and probably also re-think the window size using midend_size() and eventually perform a refresh using midend_redraw(). 4.21. midend_game_id() ---------------------- char *midend_game_id(midend *me, char *id); Passes the mid-end a string game ID (of any of the valid forms `params', `params:description' or `params#seed') which the mid-end will process and use for the next generated game. This function returns NULL on success, or otherwise (if the configuration data was in some way invalid) an ASCII string containing an error message (not dynamically allocated) suitable for showing to the user. In the event of an error, the mid-end's internal state will be left exactly as it was before the call. If the function succeeds, it is likely that the game parameters will have been changed and it is certain that a new game will be requested. The front end should therefore call midend_new_game(), and probably also re-think the window size using midend_size() and eventually case a refresh using midend_redraw(). 4.22. midend_get_game_id() -------------------------- char *midend_get_game_id(midend *me) Returns a descriptive game ID (i.e. one in the form `params:description') describing the game currently active in the mid- end. The returned string is dynamically allocated. 4.23. midend_get_random_seed() ------------------------------ char *midend_get_random_seed(midend *me) Returns a random game ID (i.e. one in the form `params#seedstring') describing the game currently active in the mid-end, if there is one. If the game was created by entering a description, no random seed will currently exist and this function will return NULL. The returned string, if it is non-NULL, is dynamically allocated. 4.24. midend_can_format_as_text_now() ------------------------------------- int midend_can_format_as_text_now(midend *me); Returns TRUE if the game code is capable of formatting puzzles of the currently selected game type as ASCII. If this returns FALSE, then midend_text_format() (section 4.25) will return NULL. 4.25. midend_text_format() -------------------------- char *midend_text_format(midend *me); Formats the current game's current state as ASCII text suitable for copying to the clipboard. The returned string is dynamically allocated. If the game's `can_format_as_text_ever' flag is FALSE, or if its can_format_as_text_now() function returns FALSE, then this function will return NULL. If the returned string contains multiple lines (which is likely), it will use the normal C line ending convention (\n only). On platforms which use a different line ending convention for data in the clipboard, it is the front end's responsibility to perform the conversion. 4.26. midend_solve() -------------------- char *midend_solve(midend *me); Requests the mid-end to perform a Solve operation. On success, NULL is returned. On failure, an error message (not dynamically allocated) is returned, suitable for showing to the user. The front end can expect its drawing API and/or activate_timer() to be called from within a call to this function. Some back ends require that midend_size() (section 4.6) is called before midend_solve(). 4.27. midend_status() --------------------- int midend_status(midend *me); This function returns +1 if the midend is currently displaying a game in a solved state, -1 if the game is in a permanently lost state, or 0 otherwise. This function just calls the back end's status() function. Front ends may wish to use this as a cue to proactively offer the option of starting a new game. (See section 2.8.9 for more detail about the back end's status() function and discussion of what should count as which status code.) 4.28. midend_can_undo() ----------------------- int midend_can_undo(midend *me); Returns TRUE if the midend is currently in a state where the undo operation is meaningful (i.e. at least one position exists on the undo chain before the present one). Front ends may wish to use this to visually activate and deactivate an undo button. 4.29. midend_can_redo() ----------------------- int midend_can_redo(midend *me); Returns TRUE if the midend is currently in a state where the redo operation is meaningful (i.e. at least one position exists on the redo chain after the present one). Front ends may wish to use this to visually activate and deactivate a redo button. 4.30. midend_serialise() ------------------------ void midend_serialise(midend *me, void (*write)(void *ctx, void *buf, int len), void *wctx); Calling this function causes the mid-end to convert its entire internal state into a long ASCII text string, and to pass that string (piece by piece) to the supplied `write' function. Desktop implementations can use this function to save a game in any state (including half-finished) to a disk file, by supplying a `write' function which is a wrapper on fwrite() (or local equivalent). Other implementations may find other uses for it, such as compressing the large and sprawling mid-end state into a manageable amount of memory when a palmtop application is suspended so that another one can run; in this case write might want to write to a memory buffer rather than a file. There may be other uses for it as well. This function will call back to the supplied `write' function a number of times, with the first parameter (`ctx') equal to `wctx', and the other two parameters pointing at a piece of the output string. 4.31. midend_deserialise() -------------------------- char *midend_deserialise(midend *me, int (*read)(void *ctx, void *buf, int len), void *rctx); This function is the counterpart to midend_serialise(). It calls the supplied read function repeatedly to read a quantity of data, and attempts to interpret that data as a serialised mid-end as output by midend_serialise(). The read function is called with the first parameter (`ctx') equal to `rctx', and should attempt to read `len' bytes of data into the buffer pointed to by `buf'. It should return FALSE on failure or TRUE on success. It should not report success unless it has filled the entire buffer; on platforms which might be reading from a pipe or other blocking data source, `read' is responsible for looping until the whole buffer has been filled. If the de-serialisation operation is successful, the mid-end's internal data structures will be replaced by the results of the load, and NULL will be returned. Otherwise, the mid-end's state will be completely unchanged and an error message (typically some variation on `save file is corrupt') will be returned. As usual, the error message string is not dynamically allocated. If this function succeeds, it is likely that the game parameters will have been changed. The front end should therefore probably re-think the window size using midend_size(), and probably cause a refresh using midend_redraw(). Because each mid-end is tied to a specific game back end, this function will fail if you attempt to read in a save file generated by a different game from the one configured in this mid-end, even if your application is a monolithic one containing all the puzzles. See section 4.32 for a helper function which will allow you to identify a save file before you instantiate your mid-end in the first place. 4.32. identify_game() --------------------- char *identify_game(char **name, int (*read)(void *ctx, void *buf, int len), void *rctx); This function examines a serialised midend stream, of the same kind used by midend_serialise() and midend_deserialise(), and returns the name field of the game back end from which it was saved. You might want this if your front end was a monolithic one containing all the puzzles, and you wanted to be able to load an arbitrary save file and automatically switch to the right game. Probably your next step would be to iterate through gamelist (section 4.34) looking for a game structure whose name field matched the returned string, and give an error if you didn't find one. On success, the return value of this function is NULL, and the game name string is written into *name. The caller should free that string after using it. On failure, *name is NULL, and the return value is an error message (which does not need freeing at all). (This isn't strictly speaking a midend function, since it doesn't accept or return a pointer to a midend. You'd probably call it just _before_ deciding what kind of midend you wanted to instantiate.) 4.33. midend_request_id_changes() --------------------------------- void midend_request_id_changes(midend *me, void (*notify)(void *), void *ctx); This function is called by the front end to request notification by the mid-end when the current game IDs (either descriptive or random-seed) change. This can occur as a result of keypresses ('n' for New Game, for example) or when a puzzle supersedes its game description (see section 2.11.2). After this function is called, any change of the game ids will cause the mid-end to call notify(ctx) after the change. This is for use by puzzles which want to present the game description to the user constantly (e.g. as an HTML hyperlink) instead of only showing it when the user explicitly requests it. This is a function I anticipate few front ends needing to implement, so I make it a callback rather than a static function in order to relieve most front ends of the need to provide an empty implementation. 4.34. Direct reference to the back end structure by the front end ----------------------------------------------------------------- Although _most_ things the front end needs done should be done by calling the mid-end, there are a few situations in which the front end needs to refer directly to the game back end structure. The most obvious of these is - passing the game back end as a parameter to midend_new(). There are a few other back end features which are not wrapped by the mid-end because there didn't seem much point in doing so: - fetching the `name' field to use in window titles and similar - reading the `can_configure', `can_solve' and `can_format_as_text_ever' fields to decide whether to add those items to the menu bar or equivalent - reading the `winhelp_topic' field (Windows only) - the GTK front end provides a `--generate' command-line option which directly calls the back end to do most of its work. This is not really part of the main front end code, though, and I'm not sure it counts. In order to find the game back end structure, the front end does one of two things: - If the particular front end is compiling a separate binary per game, then the back end structure is a global variable with the standard name `thegame': extern const game thegame; - If the front end is compiled as a monolithic application containing all the puzzles together (in which case the preprocessor symbol COMBINED must be defined when compiling most of the code base), then there will be two global variables defined: extern const game *gamelist[]; extern const int gamecount; `gamelist' will be an array of `gamecount' game structures, declared in the automatically constructed source module `list.c'. The application should search that array for the game it wants, probably by reaching into each game structure and looking at its `name' field. 4.35. Mid-end to front-end calls -------------------------------- This section describes the small number of functions which a front end must provide to be called by the mid-end or other standard utility modules. 4.36. get_random_seed() ----------------------- void get_random_seed(void **randseed, int *randseedsize); This function is called by a new mid-end, and also occasionally by game back ends. Its job is to return a piece of data suitable for using as a seed for initialisation of a new `random_state'. On exit, `*randseed' should be set to point at a newly allocated piece of memory containing some seed data, and `*randseedsize' should be set to the length of that data. A simple and entirely adequate implementation is to return a piece of data containing the current system time at the highest conveniently available resolution. 4.37. activate_timer() ---------------------- void activate_timer(frontend *fe); This is called by the mid-end to request that the front end begin calling it back at regular intervals. The timeout interval is left up to the front end; the finer it is, the smoother move animations will be, but the more CPU time will be used. Current front ends use values around 20ms (i.e. 50Hz). After this function is called, the mid-end will expect to receive calls to midend_timer() on a regular basis. 4.38. deactivate_timer() ------------------------ void deactivate_timer(frontend *fe); This is called by the mid-end to request that the front end stop calling midend_timer(). 4.39. fatal() ------------- void fatal(char *fmt, ...); This is called by some utility functions if they encounter a genuinely fatal error such as running out of memory. It is a variadic function in the style of printf(), and is expected to show the formatted error message to the user any way it can and then terminate the application. It must not return. 4.40. frontend_default_colour() ------------------------------- void frontend_default_colour(frontend *fe, float *output); This function expects to be passed a pointer to an array of three floats. It returns the platform's local preferred background colour in those three floats, as red, green and blue values (in that order) ranging from 0.0 to 1.0. This function should only ever be called by the back end function colours() (section 2.8.6). (Thus, it isn't a _midend_-to-frontend function as such, but there didn't seem to be anywhere else particularly good to put it. Sorry.) 5. Utility APIs --------------- This chapter documents a variety of utility APIs provided for the general use of the rest of the Puzzles code. 5.1. Random number generation ----------------------------- Platforms' local random number generators vary widely in quality and seed size. Puzzles therefore supplies its own high-quality random number generator, with the additional advantage of giving the same results if fed the same seed data on different platforms. This allows game random seeds to be exchanged between different ports of Puzzles and still generate the same games. Unlike the ANSI C rand() function, the Puzzles random number generator has an _explicit_ state object called a `random_state'. One of these is managed by each mid-end, for example, and passed to the back end to generate a game with. 5.1.1. random_new() ------------------- random_state *random_new(char *seed, int len); Allocates, initialises and returns a new `random_state'. The input data is used as the seed for the random number stream (i.e. using the same seed at a later time will generate the same stream). The seed data can be any data at all; there is no requirement to use printable ASCII, or NUL-terminated strings, or anything like that. 5.1.2. random_copy() -------------------- random_state *random_copy(random_state *tocopy); Allocates a new `random_state', copies the contents of another `random_state' into it, and returns the new state. If exactly the same sequence of functions is subseqently called on both the copy and the original, the results will be identical. This may be useful for speculatively performing some operation using a given random state, and later replaying that operation precisely. 5.1.3. random_free() -------------------- void random_free(random_state *state); Frees a `random_state'. 5.1.4. random_bits() -------------------- unsigned long random_bits(random_state *state, int bits); Returns a random number from 0 to 2^bits-1 inclusive. `bits' should be between 1 and 32 inclusive. 5.1.5. random_upto() -------------------- unsigned long random_upto(random_state *state, unsigned long limit); Returns a random number from 0 to limit-1 inclusive. 5.1.6. random_state_encode() ---------------------------- char *random_state_encode(random_state *state); Encodes the entire contents of a `random_state' in printable ASCII. Returns a dynamically allocated string containing that encoding. This can subsequently be passed to random_state_decode() to reconstruct the same `random_state'. 5.1.7. random_state_decode() ---------------------------- random_state *random_state_decode(char *input); Decodes a string generated by random_state_encode() and reconstructs an equivalent `random_state' to the one encoded, i.e. it should produce the same stream of random numbers. This function has no error reporting; if you pass it an invalid string it will simply generate an arbitrary random state, which may turn out to be noticeably non-random. 5.1.8. shuffle() ---------------- void shuffle(void *array, int nelts, int eltsize, random_state *rs); Shuffles an array into a random order. The interface is much like ANSI C qsort(), except that there's no need for a compare function. `array' is a pointer to the first element of the array. `nelts' is the number of elements in the array; `eltsize' is the size of a single element (typically measured using `sizeof'). `rs' is a `random_state' used to generate all the random numbers for the shuffling process. 5.2. Memory allocation ---------------------- Puzzles has some central wrappers on the standard memory allocation functions, which provide compile-time type checking, and run-time error checking by means of quitting the application if it runs out of memory. This doesn't provide the best possible recovery from memory shortage, but on the other hand it greatly simplifies the rest of the code, because nothing else anywhere needs to worry about NULL returns from allocation. 5.2.1. snew() ------------- var = snew(type); This macro takes a single argument which is a _type name_. It allocates space for one object of that type. If allocation fails it will call fatal() and not return; so if it does return, you can be confident that its return value is non-NULL. The return value is cast to the specified type, so that the compiler will type-check it against the variable you assign it into. Thus, this ensures you don't accidentally allocate memory the size of the wrong type and assign it into a variable of the right one (or vice versa!). 5.2.2. snewn() -------------- var = snewn(n, type); This macro is the array form of snew(). It takes two arguments; the first is a number, and the second is a type name. It allocates space for that many objects of that type, and returns a type-checked non-NULL pointer just as snew() does. 5.2.3. sresize() ---------------- var = sresize(var, n, type); This macro is a type-checked form of realloc(). It takes three arguments: an input memory block, a new size in elements, and a type. It re-sizes the input memory block to a size sufficient to contain that many elements of that type. It returns a type-checked non-NULL pointer, like snew() and snewn(). The input memory block can be NULL, in which case this function will behave exactly like snewn(). (In principle any ANSI-compliant realloc() implementation ought to cope with this, but I've never quite trusted it to work everywhere.) 5.2.4. sfree() -------------- void sfree(void *p); This function is pretty much equivalent to free(). It is provided with a dynamically allocated block, and frees it. The input memory block can be NULL, in which case this function will do nothing. (In principle any ANSI-compliant free() implementation ought to cope with this, but I've never quite trusted it to work everywhere.) 5.2.5. dupstr() --------------- char *dupstr(const char *s); This function dynamically allocates a duplicate of a C string. Like the snew() functions, it guarantees to return non-NULL or not return at all. (Many platforms provide the function strdup(). As well as guaranteeing never to return NULL, my version has the advantage of being defined _everywhere_, rather than inconveniently not quite everywhere.) 5.2.6. free_cfg() ----------------- void free_cfg(config_item *cfg); This function correctly frees an array of `config_item's, including walking the array until it gets to the end and freeing precisely those `sval' fields which are expected to be dynamically allocated. (See section 2.3.8 for details of the `config_item' structure.) 5.3. Sorted and counted tree functions -------------------------------------- Many games require complex algorithms for generating random puzzles, and some require moderately complex algorithms even during play. A common requirement during these algorithms is for a means of maintaining sorted or unsorted lists of items, such that items can be removed and added conveniently. For general use, Puzzles provides the following set of functions which maintain 2-3-4 trees in memory. (A 2-3-4 tree is a balanced tree structure, with the property that all lookups, insertions, deletions, splits and joins can be done in O(log N) time.) All these functions expect you to be storing a tree of `void *' pointers. You can put anything you like in those pointers. By the use of per-node element counts, these tree structures have the slightly unusual ability to look elements up by their numeric index within the list represented by the tree. This means that they can be used to store an unsorted list (in which case, every time you insert a new element, you must explicitly specify the position where you wish to insert it). They can also do numeric lookups in a sorted tree, which might be useful for (for example) tracking the median of a changing data set. As well as storing sorted lists, these functions can be used for storing `maps' (associative arrays), by defining each element of a tree to be a (key, value) pair. 5.3.1. newtree234() ------------------- tree234 *newtree234(cmpfn234 cmp); Creates a new empty tree, and returns a pointer to it. The parameter `cmp' determines the sorting criterion on the tree. Its prototype is typedef int (*cmpfn234)(void *, void *); If you want a sorted tree, you should provide a function matching this prototype, which returns like strcmp() does (negative if the first argument is smaller than the second, positive if it is bigger, zero if they compare equal). In this case, the function addpos234() will not be usable on your tree (because all insertions must respect the sorting order). If you want an unsorted tree, pass NULL. In this case you will not be able to use either add234() or del234(), or any other function such as find234() which depends on a sorting order. Your tree will become something more like an array, except that it will efficiently support insertion and deletion as well as lookups by numeric index. 5.3.2. freetree234() -------------------- void freetree234(tree234 *t); Frees a tree. This function will not free the _elements_ of the tree (because they might not be dynamically allocated, or you might be storing the same set of elements in more than one tree); it will just free the tree structure itself. If you want to free all the elements of a tree, you should empty it before passing it to freetree234(), by means of code along the lines of while ((element = delpos234(tree, 0)) != NULL) sfree(element); /* or some more complicated free function */ 5.3.3. add234() --------------- void *add234(tree234 *t, void *e); Inserts a new element `e' into the tree `t'. This function expects the tree to be sorted; the new element is inserted according to the sort order. If an element comparing equal to `e' is already in the tree, then the insertion will fail, and the return value will be the existing element. Otherwise, the insertion succeeds, and `e' is returned. 5.3.4. addpos234() ------------------ void *addpos234(tree234 *t, void *e, int index); Inserts a new element into an unsorted tree. Since there is no sorting order to dictate where the new element goes, you must specify where you want it to go. Setting `index' to zero puts the new element right at the start of the list; setting `index' to the current number of elements in the tree puts the new element at the end. Return value is `e', in line with add234() (although this function cannot fail except by running out of memory, in which case it will bomb out and die rather than returning an error indication). 5.3.5. index234() ----------------- void *index234(tree234 *t, int index); Returns a pointer to the `index'th element of the tree, or NULL if `index' is out of range. Elements of the tree are numbered from zero. 5.3.6. find234() ---------------- void *find234(tree234 *t, void *e, cmpfn234 cmp); Searches for an element comparing equal to `e' in a sorted tree. If `cmp' is NULL, the tree's ordinary comparison function will be used to perform the search. However, sometimes you don't want that; suppose, for example, each of your elements is a big structure containing a `char *' name field, and you want to find the element with a given name. You _could_ achieve this by constructing a fake element structure, setting its name field appropriately, and passing it to find234(), but you might find it more convenient to pass _just_ a name string to find234(), supplying an alternative comparison function which expects one of its arguments to be a bare name and the other to be a large structure containing a name field. Therefore, if `cmp' is not NULL, then it will be used to compare `e' to elements of the tree. The first argument passed to `cmp' will always be `e'; the second will be an element of the tree. (See section 5.3.1 for the definition of the `cmpfn234' function pointer type.) The returned value is the element found, or NULL if the search is unsuccessful. 5.3.7. findrel234() ------------------- void *findrel234(tree234 *t, void *e, cmpfn234 cmp, int relation); This function is like find234(), but has the additional ability to do a _relative_ search. The additional parameter `relation' can be one of the following values: REL234_EQ Find only an element that compares equal to `e'. This is exactly the behaviour of find234(). REL234_LT Find the greatest element that compares strictly less than `e'. `e' may be NULL, in which case it finds the greatest element in the whole tree (which could also be done by index234(t, count234(t)-1)). REL234_LE Find the greatest element that compares less than or equal to `e'. (That is, find an element that compares equal to `e' if possible, but failing that settle for something just less than it.) REL234_GT Find the smallest element that compares strictly greater than `e'. `e' may be NULL, in which case it finds the smallest element in the whole tree (which could also be done by index234(t, 0)). REL234_GE Find the smallest element that compares greater than or equal to `e'. (That is, find an element that compares equal to `e' if possible, but failing that settle for something just bigger than it.) Return value, as before, is the element found or NULL if no element satisfied the search criterion. 5.3.8. findpos234() ------------------- void *findpos234(tree234 *t, void *e, cmpfn234 cmp, int *index); This function is like find234(), but has the additional feature of returning the index of the element found in the tree; that index is written to `*index' in the event of a successful search (a non-NULL return value). `index' may be NULL, in which case this function behaves exactly like find234(). 5.3.9. findrelpos234() ---------------------- void *findrelpos234(tree234 *t, void *e, cmpfn234 cmp, int relation, int *index); This function combines all the features of findrel234() and findpos234(). 5.3.10. del234() ---------------- void *del234(tree234 *t, void *e); Finds an element comparing equal to `e' in the tree, deletes it, and returns it. The input tree must be sorted. The element found might be `e' itself, or might merely compare equal to it. Return value is NULL if no such element is found. 5.3.11. delpos234() ------------------- void *delpos234(tree234 *t, int index); Deletes the element at position `index' in the tree, and returns it. Return value is NULL if the index is out of range. 5.3.12. count234() ------------------ int count234(tree234 *t); Returns the number of elements currently in the tree. 5.3.13. splitpos234() --------------------- tree234 *splitpos234(tree234 *t, int index, int before); Splits the input tree into two pieces at a given position, and creates a new tree containing all the elements on one side of that position. If `before' is TRUE, then all the items at or after position `index' are left in the input tree, and the items before that point are returned in the new tree. Otherwise, the reverse happens: all the items at or after `index' are moved into the new tree, and those before that point are left in the old one. If `index' is equal to 0 or to the number of elements in the input tree, then one of the two trees will end up empty (and this is not an error condition). If `index' is further out of range in either direction, the operation will fail completely and return NULL. This operation completes in O(log N) time, no matter how large the tree or how balanced or unbalanced the split. 5.3.14. split234() ------------------ tree234 *split234(tree234 *t, void *e, cmpfn234 cmp, int rel); Splits a sorted tree according to its sort order. `rel' can be any of the relation constants described in section 5.3.7, _except_ for REL234_EQ. All the elements having that relation to `e' will be transferred into the new tree; the rest will be left in the old one. The parameter `cmp' has the same semantics as it does in find234(): if it is not NULL, it will be used in place of the tree's own comparison function when comparing elements to `e', in such a way that `e' itself is always the first of its two operands. Again, this operation completes in O(log N) time, no matter how large the tree or how balanced or unbalanced the split. 5.3.15. join234() ----------------- tree234 *join234(tree234 *t1, tree234 *t2); Joins two trees together by concatenating the lists they represent. All the elements of `t2' are moved into `t1', in such a way that they appear _after_ the elements of `t1'. The tree `t2' is freed; the return value is `t1'. If you apply this function to a sorted tree and it violates the sort order (i.e. the smallest element in `t2' is smaller than or equal to the largest element in `t1'), the operation will fail and return NULL. This operation completes in O(log N) time, no matter how large the trees being joined together. 5.3.16. join234r() ------------------ tree234 *join234r(tree234 *t1, tree234 *t2); Joins two trees together in exactly the same way as join234(), but this time the combined tree is returned in `t2', and `t1' is destroyed. The elements in `t1' still appear before those in `t2'. Again, this operation completes in O(log N) time, no matter how large the trees being joined together. 5.3.17. copytree234() --------------------- tree234 *copytree234(tree234 *t, copyfn234 copyfn, void *copyfnstate); Makes a copy of an entire tree. If `copyfn' is NULL, the tree will be copied but the elements will not be; i.e. the new tree will contain pointers to exactly the same physical elements as the old one. If you want to copy each actual element during the operation, you can instead pass a function in `copyfn' which makes a copy of each element. That function has the prototype typedef void *(*copyfn234)(void *state, void *element); and every time it is called, the `state' parameter will be set to the value you passed in as `copyfnstate'. 5.4. Miscellaneous utility functions and macros ----------------------------------------------- This section contains all the utility functions which didn't sensibly fit anywhere else. 5.4.1. TRUE and FALSE --------------------- The main Puzzles header file defines the macros TRUE and FALSE, which are used throughout the code in place of 1 and 0 (respectively) to indicate that the values are in a boolean context. For code base consistency, I'd prefer it if submissions of new code followed this convention as well. 5.4.2. max() and min() ---------------------- The main Puzzles header file defines the pretty standard macros max() and min(), each of which is given two arguments and returns the one which compares greater or less respectively. These macros may evaluate their arguments multiple times. Avoid side effects. 5.4.3. PI --------- The main Puzzles header file defines a macro PI which expands to a floating-point constant representing pi. (I've never understood why ANSI's doesn't define this. It'd be so useful!) 5.4.4. obfuscate_bitmap() ------------------------- void obfuscate_bitmap(unsigned char *bmp, int bits, int decode); This function obscures the contents of a piece of data, by cryptographic methods. It is useful for games of hidden information (such as Mines, Guess or Black Box), in which the game ID theoretically reveals all the information the player is supposed to be trying to guess. So in order that players should be able to send game IDs to one another without accidentally spoiling the resulting game by looking at them, these games obfuscate their game IDs using this function. Although the obfuscation function is cryptographic, it cannot properly be called encryption because it has no key. Therefore, anybody motivated enough can re-implement it, or hack it out of the Puzzles source, and strip the obfuscation off one of these game IDs to see what lies beneath. (Indeed, they could usually do it much more easily than that, by entering the game ID into their own copy of the puzzle and hitting Solve.) The aim is not to protect against a determined attacker; the aim is simply to protect people who wanted to play the game honestly from _accidentally_ spoiling their own fun. The input argument `bmp' points at a piece of memory to be obfuscated. `bits' gives the length of the data. Note that that length is in _bits_ rather than bytes: if you ask for obfuscation of a partial number of bytes, then you will get it. Bytes are considered to be used from the top down: thus, for example, setting `bits' to 10 will cover the whole of bmp[0] and the _top two_ bits of bmp[1]. The remainder of a partially used byte is undefined (i.e. it may be corrupted by the function). The parameter `decode' is FALSE for an encoding operation, and TRUE for a decoding operation. Each is the inverse of the other. (There's no particular reason you shouldn't obfuscate by decoding and restore cleartext by encoding, if you really wanted to; it should still work.) The input bitmap is processed in place. 5.4.5. bin2hex() ---------------- char *bin2hex(const unsigned char *in, int inlen); This function takes an input byte array and converts it into an ASCII string encoding those bytes in (lower-case) hex. It returns a dynamically allocated string containing that encoding. This function is useful for encoding the result of obfuscate_bitmap() in printable ASCII for use in game IDs. 5.4.6. hex2bin() ---------------- unsigned char *hex2bin(const char *in, int outlen); This function takes an ASCII string containing hex digits, and converts it back into a byte array of length `outlen'. If there aren't enough hex digits in the string, the contents of the resulting array will be undefined. This function is the inverse of bin2hex(). 5.4.7. game_mkhighlight() ------------------------- void game_mkhighlight(frontend *fe, float *ret, int background, int highlight, int lowlight); It's reasonably common for a puzzle game's graphics to use highlights and lowlights to indicate `raised' or `lowered' sections. Fifteen, Sixteen and Twiddle are good examples of this. Puzzles using this graphical style are running a risk if they just use whatever background colour is supplied to them by the front end, because that background colour might be too light to see any highlights on at all. (In particular, it's not unheard of for the front end to specify a default background colour of white.) Therefore, such puzzles can call this utility function from their colours() routine (section 2.8.6). You pass it your front end handle, a pointer to the start of your return array, and three colour indices. It will: - call frontend_default_colour() (section 4.40) to fetch the front end's default background colour - alter the brightness of that colour if it's unsuitable - define brighter and darker variants of the colour to be used as highlights and lowlights - write those results into the relevant positions in the `ret' array. Thus, ret[background*3] to ret[background*3+2] will be set to RGB values defining a sensible background colour, and similary `highlight' and `lowlight' will be set to sensible colours. 6. How to write a new puzzle ---------------------------- This chapter gives a guide to how to actually write a new puzzle: where to start, what to do first, how to solve common problems. The previous chapters have been largely composed of facts. This one is mostly advice. 6.1. Choosing a puzzle ---------------------- Before you start writing a puzzle, you have to choose one. Your taste in puzzle games is up to you, of course; and, in fact, you're probably reading this guide because you've _already_ thought of a game you want to write. But if you want to get it accepted into the official Puzzles distribution, then there's a criterion it has to meet. The current Puzzles editorial policy is that all games should be _fair_. A fair game is one which a player can only fail to complete through demonstrable lack of skill - that is, such that a better player in the same situation would have _known_ to do something different. For a start, that means every game presented to the user must have _at least one solution_. Giving the unsuspecting user a puzzle which is actually impossible is not acceptable. (There is an exception: if the user has selected some non-default option which is clearly labelled as potentially unfair, _then_ you're allowed to generate possibly insoluble puzzles, because the user isn't unsuspecting any more. Same Game and Mines both have options of this type.) Also, this actually _rules out_ games such as Klondike, or the normal form of Mahjong Solitaire. Those games have the property that even if there is a solution (i.e. some sequence of moves which will get from the start state to the solved state), the player doesn't necessarily have enough information to _find_ that solution. In both games, it is possible to reach a dead end because you had an arbitrary choice to make and made it the wrong way. This violates the fairness criterion, because a better player couldn't have known they needed to make the other choice. (GNOME has a variant on Mahjong Solitaire which makes it fair: there is a Shuffle operation which randomly permutes all the remaining tiles without changing their positions, which allows you to get out of a sticky situation. Using this operation adds a 60-second penalty to your solution time, so it's to the player's advantage to try to minimise the chance of having to use it. It's still possible to render the game uncompletable if you end up with only two tiles vertically stacked, but that's easy to foresee and avoid using a shuffle operation. This form of the game _is_ fair. Implementing it in Puzzles would require an infrastructure change so that the back end could communicate time penalties to the mid-end, but that would be easy enough.) Providing a _unique_ solution is a little more negotiable; it depends on the puzzle. Solo would have been of unacceptably low quality if it didn't always have a unique solution, whereas Twiddle inherently has multiple solutions by its very nature and it would have been meaningless to even _suggest_ making it uniquely soluble. Somewhere in between, Flip could reasonably be made to have unique solutions (by enforcing a zero- dimension kernel in every generated matrix) but it doesn't seem like a serious quality problem that it doesn't. Of course, you don't _have_ to care about all this. There's nothing stopping you implementing any puzzle you want to if you're happy to maintain your puzzle yourself, distribute it from your own web site, fork the Puzzles code completely, or anything like that. It's free software; you can do what you like with it. But any game that you want to be accepted into _my_ Puzzles code base has to satisfy the fairness criterion, which means all randomly generated puzzles must have a solution (unless the user has deliberately chosen otherwise) and it must be possible _in theory_ to find that solution without having to guess. 6.2. Getting started -------------------- The simplest way to start writing a new puzzle is to copy `nullgame.c'. This is a template puzzle source file which does almost nothing, but which contains all the back end function prototypes and declares the back end data structure correctly. It is built every time the rest of Puzzles is built, to ensure that it doesn't get out of sync with the code and remains buildable. So start by copying `nullgame.c' into your new source file. Then you'll gradually add functionality until the very boring Null Game turns into your real game. Next you'll need to add your puzzle to the Makefiles, in order to compile it conveniently. _Do not edit the Makefiles_: they are created automatically by the script `mkfiles.pl', from the file called `Recipe'. Edit `Recipe', and then re-run `mkfiles.pl'. Also, don't forget to add your puzzle to `list.c': if you don't, then it will still run fine on platforms which build each puzzle separately, but Mac OS X and other monolithic platforms will not include your new puzzle in their single binary. Once your source file is building, you can move on to the fun bit. 6.2.1. Puzzle generation ------------------------ Randomly generating instances of your puzzle is almost certain to be the most difficult part of the code, and also the task with the highest chance of turning out to be completely infeasible. Therefore I strongly recommend doing it _first_, so that if it all goes horribly wrong you haven't wasted any more time than you absolutely had to. What I usually do is to take an unmodified `nullgame.c', and start adding code to new_game_desc() which tries to generate a puzzle instance and print it out using printf(). Once that's working, _then_ I start connecting it up to the return value of new_game_desc(), populating other structures like `game_params', and generally writing the rest of the source file. There are many ways to generate a puzzle which is known to be soluble. In this section I list all the methods I currently know of, in case any of them can be applied to your puzzle. (Not all of these methods will work, or in some cases even make sense, for all puzzles.) Some puzzles are mathematically tractable, meaning you can work out in advance which instances are soluble. Sixteen, for example, has a parity constraint in some settings which renders exactly half the game space unreachable, but it can be mathematically proved that any position not in that half _is_ reachable. Therefore, Sixteen's grid generation simply consists of selecting at random from a well defined subset of the game space. Cube in its default state is even easier: _every_ possible arrangement of the blue squares and the cube's starting position is soluble! Another option is to redefine what you mean by `soluble'. Black Box takes this approach. There are layouts of balls in the box which are completely indistinguishable from one another no matter how many beams you fire into the box from which angles, which would normally be grounds for declaring those layouts unfair; but fortunately, detecting that indistinguishability is computationally easy. So Black Box doesn't demand that your ball placements match its own; it merely demands that your ball placements be _indistinguishable_ from the ones it was thinking of. If you have an ambiguous puzzle, then any of the possible answers is considered to be a solution. Having redefined the rules in that way, any puzzle is soluble again. Those are the simple techniques. If they don't work, you have to get cleverer. One way to generate a soluble puzzle is to start from the solved state and make inverse moves until you reach a starting state. Then you know there's a solution, because you can just list the inverse moves you made and make them in the opposite order to return to the solved state. This method can be simple and effective for puzzles where you get to decide what's a starting state and what's not. In Pegs, for example, the generator begins with one peg in the centre of the board and makes inverse moves until it gets bored; in this puzzle, valid inverse moves are easy to detect, and _any_ state that's reachable from the solved state by inverse moves is a reasonable starting position. So Pegs just continues making inverse moves until the board satisfies some criteria about extent and density, and then stops and declares itself done. For other puzzles, it can be a lot more difficult. Same Game uses this strategy too, and it's lucky to get away with it at all: valid inverse moves aren't easy to find (because although it's easy to insert additional squares in a Same Game position, it's difficult to arrange that _after_ the insertion they aren't adjacent to any other squares of the same colour), so you're constantly at risk of running out of options and having to backtrack or start again. Also, Same Game grids never start off half-empty, which means you can't just stop when you run out of moves - you have to find a way to fill the grid up _completely_. The other way to generate a puzzle that's soluble is to start from the other end, and actually write a _solver_. This tends to ensure that a puzzle has a _unique_ solution over and above having a solution at all, so it's a good technique to apply to puzzles for which that's important. One theoretical drawback of generating soluble puzzles by using a solver is that your puzzles are restricted in difficulty to those which the solver can handle. (Most solvers are not fully general: many sets of puzzle rules are NP-complete or otherwise nasty, so most solvers can only handle a subset of the theoretically soluble puzzles.) It's been my experience in practice, however, that this usually isn't a problem; computers are good at very different things from humans, and what the computer thinks is nice and easy might still be pleasantly challenging for a human. For example, when solving Dominosa puzzles I frequently find myself using a variety of reasoning techniques that my solver doesn't know about; in principle, therefore, I should be able to solve the puzzle using only those techniques it _does_ know about, but this would involve repeatedly searching the entire grid for the one simple deduction I can make. Computers are good at this sort of exhaustive search, but it's been my experience that human solvers prefer to do more complex deductions than to spend ages searching for simple ones. So in many cases I don't find my own playing experience to be limited by the restrictions on the solver. (This isn't _always_ the case. Solo is a counter-example; generating Solo puzzles using a simple solver does lead to qualitatively easier puzzles. Therefore I had to make the Solo solver rather more advanced than most of them.) There are several different ways to apply a solver to the problem of generating a soluble puzzle. I list a few of them below. The simplest approach is brute force: randomly generate a puzzle, use the solver to see if it's soluble, and if not, throw it away and try again until you get lucky. This is often a viable technique if all else fails, but it tends not to scale well: for many puzzle types, the probability of finding a uniquely soluble instance decreases sharply as puzzle size goes up, so this technique might work reasonably fast for small puzzles but take (almost) forever at larger sizes. Still, if there's no other alternative it can be usable: Pattern and Dominosa both use this technique. (However, Dominosa has a means of tweaking the randomly generated grids to increase the _probability_ of them being soluble, by ruling out one of the most common ambiguous cases. This improved generation speed by over a factor of 10 on the highest preset!) An approach which can be more scalable involves generating a grid and then tweaking it to make it soluble. This is the technique used by Mines and also by Net: first a random puzzle is generated, and then the solver is run to see how far it gets. Sometimes the solver will get stuck; when that happens, examine the area it's having trouble with, and make a small random change in that area to allow it to make more progress. Continue solving (possibly even without restarting the solver), tweaking as necessary, until the solver finishes. Then restart the solver from the beginning to ensure that the tweaks haven't caused new problems in the process of solving old ones (which can sometimes happen). This strategy works well in situations where the usual solver failure mode is to get stuck in an easily localised spot. Thus it works well for Net and Mines, whose most common failure mode tends to be that most of the grid is fine but there are a few widely separated ambiguous sections; but it would work less well for Dominosa, in which the way you get stuck is to have scoured the whole grid and not found anything you can deduce _anywhere_. Also, it relies on there being a low probability that tweaking the grid introduces a new problem at the same time as solving the old one; Mines and Net also have the property that most of their deductions are local, so that it's very unlikely for a tweak to affect something half way across the grid from the location where it was applied. In Dominosa, by contrast, a lot of deductions use information about half the grid (`out of all the sixes, only one is next to a three', which can depend on the values of up to 32 of the 56 squares in the default setting!), so this tweaking strategy would be rather less likely to work well. A more specialised strategy is that used in Solo and Slant. These puzzles have the property that they derive their difficulty from not presenting all the available clues. (In Solo's case, if all the possible clues were provided then the puzzle would already be solved; in Slant it would still require user action to fill in the lines, but it would present no challenge at all). Therefore, a simple generation technique is to leave the decision of which clues to provide until the last minute. In other words, first generate a random _filled_ grid with all possible clues present, and then gradually remove clues for as long as the solver reports that it's still soluble. Unlike the methods described above, this technique _cannot_ fail - once you've got a filled grid, nothing can stop you from being able to convert it into a viable puzzle. However, it wouldn't even be meaningful to apply this technique to (say) Pattern, in which clues can never be left out, so the only way to affect the set of clues is by altering the solution. (Unfortunately, Solo is complicated by the need to provide puzzles at varying difficulty levels. It's easy enough to generate a puzzle of _at most_ a given level of difficulty; you just have a solver with configurable intelligence, and you set it to a given level and apply the above technique, thus guaranteeing that the resulting grid is solvable by someone with at most that much intelligence. However, generating a puzzle of _at least_ a given level of difficulty is rather harder; if you go for _at most_ Intermediate level, you're likely to find that you've accidentally generated a Trivial grid a lot of the time, because removing just one number is sufficient to take the puzzle from Trivial straight to Ambiguous. In that situation Solo has no remaining options but to throw the puzzle away and start again.) A final strategy is to use the solver _during_ puzzle construction: lay out a bit of the grid, run the solver to see what it allows you to deduce, and then lay out a bit more to allow the solver to make more progress. There are articles on the web that recommend constructing Sudoku puzzles by this method (which is completely the opposite way round to how Solo does it); for Sudoku it has the advantage that you get to specify your clue squares in advance (so you can have them make pretty patterns). Rectangles uses a strategy along these lines. First it generates a grid by placing the actual rectangles; then it has to decide where in each rectangle to place a number. It uses a solver to help it place the numbers in such a way as to ensure a unique solution. It does this by means of running a test solver, but it runs the solver _before_ it's placed any of the numbers - which means the solver must be capable of coping with uncertainty about exactly where the numbers are! It runs the solver as far as it can until it gets stuck; then it narrows down the possible positions of a number in order to allow the solver to make more progress, and so on. Most of the time this process terminates with the grid fully solved, at which point any remaining number-placement decisions can be made at random from the options not so far ruled out. Note that unlike the Net/Mines tweaking strategy described above, this algorithm does not require a checking run after it completes: if it finishes successfully at all, then it has definitely produced a uniquely soluble puzzle. Most of the strategies described above are not 100% reliable. Each one has a failure rate: every so often it has to throw out the whole grid and generate a fresh one from scratch. (Solo's strategy would be the exception, if it weren't for the need to provide configurable difficulty levels.) Occasional failures are not a fundamental problem in this sort of work, however: it's just a question of dividing the grid generation time by the success rate (if it takes 10ms to generate a candidate grid and 1/5 of them work, then it will take 50ms on average to generate a viable one), and seeing whether the expected time taken to _successfully_ generate a puzzle is unacceptably slow. Dominosa's generator has a very low success rate (about 1 out of 20 candidate grids turn out to be usable, and if you think _that's_ bad then go and look at the source code and find the comment showing what the figures were before the generation-time tweaks!), but the generator itself is very fast so this doesn't matter. Rectangles has a slower generator, but fails well under 50% of the time. So don't be discouraged if you have an algorithm that doesn't always work: if it _nearly_ always works, that's probably good enough. The one place where reliability is important is that your algorithm must never produce false positives: it must not claim a puzzle is soluble when it isn't. It can produce false negatives (failing to notice that a puzzle is soluble), and it can fail to generate a puzzle at all, provided it doesn't do either so often as to become slow. One last piece of advice: for grid-based puzzles, when writing and testing your generation algorithm, it's almost always a good idea _not_ to test it initially on a grid that's square (i.e. w==h), because if the grid is square then you won't notice if you mistakenly write `h' instead of `w' (or vice versa) somewhere in the code. Use a rectangular grid for testing, and any size of grid will be likely to work after that. 6.2.2. Designing textual description formats -------------------------------------------- Another aspect of writing a puzzle which is worth putting some thought into is the design of the various text description formats: the format of the game parameter encoding, the game description encoding, and the move encoding. The first two of these should be reasonably intuitive for a user to type in; so provide some flexibility where possible. Suppose, for example, your parameter format consists of two numbers separated by an `x' to specify the grid dimensions (`10x10' or `20x15'), and then has some suffixes to specify other aspects of the game type. It's almost always a good idea in this situation to arrange that decode_params() can handle the suffixes appearing in any order, even if encode_params() only ever generates them in one order. These formats will also be expected to be reasonably stable: users will expect to be able to exchange game IDs with other users who aren't running exactly the same version of your game. So make them robust and stable: don't build too many assumptions into the game ID format which will have to be changed every time something subtle changes in the puzzle code. 6.3. Common how-to questions ---------------------------- This section lists some common things people want to do when writing a puzzle, and describes how to achieve them within the Puzzles framework. 6.3.1. Drawing objects at only one position ------------------------------------------- A common phenomenon is to have an object described in the `game_state' or the `game_ui' which can only be at one position. A cursor - probably specified in the `game_ui' - is a good example. In the `game_ui', it would _obviously_ be silly to have an array covering the whole game grid with a boolean flag stating whether the cursor was at each position. Doing that would waste space, would make it difficult to find the cursor in order to do anything with it, and would introduce the potential for synchronisation bugs in which you ended up with two cursors or none. The obviously sensible way to store a cursor in the `game_ui' is to have fields directly encoding the cursor's coordinates. However, it is a mistake to assume that the same logic applies to the `game_drawstate'. If you replicate the cursor position fields in the draw state, the redraw code will get very complicated. In the draw state, in fact, it _is_ probably the right thing to have a cursor flag for every position in the grid. You probably have an array for the whole grid in the drawstate already (stating what is currently displayed in the window at each position); the sensible approach is to add a `cursor' flag to each element of that array. Then the main redraw loop will look something like this (pseudo-code): for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { int value = state->symbol_at_position[y][x]; if (x == ui->cursor_x && y == ui->cursor_y) value |= CURSOR; if (ds->symbol_at_position[y][x] != value) { symbol_drawing_subroutine(dr, ds, x, y, value); ds->symbol_at_position[y][x] = value; } } } This loop is very simple, pretty hard to get wrong, and _automatically_ deals both with erasing the previous cursor and drawing the new one, with no special case code required. This type of loop is generally a sensible way to write a redraw function, in fact. The best thing is to ensure that the information stored in the draw state for each position tells you _everything_ about what was drawn there. A good way to ensure that is to pass precisely the same information, and _only_ that information, to a subroutine that does the actual drawing; then you know there's no additional information which affects the drawing but which you don't notice changes in. 6.3.2. Implementing a keyboard-controlled cursor ------------------------------------------------ It is often useful to provide a keyboard control method in a basically mouse-controlled game. A keyboard-controlled cursor is best implemented by storing its location in the `game_ui' (since if it were in the `game_state' then the user would have to separately undo every cursor move operation). So the procedure would be: - Put cursor position fields in the `game_ui'. - interpret_move() responds to arrow keys by modifying the cursor position fields and returning "". - interpret_move() responds to some sort of fire button by actually performing a move based on the current cursor location. - You might want an additional `game_ui' field stating whether the cursor is currently visible, and having it disappear when a mouse action occurs (so that it doesn't clutter the display when not actually in use). - You might also want to automatically hide the cursor in changed_state() when the current game state changes to one in which there is no move to make (which is the case in some types of completed game). - redraw() draws the cursor using the technique described in section 6.3.1. 6.3.3. Implementing draggable sprites ------------------------------------- Some games have a user interface which involves dragging some sort of game element around using the mouse. If you need to show a graphic moving smoothly over the top of other graphics, use a blitter (see section 3.1.13 for the blitter API) to save the background underneath it. The typical scenario goes: - Have a blitter field in the `game_drawstate'. - Set the blitter field to NULL in the game's new_drawstate() function, since you don't yet know how big the piece of saved background needs to be. - In the game's set_size() function, once you know the size of the object you'll be dragging around the display and hence the required size of the blitter, actually allocate the blitter. - In free_drawstate(), free the blitter if it's not NULL. - In interpret_move(), respond to mouse-down and mouse-drag events by updating some fields in the game_ui which indicate that a drag is in progress. - At the _very end_ of redraw(), after all other drawing has been done, draw the moving object if there is one. First save the background under the object in the blitter; then set a clip rectangle covering precisely the area you just saved (just in case anti-aliasing or some other error causes your drawing to go beyond the area you saved). Then draw the object, and call unclip(). Finally, set a flag in the game_drawstate that indicates that the blitter needs restoring. - At the very start of redraw(), before doing anything else at all, check the flag in the game_drawstate, and if it says the blitter needs restoring then restore it. (Then clear the flag, so that this won't happen again in the next redraw if no moving object is drawn this time.) This way, you will be able to write the rest of the redraw function completely ignoring the dragged object, as if it were floating above your bitmap and being completely separate. 6.3.4. Sharing large invariant data between all game states ----------------------------------------------------------- In some puzzles, there is a large amount of data which never changes between game states. The array of numbers in Dominosa is a good example. You _could_ dynamically allocate a copy of that array in every `game_state', and have dup_game() make a fresh copy of it for every new `game_state'; but it would waste memory and time. A more efficient way is to use a reference-counted structure. - Define a structure type containing the data in question, and also containing an integer reference count. - Have a field in `game_state' which is a pointer to this structure. - In new_game(), when creating a fresh game state at the start of a new game, create an instance of this structure, initialise it with the invariant data, and set its reference count to 1. - In dup_game(), rather than making a copy of the structure for the new game state, simply set the new game state to point at the same copy of the structure, and increment its reference count. - In free_game(), decrement the reference count in the structure pointed to by the game state; if the count reaches zero, free the structure. This way, the invariant data will persist for only as long as it's genuinely needed; _as soon_ as the last game state for a particular puzzle instance is freed, the invariant data for that puzzle will vanish as well. Reference counting is a very efficient form of garbage collection, when it works at all. (Which it does in this instance, of course, because there's no possibility of circular references.) 6.3.5. Implementing multiple types of flash ------------------------------------------- In some games you need to flash in more than one different way. Mines, for example, flashes white when you win, and flashes red when you tread on a mine and die. The simple way to do this is: - Have a field in the `game_ui' which describes the type of flash. - In flash_length(), examine the old and new game states to decide whether a flash is required and what type. Write the type of flash to the `game_ui' field whenever you return non-zero. - In redraw(), when you detect that `flash_time' is non-zero, examine the field in `game_ui' to decide which type of flash to draw. redraw() will never be called with `flash_time' non-zero unless flash_length() was first called to tell the mid-end that a flash was required; so whenever redraw() notices that `flash_time' is non-zero, you can be sure that the field in `game_ui' is correctly set. 6.3.6. Animating game moves --------------------------- A number of puzzle types benefit from a quick animation of each move you make. For some games, such as Fifteen, this is particularly easy. Whenever redraw() is called with `oldstate' non-NULL, Fifteen simply compares the position of each tile in the two game states, and if the tile is not in the same place then it draws it some fraction of the way from its old position to its new position. This method copes automatically with undo. Other games are less obvious. In Sixteen, for example, you can't just draw each tile a fraction of the way from its old to its new position: if you did that, the end tile would zip very rapidly past all the others to get to the other end and that would look silly. (Worse, it would look inconsistent if the end tile was drawn on top going one way and on the bottom going the other way.) A useful trick here is to define a field or two in the game state that indicates what the last move was. - Add a `last move' field to the `game_state' (or two or more fields if the move is complex enough to need them). - new_game() initialises this field to a null value for a new game state. - execute_move() sets up the field to reflect the move it just performed. - redraw() now needs to examine its `dir' parameter. If `dir' is positive, it determines the move being animated by looking at the last-move field in `newstate'; but if `dir' is negative, it has to look at the last-move field in `oldstate', and invert whatever move it finds there. Note also that Sixteen needs to store the _direction_ of the move, because you can't quite determine it by examining the row or column in question. You can in almost all cases, but when the row is precisely two squares long it doesn't work since a move in either direction looks the same. (You could argue that since moving a 2-element row left and right has the same effect, it doesn't matter which one you animate; but in fact it's very disorienting to click the arrow left and find the row moving right, and almost as bad to undo a move to the right and find the game animating _another_ move to the right.) 6.3.7. Animating drag operations -------------------------------- In Untangle, moves are made by dragging a node from an old position to a new position. Therefore, at the time when the move is initially made, it should not be animated, because the node has already been dragged to the right place and doesn't need moving there. However, it's nice to animate the same move if it's later undone or redone. This requires a bit of fiddling. The obvious approach is to have a flag in the `game_ui' which inhibits move animation, and to set that flag in interpret_move(). The question is, when would the flag be reset again? The obvious place to do so is changed_state(), which will be called once per move. But it will be called _before_ anim_length(), so if it resets the flag then anim_length() will never see the flag set at all. The solution is to have _two_ flags in a queue. - Define two flags in `game_ui'; let's call them `current' and `next'. - Set both to FALSE in `new_ui()'. - When a drag operation completes in interpret_move(), set the `next' flag to TRUE. - Every time changed_state() is called, set the value of `current' to the value in `next', and then set the value of `next' to FALSE. - That way, `current' will be TRUE _after_ a call to changed_state() if and only if that call to changed_state() was the result of a drag operation processed by interpret_move(). Any other call to changed_state(), due to an Undo or a Redo or a Restart or a Solve, will leave `current' FALSE. - So now anim_length() can request a move animation if and only if the `current' flag is _not_ set. 6.3.8. Inhibiting the victory flash when Solve is used ------------------------------------------------------ Many games flash when you complete them, as a visual congratulation for having got to the end of the puzzle. It often seems like a good idea to disable that flash when the puzzle is brought to a solved state by means of the Solve operation. This is easily done: - Add a `cheated' flag to the `game_state'. - Set this flag to FALSE in new_game(). - Have solve() return a move description string which clearly identifies the move as a solve operation. - Have execute_move() respond to that clear identification by setting the `cheated' flag in the returned `game_state'. The flag will then be propagated to all subsequent game states, even if the user continues fiddling with the game after it is solved. - flash_length() now returns non-zero if `oldstate' is not completed and `newstate' is, _and_ neither state has the `cheated' flag set. 6.4. Things to test once your puzzle is written ----------------------------------------------- Puzzle implementations written in this framework are self-testing as far as I could make them. Textual game and move descriptions, for example, are generated and parsed as part of the normal process of play. Therefore, if you can make moves in the game _at all_ you can be reasonably confident that the mid-end serialisation interface will function correctly and you will be able to save your game. (By contrast, if I'd stuck with a single make_move() function performing the jobs of both interpret_move() and execute_move(), and had separate functions to encode and decode a game state in string form, then those functions would not be used during normal play; so they could have been completely broken, and you'd never know it until you tried to save the game - which would have meant you'd have to test game saving _extensively_ and make sure to test every possible type of game state. As an added bonus, doing it the way I did leads to smaller save files.) There is one exception to this, which is the string encoding of the `game_ui'. Most games do not store anything permanent in the `game_ui', and hence do not need to put anything in its encode and decode functions; but if there is anything in there, you do need to test game loading and saving to ensure those functions work properly. It's also worth testing undo and redo of all operations, to ensure that the redraw and the animations (if any) work properly. Failing to animate undo properly seems to be a common error. Other than that, just use your common sense. puzzles-r9872/puzzles.txt0000644000175300017530000037073312161170560014716 0ustar simonsimon Simon Tatham's Portable Puzzle Collection ========================================= This is a collection of small one-player puzzle games. This manual is copyright 2004-2012 Simon Tatham. All rights reserved. You may distribute this documentation under the MIT licence. See appendix A for the licence text in full. Chapter 1: Introduction ----------------------- I wrote this collection because I thought there should be more small desktop toys available: little games you can pop up in a window and play for two or three minutes while you take a break from whatever else you were doing. And I was also annoyed that every time I found a good game on (say) Unix, it wasn't available the next time I was sitting at a Windows machine, or vice versa; so I arranged that everything in my personal puzzle collection will happily run on both, and have more recently done a port to Mac OS X as well. When I find (or perhaps invent) further puzzle games that I like, they'll be added to this collection and will immediately be available on both platforms. And if anyone feels like writing any other front ends - PocketPC, Mac OS pre-10, or whatever it might be - then all the games in this framework will immediately become available on another platform as well. The actual games in this collection were mostly not my invention; they are re-implementations of existing game concepts within my portable puzzle framework. I do not claim credit, in general, for inventing the rules of any of these puzzles. (I don't even claim authorship of all the code; some of the puzzles have been submitted by other authors.) This collection is distributed under the MIT licence (see appendix A). This means that you can do pretty much anything you like with the game binaries or the code, except pretending you wrote them yourself, or suing me if anything goes wrong. The most recent versions, and source code, can be found at http://www.chiark.greenend.org.uk/~sgtatham/puzzles/. Please report bugs to anakin@pobox.com. You might find it helpful to read this article before reporting a bug: http://www.chiark.greenend.org.uk/~sgtatham/bugs.html Patches are welcome. Especially if they provide a new front end (to make all these games run on another platform), or a new game. Chapter 2: Common features -------------------------- This chapter describes features that are common to all the games. 2.1 Common actions These actions are all available from the `Game' menu and via keyboard shortcuts, in addition to any game-specific actions. (On Mac OS X, to conform with local user interface standards, these actions are situated on the `File' and `Edit' menus instead.) _New game_ (`N', Ctrl+`N') Starts a new game, with a random initial state. _Restart game_ Resets the current game to its initial state. (This can be undone.) _Load_ Loads a saved game from a file on disk. _Save_ Saves the current state of your game to a file on disk. The Load and Save operations preserve your entire game history (so you can save, reload, and still Undo and Redo things you had done before saving). _Print_ Where supported (currently only on Windows), brings up a dialog allowing you to print an arbitrary number of puzzles randomly generated from the current parameters, optionally including the current puzzle. (Only for puzzles which make sense to print, of course - it's hard to think of a sensible printable representation of Fifteen!) _Undo_ (`U', Ctrl+`Z', Ctrl+`_') Undoes a single move. (You can undo moves back to the start of the session.) _Redo_ (`R', Ctrl+`R') Redoes a previously undone move. _Copy_ Copies the current state of your game to the clipboard in text format, so that you can paste it into (say) an e-mail client or a web message board if you're discussing the game with someone else. (Not all games support this feature.) _Solve_ Transforms the puzzle instantly into its solved state. For some games (Cube) this feature is not supported at all because it is of no particular use. For other games (such as Pattern), the solved state can be used to give you information, if you can't see how a solution can exist at all or you want to know where you made a mistake. For still other games (such as Sixteen), automatic solution tells you nothing about how to _get_ to the solution, but it does provide a useful way to get there quickly so that you can experiment with set-piece moves and transformations. Some games (such as Solo) are capable of solving a game ID you have typed in from elsewhere. Other games (such as Rectangles) cannot solve a game ID they didn't invent themself, but when they did invent the game ID they know what the solution is already. Still other games (Pattern) can solve _some_ external game IDs, but only if they aren't too difficult. The `Solve' command adds the solved state to the end of the undo chain for the puzzle. In other words, if you want to go back to solving it yourself after seeing the answer, you can just press Undo. _Quit_ (`Q', Ctrl+`Q') Closes the application entirely. 2.2 Specifying games with the game ID There are two ways to save a game specification out of a puzzle and recreate it later, or recreate it in somebody else's copy of the same puzzle. The `Specific' and `Random Seed' options from the `Game' menu (or the `File' menu, on Mac OS X) each show a piece of text (a `game ID') which is sufficient to reconstruct precisely the same game at a later date. You can enter either of these pieces of text back into the program (via the same `Specific' or `Random Seed' menu options) at a later point, and it will recreate the same game. You can also use either one as a command line argument (on Windows or Unix); see section 2.4 for more detail. The difference between the two forms is that a descriptive game ID is a literal _description_ of the initial state of the game, whereas a random seed is just a piece of arbitrary text which was provided as input to the random number generator used to create the puzzle. This means that: - Descriptive game IDs tend to be longer in many puzzles (although some, such as Cube (chapter 4), only need very short descriptions). So a random seed is often a _quicker_ way to note down the puzzle you're currently playing, or to tell it to somebody else so they can play the same one as you. - Any text at all is a valid random seed. The automatically generated ones are fifteen-digit numbers, but anything will do; you can type in your full name, or a word you just made up, and a valid puzzle will be generated from it. This provides a way for two or more people to race to complete the same puzzle: you think of a random seed, then everybody types it in at the same time, and nobody has an advantage due to having seen the generated puzzle before anybody else. - It is often possible to convert puzzles from other sources (such as `nonograms' or `sudoku' from newspapers) into descriptive game IDs suitable for use with these programs. - Random seeds are not guaranteed to produce the same result if you use them with a different _version_ of the puzzle program. This is because the generation algorithm might have been improved or modified in later versions of the code, and will therefore produce a different result when given the same sequence of random numbers. Use a descriptive game ID if you aren't sure that it will be used on the same version of the program as yours. (Use the `About' menu option to find out the version number of the program. Programs with the same version number running on different platforms should still be random-seed compatible.) A descriptive game ID starts with a piece of text which encodes the _parameters_ of the current game (such as grid size). Then there is a colon, and after that is the description of the game's initial state. A random seed starts with a similar string of parameters, but then it contains a hash sign followed by arbitrary data. If you enter a descriptive game ID, the program will not be able to show you the random seed which generated it, since it wasn't generated _from_ a random seed. If you _enter_ a random seed, however, the program will be able to show you the descriptive game ID derived from that random seed. Note that the game parameter strings are not always identical between the two forms. For some games, there will be parameter data provided with the random seed which is not included in the descriptive game ID. This is because that parameter information is only relevant when _generating_ puzzle grids, and is not important when playing them. Thus, for example, the difficulty level in Solo (chapter 11) is not mentioned in the descriptive game ID. These additional parameters are also not set permanently if you type in a game ID. For example, suppose you have Solo set to `Advanced' difficulty level, and then a friend wants your help with a `Trivial' puzzle; so the friend reads out a random seed specifying `Trivial' difficulty, and you type it in. The program will generate you the same `Trivial' grid which your friend was having trouble with, but once you have finished playing it, when you ask for a new game it will automatically go back to the `Advanced' difficulty which it was previously set on. 2.3 The `Type' menu The `Type' menu, if present, may contain a list of preset game settings. Selecting one of these will start a new random game with the parameters specified. The `Type' menu may also contain a `Custom' option which allows you to fine-tune game parameters. The parameters available are specific to each game and are described in the following sections. 2.4 Specifying game parameters on the command line (This section does not apply to the Mac OS X version.) The games in this collection deliberately do not ever save information on to the computer they run on: they have no high score tables and no saved preferences. (This is because I expect at least some people to play them at work, and those people will probably appreciate leaving as little evidence as possible!) However, if you do want to arrange for one of these games to default to a particular set of parameters, you can specify them on the command line. The easiest way to do this is to set up the parameters you want using the `Type' menu (see section 2.3), and then to select `Random Seed' from the `Game' or `File' menu (see section 2.2). The text in the `Game ID' box will be composed of two parts, separated by a hash. The first of these parts represents the game parameters (the size of the playing area, for example, and anything else you set using the `Type' menu). If you run the game with just that parameter text on the command line, it will start up with the settings you specified. For example: if you run Cube (see chapter 4), select `Octahedron' from the `Type' menu, and then go to the game ID selection, you will see a string of the form `o2x2#338686542711620'. Take only the part before the hash (`o2x2'), and start Cube with that text on the command line: `PREFIX-cube o2x2'. If you copy the _entire_ game ID on to the command line, the game will start up in the specific game that was described. This is occasionally a more convenient way to start a particular game ID than by pasting it into the game ID selection box. (You could also retrieve the encoded game parameters using the `Specific' menu option instead of `Random Seed', but if you do then some options, such as the difficulty level in Solo, will be missing. See section 2.2 for more details on this.) 2.5 Unix command-line options (This section only applies to the Unix port.) In addition to being able to specify game parameters on the command line (see section 2.4), there are various other options: --game --load These options respectively determine whether the command-line argument is treated as specifying game parameters or a save file to load. Only one should be specified. If neither of these options is specified, a guess is made based on the format of the argument. --generate _n_ If this option is specified, instead of a puzzle being displayed, a number of descriptive game IDs will be invented and printed on standard output. This is useful for gaining access to the game generation algorithms without necessarily using the frontend. If game parameters are specified on the command-line, they will be used to generate the game IDs; otherwise a default set of parameters will be used. The most common use of this option is in conjunction with `-- print', in which case its behaviour is slightly different; see below. --print _w_x_h_ If this option is specified, instead of a puzzle being displayed, a printed representation of one or more unsolved puzzles is sent to standard output, in PostScript format. On each page of puzzles, there will be _w_ across and _h_ down. If there are more puzzles than _w_x_h_, more than one page will be printed. If `--generate' has also been specified, the invented game IDs will be used to generate the printed output. Otherwise, a list of game IDs is expected on standard input (which can be descriptive or random seeds; see section 2.2), in the same format produced by `--generate'. For example: PREFIX-net --generate 12 --print 2x3 7x7w | lpr will generate two pages of printed Net puzzles (each of which will have a 7x7 wrapping grid), and pipe the output to the `lpr' command, which on many systems will send them to an actual printer. There are various other options which affect printing; see below. --save _file-prefix_ [ --save-suffix _file-suffix_ ] If this option is specified, instead of a puzzle being displayed, saved-game files for one or more unsolved puzzles are written to files constructed from the supplied prefix and/or suffix. If `--generate' has also been specified, the invented game IDs will be used to generate the printed output. Otherwise, a list of game IDs is expected on standard input (which can be descriptive or random seeds; see section 2.2), in the same format produced by `--generate'. For example: PREFIX-net --generate 12 --save game --save-suffix .sav will generate twelve Net saved-game files with the names game0.sav to game11.sav. --version Prints version information about the game, and then quits. The following options are only meaningful if `--print' is also specified: --with-solutions The set of pages filled with unsolved puzzles will be followed by the solutions to those puzzles. --scale _n_ Adjusts how big each puzzle is when printed. Larger numbers make puzzles bigger; the default is 1.0. --colour Puzzles will be printed in colour, rather than in black and white (if supported by the puzzle). Chapter 3: Net -------------- (_Note:_ the Windows version of this game is called NETGAME.EXE to avoid clashing with Windows's own NET.EXE.) I originally saw this in the form of a Flash game called FreeNet [1], written by Pavils Jurjans; there are several other implementations under the name NetWalk. The computer prepares a network by connecting up the centres of squares in a grid, and then shuffles the network by rotating every tile randomly. Your job is to rotate it all back into place. The successful solution will be an entirely connected network, with no closed loops. As a visual aid, all tiles which are connected to the one in the middle are highlighted. [1] http://www.jurjans.lv/stuff/net/FreeNet.htm 3.1 Net controls This game can be played with either the keyboard or the mouse. The controls are: _Select tile_: mouse pointer, arrow keys _Rotate tile anticlockwise_: left mouse button, `A' key _Rotate tile clockwise_: right mouse button, `D' key _Rotate tile by 180 degrees_: `F' key _Lock (or unlock) tile_: middle mouse button, shift-click, `S' key You can lock a tile once you're sure of its orientation. You can also unlock it again, but while it's locked you can't accidentally turn it. The following controls are not necessary to complete the game, but may be useful: _Shift grid_: Shift + arrow keys On grids that wrap, you can move the origin of the grid, so that tiles that were on opposite sides of the grid can be seen together. _Move centre_: Ctrl + arrow keys You can change which tile is used as the source of highlighting. (It doesn't ultimately matter which tile this is, as every tile will be connected to every other tile in a correct solution, but it may be helpful in the intermediate stages of solving the puzzle.) _Jumble tiles_: `J' key This key turns all tiles that are not locked to random orientations. (All the actions described in section 2.1 are also available.) 3.2 Net parameters These parameters are available from the `Custom...' option on the `Type' menu. _Width_, _Height_ Size of grid in tiles. _Walls wrap around_ If checked, flow can pass from the left edge to the right edge, and from top to bottom, and vice versa. _Barrier probability_ A number between 0.0 and 1.0 controlling whether an immovable barrier is placed between two tiles to prevent flow between them (a higher number gives more barriers). Since barriers are immovable, they act as constraints on the solution (i.e., hints). The grid generation in Net has been carefully arranged so that the barriers are independent of the rest of the grid. This means that if you note down the random seed used to generate the current puzzle (see section 2.2), change the _Barrier probability_ parameter, and then re-enter the same random seed, you should see exactly the same starting grid, with the only change being the number of barriers. So if you're stuck on a particular grid and need a hint, you could start up another instance of Net, set up the same parameters but a higher barrier probability, and enter the game seed from the original Net window. _Ensure unique solution_ Normally, Net will make sure that the puzzles it presents have only one solution. Puzzles with ambiguous sections can be more difficult and more subtle, so if you like you can turn off this feature and risk having ambiguous puzzles. (Also, finding _all_ the possible solutions can be an additional challenge for an advanced player.) Chapter 4: Cube --------------- This is another one I originally saw as a web game. This one was a Java game [2], by Paul Scott. You have a grid of 16 squares, six of which are blue; on one square rests a cube. Your move is to use the arrow keys to roll the cube through 90 degrees so that it moves to an adjacent square. If you roll the cube on to a blue square, the blue square is picked up on one face of the cube; if you roll a blue face of the cube on to a non-blue square, the blueness is put down again. (In general, whenever you roll the cube, the two faces that come into contact swap colours.) Your job is to get all six blue squares on to the six faces of the cube at the same time. Count your moves and try to do it in as few as possible. Unlike the original Java game, my version has an additional feature: once you've mastered the game with a cube rolling on a square grid, you can change to a triangular grid and roll any of a tetrahedron, an octahedron or an icosahedron. [2] http://www3.sympatico.ca/paulscott/cube/cube.htm 4.1 Cube controls This game can be played with either the keyboard or the mouse. Left-clicking anywhere on the window will move the cube (or other solid) towards the mouse pointer. The arrow keys can also used to roll the cube on its square grid in the four cardinal directions. On the triangular grids, the mapping of arrow keys to directions is more approximate. Vertical movement is disallowed where it doesn't make sense. The four keys surrounding the arrow keys on the numeric keypad (`7', `9', `1', `3') can be used for diagonal movement. (All the actions described in section 2.1 are also available.) 4.2 Cube parameters These parameters are available from the `Custom...' option on the `Type' menu. _Type of solid_ Selects the solid to roll (and hence the shape of the grid): tetrahedron, cube, octahedron, or icosahedron. _Width / top_, _Height / bottom_ On a square grid, horizontal and vertical dimensions. On a triangular grid, the number of triangles on the top and bottom rows respectively. Chapter 5: Fifteen ------------------ The old ones are the best: this is the good old `15-puzzle' with sliding tiles. You have a 4x4 square grid; 15 squares contain numbered tiles, and the sixteenth is empty. Your move is to choose a tile next to the empty space, and slide it into the space. The aim is to end up with the tiles in numerical order, with the space in the bottom right (so that the top row reads 1,2,3,4 and the bottom row reads 13,14,15,_space_). 5.1 Fifteen controls This game can be controlled with the mouse or the keyboard. A left-click with the mouse in the row or column containing the empty space will move as many tiles as necessary to move the space to the mouse pointer. The arrow keys will move a tile adjacent to the space in the direction indicated (moving the space in the _opposite_ direction). (All the actions described in section 2.1 are also available.) 5.2 Fifteen parameters The only options available from the `Custom...' option on the `Type' menu are _Width_ and _Height_, which are self-explanatory. (Once you've changed these, it's not a `15-puzzle' any more, of course!) Chapter 6: Sixteen ------------------ Another sliding tile puzzle, visually similar to Fifteen (see chapter 5) but with a different type of move. This time, there is no hole: all 16 squares on the grid contain numbered squares. Your move is to shift an entire row left or right, or shift an entire column up or down; every time you do that, the tile you shift off the grid re-appears at the other end of the same row, in the space you just vacated. To win, arrange the tiles into numerical order (1,2,3,4 on the top row, 13,14,15,16 on the bottom). When you've done that, try playing on different sizes of grid. I _might_ have invented this game myself, though only by accident if so (and I'm sure other people have independently invented it). I thought I was imitating a screensaver I'd seen, but I have a feeling that the screensaver might actually have been a Fifteen-type puzzle rather than this slightly different kind. So this might be the one thing in my puzzle collection which represents creativity on my part rather than just engineering. 6.1 Sixteen controls Left-clicking on an arrow will move the appropriate row or column in the direction indicated. Right-clicking will move it in the opposite direction. Alternatively, use the cursor keys to move the position indicator around the edge of the grid, and use the return key to move the row/column in the direction indicated. (All the actions described in section 2.1 are also available.) 6.2 Sixteen parameters The parameters available from the `Custom...' option on the `Type' menu are: - _Width_ and _Height_, which are self-explanatory. - You can ask for a limited shuffling operation to be performed on the grid. By default, Sixteen will shuffle the grid in such a way that any arrangement is about as probable as any other. You can override this by requesting a precise number of shuffling moves to be performed. Typically your aim is then to determine the precise set of shuffling moves and invert them exactly, so that you answer (say) a four-move shuffle with a four-move solution. Note that the more moves you ask for, the more likely it is that solutions shorter than the target length will turn out to be possible. Chapter 7: Twiddle ------------------ Twiddle is a tile-rearrangement puzzle, visually similar to Sixteen (see chapter 6): you are given a grid of square tiles, each containing a number, and your aim is to arrange the numbers into ascending order. In basic Twiddle, your move is to rotate a square group of four tiles about their common centre. (Orientation is not significant in the basic puzzle, although you can select it.) On more advanced settings, you can rotate a larger square group of tiles. I first saw this type of puzzle in the GameCube game `Metroid Prime 2'. In the Main Gyro Chamber in that game, there is a puzzle you solve to unlock a door, which is a special case of Twiddle. I developed this game as a generalisation of that puzzle. 7.1 Twiddle controls To play Twiddle, click the mouse in the centre of the square group you wish to rotate. In the basic mode, you rotate a 2x2 square, which means you have to click at a corner point where four tiles meet. In more advanced modes you might be rotating 3x3 or even more at a time; if the size of the square is odd then you simply click in the centre tile of the square you want to rotate. Clicking with the left mouse button rotates the group anticlockwise. Clicking with the right button rotates it clockwise. You can also move an outline square around the grid with the cursor keys; the square is the size above (2x2 by default, or larger). Pressing the return key or space bar will rotate the current square anticlockwise or clockwise respectively. (All the actions described in section 2.1 are also available.) 7.2 Twiddle parameters Twiddle provides several configuration options via the `Custom' option on the `Type' menu: - You can configure the width and height of the puzzle grid. - You can configure the size of square block that rotates at a time. - You can ask for every square in the grid to be distinguishable (the default), or you can ask for a simplified puzzle in which there are groups of identical numbers. In the simplified puzzle your aim is just to arrange all the 1s into the first row, all the 2s into the second row, and so on. - You can configure whether the orientation of tiles matters. If you ask for an orientable puzzle, each tile will have a triangle drawn in it. All the triangles must be pointing upwards to complete the puzzle. - You can ask for a limited shuffling operation to be performed on the grid. By default, Twiddle will shuffle the grid so much that any arrangement is about as probable as any other. You can override this by requesting a precise number of shuffling moves to be performed. Typically your aim is then to determine the precise set of shuffling moves and invert them exactly, so that you answer (say) a four-move shuffle with a four-move solution. Note that the more moves you ask for, the more likely it is that solutions shorter than the target length will turn out to be possible. Chapter 8: Rectangles --------------------- You have a grid of squares, with numbers written in some (but not all) of the squares. Your task is to subdivide the grid into rectangles of various sizes, such that (a) every rectangle contains exactly one numbered square, and (b) the area of each rectangle is equal to the number written in its numbered square. Credit for this game goes to the Japanese puzzle magazine Nikoli [3] ; I've also seen a Palm implementation at Puzzle Palace [4]. Unlike Puzzle Palace's implementation, my version automatically generates random grids of any size you like. The quality of puzzle design is therefore not quite as good as hand-crafted puzzles would be, but on the plus side you get an inexhaustible supply of puzzles tailored to your own specification. [3] http://www.nikoli.co.jp/puzzles/7/index_text-e.htm [4] http://www.puzzle.gr.jp/puzzle/sikaku/palm/index.html.en 8.1 Rectangles controls This game is played with the mouse or cursor keys. Left-click any edge to toggle it on or off, or left-click and drag to draw an entire rectangle (or line) on the grid in one go (removing any existing edges within that rectangle). Right-clicking and dragging will allow you to erase the contents of a rectangle without affecting its edges. Alternatively, use the cursor keys to move the position indicator around the board. Pressing the return key then allows you to use the cursor keys to drag a rectangle out from that position, and pressing the return key again completes the rectangle. Using the space bar instead of the return key allows you to erase the contents of a rectangle without affecting its edges, as above. When a rectangle of the correct size is completed, it will be shaded. (All the actions described in section 2.1 are also available.) 8.2 Rectangles parameters These parameters are available from the `Custom...' option on the `Type' menu. _Width_, _Height_ Size of grid, in squares. _Expansion factor_ This is a mechanism for changing the type of grids generated by the program. Some people prefer a grid containing a few large rectangles to one containing many small ones. So you can ask Rectangles to essentially generate a _smaller_ grid than the size you specified, and then to expand it by adding rows and columns. The default expansion factor of zero means that Rectangles will simply generate a grid of the size you ask for, and do nothing further. If you set an expansion factor of (say) 0.5, it means that each dimension of the grid will be expanded to half again as big after generation. In other words, the initial grid will be 2/3 the size in each dimension, and will be expanded to its full size without adding any more rectangles. Setting an expansion factor of around 0.5 tends to make the game more difficult, and also (in my experience) rewards a less deductive and more intuitive playing style. If you set it _too_ high, though, the game simply cannot generate more than a few rectangles to cover the entire grid, and the game becomes trivial. _Ensure unique solution_ Normally, Rectangles will make sure that the puzzles it presents have only one solution. Puzzles with ambiguous sections can be more difficult and more subtle, so if you like you can turn off this feature and risk having ambiguous puzzles. Also, finding _all_ the possible solutions can be an additional challenge for an advanced player. Turning off this option can also speed up puzzle generation. Chapter 9: Netslide ------------------- This game combines the grid generation of Net (see chapter 3) with the movement of Sixteen (see chapter 6): you have a Net grid, but instead of rotating tiles back into place you have to slide them into place by moving a whole row at a time. As in Sixteen, control is with the mouse or cursor keys. See section 6.1. The available game parameters have similar meanings to those in Net (see section 3.2) and Sixteen (see section 6.2). Netslide was contributed to this collection by Richard Boulton. Chapter 10: Pattern ------------------- You have a grid of squares, which must all be filled in either black or white. Beside each row of the grid are listed the lengths of the runs of black squares on that row; above each column are listed the lengths of the runs of black squares in that column. Your aim is to fill in the entire grid black or white. I first saw this puzzle form around 1995, under the name `nonograms'. I've seen it in various places since then, under different names. Normally, puzzles of this type turn out to be a meaningful picture of something once you've solved them. However, since this version generates the puzzles automatically, they will just look like random groupings of squares. (One user has suggested that this is actually a _good_ thing, since it prevents you from guessing the colour of squares based on the picture, and forces you to use logic instead.) The advantage, though, is that you never run out of them. 10.1 Pattern controls This game is played with the mouse. Left-click in a square to colour it black. Right-click to colour it white. If you make a mistake, you can middle-click, or hold down Shift while clicking with any button, to colour the square in the default grey (meaning `undecided') again. You can click and drag with the left or right mouse button to colour a vertical or horizontal line of squares black or white at a time (respectively). If you click and drag with the middle button, or with Shift held down, you can colour a whole rectangle of squares grey. You can also move around the grid with the cursor keys. Pressing the return key will cycle the current cell through empty, then black, then white, then empty, and the space bar does the same cycle in reverse. (All the actions described in section 2.1 are also available.) 10.2 Pattern parameters The only options available from the `Custom...' option on the `Type' menu are _Width_ and _Height_, which are self-explanatory. Chapter 11: Solo ---------------- You have a square grid, which is divided into as many equally sized sub-blocks as the grid has rows. Each square must be filled in with a digit from 1 to the size of the grid, in such a way that - every row contains only one occurrence of each digit - every column contains only one occurrence of each digit - every block contains only one occurrence of each digit. - (optionally, by default off) each of the square's two main diagonals contains only one occurrence of each digit. You are given some of the numbers as clues; your aim is to place the rest of the numbers correctly. Under the default settings, the sub-blocks are square or rectangular. The default puzzle size is 3x3 (a 9x9 actual grid, divided into nine 3x3 blocks). You can also select sizes with rectangular blocks instead of square ones, such as 2x3 (a 6x6 grid divided into six 3x2 blocks). Alternatively, you can select `jigsaw' mode, in which the sub-blocks are arbitrary shapes which differ between individual puzzles. Another available mode is `killer'. In this mode, clues are not given in the form of filled-in squares; instead, the grid is divided into `cages' by coloured lines, and for each cage the game tells you what the sum of all the digits in that cage should be. Also, no digit may appear more than once within a cage, even if the cage crosses the boundaries of existing regions. If you select a puzzle size which requires more than 9 digits, the additional digits will be letters of the alphabet. For example, if you select 3x4 then the digits which go in your grid will be 1 to 9, plus `a', `b' and `c'. This cannot be selected for killer puzzles. I first saw this puzzle in Nikoli [5], although it's also been popularised by various newspapers under the name `Sudoku' or `Su Doku'. Howard Garns is considered the inventor of the modern form of the puzzle, and it was first published in _Dell Pencil Puzzles and Word Games_. A more elaborate treatment of the history of the puzzle can be found on Wikipedia [6]. [5] http://www.nikoli.co.jp/puzzles/1/index_text-e.htm [6] http://en.wikipedia.org/wiki/Sudoku 11.1 Solo controls To play Solo, simply click the mouse in any empty square and then type a digit or letter on the keyboard to fill that square. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature). If you _right_-click in a square and then type a number, that number will be entered in the square as a `pencil mark'. You can have pencil marks for multiple numbers in the same square. Squares containing filled-in numbers cannot also contain pencil marks. The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular number, or you can use them as lists of the possible numbers in a given square, or anything else you feel like. To erase a single pencil mark, right-click in the square and type the same number again. All pencil marks in a square are erased when you left-click and type a number, or when you left-click and press space. Right-clicking and pressing space will also erase pencil marks. Alternatively, use the cursor keys to move the mark around the grid. Pressing the return key toggles the mark (from a normal mark to a pencil mark), and typing a number in is entered in the square in the appropriate way; typing in a 0 or using the space bar will clear a filled square. (All the actions described in section 2.1 are also available.) 11.2 Solo parameters Solo allows you to configure two separate dimensions of the puzzle grid on the `Type' menu: the number of columns, and the number of rows, into which the main grid is divided. (The size of a block is the inverse of this: for example, if you select 2 columns and 3 rows, each actual block will have 3 columns and 2 rows.) If you tick the `X' checkbox, Solo will apply the optional extra constraint that the two main diagonals of the grid also contain one of every digit. (This is sometimes known as `Sudoku-X' in newspapers.) In this mode, the squares on the two main diagonals will be shaded slightly so that you know it's enabled. If you tick the `Jigsaw' checkbox, Solo will generate randomly shaped sub-blocks. In this mode, the actual grid size will be taken to be the product of the numbers entered in the `Columns' and `Rows' boxes. There is no reason why you have to enter a number greater than 1 in both boxes; Jigsaw mode has no constraint on the grid size, and it can even be a prime number if you feel like it. If you tick the `Killer' checkbox, Solo will generate a set of of cages, which are randomly shaped and drawn in an outline of a different colour. Each of these regions contains a smaller clue which shows the digit sum of all the squares in this region. You can also configure the type of symmetry shown in the generated puzzles. More symmetry makes the puzzles look prettier but may also make them easier, since the symmetry constraints can force more clues than necessary to be present. Completely asymmetric puzzles have the freedom to contain as few clues as possible. Finally, you can configure the difficulty of the generated puzzles. Difficulty levels are judged by the complexity of the techniques of deduction required to solve the puzzle: each level requires a mode of reasoning which was not necessary in the previous one. In particular, on difficulty levels `Trivial' and `Basic' there will be a square you can fill in with a single number at all times, whereas at `Intermediate' level and beyond you will have to make partial deductions about the _set_ of squares a number could be in (or the set of numbers that could be in a square). At `Unreasonable' level, even this is not enough, and you will eventually have to make a guess, and then backtrack if it turns out to be wrong. Generating difficult puzzles is itself difficult: if you select one of the higher difficulty levels, Solo may have to make many attempts at generating a puzzle before it finds one hard enough for you. Be prepared to wait, especially if you have also configured a large puzzle size. Chapter 12: Mines ----------------- You have a grid of covered squares, some of which contain mines, but you don't know which. Your job is to uncover every square which does _not_ contain a mine. If you uncover a square containing a mine, you lose. If you uncover a square which does not contain a mine, you are told how many mines are contained within the eight surrounding squares. This game needs no introduction; popularised by Windows, it is perhaps the single best known desktop puzzle game in existence. This version of it has an unusual property. By default, it will generate its mine positions in such a way as to ensure that you never need to _guess_ where a mine is: you will always be able to deduce it somehow. So you will never, as can happen in other versions, get to the last four squares and discover that there are two mines left but you have no way of knowing for sure where they are. 12.1 Mines controls This game is played with the mouse. If you left-click in a covered square, it will be uncovered. If you right-click in a covered square, it will place a flag which indicates that the square is believed to be a mine. Left-clicking in a marked square will not uncover it, for safety. You can right-click again to remove a mark placed in error. If you left-click in an _uncovered_ square, it will `clear around' the square. This means: if the square has exactly as many flags surrounding it as it should have mines, then all the covered squares next to it which are _not_ flagged will be uncovered. So once you think you know the location of all the mines around a square, you can use this function as a shortcut to avoid having to click on each of the remaining squares one by one. If you uncover a square which has _no_ mines in the surrounding eight squares, then it is obviously safe to uncover those squares in turn, and so on if any of them also has no surrounding mines. This will be done for you automatically; so sometimes when you uncover a square, a whole new area will open up to be explored. You can also use the cursor keys to move around the minefield. Pressing the return key in a covered square uncovers it, and in an uncovered square will clear around it (so it acts as the left button), pressing the space bar in a covered square will place a flag (similarly, it acts as the right button). All the actions described in section 2.1 are also available. Even Undo is available, although you might consider it cheating to use it. If you step on a mine, the program will only reveal the mine in question (unlike most other implementations, which reveal all of them). You can then Undo your fatal move and continue playing if you like. The program will track the number of times you died (and Undo will not reduce that counter), so when you get to the end of the game you know whether or not you did it without making any errors. (If you really want to know the full layout of the grid, which other implementations will show you after you die, you can always use the Solve menu option.) 12.2 Mines parameters The options available from the `Custom...' option on the `Type' menu are: _Width_, _Height_ Size of grid in squares. _Mines_ Number of mines in the grid. You can enter this as an absolute mine count, or alternatively you can put a % sign on the end in which case the game will arrange for that proportion of the squares in the grid to be mines. Beware of setting the mine count too high. At very high densities, the program may spend forever searching for a solvable grid. _Ensure solubility_ When this option is enabled (as it is by default), Mines will ensure that the entire grid can be fully deduced starting from the initial open space. If you prefer the riskier grids generated by other implementations, you can switch off this option. Chapter 13: Same Game --------------------- You have a grid of coloured squares, which you have to clear by highlighting contiguous regions of more than one coloured square; the larger the region you highlight, the more points you get (and the faster you clear the arena). If you clear the grid you win. If you end up with nothing but single squares (i.e., there are no more clickable regions left) you lose. Removing a region causes the rest of the grid to shuffle up: blocks that are suspended will fall down (first), and then empty columns are filled from the right. Same Game was contributed to this collection by James Harvey. 13.1 Same Game controls This game can be played with either the keyboard or the mouse. If you left-click an unselected region, it becomes selected (possibly clearing the current selection). If you left-click the selected region, it will be removed (and the rest of the grid shuffled immediately). If you right-click the selected region, it will be unselected. The cursor keys move a cursor around the grid. Pressing the Space or Enter keys while the cursor is in an unselected region selects it; pressing Space or Enter again removes it as above. (All the actions described in section 2.1 are also available.) 13.2 Same Game parameters These parameters are available from the `Custom...' option on the `Type' menu. _Width_, _Height_ Size of grid in squares. _No. of colours_ Number of different colours used to fill the grid; the more colours, the fewer large regions of colour and thus the more difficult it is to successfully clear the grid. _Scoring system_ Controls the precise mechanism used for scoring. With the default system, `(n-2)^2', only regions of three squares or more will score any points at all. With the alternative `(n-1)^2' system, regions of two squares score a point each, and larger regions score relatively more points. _Ensure solubility_ If this option is ticked (the default state), generated grids will be guaranteed to have at least one solution. If you turn it off, the game generator will not try to guarantee soluble grids; it will, however, still ensure that there are at least 2 squares of each colour on the grid at the start (since a grid with exactly one square of a given colour is _definitely_ insoluble). Grids generated with this option disabled may contain more large areas of contiguous colour, leading to opportunities for higher scores; they can also take less time to generate. Chapter 14: Flip ---------------- You have a grid of squares, some light and some dark. Your aim is to light all the squares up at the same time. You can choose any square and flip its state from light to dark or dark to light, but when you do so, other squares around it change state as well. Each square contains a small diagram showing which other squares change when you flip it. 14.1 Flip controls This game can be played with either the keyboard or the mouse. Left-click in a square to flip it and its associated squares, or use the cursor keys to choose a square and the space bar or Enter key to flip. If you use the `Solve' function on this game, it will mark some of the squares in red. If you click once in every square with a red mark, the game should be solved. (If you click in a square _without_ a red mark, a red mark will appear in it to indicate that you will need to reverse that operation to reach the solution.) (All the actions described in section 2.1 are also available.) 14.2 Flip parameters These parameters are available from the `Custom...' option on the `Type' menu. _Width_, _Height_ Size of grid in squares. _Shape type_ This control determines the shape of the region which is flipped by clicking in any given square. The default setting, `Crosses', causes every square to flip itself and its four immediate neighbours (or three or two if it's at an edge or corner). The other setting, `Random', causes a random shape to be chosen for every square, so the game is different every time. Chapter 15: Guess ----------------- You have a set of coloured pegs, and have to reproduce a predetermined sequence of them (chosen by the computer) within a certain number of guesses. Each guess gets marked with the number of correctly-coloured pegs in the correct places (in black), and also the number of correctly- coloured pegs in the wrong places (in white). This game is also known (and marketed, by Hasbro, mainly) as a board game `Mastermind', with 6 colours, 4 pegs per row, and 10 guesses. However, this version allows custom settings of number of colours (up to 10), number of pegs per row, and number of guesses. Guess was contributed to this collection by James Harvey. 15.1 Guess controls This game can be played with either the keyboard or the mouse. With the mouse, drag a coloured peg from the tray on the left-hand side to its required position in the current guess; pegs may also be dragged from current and past guesses to copy them elsewhere. To remove a peg, drag it off its current position to somewhere invalid. Right-clicking in the current guess adds a `hold' marker; pegs that have hold markers will be automatically added to the next guess after marking. Alternatively, with the keyboard, the up and down cursor keys can be used to select a peg colour, the left and right keys to select a peg position, and the space bar or Enter key to place a peg of the selected colour in the chosen position. `D' or Backspace removes a peg, and `H' adds a hold marker. When the guess is complete, the smaller feedback pegs will be highlighted; clicking on these (or moving the peg cursor to them with the arrow keys and pressing the space bar or Enter key) will mark the current guess, copy any held pegs to the next guess, and move the `current guess' marker. If you correctly position all the pegs the solution will be displayed below; if you run out of guesses (or select `Solve...') the solution will also be revealed. (All the actions described in section 2.1 are also available.) 15.2 Guess parameters These parameters are available from the `Custom...' option on the `Type' menu. The default game matches the parameters for the board game `Mastermind'. _Colours_ Number of colours the solution is chosen from; from 2 to 10 (more is harder). _Pegs per guess_ Number of pegs per guess (more is harder). _Guesses_ Number of guesses you have to find the solution in (fewer is harder). _Allow blanks_ Allows blank pegs to be given as part of a guess (makes it easier, because you know that those will never be counted as part of the solution). This is turned off by default. Note that this doesn't allow blank pegs in the solution; if you really wanted that, use one extra colour. _Allow duplicates_ Allows the solution (and the guesses) to contain colours more than once; this increases the search space (making things harder), and is turned on by default. Chapter 16: Pegs ---------------- A number of pegs are placed in holes on a board. You can remove a peg by jumping an adjacent peg over it (horizontally or vertically) to a vacant hole on the other side. Your aim is to remove all but one of the pegs initially present. This game, best known as `Peg Solitaire', is possibly one of the oldest puzzle games still commonly known. 16.1 Pegs controls To move a peg, drag it with the mouse from its current position to its final position. If the final position is exactly two holes away from the initial position, is currently unoccupied by a peg, and there is a peg in the intervening square, the move will be permitted and the intervening peg will be removed. Vacant spaces which you can move a peg into are marked with holes. A space with no peg and no hole is not available for moving at all: it is an obstacle which you must work around. You can also use the cursor keys to move a position indicator around the board. Pressing the return key while over a peg, followed by a cursor key, will jump the peg in that direction (if that is a legal move). (All the actions described in section 2.1 are also available.) 16.2 Pegs parameters These parameters are available from the `Custom...' option on the `Type' menu. _Width_, _Height_ Size of grid in holes. _Board type_ Controls whether you are given a board of a standard shape or a randomly generated shape. The two standard shapes currently supported are `Cross' and `Octagon' (also commonly known as the English and European traditional board layouts respectively). Selecting `Random' will give you a different board shape every time (but always one that is known to have a solution). Chapter 17: Dominosa -------------------- A normal set of dominoes - that is, one instance of every (unordered) pair of numbers from 0 to 6 - has been arranged irregularly into a rectangle; then the number in each square has been written down and the dominoes themselves removed. Your task is to reconstruct the pattern by arranging the set of dominoes to match the provided array of numbers. This puzzle is widely credited to O. S. Adler, and takes part of its name from those initials. 17.1 Dominosa controls Left-clicking between any two adjacent numbers places a domino covering them, or removes one if it is already present. Trying to place a domino which overlaps existing dominoes will remove the ones it overlaps. Right-clicking between two adjacent numbers draws a line between them, which you can use to remind yourself that you know those two numbers are _not_ covered by a single domino. Right-clicking again removes the line. You can also use the cursor keys to move a cursor around the grid. When the cursor is half way between two adjacent numbers, pressing the return key will place a domino covering those numbers, or pressing the space bar will lay a line between the two squares. Repeating either action removes the domino or line. (All the actions described in section 2.1 are also available.) 17.2 Dominosa parameters These parameters are available from the `Custom...' option on the `Type' menu. _Maximum number on dominoes_ Controls the size of the puzzle, by controlling the size of the set of dominoes used to make it. Dominoes with numbers going up to N will give rise to an (N+2) x (N+1) rectangle; so, in particular, the default value of 6 gives an 8x7 grid. _Ensure unique solution_ Normally, Dominosa will make sure that the puzzles it presents have only one solution. Puzzles with ambiguous sections can be more difficult and sometimes more subtle, so if you like you can turn off this feature. Also, finding _all_ the possible solutions can be an additional challenge for an advanced player. Turning off this option can also speed up puzzle generation. Chapter 18: Untangle -------------------- You are given a number of points, some of which have lines drawn between them. You can move the points about arbitrarily; your aim is to position the points so that no line crosses another. I originally saw this in the form of a Flash game called Planarity [7], written by John Tantalo. [7] http://home.cwru.edu/~jnt5/Planarity 18.1 Untangle controls To move a point, click on it with the left mouse button and drag it into a new position. (All the actions described in section 2.1 are also available.) 18.2 Untangle parameters There is only one parameter available from the `Custom...' option on the `Type' menu: _Number of points_ Controls the size of the puzzle, by specifying the number of points in the generated graph. Chapter 19: Black Box --------------------- A number of balls are hidden in a rectangular arena. You have to deduce the positions of the balls by firing lasers positioned at the edges of the arena and observing how their beams are deflected. Beams will travel straight from their origin until they hit the opposite side of the arena (at which point they emerge), unless affected by balls in one of the following ways: - A beam that hits a ball head-on is absorbed and will never re- emerge. This includes beams that meet a ball on the first rank of the arena. - A beam with a ball to its front-left square gets deflected 90 degrees to the right. - A beam with a ball to its front-right square gets similarly deflected to the left. - A beam that would re-emerge from its entry location is considered to be `reflected'. - A beam which would get deflected before entering the arena by a ball to the front-left or front-right of its entry point is also considered to be `reflected'. Beams that are reflected appear as a `R'; beams that hit balls head- on appear as `H'. Otherwise, a number appears at the firing point and the location where the beam emerges (this number is unique to that shot). You can place guesses as to the location of the balls, based on the entry and exit patterns of the beams; once you have placed enough balls a button appears enabling you to have your guesses checked. Here is a diagram showing how the positions of balls can create each of the beam behaviours shown above: 1RHR---- |..O.O...| 2........3 |........| |........| 3........| |......O.| H........| |.....O..| 12-RH--- As shown, it is possible for a beam to receive multiple reflections before re-emerging (see turn 3). Similarly, a beam may be reflected (possibly more than once) before receiving a hit (the `H' on the left side of the example). Note that any layout with more than 4 balls may have a non-unique solution. The following diagram illustrates this; if you know the board contains 5 balls, it is impossible to determine where the fifth ball is (possible positions marked with an x): -------- |........| |........| |..O..O..| |...xx...| |...xx...| |..O..O..| |........| |........| -------- For this reason, when you have your guesses checked, the game will check that your solution _produces the same results_ as the computer's, rather than that your solution is identical to the computer's. So in the above example, you could put the fifth ball at _any_ of the locations marked with an x, and you would still win. Black Box was contributed to this collection by James Harvey. 19.1 Black Box controls To fire a laser beam, left-click in a square around the edge of the arena. The results will be displayed immediately. Clicking or holding the left button on one of these squares will highlight the current go (or a previous go) to confirm the exit point for that laser, if applicable. To guess the location of a ball, left-click within the arena and a black circle will appear marking the guess; click again to remove the guessed ball. Locations in the arena may be locked against modification by right- clicking; whole rows and columns may be similarly locked by right- clicking in the laser square above/below that column, or to the left/right of that row. The cursor keys may also be used to move around the grid. Pressing the Enter key will fire a laser or add a new ball-location guess, and pressing Space will lock a cell, row, or column. When an appropriate number of balls have been guessed, a button will appear at the top-left corner of the grid; clicking that (with mouse or cursor) will check your guesses. If you click the `check' button and your guesses are not correct, the game will show you the minimum information necessary to demonstrate this to you, so you can try again. If your ball positions are not consistent with the beam paths you already know about, one beam path will be circled to indicate that it proves you wrong. If your positions match all the existing beam paths but are still wrong, one new beam path will be revealed (written in red) which is not consistent with your current guesses. If you decide to give up completely, you can select Solve to reveal the actual ball positions. At this point, correctly-placed balls will be displayed as filled black circles, incorrectly-placed balls as filled black circles with red crosses, and missing balls as filled red circles. In addition, a red circle marks any laser you had already fired which is not consistent with your ball layout (just as when you press the `check' button), and red text marks any laser you _could_ have fired in order to distinguish your ball layout from the correct one. (All the actions described in section 2.1 are also available.) 19.2 Black Box parameters These parameters are available from the `Custom...' option on the `Type' menu. _Width_, _Height_ Size of grid in squares. There are 2 x _Width_ x _Height_ lasers per grid, two per row and two per column. _No. of balls_ Number of balls to place in the grid. This can be a single number, or a range (separated with a hyphen, like `2-6'), and determines the number of balls to place on the grid. The `reveal' button is only enabled if you have guessed an appropriate number of balls; a guess using a different number to the original solution is still acceptable, if all the beam inputs and outputs match. Chapter 20: Slant ----------------- You have a grid of squares. Your aim is to draw a diagonal line through each square, and choose which way each line slants so that the following conditions are met: - The diagonal lines never form a loop. - Any point with a circled number has precisely that many lines meeting at it. (Thus, a 4 is the centre of a cross shape, whereas a zero is the centre of a diamond shape - or rather, a partial diamond shape, because a zero can never appear in the middle of the grid because that would immediately cause a loop.) Credit for this puzzle goes to Nikoli [8]. [8] http://www.nikoli.co.jp/puzzles/39/index.htm (in Japanese) 20.1 Slant controls Left-clicking in a blank square will place a \ in it (a line leaning to the left, i.e. running from the top left of the square to the bottom right). Right-clicking in a blank square will place a / in it (leaning to the right, running from top right to bottom left). Continuing to click either button will cycle between the three possible square contents. Thus, if you left-click repeatedly in a blank square it will change from blank to \ to / back to blank, and if you right-click repeatedly the square will change from blank to / to \ back to blank. (Therefore, you can play the game entirely with one button if you need to.) You can also use the cursor keys to move around the grid. Pressing the return or space keys will place a \ or a /, respectively, and will then cycle them as above. (All the actions described in section 2.1 are also available.) 20.2 Slant parameters These parameters are available from the `Custom...' option on the `Type' menu. _Width_, _Height_ Size of grid in squares. _Difficulty_ Controls the difficulty of the generated puzzle. At Hard level, you are required to do deductions based on knowledge of _relationships_ between squares rather than always being able to deduce the exact contents of one square at a time. (For example, you might know that two squares slant in the same direction, even if you don't yet know what that direction is, and this might enable you to deduce something about still other squares.) Even at Hard level, guesswork and backtracking should never be necessary. Chapter 21: Light Up -------------------- You have a grid of squares. Some are filled in black; some of the black squares are numbered. Your aim is to `light up' all the empty squares by placing light bulbs in some of them. Each light bulb illuminates the square it is on, plus all squares in line with it horizontally or vertically unless a black square is blocking the way. To win the game, you must satisfy the following conditions: - All non-black squares are lit. - No light is lit by another light. - All numbered black squares have exactly that number of lights adjacent to them (in the four squares above, below, and to the side). Non-numbered black squares may have any number of lights adjacent to them. Credit for this puzzle goes to Nikoli [9]. Light Up was contributed to this collection by James Harvey. [9] http://www.nikoli.co.jp/puzzles/32/index-e.htm (beware of Flash) 21.1 Light Up controls Left-clicking in a non-black square will toggle the presence of a light in that square. Right-clicking in a non-black square toggles a mark there to aid solving; it can be used to highlight squares that cannot be lit, for example. You may not place a light in a marked square, nor place a mark in a lit square. The game will highlight obvious errors in red. Lights lit by other lights are highlighted in this way, as are numbered squares which do not (or cannot) have the right number of lights next to them. Thus, the grid is solved when all non-black squares have yellow highlights and there are no red lights. (All the actions described in section 2.1 are also available.) 21.2 Light Up parameters These parameters are available from the `Custom...' option on the `Type' menu. _Width_, _Height_ Size of grid in squares. _%age of black squares_ Rough percentage of black squares in the grid. This is a hint rather than an instruction. If the grid generator is unable to generate a puzzle to this precise specification, it will increase the proportion of black squares until it can. _Symmetry_ Allows you to specify the required symmetry of the black squares in the grid. (This does not affect the difficulty of the puzzles noticeably.) _Difficulty_ `Easy' means that the puzzles should be soluble without backtracking or guessing, `Hard' means that some guesses will probably be necessary. Chapter 22: Map --------------- You are given a map consisting of a number of regions. Your task is to colour each region with one of four colours, in such a way that no two regions sharing a boundary have the same colour. You are provided with some regions already coloured, sufficient to make the remainder of the solution unique. Only regions which share a length of border are required to be different colours. Two regions which meet at only one _point_ (i.e. are diagonally separated) may be the same colour. I believe this puzzle is original; I've never seen an implementation of it anywhere else. The concept of a four-colouring puzzle was suggested by Owen Dunn; credit must also go to Nikoli and to Verity Allan for inspiring the train of thought that led to me realising Owen's suggestion was a viable puzzle. Thanks also to Gareth Taylor for many detailed suggestions. 22.1 Map controls To colour a region, click the left mouse button on an existing region of the desired colour and drag that colour into the new region. (The program will always ensure the starting puzzle has at least one region of each colour, so that this is always possible!) If you need to clear a region, you can drag from an empty region, or from the puzzle boundary if there are no empty regions left. Dragging a colour using the _right_ mouse button will stipple the region in that colour, which you can use as a note to yourself that you think the region _might_ be that colour. A region can contain stipples in multiple colours at once. (This is often useful at the harder difficulty levels.) You can also use the cursor keys to move around the map: the colour of the cursor indicates the position of the colour you would drag (which is not obvious if you're on a region's boundary, since it depends on the direction from which you approached the boundary). Pressing the return key starts a drag of that colour, as above, which you control with the cursor keys; pressing the return key again finishes the drag. The space bar can be used similarly to create a stippled region. Double-pressing the return key (without moving the cursor) will clear the region, as a drag from an empty region does: this is useful with the cursor mode if you have filled the entire map in but need to correct the layout. If you press L during play, the game will toggle display of a number in each region of the map. This is useful if you want to discuss a particular puzzle instance with a friend - having an unambiguous name for each region is much easier than trying to refer to them all by names such as `the one down and right of the brown one on the top border'. (All the actions described in section 2.1 are also available.) 22.2 Map parameters These parameters are available from the `Custom...' option on the `Type' menu. _Width_, _Height_ Size of grid in squares. _Regions_ Number of regions in the generated map. _Difficulty_ In `Easy' mode, there should always be at least one region whose colour can be determined trivially. In `Normal' and `Hard' modes, you will have to use increasingly complex logic to deduce the colour of some regions. However, it will always be possible without having to guess or backtrack. In `Unreasonable' mode, the program will feel free to generate puzzles which are as hard as it can possibly make them: the only constraint is that they should still have a unique solution. Solving Unreasonable puzzles may require guessing and backtracking. Chapter 23: Loopy ----------------- You are given a grid of dots, marked with yellow lines to indicate which dots you are allowed to connect directly together. Your aim is to use some subset of those yellow lines to draw a single unbroken loop from dot to dot within the grid. Some of the spaces between the lines contain numbers. These numbers indicate how many of the lines around that space form part of the loop. The loop you draw must correctly satisfy all of these clues to be considered a correct solution. In the default mode, the dots are arranged in a grid of squares; however, you can also play on triangular or hexagonal grids, or even more exotic ones. Credit for the basic puzzle idea goes to Nikoli [10]. Loopy was originally contributed to this collection by Mike Pinna, and subsequently enhanced to handle various types of non-square grid by Lambros Lambrou. [10] http://www.nikoli.co.jp/puzzles/3/index-e.htm (beware of Flash) 23.1 Loopy controls Click the left mouse button on a yellow line to turn it black, indicating that you think it is part of the loop. Click again to turn the line yellow again (meaning you aren't sure yet). If you are sure that a particular line segment is _not_ part of the loop, you can click the right mouse button to remove it completely. Again, clicking a second time will turn the line back to yellow. (All the actions described in section 2.1 are also available.) 23.2 Loopy parameters These parameters are available from the `Custom...' option on the `Type' menu. _Width_, _Height_ Size of grid, measured in number of regions across and down. For square grids, it's clear how this is counted; for other types of grid you may have to think a bit to see how the dimensions are measured. _Grid type_ Allows you to choose between a selection of types of tiling. Some have all the faces the same but may have multiple different types of vertex (e.g. the _Cairo_ or _Kites_ mode); others have all the vertices the same but may have different types of face (e.g. the _Great Hexagonal_). The square, triangular and honeycomb grids are fully regular, and have all their vertices _and_ faces the same; this makes them the least confusing to play. _Difficulty_ Controls the difficulty of the generated puzzle. Chapter 24: Inertia ------------------- You are a small green ball sitting in a grid full of obstacles. Your aim is to collect all the gems without running into any mines. You can move the ball in any orthogonal _or diagonal_ direction. Once the ball starts moving, it will continue until something stops it. A wall directly in its path will stop it (but if it is moving diagonally, it will move through a diagonal gap between two other walls without stopping). Also, some of the squares are `stops'; when the ball moves on to a stop, it will stop moving no matter what direction it was going in. Gems do _not_ stop the ball; it picks them up and keeps on going. Running into a mine is fatal. Even if you picked up the last gem in the same move which then hit a mine, the game will count you as dead rather than victorious. This game was originally implemented for Windows by Ben Olmstead [11], who was kind enough to release his source code on request so that it could be re-implemented for this collection. [11] http://xn13.com/ 24.1 Inertia controls You can move the ball in any of the eight directions using the numeric keypad. Alternatively, if you click the left mouse button on the grid, the ball will begin a move in the general direction of where you clicked. If you use the `Solve' function on this game, the program will compute a path through the grid which collects all the remaining gems and returns to the current position. A hint arrow will appear on the ball indicating the direction in which you should move to begin on this path. If you then move in that direction, the arrow will update to indicate the next direction on the path. You can also press Space to automatically move in the direction of the hint arrow. If you move in a different direction from the one shown by the arrow, the hint arrows will stop appearing because you have strayed from the provided path; you can then use `Solve' again to generate a new path if you want to. All the actions described in section 2.1 are also available. In particular, if you do run into a mine and die, you can use the Undo function and resume playing from before the fatal move. The game will keep track of the number of times you have done this. 24.2 Inertia parameters These parameters are available from the `Custom...' option on the `Type' menu. _Width_, _Height_ Size of grid in squares. Chapter 25: Tents ----------------- You have a grid of squares, some of which contain trees. Your aim is to place tents in some of the remaining squares, in such a way that the following conditions are met: - There are exactly as many tents as trees. - The tents and trees can be matched up in such a way that each tent is directly adjacent (horizontally or vertically, but not diagonally) to its own tree. However, a tent may be adjacent to other trees as well as its own. - No two tents are adjacent horizontally, vertically _or diagonally_. - The number of tents in each row, and in each column, matches the numbers given round the sides of the grid. This puzzle can be found in several places on the Internet, and was brought to my attention by e-mail. I don't know who I should credit for inventing it. 25.1 Tents controls Left-clicking in a blank square will place a tent in it. Right- clicking in a blank square will colour it green, indicating that you are sure it _isn't_ a tent. Clicking either button in an occupied square will clear it. If you _drag_ with the right button along a row or column, every blank square in the region you cover will be turned green, and no other squares will be affected. (This is useful for clearing the remainder of a row once you have placed all its tents.) You can also use the cursor keys to move around the grid. Pressing the return key over an empty square will place a tent, and pressing the space bar over an empty square will colour it green; either key will clear an occupied square. (All the actions described in section 2.1 are also available.) 25.2 Tents parameters These parameters are available from the `Custom...' option on the `Type' menu. _Width_, _Height_ Size of grid in squares. _Difficulty_ Controls the difficulty of the generated puzzle. More difficult puzzles require more complex deductions, but at present none of the available difficulty levels requires guesswork or backtracking. Chapter 26: Bridges ------------------- You have a set of islands distributed across the playing area. Each island contains a number. Your aim is to connect the islands together with bridges, in such a way that: - Bridges run horizontally or vertically. - The number of bridges terminating at any island is equal to the number written in that island. - Two bridges may run in parallel between the same two islands, but no more than two may do so. - No bridge crosses another bridge. - All the islands are connected together. There are some configurable alternative modes, which involve changing the parallel-bridge limit to something other than 2, and introducing the additional constraint that no sequence of bridges may form a loop from one island back to the same island. The rules stated above are the default ones. Credit for this puzzle goes to Nikoli [12]. Bridges was contributed to this collection by James Harvey. [12] http://www.nikoli.co.jp/puzzles/14/index-e.htm 26.1 Bridges controls To place a bridge between two islands, click the mouse down on one island and drag it towards the other. You do not need to drag all the way to the other island; you only need to move the mouse far enough for the intended bridge direction to be unambiguous. (So you can keep the mouse near the starting island and conveniently throw bridges out from it in many directions.) Doing this again when a bridge is already present will add another parallel bridge. If there are already as many bridges between the two islands as permitted by the current game rules (i.e. two by default), the same dragging action will remove all of them. If you want to remind yourself that two islands definitely _do not_ have a bridge between them, you can right-drag between them in the same way to draw a `non-bridge' marker. If you think you have finished with an island (i.e. you have placed all its bridges and are confident that they are in the right places), you can mark the island as finished by left-clicking on it. This will highlight it and all the bridges connected to it, and you will be prevented from accidentally modifying any of those bridges in future. Left-clicking again on a highlighted island will unmark it and restore your ability to modify it. You can also use the cursor keys to move around the grid: if possible the cursor will always move orthogonally, otherwise it will move towards the nearest island to the indicated direction. Pressing the return key followed by a cursor key will lay a bridge in that direction (if available); pressing the space bar followed by a cursor key will lay a `non-bridge' marker. You can mark an island as finished by pressing the return key twice. Violations of the puzzle rules will be marked in red: - An island with too many bridges will be highlighted in red. - An island with too few bridges will be highlighted in red if it is definitely an error (as opposed to merely not being finished yet): if adding enough bridges would involve having to cross another bridge or remove a non-bridge marker, or if the island has been highlighted as complete. - A group of islands and bridges may be highlighted in red if it is a closed subset of the puzzle with no way to connect it to the rest of the islands. For example, if you directly connect two 1s together with a bridge and they are not the only two islands on the grid, they will light up red to indicate that such a group cannot be contained in any valid solution. - If you have selected the (non-default) option to disallow loops in the solution, a group of bridges which forms a loop will be highlighted. (All the actions described in section 2.1 are also available.) 26.2 Bridges parameters These parameters are available from the `Custom...' option on the `Type' menu. _Width_, _Height_ Size of grid in squares. _Difficulty_ Difficulty level of puzzle. _Allow loops_ This is set by default. If cleared, puzzles will be generated in such a way that they are always soluble without creating a loop, and solutions which do involve a loop will be disallowed. _Max. bridges per direction_ Maximum number of bridges in any particular direction. The default is 2, but you can change it to 1, 3 or 4. In general, fewer is easier. _%age of island squares_ Gives a rough percentage of islands the generator will try and lay before finishing the puzzle. Certain layouts will not manage to lay enough islands; this is an upper bound. _Expansion factor (%age)_ The grid generator works by picking an existing island at random (after first creating an initial island somewhere). It then decides on a direction (at random), and then works out how far it could extend before creating another island. This parameter determines how likely it is to extend as far as it can, rather than choosing somewhere closer. High expansion factors usually mean easier puzzles with fewer possible islands; low expansion factors can create lots of tightly- packed islands. Chapter 27: Unequal ------------------- You have a square grid; each square may contain a digit from 1 to the size of the grid, and some squares have clue signs between them. Your aim is to fully populate the grid with numbers such that: - Each row contains only one occurrence of each digit - Each column contains only one occurrence of each digit - All the clue signs are satisfied. There are two modes for this game, `Unequal' and `Adjacent'. In `Unequal' mode, the clue signs are greater-than symbols indicating one square's value is greater than its neighbour's. In this mode not all clues may be visible, particularly at higher difficulty levels. In `Adjacent' mode, the clue signs are bars indicating one square's value is numerically adjacent (i.e. one higher or one lower) than its neighbour. In this mode all clues are always visible: absence of a bar thus means that a square's value is definitely not numerically adjacent to that neighbour's. In `Trivial' difficulty level (available via the `Custom' game type selector), there are no greater-than signs in `Unequal' mode; the puzzle is to solve the Latin square only. At the time of writing, the `Unequal' mode of this puzzle is appearing in the Guardian weekly under the name `Futoshiki'. Unequal was contributed to this collection by James Harvey. 27.1 Unequal controls Unequal shares much of its control system with Solo. To play Unequal, simply click the mouse in any empty square and then type a digit or letter on the keyboard to fill that square. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature). If you _right_-click in a square and then type a number, that number will be entered in the square as a `pencil mark'. You can have pencil marks for multiple numbers in the same square. Squares containing filled-in numbers cannot also contain pencil marks. The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular number, or you can use them as lists of the possible numbers in a given square, or anything else you feel like. To erase a single pencil mark, right-click in the square and type the same number again. All pencil marks in a square are erased when you left-click and type a number, or when you left-click and press space. Right-clicking and pressing space will also erase pencil marks. As for Solo, the cursor keys can be used in conjunction with the digit keys to set numbers or pencil marks. You can also use the 'M' key to auto-fill every numeric hint, ready for removal as required, or the 'H' key to do the same but also to remove all obvious hints. Alternatively, use the cursor keys to move the mark around the grid. Pressing the return key toggles the mark (from a normal mark to a pencil mark), and typing a number in is entered in the square in the appropriate way; typing in a 0 or using the space bar will clear a filled square. (All the actions described in section 2.1 are also available.) 27.2 Unequal parameters These parameters are available from the `Custom...' option on the `Type' menu. _Mode_ Mode of the puzzle (`Unequal' or `Adjacent') _Size (s*s)_ Size of grid. _Difficulty_ Controls the difficulty of the generated puzzle. At Trivial level, there are no greater-than signs; the puzzle is to solve the Latin square only. At Recursive level (only available via the `Custom' game type selector) backtracking will be required, but the solution should still be unique. The levels in between require increasingly complex reasoning to avoid having to backtrack. Chapter 28: Galaxies -------------------- You have a rectangular grid containing a number of dots. Your aim is to draw edges along the grid lines which divide the rectangle into regions in such a way that every region is 180-degree rotationally symmetric, and contains exactly one dot which is located at its centre of symmetry. This puzzle was invented by Nikoli [13], under the name `Tentai Show'; its name is commonly translated into English as `Spiral Galaxies'. Galaxies was contributed to this collection by James Harvey. [13] http://www.nikoli.co.jp/en/puzzles/astronomical_show/ 28.1 Galaxies controls Left-click on any grid line to draw an edge if there isn't one already, or to remove one if there is. When you create a valid region (one which is closed, contains exactly one dot, is 180-degree symmetric about that dot, and contains no extraneous edges inside it) it will be highlighted automatically; so your aim is to have the whole grid highlighted in that way. During solving, you might know that a particular grid square belongs to a specific dot, but not be sure of where the edges go and which other squares are connected to the dot. In order to mark this so you don't forget, you can right-click on the dot and drag, which will create an arrow marker pointing at the dot. Drop that in a square of your choice and it will remind you which dot it's associated with. You can also right-click on existing arrows to pick them up and move them, or destroy them by dropping them off the edge of the grid. (Also, if you're not sure which dot an arrow is pointing at, you can pick it up and move it around to make it clearer. It will swivel constantly as you drag it, to stay pointed at its parent dot.) You can also use the cursor keys to move around the grid squares and lines. Pressing the return key when over a grid line will draw or clear its edge, as above. Pressing the return key when over a dot will pick up an arrow, to be dropped the next time the return key is pressed; this can also be used to move existing arrows around, removing them by dropping them on a dot or another arrow. (All the actions described in section 2.1 are also available.) 28.2 Galaxies parameters These parameters are available from the `Custom...' option on the `Type' menu. _Width_, _Height_ Size of grid in squares. _Difficulty_ Controls the difficulty of the generated puzzle. More difficult puzzles require more complex deductions, and the `Unreasonable' difficulty level may require backtracking. Chapter 29: Filling ------------------- You have a grid of squares, some of which contain digits, and the rest of which are empty. Your job is to fill in digits in the empty squares, in such a way that each connected region of squares all containing the same digit has an area equal to that digit. (`Connected region', for the purposes of this game, does not count diagonally separated squares as adjacent.) For example, it follows that no square can contain a zero, and that two adjacent squares can not both contain a one. No region has an area greater than 9 (because then its area would not be a single digit). Credit for this puzzle goes to Nikoli [14]. Filling was contributed to this collection by Jonas Koelker. [14] http://www.nikoli.co.jp/en/puzzles/fillomino/ 29.1 Filling controls To play Filling, simply click the mouse in any empty square and then type a digit on the keyboard to fill that square. By dragging the mouse, you can select multiple squares to fill with a single keypress. If you make a mistake, click the mouse in the incorrect square and press 0, Space, Backspace or Enter to clear it again (or use the Undo feature). You can also move around the grid with the cursor keys; typing a digit will fill the square containing the cursor with that number, or typing 0, Space, or Enter will clear it. You can also select multiple squares for numbering or clearing by using the return key, before typing a digit to fill in the highlighted squares (as above). (All the actions described in section 2.1 are also available.) 29.2 Filling parameters Filling allows you to configure the number of rows and columns of the grid, through the `Type' menu. Chapter 30: Keen ---------------- You have a square grid; each square may contain a digit from 1 to the size of the grid. The grid is divided into blocks of varying shape and size, with arithmetic clues written in them. Your aim is to fully populate the grid with digits such that: - Each row contains only one occurrence of each digit - Each column contains only one occurrence of each digit - The digits in each block can be combined to form the number stated in the clue, using the arithmetic operation given in the clue. That is: - An addition clue means that the sum of the digits in the block must be the given number. For example, `15+' means the contents of the block adds up to fifteen. - A multiplication clue (e.g. `60*'), similarly, means that the product of the digits in the block must be the given number. - A subtraction clue will always be written in a block of size two, and it means that one of the digits in the block is greater than the other by the given amount. For example, `2-' means that one of the digits in the block is 2 more than the other, or equivalently that one digit minus the other one is 2. The two digits could be either way round, though. - A division clue (e.g. `3/'), similarly, is always in a block of size two and means that one digit divided by the other is equal to the given amount. Note that a block may contain the same digit more than once (provided the identical ones are not in the same row and column). This rule is precisely the opposite of the rule in Solo's `Killer' mode (see chapter 11). This puzzle appears in the Times under the name `KenKen'. 30.1 Keen controls Keen shares much of its control system with Solo (and Unequal). To play Keen, simply click the mouse in any empty square and then type a digit on the keyboard to fill that square. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature). If you _right_-click in a square and then type a number, that number will be entered in the square as a `pencil mark'. You can have pencil marks for multiple numbers in the same square. Squares containing filled-in numbers cannot also contain pencil marks. The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular number, or you can use them as lists of the possible numbers in a given square, or anything else you feel like. To erase a single pencil mark, right-click in the square and type the same number again. All pencil marks in a square are erased when you left-click and type a number, or when you left-click and press space. Right-clicking and pressing space will also erase pencil marks. As for Solo, the cursor keys can be used in conjunction with the digit keys to set numbers or pencil marks. Use the cursor keys to move a highlight around the grid, and type a digit to enter it in the highlighted square. Pressing return toggles the highlight into a mode in which you can enter or remove pencil marks. Pressing M will fill in a full set of pencil marks in every square that does not have a main digit in it. (All the actions described in section 2.1 are also available.) 30.2 Keen parameters These parameters are available from the `Custom...' option on the `Type' menu. _Grid size_ Specifies the size of the grid. Lower limit is 3; upper limit is 9 (because the user interface would become more difficult with `digits' bigger than 9!). _Difficulty_ Controls the difficulty of the generated puzzle. At Unreasonable level, some backtracking will be required, but the solution should still be unique. The remaining levels require increasingly complex reasoning to avoid having to backtrack. Chapter 31: Towers ------------------ You have a square grid. On each square of the grid you can build a tower, with its height ranging from 1 to the size of the grid. Around the edge of the grid are some numeric clues. Your task is to build a tower on every square, in such a way that: - Each row contains every possible height of tower once - Each column contains every possible height of tower once - Each numeric clue describes the number of towers that can be seen if you look into the square from that direction, assuming that shorter towers are hidden behind taller ones. For example, in a 5x5 grid, a clue marked `5' indicates that the five tower heights must appear in increasing order (otherwise you would not be able to see all five towers), whereas a clue marked `1' indicates that the tallest tower (the one marked 5) must come first. In harder or larger puzzles, some towers will be specified for you as well as the clues round the edge, and some edge clues may be missing. This puzzle appears on the web under various names, particularly `Skyscrapers', but I don't know who first invented it. 31.1 Towers controls Towers shares much of its control system with Solo, Unequal and Keen. To play Towers, simply click the mouse in any empty square and then type a digit on the keyboard to fill that square with a tower of the given height. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature). If you _right_-click in a square and then type a number, that number will be entered in the square as a `pencil mark'. You can have pencil marks for multiple numbers in the same square. A square containing a tower cannot also contain pencil marks. The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular number, or you can use them as lists of the possible numbers in a given square, or anything else you feel like. To erase a single pencil mark, right-click in the square and type the same number again. All pencil marks in a square are erased when you left-click and type a number, or when you left-click and press space. Right-clicking and pressing space will also erase pencil marks. As for Solo, the cursor keys can be used in conjunction with the digit keys to set numbers or pencil marks. Use the cursor keys to move a highlight around the grid, and type a digit to enter it in the highlighted square. Pressing return toggles the highlight into a mode in which you can enter or remove pencil marks. Pressing M will fill in a full set of pencil marks in every square that does not have a main digit in it. (All the actions described in section 2.1 are also available.) 31.2 Towers parameters These parameters are available from the `Custom...' option on the `Type' menu. _Grid size_ Specifies the size of the grid. Lower limit is 3; upper limit is 9 (because the user interface would become more difficult with `digits' bigger than 9!). _Difficulty_ Controls the difficulty of the generated puzzle. At Unreasonable level, some backtracking will be required, but the solution should still be unique. The remaining levels require increasingly complex reasoning to avoid having to backtrack. Chapter 32: Singles ------------------- You have a grid of white squares, all of which contain numbers. Your task is to colour some of the squares black (removing the number) so as to satisfy all of the following conditions: - No number occurs more than once in any row or column. - No black square is horizontally or vertically adjacent to any other black square. - The remaining white squares must all form one contiguous region (connected by edges, not just touching at corners). Credit for this puzzle goes to Nikoli [15] who call it Hitori. Singles was contributed to this collection by James Harvey. [15] http://www.nikoli.com/en/puzzles/hitori/index.html (beware of Flash) 32.1 Singles controls Left-clicking on an empty square will colour it black; left-clicking again will restore the number. Right-clicking will add a circle (useful for indicating that a cell is definitely not black). You can also use the cursor keys to move around the grid. Pressing the return or space keys will turn a square black or add a circle respectively, and pressing the key again will restore the number or remove the circle. (All the actions described in section 2.1 are also available.) 32.2 Singles parameters These parameters are available from the `Custom...' option on the `Type' menu. _Width_, _Height_ Size of grid in squares. _Difficulty_ Controls the difficulty of the generated puzzle. Chapter 33: Magnets ------------------- A rectangular grid has been filled with a mixture of magnets (that is, dominoes with one positive end and one negative end) and blank dominoes (that is, dominoes with two neutral poles). These dominoes are initially only seen in silhouette. Around the grid are placed a number of clues indicating the number of positive and negative poles contained in certain columns and rows. Your aim is to correctly place the magnets and blank dominoes such that all the clues are satisfied, with the additional constraint that no two similar magnetic poles may be orthogonally adjacent (since they repel). Neutral poles do not repel, and can be adjacent to any other pole. Credit for this puzzle goes to Janko [16]. Magnets was contributed to this collection by James Harvey. [16] http://www.janko.at/Raetsel/Magnete/index.htm 33.1 Magnets controls Left-clicking on an empty square places a magnet at that position with the positive pole on the square and the negative pole on the other half of the magnet; left-clicking again reverses the polarity, and a third click removes the magnet. Right-clicking on an empty square places a blank domino there. Right-clicking again places two question marks on the domino, signifying `this cannot be blank' (which can be useful to note deductions while solving), and right-clicking again empties the domino. You can also use the cursor keys to move a cursor around the grid. Pressing the return key will lay a domino with a positive pole at that position; pressing again reverses the polarity and then removes the domino, as with left-clicking. Using the space bar allows placement of blank dominoes and cannot-be-blank hints, as for right- clicking. (All the actions described in section 2.1 are also available.) 33.2 Magnets parameters These parameters are available from the `Custom...' option on the `Type' menu. _Width_, _Height_ Size of grid in squares. There will be half _Width_ x _Height_ dominoes in the grid: if this number is odd then one square will be blank. (Grids with at least one odd dimension tend to be easier to solve.) _Difficulty_ Controls the difficulty of the generated puzzle. At Tricky level, you are required to make more deductions about empty dominoes and row/column counts. _Strip clues_ If true, some of the clues around the grid are removed at generation time, making the puzzle more difficult. Chapter 34: Signpost -------------------- You have a grid of squares; each square (except the last one) contains an arrow, and some squares also contain numbers. Your job is to connect the squares to form a continuous list of numbers starting at 1 and linked in the direction of the arrows - so the arrow inside the square with the number 1 will point to the square containing the number 2, which will point to the square containing the number 3, etc. Each square can be any distance away from the previous one, as long as it is somewhere in the direction of the arrow. By convention the first and last numbers are shown; one or more interim numbers may also appear at the beginning. Credit for this puzzle goes to Janko [17], who call it `Pfeilpfad' (`arrow path'). Signpost was contributed to this collection by James Harvey. [17] http://janko.at/Raetsel/Pfeilpfad/index.htm 34.1 Signpost controls To play Signpost, you connect squares together by dragging from one square to another, indicating that they are adjacent in the sequence. Drag with the left button from a square to its successor, or with the right button from a square to its predecessor. If you connect together two squares in this way and one of them has a number in it, the appropriate number will appear in the other square. If you connect two non-numbered squares, they will be assigned temporary algebraic labels: on the first occasion, they will be labelled `a' and `a+1', and then `b' and `b+1', and so on. Connecting more squares on to the ends of such a chain will cause them all to be labelled with the same letter. When you left-click or right-click in a square, the legal squares to connect it to will be shown. The arrow in each square starts off black, and goes grey once you connect the square to its successor. Also, each square which needs a predecessor has a small dot in the bottom left corner, which vanishes once you link a square to it. So your aim is always to connect a square with a black arrow to a square with a dot. To remove any links for a particular square (both incoming and outgoing), left-drag it off the grid. To remove a whole chain, right-drag any square in the chain off the grid. You can also use the cursor keys to move around the grid squares and lines. Pressing the return key when over a square starts a link operation, and pressing the return key again over a square will finish the link, if allowable. Pressing the space bar over a square will show the other squares pointing to it, and allow you to form a backward link, and pressing the space bar again cancels this. (All the actions described in section 2.1 are also available.) 34.2 Signpost parameters These parameters are available from the `Custom...' option on the `Type' menu. _Width_, _Height_ Size of grid in squares. _Force start/end to corners_ If true, the start and end squares are always placed in opposite corners (the start at the top left, and the end at the bottom right). If false the start and end squares are placed randomly (although always both shown). Chapter 35: Range ----------------- You have a grid of squares; some squares contain numbers. Your job is to colour some of the squares black, such that several criteria are satisfied: - no square with a number is coloured black. - no two black squares are adjacent (horizontally or vertically). - for any two white squares, there is a path between them using only white squares. - for each square with a number, that number denotes the total number of white squares reachable from that square going in a straight line in any horizontal or vertical direction until hitting a wall or a black square; the square with the number is included in the total (once). For instance, a square containing the number one must have four black squares as its neighbours by the last criterion; but then it's impossible for it to be connected to any outside white square, which violates the second to last criterion. So no square will contain the number one. Credit for this puzzle goes to Nikoli, who have variously called it `Kurodoko', `Kuromasu' or `Where is Black Cells'. [18]. Range was contributed to this collection by Jonas Koelker. [18] http://www.nikoli.co.jp/en/puzzles/where_is_black_cells/ 35.1 Range controls Click with the left button to paint a square black, or with the right button to mark a square with a dot to indicate that you are sure it should _not_ be painted black. Repeated clicking with either button will cycle the square through the three possible states (filled, dotted or empty) in opposite directions. You can also use the cursor keys to move around the grid squares. Pressing Return does the same as clicking with the left button, while pressing Space does the same as a right button click. (All the actions described in section 2.1 are also available.) 35.2 Range parameters These parameters are available from the `Custom...' option on the `Type' menu. _Width_, _Height_ Size of grid in squares. Chapter 36: Pearl ----------------- You have a grid of squares. Your job is to draw lines between the centres of horizontally or vertically adjacent squares, so that the lines form a single closed loop. In the resulting grid, some of the squares that the loop passes through will contain corners, and some will be straight horizontal or vertical lines. (And some squares can be completely empty - the loop doesn't have to pass through every square.) Some of the squares contain black and white circles, which are clues that the loop must satisfy. A black circle in a square indicates that that square is a corner, but neither of the squares adjacent to it in the loop is also a corner. A white circle indicates that the square is a straight edge, but _at least one_ of the squares adjacent to it in the loop is a corner. (In both cases, the clue only constrains the two squares adjacent _in the loop_, that is, the squares that the loop passes into after leaving the clue square. The squares that are only adjacent _in the grid_ are not constrained.) Credit for this puzzle goes to Nikoli, who call it `Masyu'. [19]. Thanks to James Harvey for assistance with the implementation. [19] http://www.nikoli.co.jp/en/puzzles/masyu/ 36.1 Pearl controls Click with the left button on a grid edge to draw a segment of the loop through that edge, or to remove a segment once it is drawn. Drag with the left button through a series of squares to draw more than one segment of the loop in one go. Alternatively, drag over an existing part of the loop to undraw it, or to undraw part of it and then go in a different direction. Click with the right button on a grid edge to mark it with a cross, indicating that you are sure the loop does not go through that edge. (For instance, if you have decided which of the squares adjacent to a white clue has to be a corner, but don't yet know which way the corner turns, you might mark the one way it _can't_ go with a cross.) Alternatively, use the cursor keys to move the cursor. Use the Enter key to begin and end keyboard `drag' operations. Use the Space key to cancel the drag. Use Ctrl-arrowkey and Shift-arrowkey to simulate a left or right click, respectively, on the edge in the given direction relative to the cursor, i.e. to draw a segment or a cross. (All the actions described in section 2.1 are also available.) 36.2 Pearl parameters These parameters are available from the `Custom...' option on the `Type' menu. Chapter 37: Undead ------------------ You are given a grid of squares, some of which contain diagonal mirrors. Every square which is not a mirror must be filled with one of three types of undead monster: a ghost, a vampire, or a zombie. Vampires can be seen directly, but are invisible when reflected in mirrors. Ghosts are the opposite way round: they can be seen in mirrors, but are invisible when looked at directly. Zombies are visible by any means. You are also told the total number of each type of monster in the grid. Also around the edge of the grid are written numbers, which indicate how many monsters can be seen if you look into the grid along a row or column starting from that position. (The diagonal mirrors are reflective on both sides. If your reflected line of sight crosses the same monster more than once, the number will count it each time it is visible, not just once.) This puzzle type was invented by David Millar, under the name `Haunted Mirror Maze'. See [20] for more details. Undead was contributed to this collection by Steffen Bauer. [20] http://www.janko.at/Raetsel/Spukschloss/index.htm 37.1 Undead controls Undead has a similar control system to Solo, Unequal and Keen. To play Undead, click the mouse in any empty square and then type a letter on the keyboard indicating the type of monster: `G' for a ghost, `V' for a vampire, or `Z' for a zombie. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature). If you _right_-click in a square and then type a letter, the corresponding monster will be shown in reduced size in that square, as a `pencil mark'. You can have pencil marks for multiple monsters in the same square. A square containing a full-size monster cannot also contain pencil marks. The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular monster, or you can use them as lists of the possible monster in a given square, or anything else you feel like. To erase a single pencil mark, right-click in the square and type the same letter again. All pencil marks in a square are erased when you left-click and type a monster letter, or when you left-click and press Space. Right- clicking and pressing space will also erase pencil marks. As for Solo, the cursor keys can be used in conjunction with the letter keys to place monsters or pencil marks. Use the cursor keys to move a highlight around the grid, and type a monster letter to enter it in the highlighted square. Pressing return toggles the highlight into a mode in which you can enter or remove pencil marks. If you prefer plain letters of the alphabet to cute monster pictures, you can press `A' to toggle between showing the monsters as monsters or showing them as letters. (All the actions described in section 2.1 are also available.) 37.2 Undead parameters These parameters are available from the `Custom...' option on the `Type' menu. _Width_, _Height_ Size of grid in squares. _Difficulty_ Controls the difficulty of the generated puzzle. Chapter 38: Unruly ------------------ You are given a grid of squares, which you must colour either black or white. Some squares are provided as clues; the rest are left for you to fill in. Each row and column must contain the same number of black and white squares, and no row or column may contain three consecutive squares of the same colour. This puzzle type was invented by Adolfo Zanellati, under the name `Tohu wa Vohu'. See [21] for more details. Unruly was contributed to this collection by Lennard Sprong. [21] http://www.janko.at/Raetsel/Tohu-Wa-Vohu/index.htm 38.1 Unruly controls To play Unruly, click the mouse in a square to change its colour. Left-clicking an empty square will turn it black, and right-clicking will turn it white. Keep clicking the same button to cycle through the three possible states for the square. If you middle-click in a square it will be reset to empty. You can also use the cursor keys to move around the grid. Pressing the return or space keys will turn an empty square black or white respectively (and then cycle the colours in the same way as the mouse buttons), and pressing Backspace will reset a square to empty. (All the actions described in section 2.1 are also available.) 38.2 Unruly parameters These parameters are available from the `Custom...' option on the `Type' menu. _Width_, _Height_ Size of grid in squares. (Note that the rules of the game require both the width and height to be even numbers.) _Difficulty_ Controls the difficulty of the generated puzzle. Appendix A: Licence ------------------- This software is copyright 2004-2012 Simon Tatham. Portions copyright Richard Boulton, James Harvey, Mike Pinna, Jonas Koelker, Dariusz Olszewski, Michael Schierl, Lambros Lambrou, Bernd Schmidt, Steffen Bauer, Lennard Sprong and Rogier Goossens. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the `Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED `AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. [$Id: puzzles.but 9828 2013-04-12 16:28:55Z simon $] puzzles-r9872/puzzles.hlp0000644000175300017530000056053512161170560014663 0ustar simonsimon?_.]/&;)L4s`sY camo T( L9  ud@ JٳJ8CKxxNyNzN{N|N3}N~NN_NNNo NNNNrNNN NNN N NN N Nm N/ N N N N0Nd N8 N N N N N Ng NU No $N'%N &N'N(N )N*NŊ+N,N-N ONJPNQNRN#SNTNUNVNÃWNWXNHzN{N|N}N9~NN?NN=NNENPNNNN N^NkNYFQAR$`Drn# Wpp Hϣq {, & ÷NW˵iGH ( !nM 6bs +? K@ `Rb PU[,a e  hArialCourier NewTimes New Roman/ & ;)F24l15-puzzleBlack BoxBridgesCube DominosaEdit menuFifteenFile menuFilling Flip$FreeNet(Futoshiki,Galaxies0Game menu4Guess<Hitori@ID formatDID, gameHInertiaLJankoPKeenXKenKen\Latin square`Light UpdLinuxhLoopypMIT licencetMac OS X|MagnetsMapMastermindMinesNETGAME.EXENetNetWalkNetslideNikoli PatternPearlPegsPlanarityPostScriptPuzzle PalaceRandom SeedRangeRectanglesSame GameSignpostSinglesSixteenSkyscrapers SlantSolitaire, PegSoloTentsTowers Twiddle$Type menu(Undead,Unequal0Unix4Unruly<Untangle@WindowsDbugsLcommand linePcommon features\controls`controls, for Black Boxdcontrols, for Bridgeshcontrols, for Cubelcontrols, for Dominosapcontrols, for Fifteentcontrols, for Fillingxcontrols, for Flip|controls, for Galaxiescontrols, for Guesscontrols, for Inertiacontrols, for Keencontrols, for Light Upcontrols, for Loopycontrols, for Magnetscontrols, for Mapcontrols, for Minescontrols, for Netcontrols, for Netslidecontrols, for Patterncontrols, for Pearlcontrols, for Pegscontrols, for Rangecontrols, for Rectanglescontrols, for Same Gamecontrols, for Signpostcontrols, for Singlescontrols, for Sixteencontrols, for Slantcontrols, for Solocontrols, for Tentscontrols, for Towerscontrols, for Twiddlecontrols, for Undeadcontrols, for Unequalcontrols, for Unrulycontrols, for Untanglecopycopyrightdefault parameters, specifyingexitOfeedbackformat, IDfour-colouring game IDgame ID, formatgame ID, generatinggenerating game IDsinitial state keys$keys, for Black Box(keys, for Cube,keys, for Fifteen0keys, for Flip4keys, for Guess8keys, for Inertia<keys, for Net@keys, for Same GameDlicenceHlicence, MITLloadTnew game\nonograms`parametersdparameters, for Black Boxlparameters, for Bridgespparameters, for Cubetparameters, for Dominosaxparameters, for Fifteen|parameters, for Fillingparameters, for Galaxiesparameters, for Guessparameters, for Inertiaparameters, for Keenparameters, for Light Upparameters, for Loopyparameters, for Magnetsparameters, for Mapparameters, for Minesparameters, for Netparameters, for Netslideparameters, for Patternparameters, for Pearlparameters, for Pegsparameters, for Rangeparameters, for Rectanglesparameters, for Same Gameparameters, for Signpostparameters, for Singlesparameters, for Sixteenparameters, for Slantparameters, for Soloparameters, for Tentsparameters, for Towersparameters, for Twiddleparameters, for Undeadparameters, for Unequalparameters, for Unrulyparameters, for Untangleparameters, for flippatchespreferences, specifying defaultpresetprinting, on Unixprinting, on Windows quitredorestart gamesaveshortcuts (keyboard)$shortcuts (keyboard), for Black Box(shortcuts (keyboard), for Cube,shortcuts (keyboard), for Fifteen0shortcuts (keyboard), for Flip4shortcuts (keyboard), for Guess8shortcuts (keyboard), for Inertia<shortcuts (keyboard), for Net@shortcuts (keyboard), for Same GameDsolveHsource codeLstate, initialPundoTversionXwebsite\Custom, menu option`Specific, menu optiondfeedbackstate, initialqhq b  EExH EP# PP@ H  EP   x# b Pb# a   a H PE 'kN Ŋ  d    Wr o U J3m EEP PPPE k rEEP  0g H8  ^ =_Ã#o  9?/  EEEEEE k rEPEPPlull!pQ  #CB("btn_about","&About","About()") CB("btn_up","&Up","Contents()")BrowseButtons()*Simon Tatham's Portable Puzzle CollectionThis manual is copyright 2004-2012 Simon Tatham. All rights reserved. You may distribute this documentation under the MIT licence. See appendix A for the licence text in full. FR1 R ContentsDB("btn_up")O, # XSimon Tatham's Portable Puzzle Collection^9R% rThis is a collection of small one-player puzzle games., &iNThis manual is copyright 2004-2012 Simon Tatham. All rights reserved. You may distribute this documentation under the MIT licence. See appendix A for the licence text in full.M,/ .<NChapter 1: IntroductionP!|/ .BNChapter 2: Common featuresD,/ .*6ۀChapter 3: NetE|/ .,`RChapter 4: CubeHM/ .2JٳJChapter 5: FifteenH/ .2WpChapter 6: SixteenHM/ .2$`Chapter 7: TwiddleK(/ .8÷NChapter 8: RectanglesIq/ .4㻽pChapter 9: NetslideI(/ .4iGHChapter 10: PatternFq/ ..eChapter 11: SoloGG/ .0ڀChapter 12: MinesK/ .8W˵Chapter 13: Same GameFG/ ..PUChapter 14: FlipG/ .0 (ـChapter 15: GuessFe/ ..,aChapter 16: PegsJ/ .6moChapter 17: DominosaJe/ .6{,Chapter 18: UntangleKD/ .8ϣChapter 19: Black BoxG/ .0s܀Chapter 20: SlantJD/ .6Chapter 21: Light UpE/ .,MۀChapter 22: MapGa/ .0ڀChapter 23: LoopyI/ .4KChapter 24: InertiaGa/ .0+?܀Chapter 25: TentsI: / .4&Chapter 26: BridgesI / .4㐆 Chapter 27: UnequalJ: / .6ud@Chapter 28: GalaxiesI  / .48CKChapter 29: FillingF \ / ..[Chapter 30: KeenH / .2cChapter 31: TowersI\ / .4DrnChapter 32: SinglesI 6 / .4ARChapter 33: MagnetsJ / .6YFQChapter 34: SignpostG6 / .0ۀChapter 35: RangeG  / .0!nۀChapter 36: PearlH V / .2T( Chapter 37: UndeadH / .2L9 Chapter 38: UnrulyIV / .4NAppendix A: LicencerA Y 1Y 9FChapter 1: IntroductionCBB("btn_up","JI(`',`Top')");EB("btn_up")E ) "8Chapter 1: Introduction}Y MA& I wrote this collection because I thought there should be more small desktop toys available: little games you can pop up in a window and play for two or three minutes while you take a break from whatever else you were doing. And I was also annoyed that every time I found a good game on (say) Unix, it wasn't available the next time I was sitting at a Windows machine, or vice versa; so I arranged that everything in my personal puzzle collection will happily run on both, and have more recently done a port to Mac OS X as well. When I find (or perhaps invent) further pu MA zzle games that I like, they'll be added to this collection and will immediately be available on both platforms. And if anyone feels like writing any other front ends PocketPC, Mac OS pre-10, or whatever it might be then all the games in this framework will immediately become available on another platform as well.^ B& The actual games in this collection were mostly not my invention; they are re-implementations of existing game concepts within my portable puzzle framework. I do not claim credit, in general, for inventing the rules of any of these puzzles. (I don't even claim authorship of all the code; some of the puzzles have been submitted by other authors.)#MAC, &NThis collection is distributed under the MIT licence (see appendix A). This means that you can do pretty much anything you like with the game binaries or the code, except pretending you wrote them yourself, or suing me if anything goes wrong.uBD+ &The most recent versions, and source code, can be found at http://www.chiark.greenend.org.uk/~sgtatham/puzzles/.sC2E+ &Please report bugs to anakin@pobox.com. You might find it helpful to read this article before reporting a bug:]8DE% phttp://www.chiark.greenend.org.uk/~sgtatham/bugs.html2E9F&  Patches are welcome. Especially if they provide a new front end (to make all these games run on another platform), or a new game.uDEF1EFF3IChapter 2: Common featuresCBB("btn_up","JI(`',`Top')");EB("btn_up")H9FF) ">Chapter 2: Common featuresiDF_G% This chapter describes features that are common to all the games.Q"FG/ .DNSection 2.1: Common actionsd5_GH/ .jNSection 2.2: Specifying games with the game IDR#GfH/ .FNSection 2.3: The Type menuqBHH/ .NSection 2.4: Specifying game parameters on the command line\-fH3I/ .ZNSection 2.5: Unix command-line options|KHI1gPII8Section 2.1: Common actionsCBB("btn_up","JI(`',`t00000001')");EB("btn_up")I 3II) "@Section 2.1: Common actions}IJ% These actions are all available from the Game menu and via keyboard shortcuts, in addition to any game-specific actions.IDK&  (On Mac OS X, to conform with local user interface standards, these actions are situated on the File and Edit menus instead.)DJK( 8New game (N, Ctrl+N)X2DKK& dStarts a new game, with a random initial state.4KL% Restart gamelFKL& Resets the current game to its initial state. (This can be undone.),LL% LoadP*LL& TLoads a saved game from a file on disk.,L(M% Save`:LM& tSaves the current state of your game to a file on disk.(MFN' /The Load and Save operations preserve your entire game history (so you can save, reload, and still Undo and Redo things you had done before saving).-MsN% PrintwPFN ' Where supported (currently only on Windows), brings up a dialog allowing you to print an arbitrary number of puzzles randomly generated from the current parameters, optionally including the current puzzle. (Only for puzzles which make sense to print, of course it's hard to think of a sensible printable representation of Fifteen!)sN 3IJ"sNV( DUndo (U, Ctrl+Z, Ctrl+_)uO ˀ& Undoes a single move. (You can undo moves back to the start of the session.)@V ( 0Redo (R, Ctrl+R)I#ˀT& FRedoes a previously undone move., % CopyT' Copies the current state of your game to the clipboard in text format, so that you can paste it into (say) an e-mail client or a web message board if you're discussing the game with someone else. (Not all games support this feature.)-% Solvej=*- ({Transforms the puzzle instantly into its solved state. For some games (Cube) this feature is not supported at all because it is of no particular use. For other games (such as Pattern), the solved state can be used to give you information, if you can't see how a solution can exist at all or you want to know where you made a mistake. For still other games (such as Sixteen), automatic solution tells you nothing about how to get to the solution, but it does provide a useful way to get there quickly so that you can experiment with set-piece moves and transformations.h- (Some games (such as Solo) are capable of solving a game ID you have typed in from elsewhere. Other games (such as Rectangles) cannot solve a game ID they didn't invent themself, but when they did invent the game ID they know what the solution is already. Still other games (Pattern) can solve some external game IDs, but only if they aren't too difficult.*' The Solve command adds the solved state to the end of the undo chain for the puzzle. In other words, if you want to go back to solving it yourself after seeing the answer, you can just press Undo.@( 0Quit (Q, Ctrl+Q)I#8& FCloses the application entirely.^Lj1ELj#Section 2.2: Specifying games with the game IDCBB("btn_up","JI(`',`t00000001')");EB("btn_up")\38#) "fSection 2.2: Specifying games with the game IDLj݉& )There are two ways to save a game specification out of a puzzle and recreate it later, or recreate it in somebody else's copy of the same puzzle.#ي& The Specific and Random Seed options from the Game menu (or the File menu, on Mac OS X) each show a piece of text (a game ID) which is sufficient to reconstruct precisely the same game at a later date.O#݉(, &GNYou can enter either of these pieces of text back into the program (via the same Specific or Random Seed menu options) at a later point, and it will recreate the same game. You can also use either one as a command line argument (on Windows or Unix); see section 2.4 for more detail.M!يu, &CThe difference between the two forms is that a descriptive game ID is a literal description of the initial state of the game, whereas a random seed is just a piece of arbitrary text which was provided as input to the random number generator used to create the puzzle. This means that:g-(܎: B[T8`RቀDescriptive game IDs tend to be longer in many puzzles (although some, such as Cube (chapter 4), only need very short descriptions). So a random seed is often a quicker way to note down the puzzle you're currently playing, or to tell it to somebody else so they can play the same one as you. u. *T8Any text at all is a valid random seed. The automatically generated ones are fifteen-digit numbers, but anything will do; you can type in your full name, or a word you just made up, and a valid puzzle will be generated from it. This provides ܎8a way for two or more people to race to complete the same puzzle: you think of a random seed, then everybody types it in at the same time, and nobody has an advantage due to having seen the generated puzzle before anybody else.܎. *eT8It is often possible to convert puzzles from other sources (such as nonograms or sudoku from newspapers) into descriptive game IDs suitable for use with these programs.4 6yT8Random seeds are not guaranteed to produce the same result if you use them with a different version of the puzzle program. This is because the generation algorithm might have been improved or modified in later versions of the code, and will therefore produce a different result when given the same sequence of random numbers. Use a descriptive game ID if you aren't sure that it will be used on the same version of the program as yours.' y(Use the About menu option to find out the version number of the program. Programs with the same version number running on different platforms should still be random-seed compatible.)uI, &A descriptive game ID starts with a piece of text which encodes the parameters of the current game (such as grid size). Then there is a colon, and after that is the description of the game's initial state. A random seed starts with a similar string of parameters, but then it contains a hash sign followed by arbitrary data.X&q2 2MIf you enter a descriptive game ID, the program will not be able to show you the random seed which generated it, since it wasn't generated from a random seed. If you enter a random seed, however, the program will be able to show you the descriptive game ID derived from that random seed.g2 2eቂNote that the game parameter strings are not always identical between the two forms. For some games, there will be parameter data provided with the random seed which is not included in the descriptive game ID. This is because that parameter information is only relevant when generating puzzle grids, and is not important when playing them. Thus, for example, the difficulty level in Solo (chapter 11) is not mentioned in the descriptive game ID.W1q& cThese additional parameters are also not set permanently if you type in a game ID. For example, suppose you have Solo set to Advanced difficulty level, and then a friend wants your help with a Trivial puzzle; so the friend reads out a random seed specifying Trivial difficulty, and you type it in. The program will generate you the same Trivial grid which your friend was having trouble with, but once you have finished playing it, when you ask for a new game it will automatically go back to the Advanced difficulty which it was previously set on.}Lg;1P;3Section 2.3: The Type menuCBB("btn_up","JI(`',`t00000001')");EB("btn_up")J!) "BSection 2.3: The Type menu;I& =The Type menu, if present, may contain a list of preset game settings. Selecting one of these will start a new random game with the parameters specified.3& The Type menu may also contain a Custom option which allows you to fine-tune game parameters. The parameters available are specific to each game and are described in the following sections.kI1 8Section 2.4: Specifying game parameters on the command lineCBB("btn_up","JI(`',`t00000001')");EB("btn_up")i@38) "Section 2.4: Specifying game parameters on the command line^9% r(This section does not apply to the Mac OS X version.)`:8& uThe games in this collection deliberately do not ever save informat3ion on to the computer they run on: they have no high score tables and no saved preferences. (This is because I expect at least some people to play them at work, and those people will probably appreciate leaving as little evidence as possible!)& )However, if you do want to arrange for one of these games to default to a particular set of parameters, you can specify them on the command line.2 2UNNThe easiest way to do this is to set up the parameters you want using the Type menu (see section 2.3), and then to select Random Seed from the Game or File menu (see section 2.2). The text in the Game ID box will be composed of two parts, separated by a hash. The first of these parts represents the game parameters (the size of the playing area, for example, and anything else you set using the Type menu).{8% If you run the game with just that parameter text on the command line, it will start up with the settings you specified.t6> Jm`RቀFor example: if you run Cube (see chapter 4), select Octahedron from the Type menu, and then go to the game ID selection, you will see a string of the form o2x2#338686542711620. Take only the part before the hash (o2x2), and start Cube with that text on the command line: PREFIX-cube o2x2.#8, &If you copy the entire game ID on to the command line, the game will start up in the specific game that was described. This is occasionally a more convenient way to start a particular game ID than by pasting it into the game ID selection box.#, &N(You could also retrieve the encoded game parameters using the Specific menu option instead of Random Seed, but if you do then some options, such as the difficulty level in Solo, will be missing. See section 2.2 for more details on this.)Vy1yXJSection 2.5: Unix command-line optionsCBB("btn_up","JI(`',`t00000001')");EB("btn_up")T+) "VSection 2.5: Unix command-line optionsU0y" % `(This section only applies to the Unix port.) , &NIn addition to being able to specify game parameters on the command line (see section 2.4), there are various other options:. "  % --game. + % --load. Y ' These options respectively determine whether the command-line argument is treated as specifying game parameters or a save file to load. Only one should be specified. If neither of these options is specified, a guess is made based on the format of the argument.8+  ( --generate n)Y  ' If this option is specified, instead of a puzzle being displayed, a number of descriptive game IDs will be invented and printed on standard output. This is useful for gaining access to the game generation algorithms without necessarily using the frontend. y ' 1If game parameters are specified on the command-line, they will be used to generate the game IDs; otherwise a default set of parameters will be used. +- ( The most common use of this option is in conjunction with --print, in which case its behaviour is slightly different; see below.?y j. ,"--print wxh+@' _If this option is specified, instead of a puzzle being displayed, a printed representation of one or more unsolved puzzles is sent to standard output, in PostScript format.j@? LOn each page of puzzles, there will be w across and h down. If there are more puzzles than wh, more than one page will @@be printed.O@hA9 @-NIf --generate has also been specified, the invented game IDs will be used to generate the printed output. Otherwise, a list of game IDs is expected on standard input (which can be descriptive or random seeds; see section 2.2), in the same format produced by --generate.5@A& For example:X2hAA& dPREFIX-net --generate 12 --print 2x3 7x7w | lprAB- (will generate two pages of printed Net puzzles (each of which will have a 77 wrapping grid), and pipe the output to the lpr command, which on many systems will send them to an actual printer.jDAQC& There are various other options which affect printing; see below.l8BC4 8p--save file-prefix [ --save-suffix file-suffix ]QCD' If this option is specified, instead of a puzzle being displayed, saved-game files for one or more unsolved puzzles are written to files constructed from the supplied prefix and/or suffix.OCE9 @-NIf --generate has also been specified, the invented game IDs will be used to generate the printed output. Otherwise, a list of game IDs is expected on standard input (which can be descriptive or random seeds; see section 2.2), in the same format produced by --generate.5D'F& For example:`:EF& tPREFIX-net --generate 12 --save game --save-suffix .savX'FG2 4will generate twelve Net saved-game files with the names game0.sav to game11.sav.1 FBG% --versionc=GG& zPrints version information about the game, and then quits.wLBGH+ &The following options are only meaningful if --print is also specified:8GTH% &--with-solutionsdHH& The set of pages filled with unsolved puzzles will be followed by the solutions to those puzzles.5 THI( --scale ngHI& Adjusts how big each puzzle is when printed. Larger numbers make puzzles bigger; the default is 1.0.0 II% --colourbIXJ& Puzzles will be printed in colour, rather than in black and white (if supported by the puzzle).i8IJ1" JJNChapter 3: NetCBB("btn_up","JI(`',`Top')");EB("btn_up")<XJJ) "&Chapter 3: NetuJK7 >(Note: the Windows version of this game is called NETGAME.EXE to avoid clashing with Windows's own NET.EXE.)?JM, &'6ۉI originally saw this in the form of a Flash game called FreeNet [1], written by Pavils Jurjans; there are several other implementations under the name NetWalk. The computer prepares a network by connecting up the centres of squares in a grid, and then shuffles the network by rotating every tile randomly. Your job is to rotate it all back into place. The successful solution will be an entirely connected network, with no closed loops. As a visual aid, all tiles which are connected to the one in the middle are highlighted.[3KCN( f[1] http://www.jurjans.lv/stuff/net/FreeNet.htmO MN/ .@NSection 3.1: Net controlsQ"CNN/ .DNSection 3.2: Net parameterszIN]O1H^ ]OOSection 3.1: Net controlsCBB("btn_up","JI(`',`games.net')");EB("btn_up")GNO) "<Section 3.1: Net controlsxS]O(% This game can be played with either the keyboard or thO(Ne mouse. The controls are:R*Oz( TSelect tile: mouse pointer, arrow keysa9(ۀ( rRotate tile anticlockwise: left mouse button, A key^6z9( lRotate tile clockwise: right mouse button, D keyO'ۀ( NRotate tile by 180 degrees: F keylD9( Lock (or unlock) tile: middle mouse button, shift-click, S key' You can lock a tile once you're sure of its orientation. You can also unlock it again, but while it's locked you can't accidentally turn it.yT#% The following controls are not necessary to complete the game, but may be useful:J"m( DShift grid: Shift + arrow keys#' On grids that wrap, you can move the origin of the grid, so that tiles that were on opposite sides of the grid can be seen together.J"me( DMove centre: Ctrl + arrow keys0 ' You can change which tile is used as the source of highlighting. (It doesn't ultimately matter which tile this is, as every tile will be connected to every other tile in a correct solution, but it may be helpful in the intermediate stages of solving the puzzle.)Aeօ( 2Jumble tiles: J keymGC& This key turns all tiles that are not locked to random orientations.nCօ+ &N(All the actions described in section 2.1 are also available.)|KC-1 b  -vSection 3.2: Net parametersCBB("btn_up","JI(`',`games.net')");EB("btn_up")I v) "@Section 3.2: Net parametersvQ-% These parameters are available from the Custom... option on the Type menu.=v)+ &$Width, Height?h& 2Size of grid in tiles.9)% (Walls wrap aroundjh1& If checked, flow can pass from the left edge to the right edge, and from top to bottom, and vice versa.;l% ,Barrier probability$1' A number between 0.0 and 1.0 controlling whether an immovable barrier is placed between two tiles to prevent flow between them (a higher number gives more barriers). Since barriers are immovable, they act as constraints on the solution (i.e., hints).ol23 4NThe grid generation in Net has been carefully arranged so that the barriers are independent of the rest of the grid. This means that if you note down the random seed used to generate the current puzzle (see section 2.2), change the Barrier probability parameter, and then re-enter the same random seed, you should see exactly the same starting grid, with the only change being the number of barriers. So if you're stuck on a particular grid and need a hint, you could start up another instance of Net, set up the same parameters but a higher barrier probability, and enter the game seed from the original Net window.>p% 2Ensure unique solution|O2- (Normally, Net will make sure that the puzzles it presents have only one solution. Puzzles with ambiguous sections can be more difficult and more subtle, so if you like you can turn off this feature and risk having ambiguous puzzles. (Also, finding all the possible solutions can be an additional challenge for an advanced player.)j9pV1S^k VChapter 4: CubeCBB("btn_up","JI(`',`Top')");EB("btn_up")=) "(Chapter 4: CubeV, &`RቂThis is another one I originally saw as a web game. This one was a Java game [2], by Paul Scott. You have a grid of 16 squares, six of which are blue; on one square rests a cube. Your move is to use the arrow keys to roll the cube through 90 degrees so that it moves to an adjacent square. If you roll the cube on to a blue square, the blue square is picked up on one face of the cube; if you roll a blue face of the cube on to a non-blue square, the blueness is put down again. (In general, whenever you roll the cube, the two faces that come into contact swap colours.) Your job is to get all six blue squares on to the six faces of the cube at the same time. Count your moves and try to do it in as few as possible.& Unlike the original Java game, my version has an additional feature: once you've mastered the game with a cube rolling on a square grid, you can change to a triangular grid and roll any of a tetrahedron, an octahedron or an icosahedron.`8( p[2] http://www3.sympatico.ca/paulscott/cube/cube.htmP!c/ .BNSection 4.1: Cube controlsR#/ .FNSection 4.2: Cube parameters|Kc11;b  1ylSection 4.1: Cube controlsCBB("btn_up","JI(`',`games.cube')");EB("btn_up")Hy) ">Section 4.1: Cube controlsfA1% This game can be played with either the keyboard or the mouse.fyj% Left-clicking anywhere on the window will move the cube (or other solid) towards the mouse pointer.n& The arrow keys can also used to roll the cube on its square grid in the four cardinal directions. On the triangular grids, the mapping of arrow keys to directions is more approximate. Vertical movement is disallowed where it doesn't make sense. The four keys surrounding the arrow keys on the numeric keypad (7, 9, 1, 3) can be used for diagonal movement.nCjl+ &N(All the actions described in section 2.1 are also available.)~M1k 4uSection 4.2: Cube parametersCBB("btn_up","JI(`',`games.cube')");EB("btn_up")J!l4) "BSection 4.2: Cube parametersvQ% These parameters are available from the Custom... option on the Type menu.54%  Type of solidns& Selects the solid to roll (and hence the shape of the grid): tetrahedron, cube, octahedron, or icosahedron.L!+ &BWidth / top, Height / bottomsu' On a square grid, horizontal and vertical dimensions. On a triangular grid, the number of triangles on the top and bottom rows respectively.m<1"Chapter 5: FifteenCBB("btn_up","JI(`',`Top')");EB("btn_up")@u") ".Chapter 5: Fifteen, &WThe old ones are the best: this is the good old 15-puzzle with sliding tiles. You have a 44 square grid; 15 squares contain numbered tiles, and the sixteenth is empty. Your move is to choose a tile next to the empty space, and slide it into the space. The aim is to end up with the tiles in numerical order, with the space in the bottom right (so that the top row reads 1,2,3,4 and the bottom row reads 13,14,15,space).S$"L/ .HxNSection 5.1: Fifteen controlsU&/ .LyNSection 5.2: Fifteen parametersQL#1#nSection 5.1: Fifteen controlsCBB("btn_up","JI(`',`games.fifteen')");EB("btn_up")K"n) "DSection 5.1: Fifteen controlsc>#% |This game can be controlled with the mouse or the keyboard.n& 7A left-click with the mouse in the row or column containing the empty space will move as many tiles as necessary to move the space to the mouse pointer.M, &The arrow keys will move a tile adjacent to the space in the direction indicated (moving the space in the opposite direction).nC+ &N(All the actions described in section 2.1 are also available.)SM?1J?Section 5.2: Fifteen parametersCBB("btn_up","JI(`',`games.fifteen')");EB("btn_up")M$) "HSection 5.2: Fifteen parameters?2 2The only options available from the Custom... option on the Type menu are Width and Height, which are self-explanatory. (Once you've changed these, it's not a 15-puzzle any more, of course!)m<196/ Chapter 6: SixteenCBB("btn_up","JI(`',`Top')");EB("btn_up")@6) ".Chapter 6: SixteennB, &JٳJAnother sliding tile puzzle, visually similar to Fifteen (see chapter 5) but with a different type of move. This time, there is no hole: all 16 squares on the grid contain numbered squares. Your move is to shift an entire row left or right, or shift an entire column up or down; every time you do that, the tile you shift off the grid re-appears at the other end of the same row, in the space you just vacated. To win, arrange the tiles into numerical order (1,2,3,4 on the top row, 13,14,15,16 on the bottom). When you've done that, try playing on different sizes of grid.6, &oI might have invented this game myself, though only by accident if so (and I'm sure other people have independently invented it). I thought I was imitating a screensaver I'd seen, but I have a feeling that the screensaver might actually have been a Fifteen-type puzzle rather than this slightly different kind. So this might be the one thing in my puzzle collection which represents creativity on my part rather than just engineering.S$/ .HzNSection 6.1: Sixteen controlsU&/ / .L{NSection 6.2: Sixteen parametersQ 1G   Section 6.1: Sixteen controlsCBB("btn_up","JI(`',`games.sixteen')");EB("btn_up")K"/  ) "DSection 6.1: Sixteen controls  & /Left-clicking on an arrow will move the appropriate row or column in the direction indicated. Right-clicking will move it in the opposite direction.  & WAlternatively, use the cursor keys to move the position indicator around the edge of the grid, and use the return key to move the row/column in the direction indicated.nC  + &N(All the actions described in section 2.1 are also available.)S | 1|  0@Section 6.2: Sixteen parametersCBB("btn_up","JI(`',`games.sixteen')");EB("btn_up")M$  ) "HSection 6.2: Sixteen parameterstO| = % The parameters available from the Custom... option on the Type menu are:l6  6 e% 2Ensure unique solution- (#Normally, Rectangles will make sure that the puzzles it presents have only one solution. Puzzles with ambiguous sections can be more difficult and more subtle, so if you like you can turn off this feature and risk having ambiguous puzzles. Also, finding all the possible solutions can be an additional challenge for an advanced player. Turning off this option can also speed up puzzle generation.n=;1_;|2Chapter 9: NetslideCBB("btn_up","JI(`',`Top')");EB("btn_up")A|) "0Chapter 9: Netslide*;2 26ۉWpThis game combines the grid generation of Net (see chapter 3) with the movement of Sixteen (see chapter 6): you have a Net grid, but instead of rotating tiles back into place you have to slide them into place by moving a whole row at a time.yN|+ &zNAs in Sixteen, control is with the mouse or cursor keys. See section 6.1.{1 2N{NThe available game parameters have similar meanings to those in Net (see section 3.2) and Sixteen (see section 6.2).gB2% Netslide was contributed to this collection by Richard Boulton.n=1Chapter 10: PatternCBB("btn_up","JI(`',`Top')");EB("btn_up")A2) "0Chapter 10: Patterna;B& wYou have a grid of squares, which must all be filled in either black or white. Beside each row of the grid are listed the lengths of the runs of black squares on that row; above each column are listed the lengths of the runs of black squares in that column. Your aim is to fill in the entire grid black or white.& I first saw this puzzle form around 1995, under the name nonograms. I've seen it in various places since then, under different names.B, &Normally, puzzles of this type turn out to be a meaningful picture of something once you've solved them. However, since this version generates the puzzles automatically, they will just look like random groupings of squares. (One user has suggested that this is actually a good thing, since it prevents you from guessing the colour of squares based on the picture, and forces you to use logic instead.) The advantage, though, is that you never run out of them.T%B/ .JNSection 10.1: Pattern controlsV'/ .NNSection 10.2: Pattern parametersRB1OgvSection 10.1: Pattern controlsCBB("btn_up","JI(`',`games.pattern')");EB("btn_up")L#g) "FSection 10.1: Pattern controlsK&% LThis game is played with the mouse.g& Left-click in a square to colour it black. Right-click to colour it white. If you make a mistake, you can middle-click, or hold down Shift while clicking with any button, to colour the square in the default grey (meaning undecided) again.8& %You can click and drag with the left or right mouse button to colour a vertical or horizontal line of squares black or white at a time (respectively). If you click and drag with the middle button, or with Shift held down, you can colour a whole rectangle of squares grey.& You can also move around the grid with the cursor keys. Pressing the return key will cycle the current cell through empty, then black, then white, then empty, and the space bar does the same cycle in reverse.nCv+ &N(All the actions described in section 2.1 are also available.)T1ISection 10.2: Pattern parametersCBB("btn_up","JI(`',`games.pattern')");EB("btn_up")N%vI) "JSection 10.2: Pattern parameters2 2The only options available from the Custom... option on the Type menu are Width and Height, which are self-explanatory.k:Ih1D o hChapter 11: SoloCBB("btn_up","JI(`',`Top')");EB("btn_up")>) "*Chapter 11: Soloh& You have a square grid, which is divided into as many equally sized sub-blocks as the grid has rows. Each square must be filled in with a digit from 1 to the size of the grid, in such a way thatg:- *tT8every row contains only one occurrence of each digitj=b- *zT8every column contains only one occurrence of each digitj=- *zT8every block contains only one occurrence of each digit.vbo- *T8(optionally, by default off) each of the square's two main diagonals contains only one occurrence of each digit.f% You are given some of the numbers as clues; your aim is to place the rest of the numbers correctly.o& ;Under the default settings, the sub-blocks are square or rectangular. The default puzzle size is 33 (a 99 actual grid, divided into nine 33 blocks). You can also select sizes with rectangular blocks instead of square ones, such as 23 (a 66 grid divided into six 32 blocks). Alternatively, you can select jigsaw mode, in which the sub-blocks are arbitrary shapes which differ between individual puzzles.x[ & Another available mode is killer. In this mode, clues are not given in the form of filled-in squares; instead, the grid is divided into cages by coloured lines, and for each cage the game tells you what the sum of all the digits in that cage should be. Also, no digit may appear more than once within a cage, even if the cage crosses the boundaries of existing regions.N 8 >-If you select a puzzle size which requires more than 9 digits, the additional digits will be letters of the alphabet. For example, if you select 34 then the digits which go in your grid will be 1 to 9, plus a, b and c. This cannot be selected for killer puzzles.r[ S 8 >eቀeቂI first saw this puzzle in Nikoli [5], although it's also been popularised by various newspapers under the name Sudoku or Su Doku. Howard Garns is considered the inventor of the modern form of the puzzle, and it was first published in Dell Pencil Puzzles and Word Games. A more elaborate treatment of the history of the puzzle can be found on Wikipedia [6].b:  ( t[5] http://www.nikoli.co.jp/puzzles/1/index_text-e.htmS+S ( V[6] http://en.wikipedia.org/wiki/SudokuQ" Y/ .DNSection 11.1: Solo controlsS$/ .HNSection 11.2: Solo parameters}LY)1)rJFSection 11.1: Solo controlsCBB("btn_up","JI(`',`games.solo')");EB("btn_up")I r) "@Section 11.1: Solo controls")@& To play Solo, simply click the mouse in any empty square and then type a digit or letter on the keyboarr@d to fill that square. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature)./rA, &If you right-click in a square and then type a number, that number will be entered in the square as a pencil mark. You can have pencil marks for multiple numbers in the same square. Squares containing filled-in numbers cannot also contain pencil marks.hB@7C& The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular number, or you can use them as lists of the possible numbers in a given square, or anything else you feel like.[AC% To erase a single pencil mark, right-click in the square and type the same number again.7CD& sAll pencil marks in a square are erased when you left-click and type a number, or when you left-click and press space. Right-clicking and pressing space will also erase pencil marks.F CE& AAlternatively, use the cursor keys to move the mark around the grid. Pressing the return key toggles the mark (from a normal mark to a pencil mark), and typing a number in is entered in the square in the appropriate way; typing in a 0 or using the space bar will clear a filled square.nCDJF+ &N(All the actions described in section 2.1 are also available.)NEF1 o  FGbSection 11.2: Solo parametersCBB("btn_up","JI(`',`games.solo')");EB("btn_up")K"JFG) "DSection 11.2: Solo parametersiCF}H& Solo allows you to configure two separate dimensions of the puzzle grid on the Type menu: the number of columns, and the number of rows, into which the main grid is divided. (The size of a block is the inverse of this: for example, if you select 2 columns and 3 rows, each actual block will have 3 columns and 2 rows.)_9GI& sIf you tick the X checkbox, Solo will apply the optional extra constraint that the two main diagonals of the grid also contain one of every digit. (This is sometimes known as Sudoku-X in newspapers.) In this mode, the squares on the two main diagonals will be shaded slightly so that you know it's enabled.}HK& If you tick the Jigsaw checkbox, Solo will generate randomly shaped sub-blocks. In this mode, the actual grid size will be taken to be the product of the numbers entered in the Columns and Rows boxes. There is no reason why you have to enter a number greater than 1 in both boxes; Jigsaw mode has no constraint on the grid size, and it can even be a prime number if you feel like it.%IL& If you tick the Killer checkbox, Solo will generate a set of of cages, which are randomly shaped and drawn in an outline of a different colour. Each of these regions contains a smaller clue which shows the digit sum of all the squares in this region.gAKN& You can also configure the type of symmetry shown in the generated puzzles. More symmetry makes the puzzles look prettier but may also make them easier, since the symmetry constraints can force more clues than necessary to be present. Completely asymmetric puzzles have the freedom to contain as few clues as possible.L , &Finally, you can configure the difficulty of the generated puzzles. Difficulty levels are judged by the complexity of the techniques of deduction required to solve the puzzle: each level requires a mode of reasoning which was not necessary in the previous one. In particular, on difficulty levels Trivial and Basic there will be a square you can fill in with a single number at all times, whereas at Intermediate level and beyond you will N JFhave to make partial deductions about the set of squares a number could be in (or the set of numbers that could be in a square). At Unreasonable level, even this is not enough, and you will eventually have to make a guess, and then backtrack if it turns out to be wrong.BNb& 9Generating difficult puzzles is itself difficult: if you select one of the higher difficulty levels, Solo may have to make many attempts at generating a puzzle before it finds one hard enough for you. Be prepared to wait, especially if you have also configured a large puzzle size.l; ΂1!΂ Chapter 12: MinesCBB("btn_up","JI(`',`Top')");EB("btn_up")?b ) ",Chapter 12: Mines^΂, &You have a grid of covered squares, some of which contain mines, but you don't know which. Your job is to uncover every square which does not contain a mine. If you uncover a square containing a mine, you lose. If you uncover a square which does not contain a mine, you are told how many mines are contained within the eight surrounding squares. >& This game needs no introduction; popularised by Windows, it is perhaps the single best known desktop puzzle game in existence., &This version of it has an unusual property. By default, it will generate its mine positions in such a way as to ensure that you never need to guess where a mine is: you will always be able to deduce it somehow. So you will never, as can happen in other versions, get to the last four squares and discover that there are two mines left but you have no way of knowing for sure where they are.R#>G/ .FNSection 12.1: Mines controlsT%/ .JNSection 12.2: Mines parametersNG1 "d Section 12.1: Mines controlsCBB("btn_up","JI(`',`games.mines')");EB("btn_up")J!d) "BSection 12.1: Mines controlsK&% LThis game is played with the mouse.d?d% ~If you left-click in a covered square, it will be uncovered.0& If you right-click in a covered square, it will place a flag which indicates that the square is believed to be a mine. Left-clicking in a marked square will not uncover it, for safety. You can right-click again to remove a mark placed in error.2 2qIf you left-click in an uncovered square, it will clear around the square. This means: if the square has exactly as many flags surrounding it as it should have mines, then all the covered squares next to it which are not flagged will be uncovered. So once you think you know the location of all the mines around a square, you can use this function as a shortcut to avoid having to click on each of the remaining squares one by one.pD0, &If you uncover a square which has no mines in the surrounding eight squares, then it is obviously safe to uncover those squares in turn, and so on if any of them also has no surrounding mines. This will be done for you automatically; so sometimes when you uncover a square, a whole new area will open up to be explored.X2& eYou can also use the cursor keys to move around the minefield. Pressing the return key in a covered square uncovers it, and in an uncovered square will clear around it (so it acts as the left button), pressing the space bar in a covered square will place a flag (similarly, it acts as the right button).lAN+ &NAll the actions described in section 2.1 are also available.[& Even Undo is available, although you might consider it cheating to use it. If you step on a mine, the program will only reveal the mine in N[question (unlike most other implementations, which reveal all of them). You can then Undo your fatal move and continue playing if you like. The program will track the number of times you died (and Undo will not reduce that counter), so when you get to the end of the game you know whether or not you did it without making any errors.N & ?(If you really want to know the full layout of the grid, which other implementations will show you after you die, you can always use the Solve menu option.)P[1y#Section 12.2: Mines parametersCBB("btn_up","JI(`',`games.mines')");EB("btn_up")L# ) "FSection 12.2: Mines parametersqL^% The options available from the Custom... option on the Type menu are:=+ &$Width, HeightA^& 6Size of grid in squares.- % Mines - (Number of mines in the grid. You can enter this as an absolute mine count, or alternatively you can put a % sign on the end in which case the game will arrange for that proportion of the squares in the grid to be mines. ' Beware of setting the mine count too high. At very high densities, the program may spend forever searching for a solvable grid.9% (Ensure solubility"' When this option is enabled (as it is by default), Mines will ensure that the entire grid can be fully deduced starting from the initial open space. If you prefer the riskier grids generated by other implementations, you can switch off this option.p?1r$fChapter 13: Same GameCBB("btn_up","JI(`',`Top')");EB("btn_up")C) "4Chapter 13: Same Game & You have a grid of coloured squares, which you have to clear by highlighting contiguous regions of more than one coloured square; the larger the region you highlight, the more points you get (and the faster you clear the arena).& If you clear the grid you win. If you end up with nothing but single squares (i.e., there are no more clickable regions left) you lose.S& GRemoving a region causes the rest of the grid to shuffle up: blocks that are suspended will fall down (first), and then empty columns are filled from the right.e@% Same Game was contributed to this collection by James Harvey.V'S/ .NNSection 13.1: Same Game controlsX)f/ .RNSection 13.2: Same Game parametersU1%:wSection 13.1: Same Game controlsCBB("btn_up","JI(`',`games.samegame')");EB("btn_up")N%f:) "JSection 13.1: Same Game controlsfA% This game can be played with either the keyboard or the mouse.i:.% If you left-click an unselected region, it becomes selected (possibly clearing the current selection).m% If you left-click the selected region, it will be removed (and the rest of the grid shuffled immediately).fA.&% If you right-click the selected region, it will be unselected. & {The cursor keys move a cursor around the grid. Pressing the Space or Enter keys while the cursor is in an unselected region selects it; pressing Space or Enter again removes it as above.nC&w+ &N(All the actions described in section 2.1 are also available.)W  1r& \Section 13.2: Same Game parametersCBB("btn_up","JI(`',`games.samegame')");EB("btn_up")w wP'w\) "NSection 13.2: Same Game parametersvQ % These parameters are available from the Custom... option on the Type menu.=\+ &$Width, HeightAP& 6Size of grid in squares.6% "No. of coloursPX' WNumber of different colours used to fill the grid; the more colours, the fewer large regions of colour and thus the more difficult it is to successfully clear the grid.6% "Scoring systemEX' =Controls the precise mechanism used for scoring. With the default system, (n-2)^2, only regions of three squares or more will score any points at all. With the alternative (n-1)^2 system, regions of two squares score a point each, and larger regions score relatively more points.9 % (Ensure solubilityr& If this option is ticked (the default state), generated grids will be guaranteed to have at least one solution. - (If you turn it off, the game generator will not try to guarantee soluble grids; it will, however, still ensure that there are at least 2 squares of each colour on the grid at the start (since a grid with exactly one square of a given colour is definitely insoluble). Grids generated with this option disabled may contain more large areas of contiguous colour, leading to opportunities for higher scores; they can also take less time to generate.k:1'< Chapter 14: FlipCBB("btn_up","JI(`',`Top')");EB("btn_up")><) "*Chapter 14: Flip,h&  You have a grid of squares, some light and some dark. Your aim is to light all the squares up at the same time. You can choose any square and flip its state from light to dark or dark to light, but when you do so, other squares around it change state as well.\<% Each square contains a small diagram showing which other squares change when you flip it.Q"h: / .DNSection 14.1: Flip controlsS$ / .HNSection 14.2: Flip parameters}L:  1F ( S P Section 14.1: Flip controlsCBB("btn_up","JI(`',`games.flip')");EB("btn_up")I S ) "@Section 14.1: Flip controlsfA  % This game can be played with either the keyboard or the mouse.S q & %Left-click in a square to flip it and its associated squares, or use the cursor keys to choose a square and the space bar or Enter key to flip.qE  , &If you use the Solve function on this game, it will mark some of the squares in red. If you click once in every square with a red mark, the game should be solved. (If you click in a square without a red mark, a red mark will appear in it to indicate that you will need to reverse that operation to reach the solution.)nCq P + &N(All the actions described in section 2.1 are also available.)N  1) @Section 14.2: Flip parametersCBB("btn_up","JI(`',`games.flip')");EB("btn_up")K"P ) "DSection 14.2: Flip parametersvQ % These parameters are available from the Custom... option on the Type menu.=+ &$Width, HeightA& 6Size of grid in squares.2 @% Shape typep@' This control determines the shape of the region which is flipped by clicking in any given square. The default setting, Crosses, causes every square to@@P  flip itself and its four immediate neighbours (or three or two if it's at an edge or corner). The other setting, Random, causes a random shape to be chosen for every square, so the game is different every time.l;@OA1  *OAAZEChapter 15: GuessCBB("btn_up","JI(`',`Top')");EB("btn_up")?@A) ",Chapter 15: GuessOAKB& /You have a set of coloured pegs, and have to reproduce a predetermined sequence of them (chosen by the computer) within a certain number of guesses.A&C& kEach guess gets marked with the number of correctly-coloured pegs in the correct places (in black), and also the number of correctly-coloured pegs in the wrong places (in white).-KBSD& This game is also known (and marketed, by Hasbro, mainly) as a board game Mastermind, with 6 colours, 4 pegs per row, and 10 guesses. However, this version allows custom settings of number of colours (up to 10), number of pegs per row, and number of guesses.a<&CD% xGuess was contributed to this collection by James Harvey.R#SDE/ .FNSection 15.1: Guess controlsT%DZE/ .JNSection 15.2: Guess parametersNEE1z0+E#FSLSection 15.1: Guess controlsCBB("btn_up","JI(`',`games.guess')");EB("btn_up")J!ZE#F) "BSection 15.1: Guess controlsfAEF% This game can be played with either the keyboard or the mouse.6#FG& !With the mouse, drag a coloured peg from the tray on the left-hand side to its required position in the current guess; pegs may also be dragged from current and past guesses to copy them elsewhere. To remove a peg, drag it off its current position to somewhere invalid.FzH& +Right-clicking in the current guess adds a hold marker; pegs that have hold markers will be automatically added to the next guess after marking.U/GI& _Alternatively, with the keyboard, the up and down cursor keys can be used to select a peg colour, the left and right keys to select a peg position, and the space bar or Enter key to place a peg of the selected colour in the chosen position. D or Backspace removes a peg, and H adds a hold marker.L&zHK& MWhen the guess is complete, the smaller feedback pegs will be highlighted; clicking on these (or moving the peg cursor to them with the arrow keys and pressing the space bar or Enter key) will mark the current guess, copy any held pegs to the next guess, and move the current guess marker.IK& IIf you correctly position all the pegs the solution will be displayed below; if you run out of guesses (or select Solve...) the solution will also be revealed.nCKSL+ &N(All the actions described in section 2.1 are also available.)PKL1r ,L MsSection 15.2: Guess parametersCBB("btn_up","JI(`',`games.guess')");EB("btn_up")L#SL M) "FSection 15.2: Guess parametersLM& 5These parameters are available from the Custom... option on the Type menu. The default game matches the parameters for the board game Mastermind./ MN% ColoursvPMN& Number of colours the solution is chosen from; from 2 to 10 (more is harder).6NN% "Pegs per guessS-NO& ZNumber of pegs per guess (more is harder)./ N=O% GuessesnHOO& Number of guesses you have to find the solution in (fewer is harder).4=O % Allow blanksO SLO' aAllows blank pegs to be given as part of a guess (makes it easier, because you know that those will never be counted as part of the solution). This is turned off by default.l t% Note that this doesn't allow blank pegs in the solution; if you really wanted that, use one extra colour.8% &Allow duplicatests' AAllows the solution (and the guesses) to contain colours more than once; this increases the search space (making things harder), and is turned on by default.k:ނ10 -ނeChapter 16: PegsCBB("btn_up","JI(`',`Top')");EB("btn_up")>s) "*Chapter 16: Pegsނ/& A number of pegs are placed in holes on a board. You can remove a peg by jumping an adjacent peg over it (horizontally or vertically) to a vacant hole on the other side. Your aim is to remove all but one of the pegs initially present.m% This game, best known as Peg Solitaire, is possibly one of the oldest puzzle games still commonly known.Q"// .DNSection 16.1: Pegs controlsS$e/ .HNSection 16.2: Pegs parameters}L1  .+ωSection 16.1: Pegs controlsCBB("btn_up","JI(`',`games.pegs')");EB("btn_up")I e+) "@Section 16.1: Pegs controls^8& qTo move a peg, drag it with the mouse from its current position to its final position. If the final position is exactly two holes away from the initial position, is currently unoccupied by a peg, and there is a peg in the intervening square, the move will be permitted and the intervening peg will be removed.+f& oVacant spaces which you can move a peg into are marked with holes. A space with no peg and no hole is not available for moving at all: it is an obstacle which you must work around.a& You can also use the cursor keys to move a position indicator around the board. Pressing the return key while over a peg, followed by a cursor key, will jump the peg in that direction (if that is a legal move).nCfω+ &N(All the actions described in section 2.1 are also available.)NaN1   /NWSection 16.2: Pegs parametersCBB("btn_up","JI(`',`games.pegs')");EB("btn_up")K"ω) "DSection 16.2: Pegs parametersvQN% These parameters are available from the Custom... option on the Type menu.=L+ &$Width, Height?& 2Size of grid in holes.2 L% Board typesW' Controls whether you are given a board of a standard shape or a randomly generated shape. The two standard shapes currently supported are Cross and Octagon (also commonly known as the English and European traditional board layouts respectively). Selecting Random will give you a different board shape every time (but always one that is known to have a solution).o>ƍ1 N 0ƍChapter 17: DominosaCBB("btn_up","JI(`',`Top')");EB("btn_up")BW) "2Chapter 17: Dominosaaƍ& A normal set of dominoes that is, one instance of every (unordered) pair of numbers from 0 to 6 has been arranged irregularly into a rectangle; then the number in each square has been written down and the dominoes themselves removed. Your task is to reconstruct the pattern by arranging the set of dominoes to match the provided array of numbers.a!% This puzzle is widely credited to O. S. Adler, and takes part of its name f!Wrom those initials.U&v/ .LNSection 17.1: Dominosa controlsW(!/ .PNSection 17.2: Dominosa parametersTvR1 1RoSection 17.1: Dominosa controlsCBB("btn_up","JI(`',`games.dominosa')");EB("btn_up")M$) "HSection 17.1: Dominosa controlsR& Left-clicking between any two adjacent numbers places a domino covering them, or removes one if it is already present. Trying to place a domino which overlaps existing dominoes will remove the ones it overlaps., &Right-clicking between two adjacent numbers draws a line between them, which you can use to remind yourself that you know those two numbers are not covered by a single domino. Right-clicking again removes the line.`:& uYou can also use the cursor keys to move a cursor around the grid. When the cursor is half way between two adjacent numbers, pressing the return key will place a domino covering those numbers, or pressing the space bar will lay a line between the two squares. Repeating either action removes the domino or line.nCo+ &N(All the actions described in section 2.1 are also available.)V1N 2ESection 17.2: Dominosa parametersCBB("btn_up","JI(`',`games.dominosa')");EB("btn_up")O&oE) "LSection 17.2: Dominosa parametersvQ% These parameters are available from the Custom... option on the Type menu.BE% :Maximum number on dominoes' Controls the size of the puzzle, by controlling the size of the set of dominoes used to make it. Dominoes with numbers going up to N will give rise to an (N+2) (N+1) rectangle; so, in particular, the default value of 6 gives an 87 grid.>T% 2Ensure unique solutionw- (Normally, Dominosa will make sure that the puzzles it presents have only one solution. Puzzles with ambiguous sections can be more difficult and sometimes more subtle, so if you like you can turn off this feature. Also, finding all the possible solutions can be an additional challenge for an advanced player. Turning off this option can also speed up puzzle generation.o>Tg1 m 3gChapter 18: UntangleCBB("btn_up","JI(`',`Top')");EB("btn_up")B) "2Chapter 18: Untangleg& You are given a number of points, some of which have lines drawn between them. You can move the points about arbitrarily; your aim is to position the points so that no line crosses another.e+ &{,I originally saw this in the form of a Flash game called Planarity [7], written by John Tantalo.T,s( X[7] http://home.cwru.edu/~jnt5/PlanarityU&/ .LNSection 18.1: Untangle controlsW(s/ .PNSection 18.2: Untangle parametersT1; / 4Section 18.1: Untangle controlsCBB("btn_up","JI(`',`games.untangle')");EB("btn_up")M$) "HSection 18.1: Untangle controls[q% To move a point, click on it with the left mouse button and drag it into a new position.nC+ &N(All the actions described in section 2.1 are also available.)Vqf1m q 5fSection 18.2: Untangle parametersCBB("btn_up","JI(`',`games.untangle')");EB("btn_up")O&) "LSection 18.2: Untangle parameters}Xf>% There is only one parameter available> from the Custom... option on the Type menu:8v% &Number of points^>& Controls the size of the puzzle, by specifying the number of points in the generated graph.p?vj1D/ 6jAChapter 19: Black BoxCBB("btn_up","JI(`',`Top')");EB("btn_up")C) "4Chapter 19: Black Boxj& A number of balls are hidden in a rectangular arena. You have to deduce the positions of the balls by firing lasers positioned at the edges of the arena and observing how their beams are deflected.s& eBeams will travel straight from their origin until they hit the opposite side of the arena (at which point they emerge), unless affected by balls in one of the following ways:1. *!T8A beam that hits a ball head-on is absorbed and will never re-emerge. This includes beams that meet a ball on the first rank of the arena.Ys- *T8A beam with a ball to its front-left square gets deflected 90 degrees to the right.X1<- *T8A beam with a ball to its front-right square gets similarly deflected to the left.Z- *T8A beam that would re-emerge from its entry location is considered to be reflected.<. *IT8A beam which would get deflected before entering the arena by a ball to the front-left or front-right of its entry point is also considered to be reflected.& Beams that are reflected appear as a R; beams that hit balls head-on appear as H. Otherwise, a number appears at the firing point and the location where the beam emerges (this number is unique to that shot).& You can place guesses as to the location of the balls, based on the entry and exit patterns of the beams; once you have placed enough balls a button appears enabling you to have your guesses checked.k % Here is a diagram showing how the positions of balls can create each of the beam behaviours shown above:1 B $  1RHR---- 1  s $ |..O.O...|1 B  $ 2........31 s  $ |........|1  $ |........|1 7 $ 3........|1  h $ |......O.|1 7  $ H........|1 h  $ |.....O..|1  %  12-RH---   & As shown, it is possible for a beam to receive multiple reflections before re-emerging (see turn 3). Similarly, a beam may be reflected (possibly more than once) before receiving a hit (the H on the left side of the example).) 0 , &Note that any layout with more than 4 balls may have a non-unique solution. The following diagram illustrates this; if you know the board contains 5 balls, it is impossible to determine where the fifth ball is (possible positions marked with an x):1  a $  -------- 1 0  $ |........|1 a  $ |........|1  $ |..O..O..|1 %$ |...xx...|1 V$ |...xx...|1 %$ |..O..O..|1 V$ |........|1 $ |........|1 %  --------I@8 >For this reason, when you have your guesses checked, the game will check that your solution produces the same results as the computer's, rather than that your solution is @identical to the computer's. So in the above example, you could put the fifth ball at any of the locations marked with an x, and you would still win.e@ A% Black Box was contributed to this collection by James Harvey.V'@bA/ .NNSection 19.1: Black Box controlsX) AA/ .RNSection 19.2: Black Box parametersUbA@B1 q  7@BB3LSection 19.1: Black Box controlsCBB("btn_up","JI(`',`games.blackbox')");EB("btn_up")N%AB) "JSection 19.1: Black Box controlsD@BC& =To fire a laser beam, left-click in a square around the edge of the arena. The results will be displayed immediately. Clicking or holding the left button on one of these squares will highlight the current go (or a previous go) to confirm the exit point for that laser, if applicable.BD& 3To guess the location of a ball, left-click within the arena and a black circle will appear marking the guess; click again to remove the guessed ball.CE& Locations in the arena may be locked against modification by right-clicking; whole rows and columns may be similarly locked by right-clicking in the laser square above/below that column, or to the left/right of that row.DxF& yThe cursor keys may also be used to move around the grid. Pressing the Enter key will fire a laser or add a new ball-location guess, and pressing Space will lock a cell, row, or column.ENG& aWhen an appropriate number of balls have been guessed, a button will appear at the top-left corner of the grid; clicking that (with mouse or cursor) will check your guesses.xFmI& If you click the check button and your guesses are not correct, the game will show you the minimum information necessary to demonstrate this to you, so you can try again. If your ball positions are not consistent with the beam paths you already know about, one beam path will be circled to indicate that it proves you wrong. If your positions match all the existing beam paths but are still wrong, one new beam path will be revealed (written in red) which is not consistent with your current guesses.X,NGK, &YIf you decide to give up completely, you can select Solve to reveal the actual ball positions. At this point, correctly-placed balls will be displayed as filled black circles, incorrectly-placed balls as filled black circles with red crosses, and missing balls as filled red circles. In addition, a red circle marks any laser you had already fired which is not consistent with your ball layout (just as when you press the check button), and red text marks any laser you could have fired in order to distinguish your ball layout from the correct one.nCmI3L+ &N(All the actions described in section 2.1 are also available.)WKL1~ 8L MESection 19.2: Black Box parametersCBB("btn_up","JI(`',`games.blackbox')");EB("btn_up")P'3L M) "NSection 19.2: Black Box parametersvQLM% These parameters are available from the Custom... option on the Type menu.= MM+ &$Width, HeightmM]N2 4Size of grid in squares. There are 2 Width Height lasers per grid, two per row and two per column.4MN% No. of balls]NE' Number of balls to place in the grid. This can be a single number, or a range (separated with a hyphen, like 2-6), and determines the number of balls to place on the grid. The reveal button is only enabled if you have guessed an appropriate number of balls; a guess using a different number to the original solution is stilNE3Ll acceptable, if all the beam inputs and outputs match.l;N1;  9Chapter 20: SlantCBB("btn_up","JI(`',`Top')");EB("btn_up")?E) ",Chapter 20: Slant& OYou have a grid of squares. Your aim is to draw a diagonal line through each square, and choose which way each line slants so that the following conditions are met:X+- *VT8The diagonal lines never form a loop.i;~. *wT8Any point with a circled number has precisely that many lines meeting at it. (Thus, a 4 is the centre of a cross shape, whereas a zero is the centre of a diamond shape or rather, a partial diamond shape, because a zero can never appear in the middle of the grid because that would immediately cause a loop.)Z/؃+ &^s܉Credit for this puzzle goes to Nikoli [8].nC~F+ &[8] http://www.nikoli.co.jp/puzzles/39/index.htm (in Japanese)R#؃/ .FNSection 20.1: Slant controlsT%F/ .JNSection 20.2: Slant parametersNk1 o :kSection 20.1: Slant controlsCBB("btn_up","JI(`',`games.slant')");EB("btn_up")J!) "BSection 20.1: Slant controlsBk2 2!Left-clicking in a blank square will place a \ in it (a line leaning to the left, i.e. running from the top left of the square to the bottom right). Right-clicking in a blank square will place a / in it (leaning to the right, running from top right to bottom left).t> JContinuing to click either button will cycle between the three possible square contents. Thus, if you left-click repeatedly in a blank square it will change from blank to \ to / back to blank, and if you right-click repeatedly the square will change from blank to / to \ back to blank. (Therefore, you can play the game entirely with one button if you need to.)2 2UYou can also use the cursor keys to move around the grid. Pressing the return or space keys will place a \ or a /, respectively, and will then cycle them as above.nC+ &N(All the actions described in section 2.1 are also available.)Pt1  ;tSection 20.2: Slant parametersCBB("btn_up","JI(`',`games.slant')");EB("btn_up")L#) "FSection 20.2: Slant parametersvQt6% These parameters are available from the Custom... option on the Type menu.=s+ &$Width, HeightA6& 6Size of grid in squares.2 s% Difficulty-- (Controls the difficulty of the generated puzzle. At Hard level, you are required to do deductions based on knowledge of relationships between squares rather than always being able to deduce the exact contents of one square at a time. (For example, you might know that two squares slant in the same direction, even if you don't yet know what that direction is, and this might enable you to deduce something about still other squares.) Even at Hard level, guesswork and backtracking should never be necessary.o>1o d <Ď}Chapter 21: Light UpCBB("btn_up","JI(`',`Top')");EB("btn_up")BĎ) "2Chapter 21: Light Up& qYou have a grid of squares. Some are filled in black; some of the black squares are numbered. Your aim is to light up all the empty squares by placing light bulbs in some of them.Ďn& 5Each light bulb illuminates the square it is on, plus anll squares in line with it horizontally or vertically unless a black square is blocking the way.c>% |To win the game, you must satisfy the following conditions:Q$n"- *HT8All non-black squares are lit.T'v- *NT8No light is lit by another light."-. *T8All numbered black squares have exactly that number of lights adjacent to them (in the four squares above, below, and to the side).rMv% Non-numbered black squares may have any number of lights adjacent to them.Z/-+ &^Credit for this puzzle goes to Nikoli [9].d?]% ~Light Up was contributed to this collection by James Harvey.tI+ &[9] http://www.nikoli.co.jp/puzzles/32/index-e.htm (beware of Flash)U&]&/ .LNSection 21.1: Light Up controlsW(}/ .PNSection 21.2: Light Up parametersS&1 8 =NSection 21.1: Light Up controlsCBB("btn_up","JI(`',`games.lightup')");EB("btn_up")M$}N) "HSection 21.1: Light Up controls]& Left-clicking in a non-black square will toggle the presence of a light in that square. Right-clicking in a non-black square toggles a mark there to aid solving; it can be used to highlight squares that cannot be lit, for example.wRN% You may not place a light in a marked square, nor place a mark in a lit square.]& The game will highlight obvious errors in red. Lights lit by other lights are highlighted in this way, as are numbered squares which do not (or cannot) have the right number of lights next to them.jQ% Thus, the grid is solved when all non-black squares have yellow highlights and there are no red lights.nC+ &N(All the actions described in section 2.1 are also available.)UQE1d  >ESection 21.2: Light Up parametersCBB("btn_up","JI(`',`games.lightup')");EB("btn_up")O&) "LSection 21.2: Light Up parametersvQE % These parameters are available from the Custom... option on the Type menu.=G+ &$Width, HeightA & 6Size of grid in squares.=G% 0%age of black squaresW1& bRough percentage of black squares in the grid.' This is a hint rather than an instruction. If the grid generator is unable to generate a puzzle to this precise specification, it will increase the proportion of black squares until it can.0 3% Symmetry' #Allows you to specify the required symmetry of the black squares in the grid. (This does not affect the difficulty of the puzzles noticeably.)2 3% Difficulty' Easy means that the puzzles should be soluble without backtracking or guessing, Hard means that some guesses will probably be necessary.j9=18  ?=zChapter 22: MapCBB("btn_up","JI(`',`Top')");EB("btn_up")=z) "(Chapter 22: MapU/=& _You are given a map consisting of a number of regions. Your task is to colour each region with one of four colours, in such a way that no two regions sharing a boundary have the same colour. You are provided with some regions already coloured, sufficient to make the remainder of the solution unique.z, &oOnly regions which share a length of border are required to be different colours. Two regions which meet at only one point (i.e. are diagonally separated) may be the same colour.pT& I believe this puzzle is original; I've never seen an implementation of it anywhere else. The concept of a four-colouring puzzle was suggested by Owen Dunn; credit must also go to Nikoli and to Verity Allan for inspiring the train of thought that led to me realising Owen's suggestion was a viable puzzle. Thanks also to Gareth Taylor for many detailed suggestions.P!/ .BNSection 22.1: Map controlsR#T/ .FNSection 22.2: Map parameters{Jq1}  @q Section 22.1: Map controlsCBB("btn_up","JI(`',`games.map')");EB("btn_up")H) ">Section 22.1: Map controlsqg& To colour a region, click the left mouse button on an existing region of the desired colour and drag that colour into the new region. & (The program will always ensure the starting puzzle has at least one region of each colour, so that this is always possible!)g&  If you need to clear a region, you can drag from an empty region, or from the puzzle boundary if there are no empty regions left.Z( 2 2QDragging a colour using the right mouse button will stipple the region in that colour, which you can use as a note to yourself that you think the region might be that colour. A region can contain stipples in multiple colours at once. (This is often useful at the harder difficulty levels.) & You can also use the cursor keys to move around the map: the colour of the cursor indicates the position of the colour you would drag (which is not obvious if you're on a region's boundary, since it depends on the direction from which you approached the boundary). Pressing the return key starts a drag of that colour, as above, which you control with the cursor keys; pressing the return key again finishes the drag. The space bar can be used similarly to create a stippled region. Double-pressing the return key (without moving the cursor) will clear the region, as a drag from an empty region does: this is useful with the cursor mode if you have filled the entire map in but need to correct the layout.^ & If you press L during play, the game will toggle display of a number in each region of the map. This is useful if you want to discuss a particular puzzle instance with a friend having an unambiguous name for each region is much easier than trying to refer to them all by names such as the one down and right of the brown one on the top border.nC  + &N(All the actions described in section 2.1 are also available.)}L k 1e Ak  @Section 22.2: Map parametersCBB("btn_up","JI(`',`games.map')");EB("btn_up")J!  ) "BSection 22.2: Map parametersvQk + % These parameters are available from the Custom... option on the Type menu.= h + &$Width, HeightA+  & 6Size of grid in squares./ h  % RegionsP* (& TNumber of regions in the generated map.2 Z% DifficultyL%(' KIn Easy mode, there should always be at least one region whose colour can be determined trivially. In Normal and Hard modes, you will have to use increasingly complex logic to deduce the colour of some regions. However, it will always be possible without having to guess or backtrack.*Z@' In Unreasonable mode, the program will feel free@  to generate puzzles which are as hard as it can possibly make them: the only constraint is that they should still have a unique solution. Solving Unreasonable puzzles may require guessing and backtracking.l;HA1p BHAAFChapter 23: LoopyCBB("btn_up","JI(`',`Top')");EB("btn_up")?@A) ",Chapter 23: LoopyHAB& You are given a grid of dots, marked with yellow lines to indicate which dots you are allowed to connect directly together. Your aim is to use some subset of those yellow lines to draw a single unbroken loop from dot to dot within the grid.AC& Some of the spaces between the lines contain numbers. These numbers indicate how many of the lines around that space form part of the loop. The loop you draw must correctly satisfy all of these clues to be considered a correct solution.BuD& 5In the default mode, the dots are arranged in a grid of squares; however, you can also play on triangular or hexagonal grids, or even more exotic ones.e:CD+ &tډCredit for the basic puzzle idea goes to Nikoli [10].uDE& =Loopy was originally contributed to this collection by Mike Pinna, and subsequently enhanced to handle various types of non-square grid by Lambros Lambrou.tIDF+ &[10] http://www.nikoli.co.jp/puzzles/3/index-e.htm (beware of Flash)R#EdF/ .FNSection 23.1: Loopy controlsT%FF/ .JNSection 23.2: Loopy parametersNdF7G1 C7GGISection 23.1: Loopy controlsCBB("btn_up","JI(`',`games.loopy')");EB("btn_up")J!FG) "BSection 23.1: Loopy controls7GcH& yClick the left mouse button on a yellow line to turn it black, indicating that you think it is part of the loop. Click again to turn the line yellow again (meaning you aren't sure yet).GZI, &If you are sure that a particular line segment is not part of the loop, you can click the right mouse button to remove it completely. Again, clicking a second time will turn the line back to yellow.nCcHI+ &N(All the actions described in section 2.1 are also available.)PZIIJ1 @ DIJJNSection 23.2: Loopy parametersCBB("btn_up","JI(`',`games.loopy')");EB("btn_up")L#IJ) "FSection 23.2: Loopy parametersvQIJ K% These parameters are available from the Custom... option on the Type menu.=JHK+ &$Width, Height KC% |Bridges was contributed to this collection by James Harvey._7( n[12] http://www.nikoli.co.jp/puzzles/14/index-e.htmT%C / .J$NSection 26.1: Bridges controls V'b/ .N%NSection 26.2: Bridges parametersR 1 L1 Section 26.1: Bridges controlsCBB("btn_up","JI(`',`games.bridges')");EB("btn_up")L#b1) "FSection 26.1: Bridges controlsx& To place a bridge between two islands, click the mouse down on one island and drag it towards the other. You do not need to drag all the way to the other island; you only need to move the mouse far enough for the intended bridge direction to be unambiguous. (So you can keep the mouse near the starting island and conveniently throw bridges out from it in many directions.))1& Doing this again when a bridge is already present will add another parallel bridge. If there are already as many bridges between the two islands as permitted by the current game rules (i.e. two by default), the same dragging action will remove all of them., &cIf you want to remind yourself that two islands definitely do not have a bridge between them, you can right-drag between them in the same way to draw a non-bridge marker.& yIf you think you have finished with an island (i.e. you have placed all its bridges and are confident that they are in the right places), you can mark the island as finished by left-clicking on it. This will highlight it and all the bridges connected to it, and you will be prevented from accidentally modifying any of those bridges in future. Left-clicking again on a highlighted island will unmark it and restore your ability to modify it.uR& You can also use the cursor keys to move around the grid: if possible the cursor will always move orthogonally, otherwise it will move towards the nearest island to the indicated direction. Pressing the return key followed by a cursor key will lay a bridge in that direction (if available); pressing the space bar followed by a cursor key will lay a non-bridge marker.lG% You can mark an island as finished by pressing the return key twice.]8R % pViolations of the puzzle rules will be marked in red:nA - *T8An island with too many bridges will be highlighted in red.Q#  . *GT8An island with too few bridges will be highlighted in red if it is definitely an error (as opposed to merely not being finished yet): if adding enough bridges would involve having to cross another bridge or remove a non-bridge marker, or if the island has been highlighted as complete.q y . *T8A group of islands and bridges may be highlighted in red if it is a closed subset of the puzzle with no way to connect it to the rest of the islands. For example, if you directly connect two 1s together with a bridge and they are not the only two islands on the grid, they will light up red to indicate that such a group cannot be contained in any valid solution. 8 . *#T8If you have selected the (non-default) option to disallow loops in the solution, a group of bridges which forms a loop will be highlighted.nCy  + &N(All the actions described in section 2.1 are also available.)T8 +1V'HM+yESection 26.2: Bridges parametersCBB("btn_up","JI(`',`games.bridges')");EB("btn_up")N% y) "JSection 26.2: Bridges parametersvQ+% These parameters are available from the Custom... option on the Type menu.=y,+ &$Width, HeightAm& 6Size of grid in squares.2 ,% DifficultyDm @& <Difficulty level of puzzle. @ 3?@% Allow loops @$A' }This is set by default. If cleared, puzzles will be generated in such a way that they are always soluble without creating a loop, and solutions which do involve a loop will be disallowed.B?@fA% :Max. bridges per direction$AB' Maximum number of bridges in any particular direction. The default is 2, but you can change it to 1, 3 or 4. In general, fewer is easier.>fAWB% 2%age of island squaresB/C' cGives a rough percentage of islands the generator will try and lay before finishing the puzzle. Certain layouts will not manage to lay enough islands; this is an upper bound.?WBnC% 4Expansion factor (%age)\/CD' The grid generator works by picking an existing island at random (after first creating an initial island somewhere). It then decides on a direction (at random), and then works out how far it could extend before creating another island. This parameter determines how likely it is to extend as far as it can, rather than choosing somewhere closer.nCE& 'High expansion factors usually mean easier puzzles with fewer possible islands; low expansion factors can create lots of tightly-packed islands.n=DF1 NFYFMChapter 27: UnequalCBB("btn_up","JI(`',`Top')");EB("btn_up")AEYF) "0Chapter 27: UnequalFGG& You have a square grid; each square may contain a digit from 1 to the size of the grid, and some squares have clue signs between them. Your aim is to fully populate the grid with numbers such that:f9YFG- *rT8Each row contains only one occurrence of each digiti<GGH- *xT8Each column contains only one occurrence of each digitT'GjH- *NT8All the clue signs are satisfied.d?HH% ~There are two modes for this game, Unequal and Adjacent.jHI& In Unequal mode, the clue signs are greater-than symbols indicating one square's value is greater than its neighbour's. In this mode not all clues may be visible, particularly at higher difficulty levels.V0HK& aIn Adjacent mode, the clue signs are bars indicating one square's value is numerically adjacent (i.e. one higher or one lower) than its neighbour. In this mode all clues are always visible: absence of a bar thus means that a square's value is definitely not numerically adjacent to that neighbour's.IK& eIn Trivial difficulty level (available via the Custom game type selector), there are no greater-than signs in Unequal mode; the puzzle is to solve the Latin square only.|KL% At the time of writing, the Unequal mode of this puzzle is appearing in the Guardian weekly under the name Futoshiki.c>KL% |Unequal was contributed to this collection by James Harvey.T%LKM/ .J&NSection 27.1: Unequal controlsV'LM/ .N'NSection 27.2: Unequal parametersRKM$N1HO$NpNSection 27.1: Unequal controlsCBB("btn_up","JI(`',`games.unequal')");EB("btn_up")L#MpN) "FSection 27.1: Unequal controls\7$NN% nUnequal shares much of its control system with Solo.%pN & To play Unequal, simply click the mouse in any empty square and then type a digit or letter on the keyboard to fill that square. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature).N M/N;, &If you right-click in a square and then type a number, that number will be entered in the square as a pencil mark. You can have pencil marks for multiple numbers in the same square. Squares containing filled-in numbers cannot also contain pencil marks.hB & The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular number, or you can use them as lists of the possible numbers in a given square, or anything else you feel like.[;#% To erase a single pencil mark, right-click in the square and type the same number again.& sAll pencil marks in a square are erased when you left-click and type a number, or when you left-click and press space. Right-clicking and pressing space will also erase pencil marks.5#7& As for Solo, the cursor keys can be used in conjunction with the digit keys to set numbers or pencil marks. You can also use the 'M' key to auto-fill every numeric hint, ready for removal as required, or the 'H' key to do the same but also to remove all obvious hints.F }& AAlternatively, use the cursor keys to move the mark around the grid. Pressing the return key toggles the mark (from a normal mark to a pencil mark), and typing a number in is entered in the square in the appropriate way; typing in a 0 or using the space bar will clear a filled square.nC7+ &N(All the actions described in section 2.1 are also available.)T}p1 PpSection 27.2: Unequal parametersCBB("btn_up","JI(`',`games.unequal')");EB("btn_up")N%) "JSection 27.2: Unequal parametersvQp4% These parameters are available from the Custom... option on the Type menu.,`% ModeU/4& ^Mode of the puzzle (Unequal or Adjacent)2 `% Size (s*s)6&  Size of grid.2 O% Difficulty' Controls the difficulty of the generated puzzle. At Trivial level, there are no greater-than signs; the puzzle is to solve the Latin square only. At Recursive level (only available via the Custom game type selector) backtracking will be required, but the solution should still be unique. The levels in between require increasingly complex reasoning to avoid having to backtrack.o>Od1 QdChapter 28: GalaxiesCBB("btn_up","JI(`',`Top')");EB("btn_up")B) "2Chapter 28: GalaxiesAd& 7You have a rectangular grid containing a number of dots. Your aim is to draw edges along the grid lines which divide the rectangle into regions in such a way that every region is 180 rotationally symmetric, and contains exactly one dot which is located at its centre of symmetry., &ud@This puzzle was invented by Nikoli [13], under the name Tentai Show; its name is commonly translated into English as Spiral Galaxies.d?% ~Galaxies was contributed to this collection by James Harvey.f>k( |[13] http://www.nikoli.co.jp/en/puzzles/astronomical_show/U&/ .L(NSection 28.1: Galaxies controlsW(k/ .P)NSection 28.2: Galaxies parametersT1 R Section 28.1: Galaxies controlsCBB("btn_up","JI(`',`games.galaxies')");EB("btn_up")M$ ) "HSection 28.1: Galaxies controls j& Left-click on any grid line to draw an edge if there isn't one already, or to remove one if there is. When you create a valid region (one which is closed, contains exactly one dot, is 180 symmetric about that dot, and contains no extraneous edges inside it) it will be highlighted automatically; so your aim is to have the whole grid highlighted in that way.  & During solving, you might know that a particular grid square belongs to a specific dot, but not be sure of where the edges go and which other squares are connected to the dot. In order to mark this so you don't forget, you can right-click on the dot and drag, which will create an arrow marker pointing at the dot. Drop that in a square of your choice and it will remind you which dot it's associated with. You can also right-click on existing arrows to pick them up and move them, or destroy them by dropping them off the edge of the grid. (Also, if you're not sure which dot an arrow is pointing at, you can pick it up and move it around to make it clearer. It will swivel constantly as you drag it, to stay pointed at its parent dot.)U& You can also use the cursor keys to move around the grid squares and lines. Pressing the return key when over a grid line will draw or clear its edge, as above. Pressing the return key when over a dot will pick up an arrow, to be dropped the next time the return key is pressed; this can also be used to move existing arrows around, removing them by dropping them on a dot or another arrow.nC+ &N(All the actions described in section 2.1 are also available.)VUJ1I xSJSection 28.2: Galaxies parametersCBB("btn_up","JI(`',`games.galaxies')");EB("btn_up")O&) "LSection 28.2: Galaxies parametersvQJ% These parameters are available from the Custom... option on the Type menu.=L+ &$Width, HeightA& 6Size of grid in squares.2 L% Difficulty' [Controls the difficulty of the generated puzzle. More difficult puzzles require more complex deductions, and the Unreasonable difficulty level may require backtracking.n=1ŊTBChapter 29: FillingCBB("btn_up","JI(`',`Top')");EB("btn_up")AB) "0Chapter 29: Filling*l&  You have a grid of squares, some of which contain digits, and the rest of which are empty. Your job is to fill in digits in the empty squares, in such a way that each connected region of squares all containing the same digit has an area equal to that digit.pB% (Connected region, for the purposes of this game, does not count diagonally separated squares as adjacent.)l& For example, it follows that no square can contain a zero, and that two adjacent squares can not both contain a one. No region has an area greater than 9 (because then its area would not be a single digit).[0S+ &`8CKCredit for this puzzle goes to Nikoli [14].c>% |Filling was contributed to this collection by Jonas Klker.^6S( l[14] http://www.nikoli.co.jp/en/puzzles/fillomino/T%h/ .J*NSection 29.1: Filling controlsV'/ .N+NSection 29.2: Filling parametersRhA1xUASection 29.1: Filling controlsCBB("btn_up","JI(`',`games.filling')");EB("btn_up")L#) "FSection 29.1: Filling controlscA"& To play Filling, simply click the mouse in any empty square and then type a "digit on the keyboard to fill that square. By dragging the mouse, you can select multiple squares to fill with a single keypress. If you make a mistake, click the mouse in the incorrect square and press 0, Space, Backspace or Enter to clear it again (or use the Undo feature).uO& You can also move around the grid with the cursor keys; typing a digit will fill the square containing the cursor with that number, or typing 0, Space, or Enter will clear it. You can also select multiple squares for numbering or clearing by using the return key, before typing a digit to fill in the highlighted squares (as above).nC"+ &N(All the actions described in section 2.1 are also available.)T1ŊVdSection 29.2: Filling parametersCBB("btn_up","JI(`',`games.filling')");EB("btn_up")N%) "JSection 29.2: Filling parametersgd% Filling allows you to configure the number of rows and columns of the grid, through the Type menu.k:1W  Chapter 30: KeenCBB("btn_up","JI(`',`Top')");EB("btn_up")>d ) "*Chapter 30: Keen -& You have a square grid; each square may contain a digit from 1 to the size of the grid. The grid is divided into blocks of varying shape and size, with arithmetic clues written in them. Your aim is to fully populate the grid with digits such that:f9 - *rT8Each row contains only one occurrence of each digiti<-- *xT8Each column contains only one occurrence of each digit. *!T8The digits in each block can be combined to form the number stated in the clue, using the arithmetic operation given in the clue. That is:. *KT!8An addition clue means that the sum of the digits in the block must be the given number. For example, 15+ means the contents of the block adds up to fifteen.; . *T!8A multiplication clue (e.g. 60ג), similarly, means that the product of the digits in the block must be the given number.i 1 0T!8A subtraction clue will always be written in a block of size two, and it means that one of the digits in the block is greater than the other by the given amount. For example, 2- means that one of the digits in the block is 2 more than the other, or equivalently that one digit minus the other one is 2. The two digits could be either way round, though.;  . *5T!8A division clue (e.g. 3), similarly, is always in a block of size two and means that one digit divided by the other is equal to the given amount.   - (eቂNote that a block may contain the same digit more than once (provided the identical ones are not in the same row and column). This rule is precisely the opposite of the rule in Solo's Killer mode (see chapter 11).a<  % xThis puzzle appears in the Times under the name KenKen.Q" X / .D,NSection 30.1: Keen controlsS$  / .HXNSection 30.2: Keen parameters}LX (1#HX(qgFSection 30.1: Keen controlsCBB("btn_up","JI(`',`games.keen')");EB("btn_up")I q) "@Section 30.1: Keen controlsgB(% Keen shares much of its control system with Solo (and Unequal).q @& To play Keen, simply click the mouse in any empty square and then type a digit on the keyboard to fill that square. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature). @ /;A, &If you right-click in a square and then type a number, that number will be entered in the square as a pencil mark. You can have pencil marks for multiple numbers in the same square. Squares containing filled-in numbers cannot also contain pencil marks.hB @B& The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular number, or you can use them as lists of the possible numbers in a given square, or anything else you feel like.[;A#C% To erase a single pencil mark, right-click in the square and type the same number again.BD& sAll pencil marks in a square are erased when you left-click and type a number, or when you left-click and press space. Right-clicking and pressing space will also erase pencil marks.f@#ChE& As for Solo, the cursor keys can be used in conjunction with the digit keys to set numbers or pencil marks. Use the cursor keys to move a highlight around the grid, and type a digit to enter it in the highlighted square. Pressing return toggles the highlight into a mode in which you can enter or remove pencil marks.lDE% Pressing M will fill in a full set of pencil marks in every square that does not have a main digit in it.nChEgF+ &N(All the actions described in section 2.1 are also available.)NEF1aYF1GISection 30.2: Keen parametersCBB("btn_up","JI(`',`games.keen')");EB("btn_up")K"gF1G) "DSection 30.2: Keen parametersvQFG% These parameters are available from the Custom... option on the Type menu.1 1GG% Grid sizeGH' 9Specifies the size of the grid. Lower limit is 3; upper limit is 9 (because the user interface would become more difficult with digits bigger than 9!).2 GH% DifficultyHI' Controls the difficulty of the generated puzzle. At Unreasonable level, some backtracking will be required, but the solution should still be unique. The remaining levels require increasingly complex reasoning to avoid having to backtrack.m<HRJ1EHJZRJJChapter 31: TowersCBB("btn_up","JI(`',`Top')");EB("btn_up")@IJ) ".Chapter 31: TowersRJpK& qYou have a square grid. On each square of the grid you can build a tower, with its height ranging from 1 to the size of the grid. Around the edge of the grid are some numeric clues.jEJK% Your task is to build a tower on every square, in such a way that:h;pKBL- *vT8Each row contains every possible height of tower oncek>KL- *|T8Each column contains every possible height of tower onceBLN. *T8Each numeric clue describes the number of towers that can be seen if you look into the square from that direction, assuming that shorter towers are hidden behind taller ones. For example, in a 55 grid, a clue marked 5 indicates that the five tower heights must appear in increasing order (otherwise you would not be able to see all five towers), whereas a clue marked 1 indicates that the tallest tower (the one marked 5) must come first.LPO& In harder or larger puzzles, some towers will be specified for you as well as the clues round the edge, and some edge clues may be missing.zN % This puzzle appears on the web under various names, particularly Skyscrapers, but I don't know who first invented it.PO IS$PO_/ .HONSection 31.1: Towers controlsU& / .LPNSection 31.2: Towers parametersP_51Ea[5zSection 31.1: Towers controlsCBB("btn_up","JI(`',`games.towers')");EB("btn_up")K") "DSection 31.1: Towers controlsmH5% Towers shares much of its control system with Solo, Unequal and Keen.;(& +To play Towers, simply click the mouse in any empty square and then type a digit on the keyboard to fill that square with a tower of the given height. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature).&N, &If you right-click in a square and then type a number, that number will be entered in the square as a pencil mark. You can have pencil marks for multiple numbers in the same square. A square containing a tower cannot also contain pencil marks.hB(& The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular number, or you can use them as lists of the possible numbers in a given square, or anything else you feel like.[N6% To erase a single pencil mark, right-click in the square and type the same number again.& sAll pencil marks in a square are erased when you left-click and type a number, or when you left-click and press space. Right-clicking and pressing space will also erase pencil marks.f@6{& As for Solo, the cursor keys can be used in conjunction with the digit keys to set numbers or pencil marks. Use the cursor keys to move a highlight around the grid, and type a digit to enter it in the highlighted square. Pressing return toggles the highlight into a mode in which you can enter or remove pencil marks.l % Pressing M will fill in a full set of pencil marks in every square that does not have a main digit in it.nC{z+ &N(All the actions described in section 2.1 are also available.)R 1J# \JSection 31.2: Towers parametersCBB("btn_up","JI(`',`games.towers')");EB("btn_up")M$zJ) "HSection 31.2: Towers parametersvQ% These parameters are available from the Custom... option on the Type menu.1 J% Grid size' 9Specifies the size of the grid. Lower limit is 3; upper limit is 9 (because the user interface would become more difficult with digits bigger than 9!).2 % Difficulty' Controls the difficulty of the generated puzzle. At Unreasonable level, some backtracking will be required, but the solution should still be unique. The remaining levels require increasingly complex reasoning to avoid having to backtrack.n=l1]l Chapter 32: SinglesCBB("btn_up","JI(`',`Top')");EB("btn_up")A) "0Chapter 32: Singlesl& wYou have a grid of white squares, all of which contain numbers. Your task is to colour some of the squares black (removing the number) so as to satisfy all of the following conditions:h;- *vT8No number occurs more than once in any row or column.Wz- *T8No black square is horizontally or vertically adjacent to any other black square.y,- *T8The remaining white squares must all form one contiguous region (connected by edges, z,not just touching at corners).nCz+ &DrnCredit for this puzzle goes to Nikoli [15] who call it Hitori.c>,% |Singles was contributed to this collection by James Harvey.yNv+ &[15] http://www.nikoli.com/en/puzzles/hitori/index.html (beware of Flash)T%/ .JQNSection 32.1: Singles controlsV'v / .NRNSection 32.2: Singles parametersR1# #^KSection 32.1: Singles controlsCBB("btn_up","JI(`',`games.singles')");EB("btn_up")L# ) "FSection 32.1: Singles controls& Left-clicking on an empty square will colour it black; left-clicking again will restore the number. Right-clicking will add a circle (useful for indicating that a cell is definitely not black).& You can also use the cursor keys to move around the grid. Pressing the return or space keys will turn a square black or add a circle respectively, and pressing the key again will restore the number or remove the circle.nCK+ &N(All the actions described in section 2.1 are also available.)T1_Section 32.2: Singles parametersCBB("btn_up","JI(`',`games.singles')");EB("btn_up")N%K) "JSection 32.2: Singles parametersvQ% These parameters are available from the Custom... option on the Type menu.=+ &$Width, HeightA& 6Size of grid in squares.2 D% DifficultyY3& fControls the difficulty of the generated puzzle.n=D 1#` LChapter 33: MagnetsCBB("btn_up","JI(`',`Top')");EB("btn_up")AL) "0Chapter 33: Magnets| & A rectangular grid has been filled with a mixture of magnets (that is, dominoes with one positive end and one negative end) and blank dominoes (that is, dominoes with two neutral poles). These dominoes are initially only seen in silhouette. Around the grid are placed a number of clues indicating the number of positive and negative poles contained in certain columns and rows.CL1& ;Your aim is to correctly place the magnets and blank dominoes such that all the clues are satisfied, with the additional constraint that no two similar magnetic poles may be orthogonally adjacent (since they repel). Neutral poles do not repel, and can be adjacent to any other pole.Z/+ &^ARCredit for this puzzle goes to Janko [16].c>1% |Magnets was contributed to this collection by James Harvey.^6L( l[16] http://www.janko.at/Raetsel/Magnete/index.htmT%/ .JSNSection 33.1: Magnets controlsV'L/ .NTNSection 33.2: Magnets parametersRy1{ay Section 33.1: Magnets controlsCBB("btn_up","JI(`',`games.magnets')");EB("btn_up")L#) "FSection 33.1: Magnets controlsy& Left-clicking on an empty square places a magnet at that position with the positive pole on the square and the negative pole on the other half of the magnet; left-clicking again reverses the polarity, and a third click removes the magnet., &  Right-clicking on an empty square places a blank domino there. Right-clicking again places two question marks on the domino, signifying this cannot be blank (which can be useful to note deductions while solving), and right-clicking again empties the d omino.~X & You can also use the cursor keys to move a cursor around the grid. Pressing the return key will lay a domino with a positive pole at that position; pressing again reverses the polarity and then removes the domino, as with left-clicking. Using the space bar allows placement of blank dominoes and cannot-be-blank hints, as for right-clicking.nC  + &N(All the actions described in section 2.1 are also available.)T  1b  j Section 33.2: Magnets parametersCBB("btn_up","JI(`',`games.magnets')");EB("btn_up")N%  ) "JSection 33.2: Magnets parametersvQ I % These parameters are available from the Custom... option on the Type menu.=  + &$Width, HeightI E 3 4Size of grid in squares. There will be half Width Height dominoes in the grid: if this number is odd then one square will be blank.kF  % (Grids with at least one odd dimension tend to be easier to solve.)2 E  % Difficulty  ' 3Controls the difficulty of the generated puzzle. At Tricky level, you are required to make more deductions about empty dominoes and row/column counts.3  % Strip clueso j & If true, some of the clues around the grid are removed at generation time, making the puzzle more difficult.o>  1c  Chapter 34: SignpostCBB("btn_up","JI(`',`Top')");EB("btn_up")Bj  ) "2Chapter 34: Signpost8 S & %You have a grid of squares; each square (except the last one) contains an arrow, and some squares also contain numbers. Your job is to connect the squares to form a continuous list of numbers starting at 1 and linked in the direction of the arrows so the arrow inside the square with the number 1 will point to the square containing the number 2, which will point to the square containing the number 3, etc. Each square can be any distance away from the previous one, as long as it is somewhere in the direction of the arrow.t % By convention the first and last numbers are shown; one or more interim numbers may also appear at the beginning.WS n + &YFQCredit for this puzzle goes to Janko [17], who call it Pfeilpfad (arrow path).d? % ~Signpost was contributed to this collection by James Harvey.\4n . ( h[17] http://janko.at/Raetsel/Pfeilpfad/index.htmU& / .LUNSection 34.1: Signpost controlsW(. / .PVNSection 34.2: Signpost parametersT _ 1[Ãd_ D Section 34.1: Signpost controlsCBB("btn_up","JI(`',`games.signpost')");EB("btn_up")M$ ) "HSection 34.1: Signpost controls'_ & To play Signpost, you connect squares together by dragging from one square to another, indicating that they are adjacent in the sequence. Drag with the left button from a square to its successor, or with the right button from a square to its predecessor.  > JIf you connect together two squares in this way and one of them has a number in it, the appropriate number will appear in the other square. If you connect two non-numbered squares, they will be assigned temporary algebraic labels: on the first occasion, they will be labelled a and a+1, and then b and b+1, and so on. Connecting more squares on to the ends of such a chain will cause them all to be labelled with the same letter.d f@ % When you  f@ left-click or right-click in a square, the legal squares to connect it to will be shown.hB A & The arrow in each square starts off black, and goes grey once you connect the square to its successor. Also, each square which needs a predecessor has a small dot in the bottom left corner, which vanishes once you link a square to it. So your aim is always to connect a square with a black arrow to a square with a dot.f@ B & cTo remove any links for a particular square (both incoming and outgoing), left-drag it off the grid. To remove a whole chain, right-drag any square in the chain off the grid.A XD & You can also use the cursor keys to move around the grid squares and lines. Pressing the return key when over a square starts a link operation, and pressing the return key again over a square will finish the link, if allowable. Pressing the space bar over a square will show the other squares pointing to it, and allow you to form a backward link, and pressing the space bar again cancels this.nCB D + &N(All the actions described in section 2.1 are also available.)VXD ME 1beME E G Section 34.2: Signpost parametersCBB("btn_up","JI(`',`games.signpost')");EB("btn_up")O&D E ) "LSection 34.2: Signpost parametersvQME F % These parameters are available from the Custom... option on the Type menu.=E OF + &$Width, HeightAF F & 6Size of grid in squares.BOF F % :Force start/end to cornersF G ' If true, the start and end squares are always placed in opposite corners (the start at the top left, and the end at the bottom right). If false the start and end squares are placed randomly (although always both shown).l;F CH 1ÃWfCH H 9O Chapter 35: RangeCBB("btn_up","JI(`',`Top')");EB("btn_up")?G H ) ",Chapter 35: RangeCH ?I & /You have a grid of squares; some squares contain numbers. Your job is to colour some of the squares black, such that several criteria are satisfied:]0H I - *`T8no square with a number is coloured black.rE?I J - *T8no two black squares are adjacent (horizontally or vertically).WI J - *T8for any two white squares, there is a path between them using only white squares.HJ K . *5T8for each square with a number, that number denotes the total number of white squares reachable from that square going in a straight line in any horizontal or vertical direction until hitting a wall or a black square; the square with the number is included in the total (once).CJ M & ;For instance, a square containing the number one must have four black squares as its neighbours by the last criterion; but then it's impossible for it to be connected to any outside white square, which violates the second to last criterion. So no square will contain the number one.K M , &ۉCredit for this puzzle goes to Nikoli, who have variously called it Kurodoko, Kuromasu or Where is Black Cells. [18].a<M *N % xRange was contributed to this collection by Jonas Klker.iAM N ( [18] http://www.nikoli.co.jp/en/puzzles/where_is_black_cells/R#*N N / .FWNSection 35.1: Range controlsT%N 9O / .JNSection 35.2: Range parametersNN O 1b=gO  Ȃ Section 35.1: Range controlsCBB("btn_up","JI(`',`games.range')");EB("btn_up")J!9O  ) "BSection 35.1: Range controlsO  9O f:O t , &uClick with the left button to paint a square black, or with the right button to mark a square with a dot to indicate that you are sure it should not be painted black. Repeated clicking with either button will cycle the square through the three possible states (filled, dotted or empty) in opposite directions. Z & You can also use the cursor keys to move around the grid squares. Pressing Return does the same as clicking with the left button, while pressing Space does the same as a right button click.nCt Ȃ + &N(All the actions described in section 2.1 are also available.)PZ I 1@WhI Section 35.2: Range parametersCBB("btn_up","JI(`',`games.range')");EB("btn_up")L#Ȃ ) "FSection 35.2: Range parametersvQI % These parameters are available from the Custom... option on the Type menu.= H + &$Width, HeightA & 6Size of grid in squares.l;H 1=i 4 ۋ Chapter 36: PearlCBB("btn_up","JI(`',`Top')");EB("btn_up")? 4 ) ",Chapter 36: Pearl & AYou have a grid of squares. Your job is to draw lines between the centres of horizontally or vertically adjacent squares, so that the lines form a single closed loop. In the resulting grid, some of the squares that the loop passes through will contain corners, and some will be straight horizontal or vertical lines. (And some squares can be completely empty the loop doesn't have to pass through every square.)c4 % Some of the squares contain black and white circles, which are clues that the loop must satisfy. 5 & A black circle in a square indicates that that square is a corner, but neither of the squares adjacent to it in the loop is also a corner. , &A white circle indicates that the square is a straight edge, but at least one of the squares adjacent to it in the loop is a corner.5  2 2(In both cases, the clue only constrains the two squares adjacent in the loop, that is, the squares that the loop passes into after leaving the clue square. The squares that are only adjacent in the grid are not constrained.)qF u + &!nۉCredit for this puzzle goes to Nikoli, who call it Masyu. [19].fA ۊ % Thanks to James Harvey for assistance with the implementation.Z2u 5 ( d[19] http://www.nikoli.co.jp/en/puzzles/masyu/R#ۊ / .FzNSection 36.1: Pearl controlsT%5 ۋ / .J{NSection 36.2: Pearl parametersN Z 1tjZ Section 36.1: Pearl controlsCBB("btn_up","JI(`',`games.pearl')");EB("btn_up")J!ۋ ) "BSection 36.1: Pearl controlsZ P &  Click with the left button on a grid edge to draw a segment of the loop through that edge, or to remove a segment once it is drawn. e & Drag with the left button through a series of squares to draw more than one segment of the loop in one go. Alternatively, drag over an existing part of the loop to undraw it, or to undraw part of it and then go in a different direction.WP , &Click with the right button on a grid edge to mark it with a cross, indicating that you are sure the loop does not go through that edge. (For instance, if you have decided which of the squares adjacent to a white clue has to be a corner, but don't yet know which way the corner turns, you might mark the one way it can't go with a cross.)e ۋ xRe & Alternatively, use the cursor keys to move the cursor. Use the Enter key to begin and end keyboard `drag' operations. Use the Space key to cancel the drag. Use Ctrl-arrowkey and Shift-arrowkey to simulate a left or right click, respectively, on the edge in the given direction relative to the cursor, i.e. to draw a segment or a cross.nC + &N(All the actions described in section 2.1 are also available.)P s 1 ks 5 Section 36.2: Pearl parametersCBB("btn_up","JI(`',`games.pearl')");EB("btn_up")L# ) "FSection 36.2: Pearl parametersvQs 5 % These parameters are available from the Custom... option on the Type menu.m< 1l Chapter 37: UndeadCBB("btn_up","JI(`',`Top')");EB("btn_up")@5 ) ".Chapter 37: Undead & You are given a grid of squares, some of which contain diagonal mirrors. Every square which is not a mirror must be filled with one of three types of undead monster: a ghost, a vampire, or a zombie. & Vampires can be seen directly, but are invisible when reflected in mirrors. Ghosts are the opposite way round: they can be seen in mirrors, but are invisible when looked at directly. Zombies are visible by any means. & sYou are also told the total number of each type of monster in the grid. Also around the edge of the grid are written numbers, which indicate how many monsters can be seen if you look into the grid along a row or column starting from that position. (The diagonal mirrors are reflective on both sides. If your reflected line of sight crosses the same monster more than once, the number will count it each time it is visible, not just once.)t P + &T( This puzzle type was invented by David Millar, under the name Haunted Mirror Maze. See [20] for more details.c> % |Undead was contributed to this collection by Steffen Bauer.b:P  ( t[20] http://www.janko.at/Raetsel/Spukschloss/index.htmS$ h / .H|NSection 37.1: Undead controlsU& / .L}NSection 37.2: Undead parametersPh > 1 9m> $ Section 37.1: Undead controlsCBB("btn_up","JI(`',`games.undead')");EB("btn_up")K" ) "DSection 37.1: Undead controlsfA> % Undead has a similar control system to Solo, Unequal and Keen.X2 G & eTo play Undead, click the mouse in any empty square and then type a letter on the keyboard indicating the type of monster: G for a ghost, V for a vampire, or Z for a zombie. If you make a mistake, click the mouse in the incorrect square and press Space to clear it again (or use the Undo feature).Q% , &KIf you right-click in a square and then type a letter, the corresponding monster will be shown in reduced size in that square, as a pencil mark. You can have pencil marks for multiple monsters in the same square. A square containing a full-size monster cannot also contain pencil marks.iCG  & The game pays no attention to pencil marks, so exactly what you use them for is up to you: you can use them as reminders that a particular square needs to be re-examined once you know more about a particular monster, or you can use them as lists of the possible monster in a given square, or anything else you feel like.[ % To erase a single pencil mark, right-click in the square and type the same letter again. t & All pencil marks in a square are erased when you left-click and type a monster letter, o t r when you left-click and press Space. Right-clicking and pressing space will also erase pencil marks.sM  & As for Solo, the cursor keys can be used in conjunction with the letter keys to place monsters or pencil marks. Use the cursor keys to move a highlight around the grid, and type a monster letter to enter it in the highlighted square. Pressing return toggles the highlight into a mode in which you can enter or remove pencil marks.t  & SIf you prefer plain letters of the alphabet to cute monster pictures, you can press A to toggle between showing the monsters as monsters or showing them as letters.nC $ + &N(All the actions described in section 2.1 are also available.)R  1n  s Section 37.2: Undead parametersCBB("btn_up","JI(`',`games.undead')");EB("btn_up")M$$  ) "HSection 37.2: Undead parametersvQ j % These parameters are available from the Custom... option on the Type menu.=  + &$Width, HeightAj  & 6Size of grid in squares.2   % DifficultyY3 s & fControls the difficulty of the generated puzzle.m<  19o  Chapter 38: UnrulyCBB("btn_up","JI(`',`Top')");EB("btn_up")@s  ) ".Chapter 38: Unruly\6 | & mYou are given a grid of squares, which you must colour either black or white. Some squares are provided as clues; the rest are left for you to fill in. Each row and column must contain the same number of black and white squares, and no row or column may contain three consecutive squares of the same colour.q   + &L9 This puzzle type was invented by Adolfo Zanellati, under the name Tohu wa Vohu. See [21] for more details.d?| | % ~Unruly was contributed to this collection by Lennard Sprong.c;  ( v[21] http://www.janko.at/Raetsel/Tohu-Wa-Vohu/index.htmS$| 2 / .H~NSection 38.1: Unruly controlsU& / .LNSection 38.2: Unruly parametersP2  1B?p S J Section 38.1: Unruly controlsCBB("btn_up","JI(`',`games.unruly')");EB("btn_up")K" S ) "DSection 38.1: Unruly controlsW1 & cTo play Unruly, click the mouse in a square to change its colour. Left-clicking an empty square will turn it black, and right-clicking will turn it white. Keep clicking the same button to cycle through the three possible states for the square. If you middle-click in a square it will be reset to empty.2 S & You can also use the cursor keys to move around the grid. Pressing the return or space keys will turn an empty square black or white respectively (and then cycle the colours in the same way as the mouse buttons), and pressing Backspace will reset a square to empty.nC J + &N(All the actions described in section 2.1 are also available.)R 1$q  @ Section 38.2: Unruly parametersCBB("btn_up","JI(`',`games.unruly')");EB("btn_up")M$J  ) "HSection 38.2: Unruly parametersvQ  % These parameters are available from the Custom... option on the Type menu.=  + &$Width, Heights f & Size of grid in squares. (Note that the rules of the game require both the width and height to be even numbers.)2   % DifficultyY3f @ & fControls the difficulty of the generated puzzle. @ J n= z@ 1?rz@ @ uF Appendix A: LicenceCBB("btn_up","JI(`',`Top')");EB("btn_up")A @ @ ) "0Appendix A: LicenceZ5z@ A % jThis software is copyright 2004-2012 Simon Tatham.@ B & Portions copyright Richard Boulton, James Harvey, Mike Pinna, Jonas Klker, Dariusz Olszewski, Michael Schierl, Lambros Lambrou, Bernd Schmidt, Steffen Bauer, Lennard Sprong and Rogier Goossens.A C & gPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:B D & The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.C uF & THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.1D 1s/&;)Lzs@ContentsChapter 1: IntroductionChapter 2: Common featuresESection 2.1: Common actionsPSection 2.2: Specifying games with the game IDSection 2.3: The Type menuSection 2.4: Specifying game parameters on the command lineSection 2.5: Unix command-line optionsChapter 3: Net Section 3.1: Net controls^Section 3.2: Net parametersb Chapter 4: CubekSection 4.1: Cube controlsSection 4.2: Cube parametersChapter 5: FifteenSection 5.1: Fifteen controlsSection 5.2: Fifteen parametersChapter 6: SixteenSection 6.1: Sixteen controlsSection 6.2: Sixteen parametersChapter 7: Twiddle3Section 7.1: Twiddle controlsSection 7.2: Twiddle parametersChapter 8: RectanglesSection 8.1: Rectangles controls_Section 8.2: Rectangles parametersChapter 9: NetslideChapter 10: PatternSection 10.1: Pattern controlsSection 10.2: Pattern parametersChapter 11: Soloo Section 11.1: Solo controlsSection 11.2: Solo parametersChapter 12: MinesSection 12.1: Mines controlsSection 12.2: Mines parametersChapter 13: Same GamerSection 13.1: Same Game controlsSection 13.2: Same Game parametersChapter 14: FlipSection 14.1: Flip controls Section 14.2: Flip parametersChapter 15: GuessSection 15.1: Guess controls0Section 15.2: Guess parameters Chapter 16: Pegs Section 16.1: Pegs controls Section 16.2: Pegs parameters Chapter 17: DominosaN Section 17.1: Dominosa controls Section 17.2: Dominosa parameters Chapter 18: Untanglem Section 18.1: Untangle controls/ Section 18.2: Untangle parametersq Chapter 19: Black Box Section 19.1: Black Box controls Section 19.2: Black Box parameters Chapter 20: Slant Section 20.1: Slant controlso Section 20.2: Slant parameters Chapter 21: Light Upd Section 21.1: Light Up controls8 Section 21.2: Light Up parameters Chapter 22: Map3 Section 22.1: Map controls Section 22.2: Map parameters Chapter 23: Loopy Section 23.1: Loopy controls Section 23.2: Loopy parameters@ Chapter 24: Inertia Section 24.1: Inertia controlsg Section 24.2: Inertia parameters Chapter 25: TentsU Section 25.1: Tents controls Section 25.2: Tents parameters Chapter 26: Bridges'Section 26.1: Bridges controls Section 26.2: Bridges parametersHChapter 27: UnequalSection 27.1: Unequal controlsSection 27.2: Unequal parameters Chapter 28: Galaxies Section 28.1: Galaxies controlsSection 28.2: Galaxies parametersxChapter 29: FillingŊSection 29.1: Filling controlsSection 29.2: Filling parametersChapter 30: KeenSection 30.1: Keen controlsHSection 30.2: Keen parametersaChapter 31: TowersJSection 31.1: Towers controlsSection 31.2: Towers parameters# Chapter 32: SinglesSection 32.1: Singles controls#Section 32.2: Singles parametersChapter 33: MagnetsSection 33.1: Magnets controlsSection 33.2: Magnets parametersChapter 34: SignpostSection 34.1: Signpost controlsÃSection 34.2: Signpost parametersbChapter 35: RangeWSection 35.1: Range controls=Section 35.2: Range parametersChapter 36: PearlSection 36.1: Pearl controlsSection 36.2: Pearl parameters Chapter 37: UndeadSection 37.1: Undead controls9Section 37.2: Undead parametersChapter 38: UnrulySection 38.1: Unruly controls?Section 38.2: Unruly parametersAppendix A: Licence /&;)z4  |CONTEXT|CTXOMAP?|FONTJ|KWBTREE |KWDATAM)|KWMAP,|SYSTEM,|TOPICP.|TTLBTREEpuzzles-r9872/puzzles.cnt0000644000175300017530000001261212161170560014650 0ustar simonsimon:Title Simon Tatham's Portable Puzzle Collection 1 Contents=Top 1 Chapter 1: Introduction 2 Chapter 1: Introduction=t00000000 1 Chapter 2: Common features 2 Chapter 2: Common features=t00000001 2 Section 2.1: Common actions=t00000002 2 Section 2.2: Specifying games with the game ID=t00000003 2 Section 2.3: The Type menu=t00000004 2 Section 2.4: Specifying game parameters on the command line=t00000005 2 Section 2.5: Unix command-line options=t00000006 1 Chapter 3: Net 2 Chapter 3: Net=games.net 2 Section 3.1: Net controls=t00000007 2 Section 3.2: Net parameters=t00000008 1 Chapter 4: Cube 2 Chapter 4: Cube=games.cube 2 Section 4.1: Cube controls=t00000009 2 Section 4.2: Cube parameters=t00000010 1 Chapter 5: Fifteen 2 Chapter 5: Fifteen=games.fifteen 2 Section 5.1: Fifteen controls=t00000011 2 Section 5.2: Fifteen parameters=t00000012 1 Chapter 6: Sixteen 2 Chapter 6: Sixteen=games.sixteen 2 Section 6.1: Sixteen controls=t00000013 2 Section 6.2: Sixteen parameters=t00000014 1 Chapter 7: Twiddle 2 Chapter 7: Twiddle=games.twiddle 2 Section 7.1: Twiddle controls=t00000015 2 Section 7.2: Twiddle parameters=t00000016 1 Chapter 8: Rectangles 2 Chapter 8: Rectangles=games.rectangles 2 Section 8.1: Rectangles controls=t00000017 2 Section 8.2: Rectangles parameters=t00000018 1 Chapter 9: Netslide 2 Chapter 9: Netslide=games.netslide 1 Chapter 10: Pattern 2 Chapter 10: Pattern=games.pattern 2 Section 10.1: Pattern controls=t00000019 2 Section 10.2: Pattern parameters=t00000020 1 Chapter 11: Solo 2 Chapter 11: Solo=games.solo 2 Section 11.1: Solo controls=t00000021 2 Section 11.2: Solo parameters=t00000022 1 Chapter 12: Mines 2 Chapter 12: Mines=games.mines 2 Section 12.1: Mines controls=t00000023 2 Section 12.2: Mines parameters=t00000024 1 Chapter 13: Same Game 2 Chapter 13: Same Game=games.samegame 2 Section 13.1: Same Game controls=t00000025 2 Section 13.2: Same Game parameters=t00000026 1 Chapter 14: Flip 2 Chapter 14: Flip=games.flip 2 Section 14.1: Flip controls=t00000027 2 Section 14.2: Flip parameters=t00000028 1 Chapter 15: Guess 2 Chapter 15: Guess=games.guess 2 Section 15.1: Guess controls=t00000029 2 Section 15.2: Guess parameters=t00000030 1 Chapter 16: Pegs 2 Chapter 16: Pegs=games.pegs 2 Section 16.1: Pegs controls=t00000031 2 Section 16.2: Pegs parameters=t00000032 1 Chapter 17: Dominosa 2 Chapter 17: Dominosa=games.dominosa 2 Section 17.1: Dominosa controls=t00000033 2 Section 17.2: Dominosa parameters=t00000034 1 Chapter 18: Untangle 2 Chapter 18: Untangle=games.untangle 2 Section 18.1: Untangle controls=t00000035 2 Section 18.2: Untangle parameters=t00000036 1 Chapter 19: Black Box 2 Chapter 19: Black Box=games.blackbox 2 Section 19.1: Black Box controls=t00000037 2 Section 19.2: Black Box parameters=t00000038 1 Chapter 20: Slant 2 Chapter 20: Slant=games.slant 2 Section 20.1: Slant controls=t00000039 2 Section 20.2: Slant parameters=t00000040 1 Chapter 21: Light Up 2 Chapter 21: Light Up=games.lightup 2 Section 21.1: Light Up controls=t00000041 2 Section 21.2: Light Up parameters=t00000042 1 Chapter 22: Map 2 Chapter 22: Map=games.map 2 Section 22.1: Map controls=t00000043 2 Section 22.2: Map parameters=t00000044 1 Chapter 23: Loopy 2 Chapter 23: Loopy=games.loopy 2 Section 23.1: Loopy controls=t00000045 2 Section 23.2: Loopy parameters=t00000046 1 Chapter 24: Inertia 2 Chapter 24: Inertia=games.inertia 2 Section 24.1: Inertia controls=t00000047 2 Section 24.2: Inertia parameters=t00000048 1 Chapter 25: Tents 2 Chapter 25: Tents=games.tents 2 Section 25.1: Tents controls=t00000049 2 Section 25.2: Tents parameters=t00000050 1 Chapter 26: Bridges 2 Chapter 26: Bridges=games.bridges 2 Section 26.1: Bridges controls=t00000051 2 Section 26.2: Bridges parameters=t00000052 1 Chapter 27: Unequal 2 Chapter 27: Unequal=games.unequal 2 Section 27.1: Unequal controls=t00000053 2 Section 27.2: Unequal parameters=t00000054 1 Chapter 28: Galaxies 2 Chapter 28: Galaxies=games.galaxies 2 Section 28.1: Galaxies controls=t00000055 2 Section 28.2: Galaxies parameters=t00000056 1 Chapter 29: Filling 2 Chapter 29: Filling=games.filling 2 Section 29.1: Filling controls=t00000057 2 Section 29.2: Filling parameters=t00000058 1 Chapter 30: Keen 2 Chapter 30: Keen=games.keen 2 Section 30.1: Keen controls=t00000059 2 Section 30.2: Keen parameters=t00000060 1 Chapter 31: Towers 2 Chapter 31: Towers=games.towers 2 Section 31.1: Towers controls=t00000061 2 Section 31.2: Towers parameters=t00000062 1 Chapter 32: Singles 2 Chapter 32: Singles=games.singles 2 Section 32.1: Singles controls=t00000063 2 Section 32.2: Singles parameters=t00000064 1 Chapter 33: Magnets 2 Chapter 33: Magnets=games.magnets 2 Section 33.1: Magnets controls=t00000065 2 Section 33.2: Magnets parameters=t00000066 1 Chapter 34: Signpost 2 Chapter 34: Signpost=games.signpost 2 Section 34.1: Signpost controls=t00000067 2 Section 34.2: Signpost parameters=t00000068 1 Chapter 35: Range 2 Chapter 35: Range=games.range 2 Section 35.1: Range controls=t00000069 2 Section 35.2: Range parameters=t00000070 1 Chapter 36: Pearl 2 Chapter 36: Pearl=games.pearl 2 Section 36.1: Pearl controls=t00000071 2 Section 36.2: Pearl parameters=t00000072 1 Chapter 37: Undead 2 Chapter 37: Undead=games.undead 2 Section 37.1: Undead controls=t00000073 2 Section 37.2: Undead parameters=t00000074 1 Chapter 38: Unruly 2 Chapter 38: Unruly=games.unruly 2 Section 38.1: Unruly controls=t00000075 2 Section 38.2: Unruly parameters=t00000076 1 Appendix A: Licence 2 Appendix A: Licence=t00000077 puzzles-r9872/puzzles.chm0000644000175300017530000034345012161170504014640 0ustar simonsimonITSF`.F |{ "|{ "`xT(ITSPT  j].!"TPMGL9//#IDXHDR/#ITBITS /#STRINGS e/#SYSTEMD/#TOCIDX0/#TOPICS@P/#URLSTR /#URLTBL| /#WINDOWS{L /$FIftiMainQ? /$OBJINST6/$WWAssociativeLinks//$WWAssociativeLinks/Property2/$WWKeywordLinks//$WWKeywordLinks/BTreeGL/$WWKeywordLinks/DataM/$WWKeywordLinks/Map`2/$WWKeywordLinks/Property /blackbox-controls.html'8/blackbox-parameters.html_?/blackbox.html%/bridges-controls.htmlyn/bridges-parameters.htmlg /bridges.htmlx/chm.css}~/common-actions.htmlq/common-cmdline.htmlp{/common-id.htmlvd/common-type.htmlZ/common-unix-cmdline.htmlkk /common.htmlR/cube-controls.htmlg/cube-params.htmlg /cube.html-:/dominosa-controls.htmllE/dominosa-parameters.html1*/dominosa.htmlS/fifteen-controls.html9-/fifteen-params.htmlfD /fifteen.html9/filling-controls.html5z/filling-parameters.html/< /filling.html&/flip-controls.htmlfk/flip-parameters.htmlQ0 /flip.html#C/galaxies-controls.html0/galaxies-parameters.html3s/galaxies.html([/guess-controls.html"u/guess-parameters.htmli /guess.html! /index.html+"/inertia-controls.html//inertia-parameters.html? /inertia.html-c /intro.htmlMR/keen-controls.htmlS//keen-parameters.html /keen.htmlkh /licence.htmlme/lightup-controls.htmlPE/lightup-parameters.html> /lightup.html#-/loopy-controls.htmlPL/loopy-parameters.html /loopy.html8/magnets-controls.html*/magnets-parameters.html=C /magnets.htmlJI/map-controls.htmlG/map-parameters.htmleS /map.htmlSK/mines-controls.html3 /mines-parameters.html>D /mines.htmlMf/net-controls.html,/net-params.htmlEh /net.htmlVV/netslide.html2/pattern-controls.htmln`/pattern-parameters.htmlNq /pattern.html:4/pearl-controls.html&q/pearl-parameters.html, /pearl.html=i/pegs-controls.htmlU/pegs-parameters.htmlY@ /pegs.htmlU /puzzles.hhcR /puzzles.hhke/range-controls.htmlz?/range-parameters.html9 /range.htmlg/rectangles-controls.html//rectangles-params.html0X/rectangles.html /samegame-controls.htmlny/samegame-parameters.htmlg</samegame.htmll/signpost-controls.htmlj/signpost-parameters.htmlm&/signpost.html/singles-controls.htmlwj/singles-parameters.htmlai /singles.html,K/sixteen-controls.html:/sixteen-params.html?V /sixteen.html*/slant-controls.html#K/slant-parameters.htmln5 /slant.html/solo-controls.htmlK]/solo-parameters.html(% /solo.html? /tents-controls.htmlr/tents-parameters.html t /tents.htmlK'/towers-controls.html0Y/towers-parameters.html # /towers.html/twiddle-controls.htmlV/twiddle-parameters.htmlh8 /twiddle.html}/undead-controls.html/undead-parameters.html0d /undead.htmlCP/unequal-controls.htmlQC/unequal-parameters.html /unequal.htmlv[/unruly-controls.html0/unruly-parameters.html0= /unruly.html/untangle-controls.html-!/untangle-parameters.htmlNW/untangle.html[R::DataSpace/NameList<(::DataSpace/Storage/MSCompressed/ContentZ,::DataSpace/Storage/MSCompressed/ControlDataj)::DataSpace/Storage/MSCompressed/SpanInfob/::DataSpace/Storage/MSCompressed/Transform/List<&_::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/i::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/ResetTable V L U Q LVty> Uncompressed MSCompressed{7FC28940-9D31-11D0;LZXC (; "@Zwȩиlv.V Q HHA Version 4.74.8702$ .Fn index.html*Simon Tatham's Portable Puzzle Collectionpuzzlesmain   T#SMWu3Cp#eVpQ#:$i%`7v۱h]FfFTw9wI+N*ɵX$%d[([9XPMI-؞z Z0 @Y2$I2bsm>̀љ۵dk1!DB JYWdkR%H؋ T@唿r0&|@?K?f{#ZHKJ ^HφpoqwLZ7tF eD<Z ^|?UM>(;x]C=@2njE1NV)ةWVRi*aO Fb& OH)NV>{D%FǂdNpIS&ajEPB=ȳ9߀;=,xttԡ Uc. < `3.iR l0. #5vG0L 3Rt)hlJi@o@`*Im9 @#WLYB *lHC˜(Z=WCufܡO > JγfhiY ^[ h9S$88h䍵'1SU"upſ 3h3 '>%/ lkd"?P|HޔCdɰrF~I=^@,!B<ū н4p;9Y9C"Ae[䫸4d~,2s%On5MBov/RCoxb bVT|)A :سĠvHߒjjq^kvz^0OYjELCv&Tqşd4iYU+6%?!RXs S8!U_h6fĦ{7D@Szy Iib1 l7?Iȃ#Sa#t -S98'*p `A 4Y zp!\ Јq^jbvg9t)`w@=q` <<4"YJT)Pá/"OqztzJW΃YG̽d>v2=L"uSx=}0a%F*Pg}"X9~ZC|K(,kٗ/Իx;ϝ@pYPHi !φ@uFl$mH @`Xăm]B6ϐ = 3P<%'t(>Y6Lcj7ܾ~A#Ckp~IF{yzQ|>=~m} ȑbXzP_$ n$F9mnhxrA۷<ؤ1hM851bvhP 3 =9Vμ!iOK%Ґ'Q:Sv nyUu!#j3|K7Ijq?I9IyA7ΒRûKxw#TTإ48<B;;JP{>/\8;tL#dO8蠀ps7Y=#;MȰt h2E^eW1u%I'r{Aq h-Cv? MԶ aj64=s%x\g@?|8BڀN24dL dhFI.> S@+¸U}\O"p,)yװ'GO_(h??1<1U eH0 iB_"$p). RVl"p Wǰe[B :8&xf[+<+݆s35=ߖ|Mo>D)ǝ~w DIHIlΪsFp{W?쥰Q|&bDdqxsm!w/孂I`_PMTOK&r(d!gke>)m<-g݁&.&R '/ 2=5Keod]S%_$7֣"8KI=&p0տx6Jxɱ)[vG`~ibs6CͨI8#gl=|qL[V!U0Ej}BI a!lgWP}Z>Ad!˝00vDɞ7Ng0i@7 |"@F2 4x#ÚηɄ5'j0Q;u҅}u ^'P2\C7brJ2αbaPuB ݍv(wL@I:&tw?LCz`s3(L-eUFh-Ls3~~l3٢a_ Y?'9+eX\dZj ] Pȯ7OxgO(' =jh[/[ {)?tq Gu1i?l )G Ȭ\%<<֟|0#0o,;\!>.QA<\պ)cê  *@Ӹjw,ʹ{H{,0 EY Ze9F2W";i{VS羂d뵕׿s;:cp2>-Poke>wbay$qfk|?|T#LQ4ϫ[zq6\ntQY]E]:,O۪CWJ) +_o$!<܇:h9g\|0:J]`3cg>}̖"C{F?^]vHX %|]zbf{fHI!)Pt J @+;f"neICg/I1I\)r=W)> =)QLY& w?OB5 :`7_w$^3o^i-[iÉLo/P+m#un_3i0kჱZ;MU0L}3U7L1dwѝjvuX6kv0"iZ$8{+E[Z H:8R L!o|sz@̠mkb09%$k/i$h9+吻/-0[߄ 0|?ca_He4m9 \b-㯄fo2|ceh OMRq/-m/neRLnh`ӾB[~`Xж`Zkr6e `m 0V{B[`&#Q `?5K9][1 &ſ*/%>t.˸ӄFB5e)b;Tv+U6Wn.nl~vW@fG[$ 3_!k m3}WY{F){~%ixpWs%.3ĹKXzw(Ͱ<Ե٬M|OKU޾Kvl|5/ {_9zLWˌӓB8F_@ϊSuSAm袤ı.xM2Z+h많?m"ے0TԬw| <95BR~U5L)D(+u Ev %VvݜG-- mi.qU-sNkÔ0pQgeʖa?j/}cڣݱǡsvTמf._V]>G~%s%fKbq' rk#`:vUEإG|P8d˨&D8ttTgT"0QuK<剘<@Zrr j޼ȓnt) pb 5h1FV,/פé*JzEiZP_S&X*0@փe4)J'6N춢OVz>,O(slH;*"wTrcu̟M&E:VCȊ؉8rP(S6&IXQ҃k&4 ae&_iR`h54#:$ YvFzЏ(9c`ZE[&43߷&%V*Oס)oɲHkQG?c*jE.G9 vA|K΂"9I?8۰2B*π>Lާ_ޅ-vJ,Z>YOfğ$!AW +fV%8u,qJQ%:=ctjSAzhK\}WVMɯ-[H?T^E "WQ=+%e|n4lO5HFW{O`qcH@i\ ϑYN<*Y&s ܚp}<v3V~g)~,hOj~⣭fmӺ9h}J&͏?$MOY }͌R|ק|JbnQDxk B} Hq`6O>6BlMV G%QVB)[E>7FKʯL2Urj. 0Kzlk%3i7z*ƈ5tUm7;+BSB% C9noo]wsGpfќy+6.!?bQhMZMs?3SIfɧ"%v^P[7A=&GaCE}E?z4 yu?E!Q3 l(!Ø@~hӦ ߶oJ d긿67mj&Zj180Ӗ!i/C_ zRl&O,UYDV%Ϻ>gǫ>pZ^U|wE./S؈"ڧ5$YB :2e&>l'5$)|&ʹK) c>t-[C f.ݿ|"6L2׃XcsIB0˙'N_O;NqSo`DmQ$,jk?,erC18BKp2n5F(k{Ƚ]r\-j+lwm-ʀ{04orT6ai5LrcABJlsl`a]uZR_Zh0H3[kmԍ-VOqU7,%:ѐup!O7aa~zo' v4YlyA޼f`WP+H#dSpԀgs}puh^l{m* 3ݸ<.lpr=}m "A~'+ iD|b-斗<-T ֡瓰z+XmyKvN:lr BΓʃ85~614T3P1Ƹ|+>m7eƮJE@ۭRqA [5MJj?J#E3 Sn[U`}1]^C׼S]]3!>k?V)is@{9&6k.nڳ{+1K*]*SE!yXdș~ZN݋րX?^*d4$fVۣez.n|2tZVex&_n&X)FN}{%AZ}2=Hq/rϳ 5i:A_dwk|Qc1L<@K-@Wu!Jɲ5f)$'7I* 7>;O[њ$1zNdۻvA,[ڠ .e<&uΩ0#ȣn.s<Za,9~m[%TQʂ89J-bM #`'bD.VxA j;Ad97OAc #WWm\vraMv ˘iySbHx79<޷}uB Wd_1]7\Ŝԯ iDzҍzow69dcˎ0m'ìԣj{"dƚ * {13YY ,=q)Sر>EAP34Ѻ5޲v&*tjz=J>y-}kɤ1!#/HB@1ܾJt)}R2ZG(Fpr|XBIE\C\mq4 TF2BtPAb15ZjphzROD$Rm#  ,-@cv y )b\VU-ZhwVѶb|[dho8N/ 26ȈZ[##fk>' pVZay&%/ QD͓R }U8h|ѥ8E?dZ-n ~M1!Ǫ(NInK<ܶ$8).=j1sf5Zd[ w+DٌwŸzS?dݬ(uñlp7gM^E%jUƘdGUcXbG$s0Fa`@kM CgV.dYx(T}M{Ӹ0MύרE->3bCj+>asE :U(ӚE* t"[/eo.-++? fH8Kr垆7 9ξ#؃[oT]Eo#{v^4G~\z\b"xdHbĞ,0GLb`+VpP/;~j(cNWAzu{\.@Q~:d8rNغ8*2EbI$݇yF9I#K:-@I[ IuP2/ *a_'Z6)JlKR7":{ywsvrŲLAd"ՙclrd@JcX8Al ,*CQI`$+ u0^-~mT|]^LCB oHq x9Z?kdG# 'b| R<%Od}VJO-0:$"$>/HnE`mB˞T n;QrRف_oR;(QeCmt,GWp0R{:>zAQagx>gm=0x5JAɷP+kZŠiwNƔ"d zOi[sNk#n;Z#Vv~v, kL=-y؅J-^RȨv& l`}c4hzx7s@@ >HNba&~sT;㲞 Y)kC']RG/ _{vo{by"/S;MW`{w:Yɻr+b`{O zrC<*{$Q=mI@/Rb&;]WX$ě ojZUSokm{vMѵ.LDo6|ѥ&"Ccr'S?<L~K&"r8T&D<GvClR)FA3@ q?ӁߺrM3zWJHKN_P//޴]|ݮ?"[Q7-(u}NUm Gaɷlbǽ ij|H#)T%mSvM*$GcTrhƥ"H%ő4TI44u͸pb\~@{`zEs5M{{%٤ #T|bSς{Acz5W; 8UL W>,ܝ2<Ռ,)hs5x%D k V+L`[w#_+VBJeogh#ڵ;*CmHJjFJ$|+*SwORzhB@qT\gh QtQз>ZTQp Q7tT.>^qL]+GW,]F#Du6U CW-&`rhhfՊQE Vu>P_1;P?OEqpim0 D=>pzMq@*@[OkLV5jYQڧ,SeLj &.N.dpb/r {F 6 _Ztr'Pݶ^ۧu|mRD3VMC(7q\ 7c?yenN fR85|8dS*H3b n]cHNj@Mj3q+xB|!eK}zl6*`~69[??*&Ɔ?kE}!n9br|U޶bpDq4@ J0XاZ?;J^W1@$nO:+^ߧ;}74꠮9H &-6;ތfyr5!B &S^iHd@r`lYDs[J $r0A0a2܅K~=X|JTIXaZ+HpJKӉ3ŮwqBM5F@CL<+Y9̝9e Kx+(Ս&־$ ؾR &`ߔajal^_){$mz+ղX5R z3pG\] *aN,0ڝzGq(@ z%S?$"%&F= a o-{y7oȸ/y`脒G!ad˔^%!FL8sTt #GC*6b!MuA¼0䴢:<|Z͎=J-' WuTd@$XtMC|#&JNZy=/YebyHۿk -k$&HzP.] UābU(E6|َ?֯)4}oJJўLxj`@.jn;KKXY-9SBI5NB@7(!H`fi+ *NM0BI"*ÇI{kW"o2 C (KugլP>Zx却@u"_C\vdc9dD0G{F 8yՇ[R՜[5J֊L^) k|md"uOn^s;߄(.3IWa*qʵ6SËa 6v+FJ#l, y)'/gel ǜ%B/gW݂24:4r30SFL .4vI_5J}ݝ+K2q*ژpvxJ+wTDxiWzv*U:ri*ys'L# tf~N fC6>-GQBlUΖ_}آ)_ʀ SVju6B>j4 Ho*wtW5@P$I١.1U ҝ69M%C{gc,̏އ_ߝq+m? ^UTrSKp \ج{NZOX A.U$Ң+FCj qjDZE6Y>N/Z[V{JKֽ@8aթ?ry2aȶ=mf;F k@A$_N؀0 r` {0lPa0EH0™:Ygs#fz_,6 1+V Q\MUM@j{cgGׇJֹzjEɸ,i#!\? dxoXNT._H!h 57ݪđ#B{5:"̱{x/Y-$ )Zh-HbIN~$'Ȕd4i!/nL@66 u <K/u+Țgi}%f Ǧ# c>d}#,vn Ct4 3W?!?8{!=.f W.1e[P'@|iu ոg@͞9ܘx Cv8N,QsA~tt&p,%Dž2"I\aZf֐ r.Xi._&*+K:){# ^Y,+JImHOT.df>ʾ19%25A1!q-Nb Cy«$\}!z~pz[({<$Sb.W1Id4兦Em4%40s$Rkزi(\'MzNg-:`Cfەo"Au{jCq!q^feŸU^5}ͬƤ-"dU=n5Y*gs;̓ zb)Ql &!뭳4Wwpoȣ|h6U<)W?V ߔUV_[V,Pw@zՃhY: v8uNi57 fEdXL w X+΋Yj6PpZHpVrˍ_ˍV-4`&{Rkj.&#͓p1ȆM:Aްƭ(jmsb6!`ʝL&X WnvųJ??I.15:ĒV@3[4/kht?!$J J H?9ֵ n@_?0`Wq~_/ s J[(\I~_ * b)e¼ Rĝ"W3̣L騃&YH؅9 ,̸ꡂGQ͝ 2e7;~Zg X\TF}BPv ٶd>2z$t3Zv-B"jF]~]Yf[PenK†oI"*fXDl{RvKi؛\s@ӳ೪XHpbܼ8C4Z{:eu[C9 J1o*khݙ=duckCZj?ixt\RaP[)VOu9ED%:.ID+ﶒ$LCQ}ؖjRJ-"I2~ *O}BaJ ]h8rÕ=Vfv充"f#o?Hvc:Gu?%([D9,Vƶ#Dҙ&|=_s'ӑ_o2F|(Uͣ u2 q( k^Y(u!uHal;SZTk(!`Fۣ?UR NdBYc죔N1.ΉaVRw3bd?D>kYngYg_&EFN;/_2gn4BG Q@BXjJ;+ d}]!|eus1 ypO&.' OezEt1jF`Sŵ#z*}=7EԔF-,Bͷq+T,lyhýꮒW-©'e 0v5B/67?eISU_s6MK60UKJjE=u-DO}/ 4Lnɒ?3͌2ھ@ <IHny=fW?)Jnof![+*cTm[^;&?  ;-@{PD 9E޹i5mqFSLp?s{ oDfT:|X_oS"*ڿ;H{\p`R![[2}k5wt->>v4z cxn4kU'U^xK8ۛe@f_)?7Mu{3WqB<݇TqL_`w>a&e庫DBLo;&x)Ҟ z߻x63WWnŕgޏrǴ4 n,{\6v#vX&؜sS Jo%c(O 8e0 o}"zJF?H@Z(}Nr0G}ť¿E%Wy~_<^ͽe"f:b{ÈXbX^&GsgoMFQt00T\@d0t@Wz)lMog|DJ`NPH09Ϥ.t_}y!rΎq!\ihpu04Gw]=qWmQ ɫí9Jk| GE![-= a2C{'peavc,7U8<l1b/Cժ\ DCЋad,gt}Vj<+ |Yy?u_z>3`l _ǍUTL1gH@Te1d3~@4qh.,Fv&:*YOo=QRO@)͂ak"2No]Bcmc2 gy˪KI()[q;c ^?X$RZ:ZΆb8cPn&J"JNsY<^I(Uky `N8'i1!*Ĵ3t|h4R?F6@r5>XHgk4y t ּӺFBEݸ \O"Ƕ *9[Js[oA p*: =1#}MJW7|}6 ϶feHV.}ohKzzK Rrop luIh fk8Njj/&nDf'WJuN[;%[ z-QjrkRqNgOQ{{hԻu]aTiYvNApqE!ػ:^bACg7pLDJ\ڄ#C  Xup*_W}"R G-He5ceo^ֿr,5`(G?xGj2u\d\,UT3n^fkr Dohr SQeV/dmH3n{Q_p^:k:"XȏX3-m2MbeL"~P$ݦv_Y@}Ҷf+}' Hp1Xsūrb&hV5"4wcxc ٧ 4'5kEx97&4n]LhJG&xq#vH^qKS߁q!MB#)7_}HuMw&"]Z,:QTޮ@ F'' s:{Y^o1wqlHn[Nz`/4L-(aXX}PlUH U3dua̐+`Q| 33%Cԥo,GgdALz~`z+ [{S$4"#X61b@ RW^P^䋗nb霨.+1Ƹ7[_ X l:(I7I52SEQ^{k=Bs20pI܌e>:\ʒ[N#nm;n䀷+EQtIc-$_ͮxQ]*@NRob ߠ9@q rLW]A`ܵjؿͮ-(rzƅ'=j*+~E kQ!nhoQ[m8xP@ ģPDR+& :|9fP_>a$|ϖpSs-sυn6(o`7fei FZ#-Hל嵘Wͼ5#l1e'}5 vb =5X 5zNi-y/lV߸7]HiYP0DQװOV1#XD|J5\ͳ̀hl†BȁHÄv)J:rT:כxbkJ;fekLZ#ݴѵJDO_+2Z~0[Vڞ1*p)WXցp3Eڽqf EKxvw񥋁X{B1[*c$oI[Gn$\ N4DSEwp+~bCHQ҇gͰd3qc?߅8 W}xa1%^"WS^`\V5tpD٬xzN:cdmX."(^"-ih+k*_V Ae˥}fkBaT+4}14x<6:6.٥Ψ̓f{[ҝAS<,gnBO'ǧ%9tc jciqaf Ė(8-RPObĻb>6z/L1xv+]%{Dv;|2(bDcTAM>fj4 Ne>DmTok@CrVBfg/s vFu@@ҿRL$0gW9Hyٕ詃HRbԳѰ;E\:Qog!Bn>|i{wc>bO.inH`Ķzwp4:-u4ѝ-᧶pRH{~wќ(aܩ5i7Tķc:|4~TL6vFfn8h0IVuw.qLMc=f$i!AK-g0-b p-d i֩M1g7CxjTBճZCIH[M ^ ɰ [yCY(6RwL8*ãH#_^JUvpâX$#vdMj]äX(l.=Eޠ=y|>a|SoPLg[h̩og[վ7N aӶX=gdEJ೅$KpҦFu%z: )PwL8j&Bg7oLGVE-/:eR^.JMq= nug>D=E(J%2v\8К݂ K Od -D& 6u&.7r"xZj.j[ʹ!f~ 2w{ g^>h0*p@gE]]f=n' 2#8۲AaT^Mor40]ck2A<\פL,H!7 aLDKPwVݶ lIΦi` -죑Ev,lx{zP3x{_3]-RFͪFvHZEJ\in&?GqX(ptV&78[PC qG3k{ Ύs!Ҁdv9CC*ʹ?ٮܛқX/`Opn?jp̞FdE'W4A\`5hOjڛ `ܜ7!< ݲmX 1KQ%fw!  *.{\ϷIm`YB`Am;tCFiDus-l~ys{ :N,!\+G37ܗjϜvC"@kh8 YENRkN kĦ)"OB/CO`KEOŞp nc~57  f-[u`Y|d&;ҿ8m*}$~faah6Fx̒a\{/kRЖ=0ʩh,A!S kL-"51-,;ػhX_Jq)~Vb~oR/ c};Wyp+ @X@xyvHzZd)[ Y]ۀu!+9\ \ : ?>HxYTR7p|$ ^H!GAY6%5`:aƬbUGg{34賽zJ=f/HxyxNcF:ڦ=Îv[E> -+e oċ @(O<֥HZdGJl [V` HSκe*ۗΰT ЗjXqAzš$8`ooqt_q3Tdb<ۈy:loBpY^'cC>*3iXRߎdqDz&3:hهFdTKsI)TzUy$ Oc1ѕ/ nmuZxbVlϑnpy<{7!r-Aˢ*<3e>ԽnsE.v!gfZ|iVۨt25#.H*6 I_$ wAO;q9CdxmƽS M=9ef F "/|ձu mȴ (#y܌Q)1.ZL0-C'~c]L 2jp71I;3X_Az@ '#2*"aW{KyxW !^}ta/ՐM!ր\Jxd԰\90%ξ<0G< rŸv`g;uyҾ <7FTu0ݮosim8V2\!='i3[ \VDjm:sF3vJ/ !c2+ Bw|F6ucpLx@ug 8S=iDW@90ﱜB[3*:ͧxoˌGkт@Ni,u\sKchnx ss1լ%y'ԃ+Η'̦: #sͫuk#Vwp-\2D`3eח9l@7c^s`c{-9Xle^H6"*qt=zIg;|rIoN _p Ekڲ/Ֆ* lkmVAE_ZӚmIk' 3UE``s̙o%#2֖,QA$ˌ`W11VB;Q: !`\DHnD E/l JG!C.qf :ooACU0dDo0ߘmQ=1j`ؼgMI._'崱8aHm)E]-Ghb^ _)w?xY90uPhU&h<%9L’mF7ؤll| !`\l=#7ڋ\o+8P+rwE^!KBB8%Q$H'aBn:LȖ;UG_OK+tiԌ1,·/ ÙT JyIyc-ڠ.EW;e?LD\p FGr@Ny#m `꥿IAǯ@%39a<. d2?|#h9\$E0~nRXȫxTR?7 ÷=I ZphO/1%ר< 8#8W#Ph mw6kߗ5/S{կ4`_= 'ٵ'*Dpkfa;HlꙬD;3F K>qU߮// ٠FC[L*p[z+~^_jwx^p>Z!҃ssBT?@tZIh*F3U'91,>WJ 'Iu5fMT]}ll|WƂeI]=r(}W]AZV@-G_nC3gG|aܺ1ɩ߸T2I"FeChUE4~Eoyql+ (Q4I8FWKDo}Ɏ~BTZwЭ>.M;Z_xyvkɐi%^\>htlc]a&*5N*sU SK˼IVy0-C?ZMD۴[O0R~OaJJmS[NoMKPV%姖 dWrl> z"qބ |gC4` 9_"GK ެI]%B_X  8`PX̯2)҅ 0muS?b1j+q/"_%SX-ZUj૳n#: ^ Ct@[J!ɒ"QKA1륪/n 8 PyF' I!Rr 1om\G/AX#P%%mcck"1©$гj+kgk|Q{UG~u¢?k"_^@T E;wA[/jDAFW~vg Zi H{X$ ?G4T-tJ*Q ܋$Lyz˨[z4_@B!r0T,EӀ@gHl!!_ HcCҚ5MOf1W|9bg]S+*$=-[3M^^~^)VÂçlICFzqjN,\8ZV5Ȳi,O^QݵLԣLTUT 򨍡z*+F#M^Gd2SĨߦF 꿽y~8ui>QPEf (H23MrND"_z45nackI;xL}VO:>t.F[}->Qc3,Nr]'u^-hn>/T>2=RݰeRʹŚB齣ہX[&Z4H3xr4+Mji-ؿi%I{:eTaOLO EUJY^b]\fM L/Éq0lc%V\:b~M A762DZCV6*u` QJży'̅N}ElP〈뒈TԢEOC tGq5ϻnmX-j1N,uiH8tLaV 7oURS+R-KjR1%:6ntнi*K1"|itJR?a#n |mk7jRĭb[C!ժɞFs z3͐ &nALkofa}[ ki鶅-8-ȣy[lu6If?b6mca9! 3ܾSuR=oE噽CXڸ{F8=(HYLǘm?Q#&֍AxyH?kU%s n̴|‰*: .A_r=+ z jT 6w?6d3Åʾ$Dss%!JYT߆dJvM }9F PwNu%I j;q]m`>piRDGrMC&t6XŌ}`,;~ߔy][RSZ>fZ}$VO|H&ܠ^,Nɇ,^GC"A{0*ďu|tMҴjF-)fbk~JEw&$AC?!7)0m\8]m՘k# ":&4QByeG I U{C3{ Fya[ŏA6=~_hQKa|B1!- 7.Ģwz(]2 &telXxzu:n N!ͫXvHs81 GnY*ȈzTWlm1P!-;EiSorN+5Bz!L~+buU֒hE)v@@HmeUT6hrZd>uhJq&Ȱ~Ʒu~ břWG~;r*4mB52MMTy%*?#@5{cn(}Q6zl\o'#Y޷S䂚CBKǭ?r\#;D]p<2cGSnfp<9 8 -5 UL20G논-k]!c5 n;7^ܗ&EH@h+2uobfo6(6&@x +!}u{]{۞F?ΛmBT󬜙b=[ 2:V=Ylgpr+8>F"i8) ^敞d^FMfYIW.62W$[#јKC>ƹKYi_3PU(C&` ݋lXxGq]brlQݐҨFok/G Fx=iX,Ci+qwe4=~rp=$@dXbH)36%p15xf}N/86V9?TǡY(aYSؿq bolj! +μ̚s3Am|s]+/! TSF\NI DcHRɿfy^#Q d(7GԢ#oKc(bi|앉JX? +o@L)?Y8]*11| g<E# V˲-)iR=O-kyl_UHX> ҾYmϛ5b߰&7;_**2QB:ZďH_tƢ1Jb-kْxw>˴r>'݋B\ޖ:37,aٴDܾn'qLOm"P=XK略БlN5cPؠAɫ.W7%7PXpi7E*QU4>Ņ\kuTlˤc/XYzC yFL Oaϩo&z]ݲ&ss}Ma8hӟD7:8FĢP@< |-BLu#_Habơd~2D.J/V޻NEq%%.*)GLd-fmr)t@p߳ă2ãyǴ>U0w2n|QtÆ /A\YBpp|\Je|{,?N|vn oijKJ:id@ذTӎ'I.Xۧ er +ٲimtv\#[+pS| } + O?.?Npx%.)Hwٺ0uzgl-fvãӬsa(SĨH0[ $r Ef C>$10h]~)FtJ5jmjonw(3DT`1tRN@U6w7ҕS.B*3f$Qho^)#u\צtd 5D5k3j,6 Fu~&~%ЕU4522 60wD+2Ľjж11lmXpU2z2UB>qr<̏>vmh@CdfnyO[3nVD)TnJ}ToK/yfASkOؿ*_ۺ1e`Ţbς*AuUYw>;y*mt$SeуU6 8RRz4b{Ȇ=: 8r?M9RH= PQЭ|`酃T!pUJa nѽTw:-b5cʁDDm^Dy?pʝڞvOkhqs s$0o;1zh;ЮwN6] '|>#SϽǖҿVL\Dj?>1&0"5+j . vQºlMlu]x2qyD?-syQO+5}spŚ@5ljRk?0_evƹ0kbFď_`])29/^\V0ο?oxV:N:y-0)wB?x"j~NSE[aJf6:^V9h'ț|;0Q.p"WK/J13!ҽ3~XԩV5[o&!3k\:(mHeA˅&!Wk1Pn:yYS3]m-3D}b*I~Mᄇ}ƗDI}Qnno9]YJ4vC_ f~>[W<,n3ݥzdB^;ȑmb)jXt_: @MN[WnRֽzZ<& 'U;ʎVGQV6&8ŅOF;Ћ1:wC /8g,|) -u[ܔ]Psw &ݫ_Keə_[fB,)6xX{kyŐWk&I$c+|2=s4B{aIWsK+!|Ls1l8%S2ZZ N j σ&Ħ`j]jMVC!N,t*lZ$M(q{`Ev{N\V9aSP3!M5 W%;o]|*ު0_8<§jLt}s.Q*+M2 h(moS'z/<7 IPp',<:~[`ӺCN|6+ų(v{9>'#k_- sٿbz8MP|G&ᙘ^ 3'}`!썠mX1!{,SN8 #LOd~pnPUB9Q50&"5,O-ÛI Yۃ\ zaqH)p?@c^o`S ՘i>:p/RnYm͢b]:uZCJϳĖi-n z=inOb}j%`\'Pk\P۲ᗴlwS'+rr[Pz¹FHxj E2n)= ܇FE/КdAyp; I{X S1.c d{P"iHapS4+-gC϶5M)R6a TAE=Ec@ǟK޽[#  l4B>^t3OH"x1wݲ2PVߦ2 Vʒp~mI{yɂVmowLpVe%(kթ5wDZ{ioW9b`;AuIg6lh5M\"z-pΝemWm-THBePB_ZM_ R`X1(fXv=&5ͭUqC2lJ3ۢ-\+sU#, 8E}%6 ū8ԍ5 .?Ŏ;IIȶOrNeA;>&s/ټ#v~2鿶t  BF mllVG@>+|T:ǮI_R/*~&yА0[y|7+v{jvZb}בsGr+## Al \$)sxcjN~CQQ,G ջ>\vFI дv6+c{ڕ8i@L7i M)A7Uw%9Q%=N+j\qsVEݷVM!oй=Y[ڸӢŒAF"`Vx<@?["_ݍ*W N!V;֯IF lTِglCY6R]kʜ_/8i&L;V~P/g.$ʦ;׈ȡ]`ovAI`!f|ѦH@v4Pծ}3HtKjN~߄dbXw5ʎl?5nۘQPyΩu<5 KK#JK;N >>^筵&3,6xЩN}) ӒM}8:t@j&o*k&鞗{QJL݉ȣP؁'ݥV<><|0G  qVH9|P o eE sG /'F~#rU;Dr:kU]@ǘѿޜg\w"4//&鐜D w,;+\L Tt&ݞa9F*r!t]NΈw?cܻL[z\8RքJ qkW$G/8'QoAw"V'~}~t"%>iTw{bA5;R^i>{/kݺ7\4~54dڲ$^GTݹwp-ݢT/3\J|du#Mt2\JK`íOwoLkL,M`E Av3OrP=p\\IL>KDR#ogsn{=l~ 1"Q.et2^.5;D!r=jڟelh3щYkj7Q|]35g'deE@O(0_[\s%a~!0ˠBE; ^T׍+AFz]=[\@| p C9 9lSh*G dʹtfZ_0-_[c'T/kwa 7[]n a{H],.kfXC İG}hz-AZT3c[~a*Db> G~>:6´/^f"!U%9X{K`.l"rTVL*ߠ&!F7ÿ Sd|3W \3HCT"zc|)ԢzNWX3'$؛ t)1@ ї8c;{\;3U2 Cȗ2! zЙUڗ"5-qiFbUI33Ί@jWKb < {AG"2:EW3FOi@ /Df!zʋ:+s6@4,V I@0>#LMۦ>$/QL_݌/d~[RVWH#(ϒfRky®G gMO/gRxx>\(}} 'l8~[yjo5y*Z085U7eb7ӵe6[]T^L* j8pζOYO]`xeֈeKP軒4]yS(\Vro&*aM4^T:jj]2KXPO-7s [Ë5V2r4B@GH|N;+-I!YS8.ZQsHL/ pB!D䈒U\pNǼ4e05Wmv鿙`Oyf~85Z3G_OuAX uNAdgɝx28I;kǰŋeLBߥRxkoQne]u_wQg4{&*OH);93`?аbM1ٟ ,'ב! tϘ UX?`S oMuamH9yqM$'FvSVwωSeqЯb; 8foVFw'}p ?y]Ï`;j]p*ة 6eap73 ݘ#6{5r- MF;TG- kcwl@jR~8̃dCӜwbx>=l3>_ ;ԂԂy/ 1Lwi&'%%H[q){ ׽_A*"^ ù;'74<} l{m31T EJ>N/1RTrm_sT Jt  '%n4`.K `bK5tZnSc^so yg|3 N/\6Fzq}F?1&f0k=X~ZjIUSH$Dɦ.%ErD> UY㉡ tCӒ= @D|sxث\ XNsSq|e ]ʉp]󛒫>ʃ3R(ʲndٰFj+ƛ6u[pzpA|Vu6E}Z^?ĒĴEfKJWܙ:htIN(H54r/7e Xxbz}~75Yrtdþf6E%TdR_ьe3TP#oe?].P~7D"#!5I1,[ Jno?f{܁,!IJ^0֌rEHv>ugZ9mE߻ ڶa)P.;P\tߔ&Xtd_ sB 4F5wv\]/۲+Jr&$I,$!bă dmpÀ1 !!RmPF0B8xb!A>bFm/;E4.IN?%Hơh7\ QRNx1S?~v%v=4"rkTniRyB!cBZ;_8DsM|nTgpjDVKOVᒀh¬s}(:Iɣ[Ikg{7BN?y:Ah`"Oz6$0{=L(gq|q\\znOQxu#w>0_ |Oc&}̚\Mim1 $)NIbb}r9W󰔪i dȄyQ3_WK⹈$b1$9 HtR ~%^ΗL>!{J&P+ⲈiJX]BXۧC̡9* "3pF~S$CX@ʕ;%{Y 8$T bd+n\C:!-J?W`v;OKBSӇ]MSIIcmјF<^Fj:fPÉJx/qW@#rpZ8I$do Yz{K\Phcjyǿv a}LpUbDR/ 'Ln 3 retMXjʓ:WlVk01?A+G]G% YȞB[&d=FD}"2;=?h"Vr? t9Eȸ5K!Q IvvXeJ-?=MJQH!"bi91ua\vTɨ|z&wWaP /CbMbƵd-EA gwDl2ެ|mTJ.a|*&3?F6FɗZ@ чfFq*~7tST_t |8R/tӳGUˣ^<^5ШKh2FPeG"Yp@.~;z>\Ƶ@a =x8mRV{J,~GԥL :M JQ X/R CmbV2]Hf6 o 2FSUZcgՄ "fx om;hx౥k{ZG-:x3N6AռIg1BpYqs+x0QD'*^rDYWI$(!moPpjk:&}(LWy]s2F, D冿'=2÷ыiά&+1;g~O͔5ʔ|jmTғ04lږ 7^5bRyb͹8'VSRY^-r%[8ᙺōz|p'"ۋS8zO5;/*4yI5ΚbT0s <_w]fHыOB3pZ E *a'պ0*^S aH0!Yvj7yoA4*|;6ϖN;#c5Mfi*@w-Y(C'Z՞I Y;vm*ÓK ?1pk-m޲^"Ub^Q _N[{s٨l]D!| (ۦpD v\WV$ea05Q9 T5·&،3nXBdTk;Snͭ V] bK7klHsMM&\K4!W9z d]W54?cz[ hïuu7+aZ@m0Aoys228^E9Y/gZO{&W0i1/Y~Ш1̙`4՘SIJ0WDpeaDc &* o`}:l102bR?ߎbؿ0l$ED|f)e9b!=A˅<[Dž&+D_ sjגe<5̩Y'%[G1Oܟ_pc {x>\42E^=ìlqlz1fWa0Kc I˝\?.I:aDp6 #=Ƌ+9kw翦#tE#y74̨y;}r6:k)xdyڤD;M,NT \_A>IdI&4E0\b;fؕbEDYgD tq%^> M +A^qG3~̉/xփij@FSFut;>zKп~?6=-h~P8GTF]%a/n3[vCPWHOKش~WkSP~ĩ?zIe *Q,iİU6${vq:zv@V@Ny|gbM6: jOnoiog2J2Jzk8!Rl;'%0)٭)=D+-~ۤM R RSˆʰ&%@$cUYLkzq} y\\"ƿiUraʚ; 2ytM4 CRW _b ,[. |ꖪ\7_\v_4$='u7gu?2c} 4] ꢠ |-gYm9OFK˩ڷk·Z tnLN1^sʙ ג8;ۀ"h}DTH1.ڋ } pth6I$3ҵ=_Q˶OI멨-t翂v=FSz^SA}yAPl/lr#.,6Z C74uH8 cLS-t G[J3j7Kun=9?|3A-YTh|9~5a5Zkw+^hdWZaoz)DB㲚m;UIL:/wjșBF^}j) pg{# /}/\98ѳ u6`Zq_ۮǟ¨baow&'>V jU 3*2 ȕ#}@XA\\~| UCKg9YQ(qK4>G /m0=4\ ea;Iq{%[.K|LO%a?q9>]8# 7˝Lc`JoKmw4;J䧢c =^>!6醾:(A4fr:\ k9NH|Q9]OcLT^T\Gi?7$x>?~4">tL}ݙ,$=nƘ]t!Yx[&N3ZPET4@cQppG%T WG o"NA .E<עk\ 4 EH4l?_/ӕ/F0n3 J#*slD)DaK+<(R?Cy1b%}fq .ӏXDYoY]g.7yrgAvEd9g%i D[+.oX\h@ai Kﴀ'f1)P͙ !|xH)Mf][q`o|F.`s ቆ "s)`kwoRޔF+\7bM;^JF9VShZ^2PCf`87tj4<ވ 3pI9E" #C1@0uL0X H-ApeS6 9 V P%㲷 Cu\wh^=LX}ixC8ҶpB %edz]S".Ckk7/ZS(%!b&jcoAeG %lK>3 qjo5oe qSڸnrLBaavH{ :+;ڪ%Mp戌M^R}|^xOšCYSgXJQ `u[b~vϴWxh|vqटJHQ>o/XKw}ER U XLAQ\]L.@VT\!F3# QކR7tA=dho ~` _'(҉+/$RX>8WO)4t3,>9|p ;xދęq(ya9z;%ȱcϻ9 pq_Zt&}o;~]|N{<1z]o#x-2dmIaZu:2^Ƙ&1 p2=uNoVUlY-t@5IO $i|:.9Gd+O~ą dpJw)mS3FtOO2d{+!Oae9.bxZ25;&kUh[He+y@OIZl!y5L&rŭ{gEdBl$7W ;J)Skю#)|^tZxҥBQsQ6ov%pM{S[VYx :cl,yxcfhrsTld F{v奢]c9q nkoN@ y(y (>Ł-[ެK,ɖmpn< $]2b\h4︠!D>%܍}EQ> df$[v@,\MtAr=A&)O9yk3zDjsWjöYzV}}{t ZGOpt}2dڌ[.CPUK-H35?Zv2}oWϾ 4'>#-;% ݁$3]77R[ q'ʪ]!- f~{ڗRPuIR`74h0?,W<_̼X?VhCѭ`m:cp 3GF 4,qWP'd6fµ|Oocy7:F$d&2O5:3>Y9Esk)wZuHA3_ >四`kkk( An>s:f&9ñ39ge {ROꊺ~J$W*I>fz.Hx\Ew"_VedНxW/Ul.GT*J*f[!|l_A@^m'YERƯ,v(y9)klL< _\H2@FBd&Ng %|'jVb ^=#?yzx;5OM %rlEfcKƖn'S!@'G; e#jpvPj\hQ/=&p&~@ =Y3=J47ʊ٨y:*d|9lT Ⱥj=B^6|[y }RBC  >tZ |0h88(" ፧ ydvl[BшNm.Ha _G鋐x\w/p ym{6H(eMDvuF(ރq!iC8(9ÿ,P-ii" iDv?딍R zY OF3) - $j,QqhNlOI)fE d(&Gw) g P\k-vA9-Ye~ @ ~:jA_ݘ|6Z@cY*{L.zĤ;6B:/=xDh<]H:DO'dh660JfI߳n p$x0\{գ&ҀQbek\d|$xwEBx+YCRst,X4xEϚMaWD\Ԗ0WsM2tǯGvb`1(HSGrǤ$:I-<,HF׸ڥ^WiW]4yb X Kj=}>uhm%&j= EZIR#`ՕK{Z%]`~ ugn9 kM`AwM@! %zdJ8I_8JxmFc/zE\uy^WktW̯v?Lx[K 55@w-0?b*@>k]Wi\}ΏѰHR֊#ق\f㮬zK>|/eialCxM<qn@;RphXq; -𖶵<HԲ ;/h&ʿ!(@|`g\~MyƵUVjW+T<_"}XPu+&7xYk\ x"zݥ*ߴEu}PTt"Tiu!D]F4ob͏BCziiu6iNҧ9h2< +s\';;H 3J"ůQ=sz@- S,}?0\^ΣOxֈ8΁6¬ Xf1 Gio=0iM,^?%j5Gי@L0j# 癅(01rba3@[ojEaOPFhI0?cR5󞞰6^dvHhd!=LʘEVͲEKBT-BPB$Anl6SWh+7fde\>F x3"-h4TeJ9ʴ}R* /X=0 Yz21hZ &22>n I;ș~c ϩ}} \[t`?}KR~Idh࿝QWbΗr(3C/((v"Hpߙh~pH2(z@ Ҁb:b i kb*1ry!Sp|`(qxr$G&ژt5hcKiƫ60Z7CR4Hѱ=S/"?,RǠj2OW'H_v)w~ )j8yB~~<+0ϐ)/Qy{pNp/ ~GOZ&cߡt9ߓ%ذ)6Ke1PgiS|R}4R R噂Y"|+l~j8qM}69Mf뉉Ѽ̫xiQn <֦c"^1r?M >bfh~QB,~4X5q8m[ڌ/f}^?2G Hџ$?~C@O"rbA~7 N< ~t޼b^9R8]qܩE#$4$b~$qhkw1? LIBg6ğ^ zT,#qP t N>uR4d\K9/}Pऱˏ7/O!Xm N(یY!$^@E!(ȵ&!}ad?l1e}|H.ù$n<^=\]3AL/;5u~&F"pSVg$+cSFϪFuK 7 8uϼo)׿SE68 ׋pf㔐HOycdIxU?™.q57ߓ'+E mԵM.Ƹ;ד_YVdQP˻F``qF}pTb0RgA !8:#HW?gFbnJ# i;e$;fH/FK _U Z<%|hTgIz ,N2"^nЫ?.BwnLo |wXh>&~T~pU'*bW:NPJw_KugG-7!PF1,Ӽm@ ͺP1q&?2I 䡧e)]om)8BP!I**Cg)VR7\R T^HqB^pq !s@-, w#B)m0qNj_9vR=P~[Ũ&Id?'j|[ȸcRQ!C^tǹiOHOG. q!n.gHCJLqHKNH*[ .PGeq$/I ./yz^r! B/KHuG U-wGKLB#NZa\w_o-}Vo9&pG\h5"[ҥ}L/ߋ^ssPB20Dwi)Õ- λYC tٜļq8Tr6T61i/2yf,c?Ӻ)DXvX*P_[5!Ng>@'9GJ0܈[_/DE~!Dk,a8c9-Yô< B3(iȻ}FMnCo%Oj'RZhwlC|r wQO_AG8|7d@^K?+d5uz=9R(m]HuIhʺϸ2=3341@J15361 ў1[1o L{squklM_pR+ 䙺V~`{}!i_q+q؂3F>/} Q"'&eDeds'""E$/3"h=FÇAl??1"q}AU_`3 h3È0D1LQ?$ct3i%Ù|a&fɩՙ? 3Z5g6d &/a4?Ld? $-G+rfdԗ? ?LdTd`-Ћ/Ԥa'Ԭy,߸.2T[(o^wrm҂l#(f0`3f03`F̂3 f3f0`3f 0#`f̃3f3F0`A?V1pNFgw+ W8;_ǿ9 ,*o@׮]]~uZBF񟉑h, ;,J/>X8FcI,0M{@ٲ@~#73dnpMga-Z\/mZW=UZWKɣڵkHe [mnnߊN XrQ{SAL>&čǹ1bvd0?0)AzO+ew7,e,d5t 19yuy UhA ww!oCsڋݶ}857͏-6'g#cwkxpj sVO;7 7V;n:?7gf<37מwCJNow ºlX|'}^9~</}Ь&ؿ_)'Xt ~%бB=?PehŘݻ2OKͦ]-"e5 Oe͖H݃n*.+>&ebJ-&,\…MVybX= r^3z8lCIP[Q׹l:"<*)s,P3גrˊ0qt.!BDW@B-JɯnnXVBvUj,b%EEf1}L|Y JfY0r{^5Jџ|˿:8i }@A67bZͫ75P=\;iZӂXʌ=vgB|Z@p&s*c(Q5) %;y}SBP\DGYeSz}-yCvb`4vV/[aj!Y\L8ݠ{)}ff?.{m4QkQ+\d7LgPVWM a/+: g .[B};w !:¥[i"`2u%МU= 1>f0SgHM.P4X2`U)"|}H% XY"-s׀+^WsyC2!ؠeR'&9ifAhɐE;Z"atKʂW̿8І)˝1o/U&NƘ/_`5YK7Ya0'׮Kƴ%fTSnlZ|M5S,rrӖ;c|Q[|.mt \M⤡CM|~NYE=PJrҪڌA onL7̟&K ̐xO9HՒsg!ƒL$:N68.&de$[E8$W F S! =.as<@U*(qXUuSKI0Ze55,ZSH-Ubm;SH.AhUmx%--'KWf_S5hN#?U^Nl4:htd$(J0'FuR>UVnFM#E]9:Xq'&)"g#|½(h`髲7p3/Uע~Vm4|5~.\R , ,d2JR.⪜6ʝ~2e'e&%I.%sSTW"SX~c'fY+)LP.3W\쓦,PL:"0Or2bt;hS,Yi$mButuvfk.Т( :.:Ϡ?>`xV)kjϋ1Q1=kV8]8cҒX3p @x=A|*ъ`F2Hz[|hS|ڭ'a4g߷s9w8q3 ll39Zcȱw-8f%Y-~^&krB0R X\PP@)'Sf~k; q^6lf1rÔH,|D]ApVc((BlI uݱI d`pu'Yj+DTэǕ$=t r,t-@B iEE|zRaNR"9^-'hEB.kf!VuWrSURJO*)Q=]V*LWJ{6$Wb(V] "X"3U "W %;,jmfO&y,}^dDY/=;Z9ۀÙp].!J.p`,}ƕGsݼ1LAjhXa 5אĆQ6Nobk2jÇU<"%ķ'3C"d$ԏ:'c+D- 8j4TTŋ+`EpD6D \/&aUX A,۹i&nC35r÷wZ9ք r* l%O7DW&M#TjྦͳSUFppD>+UG37r 1ۅ>ݩ?k* Wi Yp*U ltzvHNJ>L ׾r+3:B"&q*qDcS0]}L _*2Z($c!\c[Y^3# XwVBnWthDTָ.%xc< ,@?,t#5lP*ƞŠΕ 4,kVNܕ&LL XB|cɨD@Ay) z]fވZ.*>E6F>[I]/OE:_U07>ĵ* :p2uY\FơW"Cj.OgJ*QBk!'UڣIŹd:ÌCdۀqIQ a3Ae7Ɛoa"aY! YTE@-E6rQD% 9J(J-%se{vu/$M\udL~@._(]B, @{ G TD+Ҵ8)"'s3q2}l =(Lum$ ~@mch poDj*BmlUv˜1] }lw )6%Dn]T;VǓσ]H#*+t463{Kc6d;1 $zc QzԌ"]0uR1":/fɘ-w@R#PB]1Яf.O|GHW녠<779Zut/QR[ᱼrəPV6;0)W[~٣Q~|+ a~~ 13YѨE8dM;qgƄotjסI63WVԚ3b'Ho=<~ه:c]Qͮͮ6%/f㜫=1$ΊB}hI$>W%In+7iYi؋ZlcəθtTx]R𢗅%bϦRBǓ*[xMkCcyʠΚMԊk5ELSUkY҃/UlDQy 2 *P .Eϸ61| L*Ok~,6#Bjbj%ꆵrqϘ:EURUg@A32Wu}jTzDS]OVY,OR_MPHR$Ot P*16n!byAN%%BJ4lJ0.K][vv y}n@Ocӛacʝ#օԻ'ΆW_ɰ\A"VH\Â<ƻ\Qk9yvbB2lZC^%Z(znwe3FjHu9)\ h_ឤX+T=׸$?!ID4iKV34_\ӖG'}Or@OJBI$d*yy:Wf(~=p̋^1j/,NNPYҝ,D9l>j~E/Oɍԫ1_V \lgƃ8_OD['gIz_1K"Кկ 5̓{ rE( p 矠"t,5גDr➖A|+ 6:m)fڿn٪MaJC;c@#w Yx)~([؛߇9:PP~X*mSC7NVE١Xo+Ɗݾ)Yg T|?~+Jl&c2(ha@Hsa4e¶ƌa"%mt'tDRK\dSdٚx:$=A9IE9l?REhUj33?*~`3+i1>6N+^O.dɾOD>iA/c zK Nz-ZzLi9&L#*DԳjݣЀ='K6&]%006(v'z5M^5U ]d@O ()pZKҧU/a$˛K73ĺa)Ʀ: r\1"#iZ-u:$T4֏4kcWr,E*8P 33S56U~hFX5]nO&e}:85  u>;%X;[~yg #aN)\ܾ%r]D CXZҦP ySh1P#azr765N_o-=ˋ& _A\z%F EVLy?#cه,j88}=~YY=sK7ք5у#"Ė:v G Ra~m~Zjt/L&{0:~NTD^0x _<ʇs&{uSL({[ }Rm'8 VYۢ(Cb`rt9mMsw,?!Ob.Ty.VX@zTɾP LR+RF(-c, PbT;p; 0wF Mu)_^lE+7,0i*X@ n43%ʲ]3;-,NV_YrnEuc; y_DT}nA o@1jQ9vViuKrKu=0 9q1"PYZ]kÕR\#]: I,hPxd}TWiy;a鮔Hp.rh𭝫:RCJ4iXl"`gY7S 36Q Rn+/9yk@\S/o1 VD Ksߢ.D3! &PߚL8+"mwUĔfuCܗRX<̛S!s6!s=]ZĢk5(P_Q4[j4Nnr%c;XV]Hʯa_1@m 쌿 ]]bPsq!,|vFRcy_Jq2qio8 r-:)#K>[L|1_ZeHcg[ɉoW_iaW_E|\a0$gRVb BˉgE5e@aV^l-[ŗ+#*o"~C;3,䳩걂 Lirb٨,'dJ42 8Fz-,%_y=xRBɦQd5 Z&o\KH(3ϲCduf'er. Lz &珐#Jy FnFXnB%*y'\>JqkgQR$}-7jLJTI natlalq cMr#Ʋp57/ 7j3{_,.Nhf,-FCuMj+p'^@UcMؙc_fCͭ UƞY"jV2Tб;1n{(Yut_NՃPK'^%s4Z .,6,,ګN0Xfp)\E]x3QF>cî|E+p-t+ds<6 ք;,^:l 7h!60Dy~LVHC#  X~+aQx7ZF8(̐aA?@r~,v --N,!Y}8V>Qy^#oS9Q`+b!3hyHN!\g  mD(WT;qmDNoᎧĎt?  %e5d:k[:XѨpNc E*rϰ,__QRW*Ks~,k)Z *?l&*7 7`F6QC_߽'3(UaSz"A⎃ ^]&vj49(62nfႤ)ܡ߁\ MeŀdS ۭ%I} aPね]Ќz@Ibt0tTS d?fp~E@Ժ.# u ,".;}wavăvː/_T d >xOwqSBbl$a_454.μu{((i]tdoQ&)r2}"i {O EfQudrV6#`FZ4* >Pr<$&ڷQ[PÐkbpn5v'qR}'ZGM ўѠnxDW Jж+Ӓ2@Aoa8*@4!)4߷VV3o IGy`xm1teӖ8B*k~ t`1$_C(jlhlώ-vG j|n!ĹY1QY Lr4&nD/M~wB2e]F 4YK66|u/U)1j`.!v:#zSp :r@<'{暚rf))_Ba]څnucn] cKb!)t aAHE=YS te&!fݚwAȭͩOCM PVIRbMQKC* !H#D;%;،JA;[n $DAx"fBR^м> gw[UD{V*.h)N.]ژ:+VJ:'1Ab"53 ޭݞ [%d~!r/| J{1/}6w\M]pyQxVu2#tm=5Z|Uw,`.2ji8l4_;|Lm_0yХ_$Ʉ jO2~a秪%4(zߐ#}omмCs|^nҳȤEz6 QKҏlBr "7aV{|r' iB5!x/F' s9/!w&b3$o!7~;i o|JRoiEtA: -Rd<,*†yfq޶\/8i+WIVYc7L{/h7ՑV-S ~ܾRF3,vq{{X5?+Don/R;Q|t>|CRFm{bz{V毝M*gc+;X5mF|"D /UZ45\w]wWXO;X=?;1 .#TʊLV;2އ%&iQYݢ԰i`ȣ3tn6|CGqHPO+4x ߳!0/knϒ(=AˤKZLFS|OS-(E歧TZ{%i|JF=CVhkbd½f|IyBaÛNډef_KeVMgP [N7.CTݿ_gSI=lV|ܒH+t4:ݺ/Si<%76z5*{CCVĺԜ|}6irPjTW)t'i}@!w†z RUB%]y37碜mbRu<pƸ{ಔtXZ[{Nfd!DREsoK;MC`W1cs[#gͻr׉2 +nnP)E7U6 ϦDwycp#l78NWF ׾e@ eJ?vw٪qm.}'gTMZރp0%^ V'Q)RP~>n>ě6F?g\akMdJ_cK ]A\cܷ=p(ܨ4մ7Zuk[elmϹxߥw(~n4ǟDt{@-4Wx?s@Z&; a)s)Ù)[in _ 0 BJ__zo+2+6ڔ&}hMwh HTiv$qfMA ;odZݽW0åBrgivѡ(-gAG͆V}a}ղ|9+ʿu([J *IeoOm3H=D&vt.RʴUޛl+pEIqK&䱩W܍P~Yf,˼YPN/Iߴ{zHou>1?x 9ӐܥzNۭLl_@@Q88@IA2H+  R}CQ#<&/ GwDŽ{Qy7tWV/p.Z3k8Rb"cK4G%nN1 W(&"~[1e%p_`I߷[r͵]k -PFP5+di.Is_tR;z0%S:~Ij2*KeRWtC@9GPK خ(& ੖oySǃKy2](g9Id*Tn3홒KGͫŮvQxPN4cx ;+CĤ]Y؀u:h3p+]ׯwt`b`sZc"AZ:ڈձ0ccdFwwP5^Pgm.4&/J>θ"0gD).'F޻kUZIbCUVPsz琎XGee `4<.fxHR$\ YX uQ3dդ GF0)iuBj1-a:WN-E9ʣQGCVrV#o1A׃ 6mL}tj-j'jw.73+uIum"yᵂcψh?s8h@o f,kv5"v?t !dDi + [č^ W/2!HģҼи ǸV%N_2'HIrGnqt-Jf.j2 \`_CLȢj ӗmٞN'yZXwᖲRH-:],eIY\dl&b]f7sYw&fx~Uf ȏhژS 7j%-Hm>x9Ou_ n~s5j`Ϊ肒w s9G:t~ԭc*zYrnv ΏwuܣɅI qhV &i&|}6@OF=_AXV2C!!W\a}K@q;9mG*ajӦ.2Nf|$m<^8Cdz*9],[me#0DŽfe{J !z6d83LELN(SEuoKXe_ nj8tclE\3zPq_"f<$ gtj~ʰMˡ{=s|Pi55[Md( s!w&Q6/jQ:_fiQ(Gs[Y1@\'}@/:k-ƤM@$IY3U N6 + PR>g[`'#3&8˪?. DmGkb ة {H"pʵPf!ٽJ")eM捦`JHQ {MɃ5LmOF Wov!HS0R+ ^s`Dcɀӛ @ 4׸ ZRСTGk$ >4SNSc+5; řYFcpO@̜e.#Ų]XkF'`&Vh9ryWe7..ݱ\vfǖH԰9*ndC` rXVo N9%(2a}TCBR9ސ%7oVY[9pfkp[QF/P+#D'c.#Nٮ@zSU+n |a/b-Ƣ+sPqb\HfzÊ%tp[ஊ᡹ *(+׸_A[m2;Tŗx1}*< C: F| S ]VY7{k`gRA $do0H'7= ާ r"汧B*9#3&Z:~,-X-aSo,3]^ӭ`3u9mH(W|Qg̱)yj&[,&@= jn򚿵7\5Jցll{},T&![8()b}U?>ӡ]2@`S{wz;$8(ađg,gkekѻܩZ'{@y\WM3ʟC ҚҹJ,U^l->>bQmť!-i& yQ3\ż?א /yƢ?uo$箤⽇TM{{Tx ]pҁtww?=_Q0]/3=fLm_V:.bz ԋy1۝\=n{NbL<#&>c9}I ɜL;SC&V6vՙ C3hKt͠Ƒh6scHgm窞z3 >)|!j(=l%qArCJ+o@y4i+*&[G7> OfM(aڳ?10b?)Y=Du@8|bQr/ T'꼡g$^ֲ*i\՗i3] KpEITg |Hn5;Q~LyC)rL0sɼ÷w%4{;'<)؟Y4剏~_;"/BWc{~枂2'c@>=gkk'>'Kټz=PLkY05燥7oV3lNb׊؋ ?h2cڜ}mؓ`n9_փnCn#uEU=DWfs2vS ?Q_G=n#nKrM&R|M'J4fS6t%${5̗rjߘ"Z#ɃOf{pWn~e~gc4>A_o/?pۭ`a~IX)r䟺O#_H}|0Cnъ?hD-D#VgѤST1CuRiƱ-zQO/+PF 6UxJOMeP/6g!cZ?ב[9gb1: Q埚6H4ln?Xg}vXvz2̬ PV{)yq>qx)MJq 7\˃AWx`a`s+Mr?-<|1= }M\3#z4;Q)rgÄD>Y*7J?Xx},~{&%p0R6V=Qނ;9B2n,f~yrKf)[Kaޮda+/꺒6mLs,aN̛aعf mۂnOz;5fqNI!>}}znw _@,Ӽ^&mr/he<;SHt1e/| > ܓT=Q}6lڃvɞW̳ D((]oL7t`* 4cFluͩ1C5ޅ/)/E[aM!uJm׈D=lF$lEPhpV R=b6X[kL C. 63o"$n9;l+aL hǻ JC1Be$qeCrҦ2m0W5 Dd{.=-^c5vB/xܫ!⊘C"]ap[ QnY@ay]0%caHZl828 03)0Mָ> 2zNj7bw" k*NaIXkl%BC/_*Bm2+|c$nSۂ|0ld @G8۰Pq՟C{?ґAD$먶eRݯJ{:z[=uP$zF*qK 6FP?x'}0b."2->PzĬc׮@_c!ׁR*qy k$__QԋGg5viUGj{bvQQmF 0XЬ0Io,(nnВM1O0=E(~Bк"Qy}|B"9d<3i 3C- ZYD.-ԟKxCƩK"QJr>_#Jԕ;):SM <_[BiB9Y"]@*V;mr]bNH05,P~ ZlP%:t>Y(Ue/9x_*zԒn A !h Y׹x3Fh$ AfXS ȵ10㴭er#н~ZnȄu 9X^2A-{n 5RSUc"p dpE=Jc%;LxJ2ۥ%8 T7Z>+ ě׵;hb̸J0 3y\vC_R [_f;b:Xp%u2[ o̱SZO Һ`u" lRHAg\}*_k\P6L0LvOQwxHK%yN 8fOu05V-]ݕLQ0߂BrVe@1(k-ȖYn֌leQX=C+>0cUlrc<̀(ώY2#vzՇg_g\0` cƥttY VXE{P,(5iL=35$RVySL{JZI`qRq &V-/o,ZuO bqAkiN!YLDUhRYxK2ЕL̼^hHzU,k#6Q|1|IW O :5U\4e:O>6Ya+̲lՔ Z" r.KYu ^ <--ʿ[d~d:.#͢hUmjy1%4y#IF|g!*8dnUUnrKb { (\x^}"F::XSqaY)X[$!A0 R׀B॑:ٸ1S-C9Mpcx c&\<]:RVj޶R ~ρap7t8խQ߶ (%l;׳.Yc*C J'1nveZ[s $x ]-αըhLe#%nII 5!)a3Pk]x )n 6/YL&TٮHFBjd7fN dJȶ.3Mc `]Z-6@H2ԇ.nHf\? JB!FՓ4[rh"25ꂓP"fO!%~Xx.?N&GܔcA @azt%A[|R~ p+QPcV9t kU6̄勅h7]1(7kTZPRyW^*VxY7d.0E֤!VQP:\IViP+s}]ac ]6ې$fx^jsexd,^nSk+HMH&4s~3ߥ 9(!ROm[ؗi[Pl60>NB<; MYFV.Xb&1K^kj+$bbQ@7$alKȍeJlKetS DBmmrֲ% 06m9|bbɍ̈^X!1qԻGGDS [\e;2\6$'0^sX)TD6l%Ÿ6z%2Ҋ22?`klupwn:[$:Nb uK"\7wiGOPHc E$OUѐ%Ra 4%$kJZJ Z);VdV78pVA N,HvTE82F5 VJ~]C }ni vZ 1-]@걮Atj4I0t:+ 5ՁlVh2;:ndfםEP%OPT_ Q@%oiUօűŪC(H':z z0"ʸu$d n<ҰT@v6-PۂL~a [oK bZ "K#ȋUK wsM#[%jo5e99eԋcEX8bO.a{cF(:l84҂iK9 "e@LS-|ژ $"m3:VAb^k်moEsXqy7c@!3;U$'iO9ZR8).@B';Ua_ UUGݚҀEy@GL(=ĩ I wK8Jb5vsD&s*!51?p֬)Ne+P=7B x-2O+B;\.ZyJb)ݦ |Gn$5ݮ g-Bԋ >&C3wn{e1Z2ө+ %bA=`Y7[畲k28u50dK;g HKlv<\ӆj .FA{ȯ+=ū֕ V,RfkEb52@)KrO AJIq e_tHt EXgK&ּRnXEI*/6탃+!^Wqlb}`:F؏7g4>9^'Gq"}8l]o;gWWY b(쁃_|h[ܺf(HYM lERa%v"q7]hV ֤궨i@Y.Եﳶ TtګR4rMDX,VR|(A>}y8Q^IBP2{NX9y(}ZXl؇JNӹ*~̏^DѪx}C4&7OG*{:Sqp̰QlO9qt&Ycv;-+Xʉ%3,SsWrIֲ;ٖtL)>q)Iop߄[#eŖO<${#F:89;䌖/=nFm.D(ίUy/3sƆݕ<#Eri[IVLlCS.Uޠw2g{$s??V14iAӾ'kgĩ X%fޭ1݀l4? ÇxP#%.M )=3HPu(c27"A%d3k2@&EMmr^TlKѹ,>N(Wߒ#zHEWbh|!?B>WYs-9.J)usA)S;yh +|- ^9gՈ0qWl>82Nnxzv5Z0OC֤'z (n}8~_ (V4:rK>v[kjܶ*z3"'tpsO%#8{8GM6{,)#v='KSɍ X\R75&NkpsV#--N`GײdiOo~'ȷ^_;frRw -}M8 *GG}ׂKirAP#@1 Ggw_8'bb'~~$ʹ4̫ tAHBʇh><`ӴwC:͹ʺ͉|%C>[Fd(x6Eotbc'3P/;*,~'rj&_oiX@zRE{GJ=YwW[҉J.{8kЙ :4ډolY*<a񏿧3GFd%2 w)S4UR=_h!tBްAMVSz:6:ﶭ5 I| EٛYRxRO8J+w">uwv'R]Ua}'Yp/:OZOi{~Zz`LFujT›_47uu -|vu+Muuz/|墊6QTY_Ja[rC8pLm{LE+}"X|t F-ik3v=%]Ԕ*FYNqCO o!S(τ3,~ѸxdĿ3{c-q&=5} e Y&YMtsQ%IlT=s[wHlfR0–!5Agݧ |\OPz#aPl'j;O$۠=RHofq*rAϠ*'1hXTDBVOYmRQɹϏ#DMK8>Vrm]b\=LN/gwih l j\Im[Yڶ oKAlݻ.M=H%Y#'=zqV9gO; =gPTn>ѿ J4O_v=μ-򤈓L齺- :T\ܤR Ɣz'EntZOЎYP o++tR[wmL9Dn>]5p!K%2Gw("FkJ]'{%!e,8xo@H#nEj֬:, IErYħ͟7F&&I`Ԫ\oޕ)64a8Nȴ`?XPW-06+g[eIY8G,8~88l)ҕL)ÕVPhɴW:}_ ڍŁmWBQKp+!Ũm1ӧӨ\|܃!Юj,h;ZZw7맳YQ'Bc`0"N\%%DZr>qӺyP/ zD$Ln')t㜙2n)3@ fZNoR? ?; J : f&Rf=1l4iʲ.B& n#n ($\f>9716%٢Lrfag@vZ.R|@Uvk""apk.{wnLH,'jjMrY/n>%O"=DhBCʋ?A #hjh;2XXfO`PicX?-KarL, ;YeI=&c8jS6iUbz0U. zUnjABԊˣV9s((:N]X ,*K*!(ZV)>ߺu3"a>:Re e3(iW+rQk6~ɐ9tf/q(:"ga-x4F:Gzz>)mR6d:8:4&C(R|dCWw_q>k;H‰QhlwWl`dwFE7pV kJ((+:OBrM[Af%(?QvĞx/T-dVWM9%S?Q1bu4A[ޡE`k0Iqxփ" R'SgHRj[-G#]&` þ&.XsM̐Mj} WE{iT_o5/!@T 0J1>`7 +P]QeyzP$}gcmsH#u]O'|!_QUUG .aJB<ݤ-J}-ΘܲdNV ēa/?e4'76tXXK̖n?x10nbADlc#(]wd{}c$@)c6bep5bkzx;o+b օnMop76UT52)1F P4um+u(P;gv;15v"=k7a'=l5K >/e15c@rx]Mٺ o*'KRмu^ bfeE ? q-_zWb֢BcKZMDVo"*עUHָ!j5vJ nh@3٦[a6FmקU@EXh bڋ:P` -dZQgBSDqdjѵ'[O7u?]-ULWF|**0XF.U%]t~&NC+'pNVsĚԫ9fEb"߰j>Aj5Wanz};3SW4KV95Ya}KN>˂VݫCV1ذN s iUXOh_Escf#4.uIւ@Tho+L67fq%5~ٟxV,B]QLƪy jXȚw+x 2}X(7%2Rev2q 9RVuJt+GkLmq }};6.ZR$@P.>\"RxY)${`v7PJM_}ĎR2=I : ܘƿ,2,cJ keBңݳϙm Rt9D:_\}-oE}$v^2՛BGt0*a!hVT %Vx ~!6R>uX;,SL{ юL3Zg# {mL-:G$z+uF+Z' цRtAr|+_L=7~5 %heQIfR*fo)E/|b .ڈDCT 0l)v:14eP^'7Āy ڠ( vv(-$>Nݏ|tސHe,if,5zÉAYg#[$* ͂"-uVdIՉT 7@1{Im: d YU o0B̯Idaaݷ@X,8_(kC6-Uq >pQ`זtb6A.m~IПՈy ̚F4\eӲEf՚Iģ_;ǧut"DtlY >^:~lBd~}fޖ?h\;)1eD6G sRPigh?%=ûmR+mF I}2+0QlΥ13,4-:pDoeHY0Vza1mf>Dǔv2Xs*u^_TfuM=ER;\p9<cB ~ɽ2$? 6!(gu /u8=P)RԏjpBq#:;;$LhBRlЭ$Or=!rQQJR>Y$ih :6.Ām,wD,zRI C6@, =}( W P5= ab3| ȟGnj?;Sh=n =ᴅ]e޹ -C#aQإ Rsakc Gȁd-w:e`n$;I"m@#F/3GQ^%)1*9IHPַ3W49T}WR>ДTzcR T%{R Ɩt;~ ZKwI=zH&NX)x(U("0,wougֽF]`b^ WT||Go\[/h^յ:+_`?j6Q9!9bWʂlƬ*?XiF#a0o(A~cA`WLN!Yuൿ/fPQ34qHn̻#UЈZ=@Y^*ayhˆ%?~j6p_dWЮ;:(TC(~)dy{#F~2XD Sb= (ׇ3`= f,LԒ>%Lg+QmHߣC\RZhDgGakJ𣤀Z`kFS3F)GRn 84 f.1,;i Dc6q}8~.|Qǡ;. 6ѧ1ِlkdXw)߻z:0ÒFn lG"؁F>.4gq@ .Hg&NJ_+ Qw+ H-,j5]j(T$vx-ɖҳgo-.MS"TqδL HGwIxd\<6W|X:i[$Jf%Skm?^N΂fk!3*LJ ,%|-@NCC_]D ޼cqg{ =!TO˟!|J}FV,8CCٞ+!iN"vWKxh^e7;"yKԹ(29uLJS8NPo>|)a w7;r{#W*E]0%abgo9nrߓnI9gf_$ev3Uzouqz]9@?y=˟N!-?v{>iP֖A+c(іO"=&wǻ =Oy=s?0+z'fQ=s=T"ɱ&<kJIw~z?Y=Ԗ<#ǴRqx"B)>l+EҞ;j Gѹd7_K'šx=ZNeb`GcDEz*-:\%Vn9XXz'pT%# 6ީ'@ʫ?rxc;Ad_D{5;<ɚ_!{8"&7MmAǬW.;7&v^:E:{nyk*$B`R?wB_-cɓypDֿm3n}$ 3Jo fiª3oQ i?޴M"Cӽwіi>(&uo>M狞c >>eW=y!FPDdc8bOnyErNeg!q)v&s)gںm"QgYSކ8⥥ڣllo+Gڛ*rWo-$>WnLݱ)4A7\Kiz2m[JܬlOyF98ir)"`c>D6O0%b˿9v^{K~ T/ĞCfXNIn'.\FCUpt!:Έ[nQ},xƐ~p=AaU/)ɹL=p6uhXޠL7O$MX䤡:) z;i)n<'ὔg.3$Kw3I;>M%ā䕉t=b,%_{4) jobS{OgocR}3Vvvx\:E`cIj`ntc?9L[="Hmw7vx/1h'-ECjv:CӞ&&?3CĞD@;<*%L6f9IeQ"x,";OnT2K-fPlJ0!~\>[1}pwMI^?l9>a<O[o>,t7o{Pe8 ua\1|" {7oJ'}/a᥾%ɑ{sAc_z,h*O8 a5͒ (4anHd7pK;|7fCg>Oo*wۨ0Lz< ԲdW{!O(;fze}!<_O߿/#o!\۝fG+M$|>s{>*zKG'' u.uC?g\Sݩ*a)5*7[%" 3ʠ*le6 QI)P y )d,U ;L :hPy^,Ɖ27&)q+ޱ ,hږ,Tty] P,?Ik4(={ *+)2w>C6ĕh??kZ4+{aDsay}`E\q]В%$im7#E/F1 ίA& % 0O³H&9#Σl s>í CbtfN$7&FԾNMnӢE#CQ?@qN:8~WCޜQ/#>adTy#hXE pJ+j՟tA˭]cI 4F͹|j}hp{0d ‘Ul C5*ExD֣Getķ Ww핾vyd^!8**wڗA5+eMiYm댁ij&~_1X- .yjS$QQ](8}4]rpr涃=h"&/輥0?v9[$GfrIwnF@R+VYf)֚tiDFP)k$^2}KNkaWoX7LF񅦩"(0 oʼn^Ne(!Mb;P_0)"_z؋_H-yɗkb4v!Ex0)"j-\85r:CRSk_J,;s6{<`R8p8`& ^Ѡw>W݇+ce-K AYܹ]|,Ȏ}hJ d*Qaɗ31\TS#,]FBmf2{*َ&"~{ Bl]kf) ~MPK_Z^x"||ԳU=I< 煠F8:v>&Qm͕7DZ?N2{g)v+d/5!?- ?UF<тv? -+QS=+2XM JU4l^ިIeyE"nёD?:6PnJPZ/@qưT_];xjD&W=xM5z 9>glpClzl{ L}sʼnX/! k5G~qi B O· }L ev![2G0W3К <\ Z6 |\d\^/]ȟ(:2-sWKS+ԋqocGhbLhG1'J1KM[yȟ};TUhSޕk}z"Ȍkd$4`y$na)~;$$]82v(|&Իqn eZ QUIQn}e;A7V`Crwy6 ~`}%6#3dG/n&*w8צ^ţjS9ZY/hn]у?؍ŏW%ٝc 60ZHa-zwJegi x(cU&^Spt jQ9P}8nϱgv*{*HTrYq|e;y݊ݫ2ˢM"7'4dԄkЇٞni%愲1PA= `?]=҇>}Tn;aV(!k 7 s"Q`Ji.-Fd9ꖳpeK!ӻx%h>Q@p>Kh9=*~U5>FhOD{R1Bw1CSʩ7OȲ q &'{>z)LN0þOT i(>\@7NQZ>tƌ[.G=khu_+oѫ>575/pYʚ 6ZSB݋_z{M~P=97x~?o`Z~շNm zE(w5=9Ӈ ޑX'vT^jbֆ> ~|, aPҍ}Zy ;i|2>*'c*嗂T4UwrO3}t7QxDjn"Dy38ei=b%I(2I1SGUOǺWg7MGb='} Q_UΝdJ =?eTO{t+W~Y93tE!j χf>䧥GW3gy~֪Sik|r?<~9>(@~AwZ&OPzv˞Wh^~-U5L?Ǭi-z~ɫ9*z]aϿdκ_p_jW{PS(3^+o*LW^J >j>˖+4;:G~ ^{2}:PiϑF)jeqKDnZw@ON(|Kg圬%YaԈa𨃏J5PBl>cSm/RDtMڝlmE嚴54t&x+ÂuORm iȓnEo^7Vvy*?3sed?CA"|R֭p>Bm'婄NI3tع&tmKX"r~$3~d4۟ƫ0f:C\8C+iC1gƧT0] S|lX]:om8&]aW?|vhc`Kd魚T# 6"~Ԓ9ot~TjX ÖLP9詿3PRCa^?DQ5ZLvo-EOcB>ӲŏPdNM21;SؕZh>2_Mg`&QW{O'B _r55ʣp LOinq-ˣ< !k:WMV4 6X0hYA6dxH,rǛV_ltrsIw': B YPeaag_Cȼ.[ióRFmYYa5%/2DbEf!eCj h-RqD3_fIrE#&zЛYOBOo ÷ > x?MmsӍxq鑡Ri|.x|1D[7vѳQMKpJDtUw}x7A:/l;FXFܖ)t1e B^Ņ9&/xS T<\c\aǁ^$"C5sDb^Q;Q m<"}n # Ti41TAᆳwd-}p'KfO9nצ!mŠj4|WW%xd|2&IƐ 7_>'d^QP`9S/pxY~yX'A$ trw t1?:7$I[VvA".pjXΠ<&Y#k̵b!;Gk*/6-HpjG tş:\f<cWKvԱH|0m~\{$0(v' t>Xq =^sQvL@If#ZV% oG)OܒT);|" '&m6v'dZ>_C[H=h{f,X08+ 9z\=νaicBѸ3Βsk *yڜTeFFC5ϒ +p[],Sh,fD| _7˜AD3o~ZdQk*LĜ؊ΑK? +,3Z{X:v#32-] ?›Q:P+ńDޞk#bOP6ժVk< ;w=|NM=*x}ξ#nOEvL@9k2D乇axIw$wji-&)fZ\g=:DB`:3%DkNf "ɱq)54$lY\ਚ,&s 1V(K3,x= _0^H<&-űK]}OG^ܓ`>  DH'%*, o6`[3:O/ v]+@Xe_`1φfEvW:3v >ͻ';HIH`t$ lБ x ڔ}`Qs%zobx QU#%MfxVCJa|1>CnֺUmGj2쮭ITs}åݧ~4dzms?f.f-eC!D q\o][˲fcj!YQ[E{)h9Duƥ2CcrDUsSf/(H7<;ѩ< E:s@緮 Vj%1npui|VKp_%e!+8 AU[tFDRI3\ ^'} Anxj`^"VFТo=yJ@x*ytD[Z88M8Z 1=zYS`>V޺!p]ڌg V̋δ Z`xHm!L/5}vڟxũFl[+[8%ژ={W ZXj~+͙2`d ,W 0l\zw`e`\h X3|-+ͦTo0Lmblr I]tnd>ELRqF2 UNޒz:#WG0%OwHdMGwyNHjȿāJilHj !t8"| I_|"0Ձ7ܿ"hnxc oɁC qsv$Fer\\L {ݠY[ʉ\u8AF)؏WY.kqHA0Yc[qcomYSVWT-4L!vQ1a)Vr띆VS؃ n6eKR%(bSg SZ/k@5ZYaԓwQ OB.؋NCLnx wut&[MMF{SĩJob(h~Mqhխ'Ч.dV8!UN3 i-M.U V{9b+``87>(+Dp<7+RZUsnba %t<(;O7kcc]q[zGT&|"DGqɑzD ߎ4$:NV++R뢅P?9qd]R_r RVëЌc,%ƌ#BTkOec(`[_E6'G+3%8 b0L$7ͮ5z*4vԶJ][e"sx%"̨BR% Xqܹ(a-vqͅ5M)w_+ n#tUp #+Uё4m!-8J{Xj$N) ,7Xdk D"NHpʽSfcG6*|m:<ɍ}żRXsK>I |Hkzu{g)yx߷`UVu =s%zZGV{ӫgDD  u%qF%+UZ„nkmt>H{$hdWa172F|'كLH`E0eX$:  E0%BW 1j@!M1G$ÃY @*s6ޛ x<8۽uyu%0Cxf0I &G Gؒ)C.!\6drONB.}mc9R`֣(>5WT-W6$fP)QVavs ]2_W ~*HkCRR훂~~ȹHH0Ѝͻ2m[N*5wA/He) Y⽱ۈ 6Gn-l]ݣİ Pt5_@\-a& 1٤tDw`jɌ~֦':ܻkbioPj̜BLh?ً+g#xxAׄKVMڊ%;aZe I ^R^\cUk#ӬMV:ڠ0EH뒸|@5BUjpc-,UG6wqWHecSVg#<ޢ`bfm(bYyo 4aZb0q5_hX`yѢy\f3g(%['T˲<ǖŝ3V(7kd_Zlj.W;!`4)._$adG",olc2hfmFeɶ6+Ɣ|?~1H*s^5W%;񜴋ۀ*뚝gDmgcE fXH_ \p\Ƀ\]rj+@5υ[AG )z*o8sn[ Lrkq5DRW9Wdvު@ ux+ no+,Z$ɴ\g.|я*òH2f7ҪB fQхL7XLtN+bTJC-qU8yP@R0p(0Vm*Mx#+aـ"ώ1I}̅u_QU U8õ y;K^]m.͞4ƥ\ZG8U[ ESdE3U MQ G-d&x ]teRV W7V#O54 ;;8/) .x=5:#Qj m k7qڋhV@|nYYyD9%`xsOL̶`!Nn/"Yo.:$lV@]PqŶ[I$U'nNʥ@] /hM܎z&fJtObܯlHjz! 5?:CqӼyW t!4^Էf< g_Wv]"` Q8!1QxmY+ w| zek]2NfъcFCҲށ٢'#pg#56]&ka%jZ\SwKy1 SuQ!#Z<+B~irax>ehfmc\,8}]FQ3)2WogޮUB'TNHr'EC=l%{Tyû( h# /V?A@.mmr9%S.HBBU@Brg`F*DJQ\6Vȑ3O+ "80#//4 P1 )r0QOT0δRt@ @0(`/U 7 v3B\{2ʆA9 U C0Ј=@7~(io.92-D}!~ y"z[G6ł:Nt,gK & tqkG "zxwa7}q:@Mr>ȧvA ˙LD"jv;VLmf^vbv%F\vGnKvv5GɁCmlL yϑ-e vn'6}܈X83^VS9$ն}+f\ a_As[du^JRi,Ԏl[raq%]~L<4iCe^a vTgo*6c[ɍmK2=>]=*-3ǵ{.\;2y/>.y7)T0z iFVq!PtDMIŇ8*Bk4;xǹK3M3Wq5-]qvh=A rHhEF:k@u@Y{LD)=#@H£_)`&Zթ r}<n_(]q=[h PITu>tǮjv !{"Xhn?H9P dPn|zbAӼ rP/>t&}#0RjG@B zHgP )ĪxNꖛx"} q֖Ǥ 4Olu_s (,de _aM&C!l*, R!'sET'ǃ٪1r;rO,j[6AKJW93:S4; 26P3ę_/*40eXAx7;q 7)LD4xԩ moKAB[eNN=irJaE޽&'5:)%4uk%υ5.;Gָ*ئ1ˏqБg#خMP1=n =Qb$m?=TuZe x֭;Q6>!ekއ ӊzG@#k찦p~nx_yIҀT4䟂9n~!Ml*%KFSwK^U4ڜ̈́%c" "GƯABDjb8ܲRjachd`-NZc2'm k$݆pZaKgJ'a۠PzbBr.^ ~J24FB1u{vC^;9 oEhֈ80֖h`< vT+͕.| 03aa\ iqZn,Q nfEY`t;`g^ƊdP1.9-01U?H=8*,P=tVf[nJ > k,9PA osl=}qB4%6#HoUdͯű#HK9vA`a@ P^ aQI~w?,h%r7v>`NLs sfYB_rL - wJIs:J} bb\RUe[ބo2Z X;H:Ċ5SɈi/s0or_@&NFQ̒GXϞ3Ɲu,5[+Ny׼lES81 |sɯJ)e+kĢwKrJ@|+65KX)}-+y *95i1̾h@hLE PXȠ2\"ٯ225'WgL6I n #6/;QLC1^ b|:JQWͷTA#JUtFLrmgCES@!);bĻa!PJd'KX(~5)``Bɶ'Jhv0#FrV*uiH`-}_%&.C#Gt3ƫ)"_d O0@c\,RqnD#͎s^h78sXqE:˥u:D֡$2ji)Z[=NQԃ}BO5YL2A/4b\vY| 䯞{p' 1Y!|۶o G 5ܸ**^?U3 6.ؾ0"dܨоֱ`g@7ݼ#fI]$z"%6tV5G[RFtK +kdl +v)8m1*hb0>)Jmi Zd#XHŊ~o*>|/葉$*5TtqhՎ;+2KdbIc#iQTmz)p`)֤%8[%LԨjt'wYsV˖|Kǀ]-=lWu8pa/Ep(}Hmo Im䶇1o-ʧOE5녳}+3ET1`Fj}gdtC=-(s뛾 6< @#h?fB9hK]'S/[MZQVa讉S)t(XE6d4ŷa^VOw5u(N +l)Y(I ]$&87h}GwXGg76NjX_о],J72pG:\@BK?OL,k_oE׈i \H鶣pb  9Oo1-:6׷`BVF։ VQ`jc0a:QjcLUV](r&erL#+CˌJW铯j-=O gBYrg9vF&`]gI,`ZTLΤXOzy+ |mwCZ*pÂ= (\ixG 4E6nl2K>9s< 6:lhY6P.sJL}U.`c),"P;M˚>8y w#5OB]Lӯnh:KZ!A &F =K(410NI(SpK \`ڍ {S l0W;~vMw2,"ۦgo"MZQ~ un[XH!00;X\ )8x*,go1xhP-jzP ]>3jIcm63*&cR[Z^i׾jͷc9Ypki 8͑kr@aҬ)HLjbޣ|])8\`u) j s5Do4 T8[q۔Zp%aJZ J[6{5_΢gSYK$T[d#k*T+ь|d)̣]zX{(-՚y*ѼB"HvbJ0*ЫKO_! EsOc+2TU%jnbJs$f#p-T05E9qJ 6ykַN>Q"rNmĠ#b gv:;)Z;ieєuYs0;ʼ$ G#GAH[ H??;uBI|nF&{Sqg#6Pe3QM<2k'1<ˬbQ#\_R~`I` j᥺K_]ѽf2澡*U/W Nύbp1FޱA̴p]@j71 ߾ezvR댿ΫSw ݨľaUoW3:(BC~d9OΆh5<_E/v};hDk9,=:05|7̄GQB3_Cg !R.[< yQDŽ]y<.8l,o+'] y=v{cFSY})$16m8 #^~1-Nh,r- 7={ZnE=FIz+$\rП9zJFA6 bֻ$ >M͎ƨr0LoS L[5g^Oww/}?kʵr5W$oIWE6&Gse9t:6.*)m6gȚI2 tRN?.$m5A`Na/5g ⃪N(lU$X[ BR+arXt;Kw|AKTR,6XQ%PYzkpt{gvCy`j>(@0G4NMevޔrK n j/Iԡ৆j #-esJ-%nN^Ke/y7Cq| 7?YLhfxZab~@pg?"F{eV-8=Zf>Ngƅ->%cObdgckRkl^*^vR"iy FC%"Ko~SY EX%FQVE^k2|$*Bb868v|=Er#|;f0D_|-FHvghlu& kWs* rW;8x~\Ro?L 4a~ u~~\ @ SaDc"OblzeU*m;v"KbEI"?K~1sL'mܣ $DcD) p,Q &GRE$ LA$(OB83$NL<:>c/Xp0rZO$ x l 0x 1`'c}MP򉴤,KcrA^f6, mANL< &dϔ2t #蛹bg8KD -B ٽd8p")B$̳9̨IgB OA=RO>(̠g_-_5( j(P-g1B"u CperE9-Hs !E(0eTT ڊ*Dy*mE"P[D*D,."D\(TD.\LqB^*aRQ!q)Tg\iÑKJuo/ e`MN ] LJ q MZIy+xRֈkvJB]XVd^CJJ޼̆iJEw\+SB bٕd[V2h Jq)J zE+KM ȁI+_>"_s`bK[C/G:;EWV1Ay%.VC2([(#2z*M)Ӣӑ8w%Ԕ$KFXNmZ`oI2j%U˥KAKs[.i,\Zr2aljJWlW*/]YY<UZܪDr 2N+*海Ǿ_5mm:~pHO5 ;6tftѧ=[AsPyxq:fyz5ud&zKp|Q/WѼr_wFL|0#6$T'%R@Pm]'irLIh?@հ=vHPt_"9L!g6 b4hj$*d<24N75p94Gn0NMy|@yD0q؋ ޵8ʇZ ĘCdpCx.z<%*8!?1? #A^ c{["|]980uzʚ רb_HtȠ!2zGyiLC[~NPR^s|@]P }`לb%Ñ7G/9!X@'R[LrPA I3ԯ+|^h%0Ӈ̎8Af$ߑ@\2N~Tkb 8-yozP2a#Q X~wo%6uxԒ^#1 4^|dbw܃:8TqAm1hW!XE VAK`ၵw>Դ܌`?gupIB.K "Y\m{udu*Jߦk᱕1W_ PoSAW;wC/_؝1k9yp0wrog0ۣ?j`_`m4N/Qr֕^Sli 1Q1aV< ҃ $-bNS8bgA \K7`uɴ1n׹b!iX߲Ċ?/ Pq7ex8>7'VM+[_ BÖ;M[d9n@nlh+*,u폦EYmz4#aMtPiJ} Q^:՝` \!GӉ]׏C&e~9IUm+aH5ۏsSY]UߵjX*w[Uϛ/mlĦW8猭ۏʌ3~ߧQfԪEh6ʰtj޶/|xllZZXG>WU+F&dSfKׇ SjMfMqMʜ;7gϚL=m޻wami1}M&%p>SOMTD U͋}N+ͦU⊏zC*mȢU:;&Tg7n]wm?56~[a3S2i؎ꫩs[ tя**~"*_X9ej޸e1͚\d .kqp5Uv拾kAF|]H6,&Z~ixfyQOØf< ]3{7a};}&[w J].9bHz.I=՛}}cegvR.g]93&fXѾ?JLu?UUgog+!=4[iSZg_d螄̚ڡgYrV]^}+] n'(I'nNd*07*X]'Pq;qq"FE\㚝(NIDjTLP9u^M4䍢2\"BqݩȻѝ8 ;9E-~|'*WۻjViL\`!fQĆ13UjR;;1W,6XPT"B\v ΢ @@  @@  @@  @@  puzzles-r9872/version.def0000644000175300017530000000002012161170564014565 0ustar simonsimon-DREVISION=9872