Kirjoittelin piruuttani Miinaharavapelin uusiksi käyttämään 8th:n graafi tukea. Ohjelma muodostaa pelilaudasta edge-listan, laskee pelilaudan solmun (ruudun) naapurisolmujen miinojen summat jokaiselle pelilaudan tyhjälle solmulle ja tallentaa taulukkoon. Filtteröi edge-listasta pois ne, joissa joko kohde tai lähde solmussa on miina. Tämän jälkeen filtteröidään vielä lisää ja jätetään jäljelle pelkästään ne, joissa vähintään joko kohde tai lähde solmun naapurien miinojen summa on nolla. Nyt graafin avulla voidaan aina automaattisesti paljastaa valitun nollaruudun ympärillä olevat nollaruudut ja hakea samalla taulukosta ruuduille ympärillä olevien naapuriruutujen miinojen lukumäärät.
\ \ Minesweeper game using graph and tile. \ needs nk/gui needs nk/buttons needs games/tile \ Free version doesn't have sound support: 8thsku? constant sound? sound? #if requires snd "snd/explosion.ogg" app:asset snd:new constant explosion-snd "snd/zero.ogg" app:asset snd:new constant zero-snd #then libbin font/Roboto-Regular.ttf 42 constant FONT-HEIGHT FONT-HEIGHT 1.75 n:* constant ROW-HEIGHT { font: @font/Roboto-Regular.ttf, size: @FONT-HEIGHT } font:new "font1" font:atlas! drop 670 constant WIDTH 656 constant HEIGHT 10 constant NUM-TILES NUM-TILES n:sqr constant TOTAL-TILES "gfx/tile.png" app:asset img:new constant tile-img "gfx/flag.png" app:asset img:new constant flag-img "gfx/mine.png" app:asset img:new constant mine-img "gfx/explosion.png" app:asset img:new constant explosion-img [ "gfx/n0.png", "gfx/n1.png", "gfx/n2.png", "gfx/n3.png", "gfx/n4.png", "gfx/n5.png", "gfx/n6.png", "gfx/n7.png", "gfx/n8.png" ] ( app:asset img:new ) a:map constant num-images \ tile states 1 constant UNSELECTED 2 constant SELECTED 16 constant MARKED 32 constant MINE \ max number of mines 15 constant EASY 20 constant NORMAL 25 constant HARD [@EASY, @NORMAL, @HARD] constant level NORMAL var, difficulty nullvar board : random-tile \ -- n rand-pcg TOTAL-TILES n:mod ; : place-mine \ gr -- gr gr:nodes random-tile a:_@ "state" tile:m@ dup MINE n:band !if MINE n:bor "state" swap tile:m! drop "mines" gr:m@ 0 ?: n:1+ "mines" swap gr:m! else 2drop then ; : marked? \ tile -- tile T "state" tile:m@ MARKED n:band ; : mark \ tile -- tile "state" tile:m@ MARKED n:bxor "state" swap tile:m! ; : selected? \ tile -- tile T "state" tile:m@ SELECTED n:band ; : select \ tile -- tile "state" tile:m@ SELECTED n:bor "state" swap tile:m! ; : mine? \ tile -- tile T "state" tile:m@ MINE n:band ; : mines? \ tile -- tile n "index" tile:m@ board @ "mc" gr:m@ nip swap a:_@ ; : show-mines board @ gr:nodes nip ( mine? if select mine-img tile:img! tile:draw then ) a:map drop ; : index> \ n1 n2 -- [row,col] n:/mod swap 2 a:close ; : build-graph \ a cols -- gr >r a:new 2 a:close ["nodes", "edges"] swap m:zip gr:new \ connect and filter edges gr:connect gr:edges ( [ 0,1] a:_@ ( r@ index> ) a:map a:open ( n:- n:abs 2 n:< ) a:2map a:open and ) a:filter rdrop gr:edges! ; : score1+ board @ "score" gr:m@ 0 ?: n:1+ "score" swap gr:m! drop ; : neighbors? \ tile -- tile a "index" tile:m@ board @ gr:nodes 3rev gr:neighbors nip a:_@ ; : finished? board @ "finished" gr:m@ nip ; : filter-mines \ a -- a ( [0,1] a:_@ over gr:nodes nip swap a:_@ ( mine? nip n:>bool ) a:map a:open or not ) a:filter ; : filter-cascade \ a -- a ( [0,1] a:_@ over "mc" gr:m@ nip swap a:_@ ' n:>bool a:map a:open and not ) a:filter ; : filter-on-init \ a -- a filter-mines filter-cascade ; : traverse \ n -- board @ false rot ( >r marked? !if over "mc" gr:m@ nip r@ a:_@ num-images swap a:_@ tile:img! tile:draw select score1+ then drop rdrop ) gr:traverse drop ; : build-mine-count-table \ gr -- gr a:new swap false 0 ( nip dup>r gr:neighbors over gr:nodes nip swap a:_@ ( mine? nip n:>bool >n ) a:map ' n:+ 0 a:reduce 2 pick r> rot a:! drop ) gr:traverse "mc" rot gr:m! ; : highlight \ tile -- tile tile:rect@ 0 [255,255,255,128] nk:fill-rect ; : render-cell \ tile -- tile:draw finished? if drop ;; then selected? not swap tile:rect@ nk:hovered? rot and if highlight nk:flags@ nk:WINDOW_ROM n:band !if tile:rect@ >r marked? not nk:BUTTON_LEFT r@ true nk:clicked? and if mine? if show-mines explosion-img tile:img! tile:draw sound? if explosion-snd snd:play then board @ "finished" true gr:m! drop else mines? if mines? num-images swap a:_@ tile:img! tile:draw select score1+ else "index" tile:m@ traverse sound? if zero-snd snd:play then then then else nk:BUTTON_RIGHT r@ true nk:clicked? if marked? if tile-img tile:img! else flag-img tile:img! then mark tile:draw highlight then then rdrop then then drop ; : tile:unselected \ [row,col] -- tile tile-img true tile:new "state" UNSELECTED tile:m! ' render-cell tile:render! ; : draw-board board @ gr:nodes nip ' tile:render a:each! drop ; : init-board ( dup>r NUM-TILES index> tile:unselected "index" r> tile:m! ) 0 TOTAL-TILES n:1- a:generate NUM-TILES build-graph "finished" false gr:m! "score" 0 gr:m! ' place-mine difficulty @ times build-mine-count-table gr:edges filter-on-init gr:edges! board ! ; : toolbar nk:widget if { rows: 1, cols: 3, cgap: 8, margin: 8 } nk:layout-grid-begin 0 1 0 1 nk:grid nk:rect>local nk:grid-push "Restart" ' init-board nk:button-label 0 1 2 1 nk:grid nk:rect>local nk:grid-push "difficulty" nk:get "difficulty-sel" nk:get nk:get-row-height nk:widget-bounds 2 rect:@ nip ROW-HEIGHT 3 n:* 2 a:close nk:combo "difficulty-sel" over nk:set level lookup dup difficulty @ n:= !if difficulty ! init-board else drop then nk:layout-grid-end else drop then ; : info-panel nk:widget if { rows: 1, cols: 2, margin: 32 } nk:layout-grid-begin 0 1 0 1 nk:grid nk:rect>local nk:grid-push board @ "score" gr:m@ TOTAL-TILES rot "mines" gr:m@ nip n:- n:/ 100 n:* n:int "CLEARED: %3d %%" s:strfmt nk:TEXT_LEFT "white" nk:label-colored 0 1 1 1 nk:grid nk:rect>local nk:grid-push board @ "score" gr:m@ nip "SCORE: %3d" s:strfmt nk:TEXT_RIGHT "white" nk:label-colored nk:layout-grid-end else drop then ; : sweeper-board nk:widget if { rows: @NUM-TILES, cols: @NUM-TILES, margin: 16 } nk:layout-grid-begin draw-board 0 NUM-TILES 0 NUM-TILES nk:grid -8 rect:shrink 2 1 "black" nk:stroke-rect finished? !if TOTAL-TILES board @ "score" gr:m@ swap "mines" gr:m@ nip n:+ n:- !if board @ "finished" true gr:m! drop show-mines then then nk:layout-grid-end else drop then ; : new-win { name: "main", wide: @WIDTH, high: @HEIGHT, resizable: false, title: "Minesweeper" } nk:win ; : main-render { bg: "darkgray", padding: [0,0], flags: [ @nk:WINDOW_NO_SCROLLBAR ], \ local window variables difficulty: ["Easy", "Normal", "Hard"], difficulty-sel: 1 } nk:begin null { rows: [@ROW-HEIGHT, @ROW-HEIGHT, -1], cols: 1 } nk:layout-grid-begin 0 1 0 1 nk:grid nk:rect>local nk:grid-push toolbar 2 1 0 1 nk:grid nk:rect>local nk:grid-push sweeper-board 1 1 0 1 nk:grid nk:rect>local nk:grid-push info-panel 1 1 0 1 nk:grid 4 2 "lightgray" nk:stroke-rect nk:layout-grid-end nk:end ; : app:main ' init-board w:is nk:rendering new-win ' main-render -1 nk:render-loop ;
Itse en ole vielä tähänpäivään mennessä kirjoittanut yhtään koodia piruuttani.