import text.jrt import keys.jrt import random.jrt : !+ ( v p -- ) dup @ = swap boardh @ < and swap dup 0 >= swap boardw @ < and and ; 0x80 const FMINE 0x40 const FREVEALED 0x20 const FFLAG 0x0F const NEIGHBOUR-MASK : place-mine ( x y -- f ) square-at dup b@ FMINE & not if FMINE swap b! 0 then not ; : rand-mine ( -- ) begin rand boardw @ % rand boardh @ % place-mine until ; : reset-board board begin dup board-lim < while 0 over b! 1+ repeat drop ; : populate-board minecount @ begin rand-mine 1- dup not until drop ; : mine? ( p -- f ) b@ FMINE & ; : flag? ( p -- f ) b@ FFLAG & ; : revealed? ( p -- f ) b@ FREVEALED & ; : squarecount ( p -- c ) b@ NEIGHBOUR-MASK & ; : do-at ( x y cp -- ) >rot 2dup valid-pos? if square-at swap execute else drop drop drop then ; : do-neighbour-squares ( x y cp -- ) >r over 1- over 1- r@ do-at over 1- over r@ do-at over 1- over 1+ r@ do-at over over 1- r@ do-at over over 1+ r@ do-at over 1+ over 1- r@ do-at over 1+ over r@ do-at swap 1+ swap 1+ t >t >t >t >t >t DOES} theme ! ; ( cursor grid block mine flag bg ) blue white black magenta red lgray deftheme win31 white yellow black lmagenta yellow red deftheme hotdog win31 ( minesweeper board drawing ) : draw-neighbour-count ( b -- ) NEIGHBOUR-MASK & dup col-count dup if [ key 0 lit ] + else drop [ key lit ] then draw-char ; : draw-flag ( -- ) col-flag 0x0d draw-char ; : draw-mine ( -- ) col-mine 0xec draw-char ; : draw-block ( -- ) col-block 0xb1 draw-char ; : draw-revealed ( b -- ) dup FMINE & if draw-mine drop else draw-neighbour-count then ; : draw-hidden ( b -- ) FFLAG & if draw-flag else draw-block then ; : draw-square ( p -- ) b@ dup FREVEALED & if draw-revealed else draw-hidden then col-grid ; : boardx ( -- x ) pagew boardw @ 2* 1+ - 2/ ; : boardy ( -- y ) pageh boardh @ 2* 1+ - 2/ ; : boardx! ( -- ) boardx textx! ; : boardy! ( -- ) boardy texty! ; : next-row ( -- ) nextline boardx! ; : draw-border ( end mid -- ) boardw @ begin .- 1- dup while over execute repeat drop drop execute next-row ; : draw-board-top tl ' tr ' dT draw-border ; : draw-board-bottom bl ' br ' uT draw-border ; : draw-rowborder rT ' lT ' .+ draw-border ; : draw-row ( p -- p ) .| begin dup draw-square .| 1+ dup square-pos drop not until next-row ; : draw-board ( -- ) col-grid col-bg boardx! boardy! draw-board-top board begin draw-row dup square-pos valid-pos? while draw-rowborder repeat drop draw-board-bottom ; ( general-purpose drawing ) : clear [ key lit ] fill-page ; : emptych? ( ch -- f ) dup 32 = swap 0 = or ; : drawdot? ( st -- f ) dup b@ emptych? swap 1+ b@ emptych? or not ; : dot 7 draw-char ; : sp 32 draw-char ; : spacer ( st -- ) sp drawdot? if dot else sp then sp ; : draw-spaced-text ( st -- ) begin dup b@ dup while draw-char dup spacer 1+ repeat drop drop ; ( menu subsystem ) 2 cells const menu-optsize var current-menu : menu-options current-menu @ cell + 1+ ; : menu-size current-menu @ cell + b@ ; : init-menu current-menu @ @ execute ; : menu-option ( iopt -- p ) menu-optsize * menu-options + ; 0 var, menu-selected : menu-activate menu-selected @ menu-option cell + @ execute ; : menu-selected! ( i -- ) dup 0 >= over menu-size < and if menu-selected ! else drop then ; : menu-selected+! ( di -- ) menu-selected @ + menu-selected! ; : draw-selection ( iopt -- ) menu-selected @ = if draw-mine else sp then sp ; : draw-option ( iopt -- ) white fg! menu-option @ draw-text ; : draw-menu 33 10 textxy! textx 0 begin dup menu-size < while dup draw-selection dup draw-option 1+ nextline over textx! repeat drop drop ; : await-menu wait-key key>scan dup %esc = if leave then dup %up = if -1 menu-selected+! then dup %down = if 1 menu-selected+! then %enter = if menu-activate init-menu then ; : run-menu enter init-menu begin draw-menu await-menu in-progress? not until enter ; { :timm defmenu ( itemcount cpinit -- ) CREATE target current-menu !t w>t dup >t menu-optsize * ALLOT DOES} menu-selected @ >r current-menu @ >r current-menu ! 0 menu-selected! run-menu rot !t cell + !t ; } ( minesweeper game UI ) 0 bvar, cursx 0 bvar, cursy : draw-cursor ( -- ) 1 boxstyle! col-curs cursx b@ 2* boardx + textx! cursy b@ 2* boardy + texty! textx tl .- tr nextline textx! textx .| dup 2 + textx! .| nextline textx! bl .- br 0 boxstyle! ; : move-cursor ( dx dy -- ) cursy b@ + swap cursx b@ + swap 2dup valid-pos? if cursy b! cursx b! else drop drop then ; : curs@ cursx b@ cursy b@ ; 0x21 const %f : await-command wait-key key>scan dup %esc = if leave then dup %left = if -1 0 move-cursor then dup %right = if 1 0 move-cursor then dup %up = if 0 -1 move-cursor then dup %down = if 0 1 move-cursor then dup %f = if curs@ flag-at then dup %enter = swap %space = or if curs@ reveal-at then ; : result-message 25 8 textxy! 0 hstyle! 1 vstyle! 30 5 filled draw-box 27 10 textxy! draw-spaced-text wait-key drop ; : display-result game-state b@ dup WON = if cyan bg! yellow fg! s" YOU WON" result-message then LOST = if brown bg! lred fg! s" YOU LOST" result-message then ; : play enter col-bg clear begin in-progress? while draw-board draw-cursor await-command repeat draw-board display-result enter ; : start 20 12 30 init-board play ; ( title menu ) array title-text t", SWINE MEEPER" 3 :noname col-bg clear ; defmenu theme-menu ' win31 s" Classic 3.1" 0 defitem ' hotdog s" Hot Dog Stand" 1 defitem ' leave s" Done" 2 defitem : draw-title blue bg! yellow fg! clear 17 3 textxy! title-text draw-spaced-text ; 3 ' draw-title defmenu title-menu ' start s" Start Game" 0 defitem ' theme-menu s" Themes" 1 defitem ' leave s" Quit" 2 defitem : title textmode reseed! hidecursor title-menu textmode ; ' title ' main redefine { s" swine.com" writecom }