Kirjautuminen

Haku

Tehtävät

Kilpailu

Putka Open 2025
Alkaa syyskuussa!

Keskustelu: Yleinen keskustelu: Miinaharva 8th ohjelmointikielellä

jalski [18.08.2025 21:47:49]

#

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 ;

wy5vn [21.08.2025 21:36:01]

#

Itse en ole vielä tähänpäivään mennessä kirjoittanut yhtään koodia piruuttani.

Vastaus

Muista lukea kirjoitusohjeet.
Tietoa sivustosta