main.bend source
main.bend on the hub · documented module
# main.bend: Minesweeper in a window.# ===================================## Build and run it:## bend main.bend -o minesweeper# ./minesweeper## Left-click opens a square. Right-click flags one. Middle-click, or a# left-click on a number that already has all its flags, opens every# square around it at once. `r` deals a new board.## The status line is the window title, because the playfield fills the# window: a 16x16 board of 32x32 tiles is exactly 512x512 pixels, and# an Image is a quadtree over a square.import Baseimport ./game.bend as GM# The seed comes from /dev/urandom, so each run lays a different board.# If that file is not there, the game falls back to a fixed board.def Main.mix(bs: List<&2, U32>, acc: U32) -> U32: match bs: case Nil{}: acc case Con{b, rest}: Main.mix(rest, (acc * 256 + b : U32))def Main.seed.take(r: Result<&1, &1, U32 & String, List<&2, U32>>) -> U32: match r: case Fail{e}: 20260917 case Done{bs}: Main.mix(bs, 0)def Main.seed.close( pair: File & Result<&1, &1, U32 & String, List<&2, U32>>) -> IO(U32): (fh, r) = pair do IO<U32>: File.close(fh) return Main.seed.take(r)def Main.seed.open(r: Result<&1, &1, U32 & String, File>) -> IO(U32): match r: case Fail{e}: IO.pure(U32, 20260917) case Done{fh}: IO.bind(File & Result<&1, &1, U32 & String, List<&2, U32>>, U32, File.read_bytes(fh, 4), Main.seed.close)def Main.seed() -> IO(U32): IO.bind(Result<&1, &1, U32 & String, File>, U32, File.open("/dev/urandom", "r"), Main.seed.open)# One frame: drop the image the window handed back, fold this frame's# events into the state, refresh the title, and hand the loop on.# `rest` is a closure, not a name, because two functions may not call# each other in Bend.def Main.turn.go( pair: GM.Game & Bool, w: Window, rest: Window -> Bool -> GM.Game -> IO(Unit)) -> IO(Unit): (ng, al) = pair +g2 = ng do IO<Unit>: w2 : Window <- Window.set_title(w, GM.Game.title(g2)) rest(w2, al, g2)def Main.turn( shown: Window & Image & List<Event>, g: GM.Game, rest: Window -> Bool -> GM.Game -> IO(Unit)) -> IO(Unit): (w, img, evs) = shown do IO<Unit>: IO.pure(Unit, Image.drop(img)) Main.turn.go(GM.Game.feed(evs, True{}, g), w, rest)def Main.loop(fuel: Nat, alive: Bool, w: Window, g: GM.Game) -> IO(Unit): match fuel alive: case 0n a: Window.close(w) case Succ{f} False{}: Window.close(w) case Succ{f} True{}: +gg = GM.Game.tock(g) do IO<Unit>: shown : Window & Image & List<Event> <- Window.frame(w, GM.Game.image(gg)) Main.turn(shown, gg, w2 => al => ng => Main.loop(f, al, w2, ng))def main() -> IO(Unit): do IO<Unit>: sd : U32 <- Main.seed() +g : GM.Game = GM.Game.new(4n, 40, sd, 5n) +px : U32 = GM.Game.size(g) w : Window <- IO.try(Window, Window.open(GM.Game.title(g), px, px)) Main.loop(U32.to_nat(4294967295), True{}, w, g)