From 1ff9279f664c56277f9bf313e8d212cc1e1456a7 Mon Sep 17 00:00:00 2001 From: Jack Harley Date: Sun, 31 Jan 2021 20:17:24 +0000 Subject: [PATCH] Significant work --- report/autoplay.png | Bin 0 -> 9328 bytes report/report.pdf | Bin 177564 -> 187068 bytes report/report.tex | 2 +- src/Autosolver.hs | 20 ++++++++----- src/Main.hs | 21 +++++++------- src/Minesweeper.hs | 60 ++++++++++++++++++--------------------- view/css/minesweeper.css | 6 +++- 7 files changed, 56 insertions(+), 53 deletions(-) create mode 100644 report/autoplay.png diff --git a/report/autoplay.png b/report/autoplay.png new file mode 100644 index 0000000000000000000000000000000000000000..1bb02bda69a0e5f2d5ae361d57b6b7f2a91463c0 GIT binary patch literal 9328 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 literal 0 HcmV?d00001 diff --git a/report/report.pdf b/report/report.pdf index a72d5b802fb40c899ae315cc43f975b424540a6d..1984acb9754a73a2c7d79e5c346d68495ee07fdb 100644 GIT binary patch delta 16825 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 delta 7390 zcmaiZRa6|zvNeMyQ>~{RrP+TUR5pC-PMAda||SA*HKX71@l3H?31%U7J*QqWMWb@s4!DHF$aJv zS(_53IO(npmv08I^tw0NHZifimv|#?dadgiA7}9K1=aw=M1nL-pf$P>r|`g0#>44G zIij0+f=D1zfPLzK_4PE76E#GSD4SdG#U!~@TU%NRB)2&Ht%O>_kE<>^{@}c=#<(4M z>7MF-H&f#~!NhL2Bh}OidpoGARDu?9Q5-gmLwA^ZC?odgD_)Ad;;i-;TQo@Ba}2@@ z0lAhV`xTMD)zz!p*?C8+5~?99x9#Ew*HPgtm|1FGguG*14HPBch^oZil--GJVZt!W ztPeyyN>76xp1tYC_*cAS)%FMQo`@AGPa4nSp*9Vmvr-P#7w~Tyxb{?(!nIhv`mZ4{ zG*7aj@&jWc9^Q-6cca`6vd>qJy)wigrh;Tp6qU`CztAm&NzHB*eQVi+$jKSUxPafL z>BEs?5T`&C!SPq|+rUn4Dy;>c=uiYxvVsb|?^tJ)36Ys!gN|{mb(>FvsjCcz9VQ51 z+1~}hs7@Jtk2FoOx7Z@p*=uuiCZvK+lqB8p^Y=Ym1DnQ!hV{#Q zCN+dc2<%{^X!ots?3e9o_$7-2`k(HUeyq>9Jdte7+k@4wC@f!cuW`iqq0_?z30|Wl z7WXPJREP~bxUY1S{@f_MV_YKfvsL$YrI)ABHg?6xdhI?ZRo4@>YvvlHmm}F&I!brEl zXNftIeW>O!4qEyQUkegrfK%GItv8R{@kLL>UT!Y2|MqvPNGyM6OkQicW_HqI9%L6; z@F?aH_Zi8kKf$@PMy30O`(EfJBF)Laa%4qwI?~_Y8Jkz z&O*G6B2mg?g*__zangS~fpxQ@(aYb=6;(Dt%|JUFjh${1VvSP-D#3%BC|8uf<_W9! zs}O`y-|Jl2^a}{?RGjIm__?U~sB6Nv(xzU#$&nJ9SJjc8 z!J!MEqYc~3C*0(gu-;;M-)pa-kagnboKzk%_vN@EM0LV`?eL*6j|!>CZzVWEz;OX{ zg852HteD~c#JIH+?i9(JbbPCXm2SLq+L%46?7~(jy^gbS+9IB1nKPXouPXd^+Q)b; zZT-?=t&JtP>1MsT&nWdB%~aKtoiXtr+=7LCF(YR3ukO#@O>^OdwjOsqc{y^gJ7T}% z4?UhAbbaIDL3ors-g$egDql=_zYj5l5y#;c^tr#C*SwOr&|cft=zMexY+d(!GCHgC zLN50Ys2G1Ds95`wk;mm9)akVG7&NE**VlU;dSWG=**7l02E`CMu@OG5D=S;mNS!#_ z)%5cPFQ0swZuo&F>sb7a6Q3eOE@c<9RUq@yWZy);$er32T>Jw;DNh~cNEu-c!|S4= zFi0l#5piOur#JFgE;>%S*t+qcO#PDWi_DX=L*oyc?r|qv?tUZuFd30Eez>UoCK-rW z(2(_J{iQ+oLI%Q&)*_!2J*l6vxt(ysr6fcn+i^WlJ94T^@!-wF;=AxIIYm|F(3M`P ze)tDdQ;q$3NRBi&IZjjoHs^dk3=(Q4xa2OgN9o9J#yW`^7o%$CjuDn8_c;zG_Ci%- zlAxEe$ZkRLfDTGSzQK09plAAVEjNuYh_vPY);he^z|+CRSzhW(ufaIYamyN4X}$(T zoK{*-eZfS6%=w`#3KmKlRT>{Pg9yN?l8 zm?xid!%-BLWsx+%d2H<{yH!CRL+L;@wt}`O1)Pv^W7PEF5jIo{vJ4VXuHpz5_hUnT z#OqGtaYp~-EdB6w-gG3JF4m^-?E4HfyuB=sh|~FHb*lfj_OF;R5pZMuZHQ@Q)DWMs z{E)OUzMU>6yQJQn5jC|i4C}3*62X!6q-?0>q*M{k?61f*1gI%zwS8<8dNO`}T`$`y zVA3J!yurwDaI;i)_iz-jlK6LK>Q-MmTUxo}%G|}GK`TBYiorj>P_dgMEg_O1m%u7x zC+R#ZY%!hxl}6dvXmeh0Zsz;Ib$3ZmJ#JdjL$pO_PRsDkKzsBtm>LE}3bgIsK&Nd! zBRpG=oTI;Rtg=^xU#<(Nu2ctvQcVY_R1lE%U1`$Cp}Q9t8VD^JkL|K$S}gFL`bM>D zjm2?K)u@Yal&1ep3*T?!+9>&4{4B^*mfzC8cb=0n^Hys}de&xxQE)^__6(&}4Eqyv z%+97d{U?nOu&fsu?6?x-kBoc~BpZ_Y%LTN0_Tj=O!>)O~9rNOA;ae~IsGr{qg|F5N z3V?=G=A>%X-(0~{#x=bvLtct){Cx-sL`xMYvh#aoM7@Ik1_U!zZcdsC%0Guc!4(@I z%I!*Y@{*PALWBJ=&(W`yC%UugaDUtKxnh}ZlX2Tf`9a&p4HY;ER1b-kKU=ccXLrQb zuio)I2JyT{vk;Ps79%j;=xsatnIyd9j- zpyro?wY5JHxT8kKP0H5kCdTo7K3~2!1NdmOg-o=Y0UgngSZG7v+0p?#08&%|;nb~j zQe-JDzyJt-5DL#WI048cD!|&w_inyD3 z6NLN|;OE;ia^WADXiZHbY?R1_CM_worq;;Dr`9N}WOBUV!rnJP5D4N^!FdC+pd#S9$cW8%;uP#MmF~Q=B zQo8b~bF&~@1}M`Kq=_|T*1Eu-<%gCoE6EbQ3gM7d{(yX=@NB59_Gqsf=Id(&9Xr&b z0DTiRP|bvMo2O$iNKC)ME60!fn>S)KsO>RYC#s5@&UR&m2y!yuQgaA zOnLrwqymvEQ$isRT^}hiiOWt{LaQerEQMGWVQZWKUkGzeh9{*+oGT(o5@`Ia;nLr^ zDNwkn@BsN=_3F9ZvFUOM81A;*hzsS;B(||@dLb$H>_jRUObC!J>psF*SDuM39zX$YX{g z)9W;dpC6?qzNFW4&)`oJjE%Z=2bQ_@f&Vnl**@hWjw4X79zSd1ss+nGdzK6=4d#RN9Ny?tXL||#22?awX zr|7$Io*jm*UKZoRX}k+j-8ji_HX1xX7`9|S^Y4LRJSJ^_*h@w@Emhh55rjk4qoC16 zU>dM$g6Y*{iBTWOrB3Z6%0`L)Wj8YD><;qXru#)336Xk9ludtsfr0xXA+?<9N}Zd_h;z-sKer|5eqAl+ z`t>}FwC;I3aC_|i@et-Kdr9hV*2ucwym7t{GJe0FpB|rKPtWo9mWV<-6f9C##VnV} z+0@$MB9oo8A%h%f=Q9H*dxTrEsu)tbFa3g@zD}9Z46k6^=qCvPi7l+mmo7D0CoVhkPhb z#UKBO-+j2#@WK1U5gl!P5Q(FelV)_pYbZg|?c2QKEZh4n%dvU6=D_Q>lE$2w5-G5A z=(C9TcPj*v!k!vI0To3o9}Z_FTP|Wo2%Ck2En5=U`qUape7^i;q?_Uma%(W`-7WMq7wLaTl4#)MIEwc(P z%%*nXLUgqM#QS!}H1jqXaerW-8@Dl4K-W+sVW7rf{wYGBclN;q)uDv&d&aWKFSUEG z&``qfUrn>f)Q1{esyC|~x>ELP=S~A*c3ufua{8`WLT+!(r!=}xsKj$J(j~bm#slls4?$Q2>xh@4RxH`Lg-_UX67mW*8$duSJS#k z)HOESlBW_^#Y{%qBCuE4b=JsPlap67TeVpy@9O@timPcqgQM!wm~o(1Pv9>YPT#b4 z!u;h7$v(=209#yXUK|%Hq%j)6jth@(UA$bu!Vm|eYk2N{#>Fe%B2rYtjqJnvacGyp z4~e>DB^REVgZosgXw!aZRi5|!ZWu?p!9Fo*6kOrQdX!vS!KnxzsPQLVGimIzR&XqA zE(OaC^1}6zI0*wAZ7FGc(&MoG+27dt-kOqDGpqAeCjNc6QST%ONhY(}1?^)B1%#6f zx}=y&Ex2QIcT18#xGKII$olBIUHFP0?pcLJ%S}8_;L$p-&DQUz&QWa^mq^1amb57q zPYybJhZtKVH!gJ20?`ky&RWf+v{!ttRehC0oE$BJ-F0Kq#Qu%i->n4Jnx6J^m?*t9 z3OCL4qPZeG&cm7WXrrA&^u%S_&ie)SKcSI1-w&m8%Yzo$vl?J-nWIe!=4L9RS*aDjrlRM=QV3jR!Xw}cT;D;77gZL3sOXGO{ znWNTwi^qTr2@wYQI=tEtYzDdA{RL(I+-}X7a^4t6xybh(Wy=@ZOf1_*3W=4`GJ69@ zIGzEz-Izk0F0QctJnDSynH6fCj#aomWf6Zi6};2>^5|~H&*K%KNO&csKZe68k}6!~ zH%77xYq1?|0T#YeQnENsnZw*W>#N4VxlNzA;QNw5+*K*m_NGJIzz@fBb6f=`A%$PO ze!%Dz_`j@K%rBr^Bad$Q+Ae8Fil3TQYN(f56&{j~l2%}cg;LKVs%sBvSt}{m>Q^oI zMvtbCrW2*K4YAvN4Wa=Tw_lGb>9Tnb+fwUwuyc znVwUHGfkdfWl@mLq|6&1TAGH; z7Q*B@!6JRw`{|t8^Z;bezyi{k@pvgXxM94*o=K1`0-*7AA2y{e*2 zViX_)Cq=p-K#LLI&ydMy_7icf)edz=EeS`915>c2-#Af8MW$C?LSj|z6kab4X`G*| z_>I;aIpw}!a&#R_yDO(wJ_QOtWjvsMe=i7dh!mWpu=@6FN6?E}9bbeS7^Lk`A|K|6N zU`RErKr-)s-jsP+lzE<-x%DHkp~WCan=^H)`Xrtt6S{SNwR9OA90IW1(av+W)~o}r zyDVVePWsUgdr0%$CW|FUDi;NeeL1;ljiOaKzoB`+-1e8q<{vb&_SBJN zKdtR<((-Ltsr7Y3vUvQi?e668nftpo?omz&`+7I#lV8=H?i(m@d}7v_8E9hwtPC%rv|I^tLo_8P`ctNO#kpMaK&F;eM$Ce=9W&euei>Ju2ZL&)S%(v)c#H@ zr8z)#vEpT&J0&R(#zlWW3p-P`%7)Il{B)@?+JH$haHj1#fAYiLn7Kyab6&2))&A?I zxQoF(EMY1qA*HB!ks1ivm2>{0m=3f!{W85`3Y8UBYnIv6%mFXaos8VeT{d7hJ{UeZ zP@5Anxy1kUvfPfsnSo2-ptjdXJmp(+Bu^@jXop||y;~vDd)D3A3`71WwBhoD$)pjW zX|GdxrYVRse#+I$WKmGLCwml({}Xq#^G~h>&ou5|Eu=_sorC~79)g=y9NlM-vFjS= z9w7~L+dDLoWPJ63lD@O!7h{K%>*N?SjCy6^4dET>qSROVKf;xgs65xyls0t}Oc>o2 zj>zru5zW88=_7TMmI9#Uo_ZQ}Z4C13Sj*ueW^+*Gjvq zCQ}%(X~Q=@u_-1x;Jum~?r!hKt{^E&_-#tbUVLSUD&&MN7ldz@i;SA` z9a&t$?|p8{^k%HJQ=>9SWmrJJ-Ox?VCN-^XDdRczql_0h_MfNih4kOo1F`3f!ykqz1M(xj-6x)|fk2&wptRmb3W1F3p?P zkHHIT)E~qPOVKaK3wx`7K!1AMBW}}6KkPg;f#@Zdufz*0?U|)U9SdHfBv0i^XFao; zI=$p#xW+np%guonqC`(i1wpUWfY@V`Nw7oEfw?Kvs2Ke8L9PDZuYCr7`Nqg@*Lo{a zV`xKTHZ}*$9{R`w?c8nOdB3%@ar>9% z@!A;(frzAs&Z0)J2tuJCK{IY3zov(+f{nKw6PJP*1Ox$tzye_KzX#LV=TK8n!J_|a zE@{(e=TIq7A^-0?0rnwv#61E;q<9njgC|=JV1I^I%%wN?>)wE|>IH6!39%lkE>rwd z$iSikBEP)qC1d~1_+kbKP8IW7hTD{w0X;`nnVSvso>ZTYHq?Soji8 zjMYVV#n%2Q!b6jbGc?xc2?dJ`t%k0Atb@DqkJNV@E;>+r3XHh`=YUzYjibqtT4CZx zkK=tqccmRDkI<}2yQ-;5^BZ5+gec)2_P)WWQro6`e{frR@xDNP<5rqQqUX~3LC;h{ zl%fRS5PVV9s2ix||3auM^|c=}8E)Nl)t}1l()7%DXURv`KX3X}J3F|w|9%vW(ocIF zIn-NSBI&o9dI<}-vdnMOpcad_`9v^N#Rcn*e2;R8mjBo0CU&=uCG0Ez6YBNzrV^{W(eQaE@MuKOGnbAy^LMg8mr}^*3n38d+zVq^mQ6} zRrw*d$7P26qBVk>ahXC!hw>UrM1ywYzjB%GJBv!5Uc7)x2?JRI`SqOw?U*0}ARxan zlR214fC>DMQ_sW0n+XCDW&$zk0r^$k?LGdnME+wbFqumzKt!P;BC;S^IYqFLoPv;| z2uKM6R!|UuDhLY82`EZ2{ofQz{QrGbhYAY)r#RTtgx`_LQYn1#U?Z@Bldh{XMMet0 z<=m3Q_M_U0j2&cRXFdFaVhbEB806N&U}Iy`SCj!7+7g9ZrFI=<{?kaVEHn`l!_*aG z6JFJYH@~59P9`Cn99Z?yqjkg*&+JsnLZh+HXs;@6&qt%PIR!c~WDq7o{4$$Sc+e&E zNMXf_)?oj^Rdzj2lWzz`pX4pQ`h+ca&#QMHHcM z&3tiIv5?>P_3%CIuv9!`N@B#DO>~g~xSO@O2wosW3U>LT`(anGf)NiSA-{~V7_x+R z9UfvWErLQ2F5k=l8nkn4&M$_p)XU`<4g-S-7R_u@fBY3!|8siB_#^0?Aoy#iG~okD zKxgAzY{GAW_gQYHMUQ4(v|ebd=*V6-5YB06=O#lNAwwwl8MXwbwm;1T5u6lG3e^w# zbg13aYp&0_bE8klo$i?Gn0kOXKsY8kCLYAEQkt|r|Pc!QJaOozWImW2fmpSqL zB|UjG(?hE$6P7CqZMwV`{b1+R?M*ZB$9{RJjCkf-_8CVPj>mt)kTOYtTrR?xRdeL; z0?#Y3$-m@I#~{w&PQsvTE|KSVsr~QUGnOa7QETwXe--n8Hl}ypHgCQC-`d#&!NNkq OLJ%N3yP~!d@P7c30M0D{ diff --git a/report/report.tex b/report/report.tex index e5c2cde..01e2bf7 100644 --- a/report/report.tex +++ b/report/report.tex @@ -45,7 +45,7 @@ \begin{document} \maketitle - \includegraphics[width=12cm]{screenshot.png} + \includegraphics[width=12cm]{screenshot.png} \qquad \includegraphics[width=4cm]{autoplay.png} \tableofcontents diff --git a/src/Autosolver.hs b/src/Autosolver.hs index 642be72..382fc63 100644 --- a/src/Autosolver.hs +++ b/src/Autosolver.hs @@ -4,17 +4,15 @@ import Minesweeper data MoveType = Uncover | Flag | None deriving (Eq, Show) -probabilityOfMine :: Board -> Square -> Float -probabilityOfMine _ _ = 1.0 - -autoplay :: Board -> Board -autoplay b | fst (nextMove b) == Uncover = uncover b $ snd (nextMove b) - | fst (nextMove b) == Flag = flag b $ snd (nextMove b) - | otherwise = b +playAutoMove :: Board -> Board +playAutoMove b | fst (nextMove b) == Uncover = uncover b $ snd (nextMove b) + | fst (nextMove b) == Flag = flag b $ snd (nextMove b) + | otherwise = b nextMove :: Board -> (MoveType, Square) nextMove b | not . null $ uncoverStrat1 b = (Uncover, head $ uncoverStrat1 b) | not . null $ flagStrat1 b = (Flag, head $ flagStrat1 b) + | not . null $ uncoverStratFallback b = (Uncover, head $ uncoverStratFallback b) | otherwise = (None, (0,0)) -- filter: @@ -37,6 +35,14 @@ uncoverStrat1 b = filter (\s -> adjacentMines b s > 0) $ uncoveredSquares b +-- filter: (first covered square) +-- covered squares +-- WHICH are not flagged +uncoverStratFallback :: Board -> [Square] +uncoverStratFallback b = + filter (not. isFlagged b) $ + coveredSquares b + -- filter: -- uncovered squares -- WITH at least one adjacent mine diff --git a/src/Main.hs b/src/Main.hs index 6845502..e593535 100644 --- a/src/Main.hs +++ b/src/Main.hs @@ -30,25 +30,25 @@ setup w = void $ do UI.div #. "container" #+ [ UI.div #. "row" #+ [ UI.h1 #+ [string "Minesweeper"], - UI.br, - UI.p #+ [string "Jack Harley jharley@tcd.ie"] + UI.h4 #+ [string "Jack Harley jharley@tcd.ie"] ], UI.div #. "row" #+ [ UI.div # set UI.id_ "gameCont" #+ [mkElement "table" # set UI.id_ "table" #+ rows iob b 0], UI.div # set UI.id_ "infoCont" #+ [ - UI.p #+ [string "Instructions: Click on a square to uncover it. Right click a square to flag it."], - UI.p #+ [string "Flagged squares will turn yellow. If you hit a mine all mines will instantly be revealed as red squares."], - UI.p #+ [string "You win the game once you have uncovered all squares that do not have mines. If this occurs, the entire board will turn green (except the bomb) to indicate your win!"], + UI.p #+ [string "Instructions: Click on a square to uncover it. Right click a square to flag/unflag it."], + UI.p #+ [string "Flagged squares will turn yellow."], + UI.p #+ [string "If you hit a mine, you lose and all mines will instantly be revealed as red squares."], + UI.p #+ [string "You win the game once you have uncovered all squares that do not have mines. If this occurs, the board will turn green to indicate your win!"], UI.p #+ [string "At any time, you can refresh the page to start a new game."], UI.p #+ [string "Good luck!"] ] ], UI.div #. "row" #+ [ - UI.div #. "card" #+ [ - UI.div #. "card-header" #+ [string "Autoplayer"], - UI.div #. "card-body" #+ [ + UI.div #. "panel panel-primary" # set UI.id_ "autoplay" #+ [ + UI.div #. "panel-heading" #+ [string "Autoplayer"], + UI.div #. "panel-body" #+ [ UI.p #+ [string "Not sure what to do? Click the Autoplay button below to let the computer make a move for you!"], - UI.p #+ [autoPlayButton iob] + autoPlayButton iob ] ] ] @@ -85,7 +85,7 @@ setup w = void $ do button <- UI.button #. "btn btn-primary" #+ [string "Autoplay"] on UI.click button $ \_ -> do - liftIO $ modifyIORef' iob $ \b -> autoplay b + liftIO $ modifyIORef' iob $ \b -> playAutoMove b refresh iob return button @@ -99,7 +99,6 @@ setup w = void $ do cont <- getElementById w "gameCont" let cont' = return $ fromJust cont cont' #+ [mkElement "table" # set UI.id_ "table" #+ rows iob b 0] - when (isJust table) $ delete (fromJust table) -- For some reason, ocassionally threepenny will fail to render the table after a change. diff --git a/src/Minesweeper.hs b/src/Minesweeper.hs index 761a47b..b4d3c68 100644 --- a/src/Minesweeper.hs +++ b/src/Minesweeper.hs @@ -12,7 +12,7 @@ data Board = Board { size :: Int } instance Show Board where - show b = printBoard b + show b = printBoardGrid (mines b) -- -- Functions related to creating and initialising a board @@ -101,7 +101,7 @@ adjacentCovereds b (r,c) = sum $ map (boolToInt . isCovered b) $ adjacentSquares -- returns true if the given square is adjacent to a covered square adjacentToCovered :: Board -> Square -> Bool -adjacentToCovered b (r,c) = not $ all (isUncovered b) $ adjacentSquares (r,c) +adjacentToCovered b (r,c) = adjacentCovereds b (r,c) > 0 -- returns a list of all the squares directly adjacent to the given square (using arithmetic) adjacentSquares :: Square -> [Square] @@ -114,26 +114,26 @@ boolToInt x | x = 1 -- returns true if the game has been won (all remaining covered squares have a mine) gameWon :: Board -> Bool -gameWon b = all (hasMine b) (coveredSquares b) +gameWon b = all (hasMine b) (coveredSquares b) && not (any (hasMine b) (uncoveredSquares b)) -- returns a list of all squares on a board currently still covered coveredSquares :: Board -> [Square] -coveredSquares (Board _ _ u _) = booleanSquares 0 False u +coveredSquares (Board _ _ u _) = matchingSquares 0 False u -- returns a list of all squares on a board currently uncovered uncoveredSquares :: Board -> [Square] -uncoveredSquares (Board _ _ u _) = booleanSquares 0 True u +uncoveredSquares (Board _ _ u _) = matchingSquares 0 True u -- returns a list of all squares in a grid starting at the given row with the given boolean status -booleanSquares :: Int -> Bool -> Grid -> [Square] -booleanSquares _ _ [] = [] -booleanSquares r status (row:rows) = booleanSquares' r 0 status row ++ booleanSquares (r+1) status rows +matchingSquares :: Int -> Bool -> Grid -> [Square] +matchingSquares _ _ [] = [] +matchingSquares r status (row:rows) = matchingSquares' r 0 status row ++ matchingSquares (r+1) status rows -- returns a list of all squares in an individual row of a grid with the given boolean status -booleanSquares' :: Int -> Int -> Bool -> [Bool] -> [Square] -booleanSquares' _ _ _ [] = [] -booleanSquares' r c status (col:cols) | col == status = (r,c) : booleanSquares' r (c+1) status cols - | otherwise = booleanSquares' r (c+1) status cols +matchingSquares' :: Int -> Int -> Bool -> [Bool] -> [Square] +matchingSquares' _ _ _ [] = [] +matchingSquares' r c status (col:cols) | col == status = (r,c) : matchingSquares' r (c+1) status cols + | otherwise = matchingSquares' r (c+1) status cols -- returns a list of all squares on a board allSquares :: Board -> [Square] @@ -156,7 +156,7 @@ squareAscii b (r,c) | gameWon b = "" -- returns a string indicating the bg colour class for a given square for a UI render of the board -- intended to be a used as a CSS class squareBgColour :: Board -> Square -> String -squareBgColour b (r,c) | gameWon b && hasMine b (r,c) = "bg-red" +squareBgColour b (r,c) | gameWon b && hasMine b (r,c) = "bg-blue" | gameWon b = "bg-green" | isUncovered b (r,c) && hasMine b (r,c) = "bg-red" | isUncovered b (r,c) = "bg-light" @@ -185,18 +185,14 @@ squareTextColour b (r,c) | hasMine b (r,c) || isFlagged b (r,c) = "" -- -- uncovers a square and recursively uncovers adjacent squares iff the square has zero adjacent mines --- N.B. not very efficient due to lots of splitting and remerging +-- if the uncovered square has a mine, uncovers the entire board (lost) uncover :: Board -> Square -> Board uncover b (r,c) | not $ validSquare b (r,c) = b | isUncovered b (r,c) = b - | hasMine b (r,c) = let Board s m u f = b - in Board s m (createGrid True s) f - | otherwise = let Board s m u f = b - (rowsA, row : rowsB) = splitAt r u - (cellsA, _ : cellsB) = splitAt c row - newRow = cellsA ++ True : cellsB - newRows = rowsA ++ newRow : rowsB - in uncoverAdjacentsIfSafe (Board s m newRows f) (r,c) + | hasMine b (r,c) = Board (size b) (mines b) (createGrid True (size b)) (flagged b) + | otherwise = uncoverAdjacentsIfSafe + (Board (size b) (mines b) (modSquare (uncovered b) (r,c) True) (flagged b)) + (r,c) uncoverAdjacentsIfSafe :: Board -> Square -> Board uncoverAdjacentsIfSafe b (r,c) | adjacentMines b (r,c) == 0 = uncoverAll b $ adjacentSquares (r,c) @@ -207,25 +203,23 @@ uncoverAll :: Board -> [Square] -> Board uncoverAll b [] = b uncoverAll b ((r,c):xs) = uncoverAll newB xs where newB = uncover b (r,c) --- marks a square as flagged +-- toggles a square's flagged status flag :: Board -> Square -> Board flag b (r,c) | not $ validSquare b (r,c) = b | isUncovered b (r,c) = b - | isFlagged b (r,c) = b - | otherwise = let Board s m u f = b - (rowsA, row : rowsB) = splitAt r f - (cellsA, _ : cellsB) = splitAt c row - newRow = cellsA ++ True : cellsB - newRows = rowsA ++ newRow : rowsB - in Board s m u newRows + | isFlagged b (r,c) = Board (size b) (mines b) (uncovered b) (modSquare (flagged b) (r,c) False) + | otherwise = Board (size b) (mines b) (uncovered b) (modSquare (flagged b) (r,c) True) + +modSquare :: Grid -> Square -> Bool -> Grid +modSquare grid (r,c) newStatus = let (rowsA, row : rowsB) = splitAt r grid + (cellsA, _ : cellsB) = splitAt c row + newRow = cellsA ++ newStatus : cellsB + in rowsA ++ newRow : rowsB -- -- Functions for turning a board into a string for debug purposes -- -printBoard :: Board -> String -printBoard b = printBoardGrid (mines b) - printBoardGrid :: Grid -> String printBoardGrid [] = "" printBoardGrid (l:ls) = printBoardLine l ++ "\n" ++ printBoardGrid ls diff --git a/view/css/minesweeper.css b/view/css/minesweeper.css index 4bf4efe..0158e9f 100644 --- a/view/css/minesweeper.css +++ b/view/css/minesweeper.css @@ -11,6 +11,7 @@ td { font-weight: bold; } +.bg-blue { background-color: blue; } .bg-red { background-color: red; } .bg-yellow { background-color: yellow; } .bg-dark { background-color: darkgray; } @@ -32,7 +33,6 @@ td { #gameCont { float: left; - margin-left: 17px; } #infoCont { @@ -40,4 +40,8 @@ td { margin-left: 20px; font-size: 1.2em; max-width: 350px; +} + +#autoplay { + max-width: 300px; } \ No newline at end of file