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.
wy5vn kirjoitti:
Itse en ole vielä tähänpäivään mennessä kirjoittanut yhtään koodia piruuttani.
Luulen että et vaan tiedä mitä tuo tarkoittaa. Eli se tarkoittaa käytännössä samaa kuin "huvikseni", "harrastuksena" tai "kokeeksi" eli ilman että siitä on saanut suoranaista hyötyä esimerkiksi palkan tai opintopisteiden muodossa.
Ok. Ajattelinkin että onpas raamatullinen motiivi koodin kirjoittamiselle.
Aihe on jo aika vanha, joten et voi enää vastata siihen.