sourcegames/minesweeper/Mines.xtl

1⍝# Minesweeper's rules, a library: minesweeper.xtl (scripted), play.xtl 2⍝# (at the terminal) and the web page all use these. A 9 by 9 field 3⍝# hides 10 mines; open a square to see how many mines touch it, flag 4⍝# the ones you think are mines, and open every safe square to win. 5⍝# 6⍝# The state is one vector: the status (0 playing, 1 won, 2 lost), then 7⍝# the mines, the opened squares and the flags, three 9 by 9 0/1 boards. 8 9ᵇ⁼u̲se< "Board" 10 11⍝ :: Int 12⍝# The field's width and height. 13ˡn ← 9 14⍝ :: a -> a 15⍝# Where the mines are, n by n (1 a mine). 16ˡm̲ines ← { s → (ˡn c̲at ˡn) r̲eshape 81 t̲ake 1 d̲rop s } 17⍝ :: a -> a 18⍝# Which squares are open, n by n. 19ˡo̲pen ← { s → (ˡn c̲at ˡn) r̲eshape 81 t̲ake 82 d̲rop s } 20⍝ :: a -> a 21⍝# Which squares are flagged, n by n. 22ˡf̲lags ← { s → (ˡn c̲at ˡn) r̲eshape -81 t̲ake s } 23⍝ :: a -> a 24⍝# 0 playing, 1 won, 2 lost. 25ˡs̲tatus ← { s → f̲irst s } 26⍝ :: a -> a -> a 27⍝# The state again from a status and the three boards. 28ˡs̲tate ← { st m → st c̲at r̲avel m } 29 30⍝ :: (Any a, Num b, Truthy b) => a -> b 31⍝# A new field: 10 mines on the first 10 squares of a random order. 32ˡn̲ew ← { x → 33 m ← (r̲ange 81) m̲ember? 10 t̲ake g̲rade r̲oll! 81 r̲eshape 1000000 34 0 c̲at m c̲at 162 r̲eshape 0 35} 36 37⍝ :: Num a => a -> a 38⍝# The eight neighbors of every square at once, as Life counts them 39⍝# (lib/Board: the board in a border of zeros rotated by every offset and 40⍝# summed, less itself). 41ˡa̲round ← { b → ᵇa̲round b } 42⍝ :: Num a => a -> a 43⍝# How many mines touch each square. 44ˡc̲ounts ← { s → ˡa̲round ˡm̲ines s } 45 46⍝ :: a -> a -> a 47⍝# The state again from a status and the three boards' parts. 48ˡw̲ith ← { s so → (f̲irst so) c̲at (r̲avel ˡm̲ines s) c̲at 1 d̲rop so } 49 50⍝ :: (Num a, Truthy a) => a -> Int -> a 51⍝# Opening square rc (row, column from 1): a mine loses; otherwise the 52⍝# opened region grows by flood fill: every opened square with no mine 53⍝# around it opens its neighbors, again and again until nothing 54⍝# changes. Squares next to a mine have a count, so the fill never 55⍝# reaches a mine. Opened squares lose their flags. 56ˡr̲eveal ← { s rc → 57 k ← ˡn ᵇc̲ell rc − 1 58 1 = k s̲elect r̲avel ˡm̲ines s ? s ˡw̲ith 2 c̲at (r̲avel (ˡo̲pen s) ∨ ˡm̲ines s) c̲at r̲avel ˡf̲lags s 59 start ← (ˡo̲pen s) ∨ (ˡn c̲at ˡn) r̲eshape k = r̲ange 81 60 o ← s ˡg̲row start 61 won ← 0 = '+ r̲/ r̲avel (n̲ot o) ∧ n̲ot ˡm̲ines s 62 s ˡw̲ith won c̲at (r̲avel o) c̲at r̲avel (ˡf̲lags s) ∧ n̲ot o 63} 64⍝ :: (Num a, Truthy a) => a -> a -> a 65⍝# The opened region grown by flood fill: every opened square with no mine around it opens its neighbors, until nothing changes. 66ˡg̲row ← { s o → 67 zero ← o ∧ 0 = ˡc̲ounts s 68 more ← (o ∨ 0 < ˡa̲round zero) ∧ n̲ot ˡm̲ines s 69 more m̲atch o ? o 70 s ˡg̲row more 71} 72⍝ :: (Num a, Truthy a) => a -> Int -> a 73⍝# Flag (or unflag) square rc, if it is not open. 74ˡf̲lag ← { s rc → 75 k ← ˡn ᵇc̲ell rc − 1 76 1 = k s̲elect r̲avel ˡo̲pen s ? s 77 f ← (r̲avel ˡf̲lags s) ≠ k = r̲ange 81 78 s ˡw̲ith (ˡs̲tatus s) c̲at (r̲avel ˡo̲pen s) c̲at f 79} 80 81⍝ :: (Num a, Truthy a) => a -> a 82⍝# The field as text: # unopened, F flagged, . an opened square with no 83⍝# mine around, 1 to 8 its count, * a mine (shown once the game is 84⍝# lost); row and column numbers around it, spaced (lib/Board). Every 85⍝# square's code at once. 86ˡc̲odes ← { s → 87 o ← ˡo̲pen s 88 shown ← o ∧ n̲ot ˡm̲ines s 89 boom ← o ∧ ˡm̲ines s 90 1 + ((n̲ot o) ∧ ˡf̲lags s) + (2 × boom) + shown × 3 + ˡc̲ounts s 91} 92⍝ :: Int -> Char 93⍝# The field as text: # unopened, F flagged, * a mine (when lost), . an opened square with no mine around, else its count. 94ˡv̲iew ← { s → 95 lab ← (ˡn c̲at 2) r̲eshape "1 2 3 4 5 6 7 8 9 " 96 (1 20 r̲eshape " 1 2 3 4 5 6 7 8 9 ") c̲at lab c̲at₂ ᵇs̲paced (ˡc̲odes s) s̲elect "#F*.12345678" 97} 98⍝ :: Int -> Char 99⍝# The field as a picture X_eTaL draws: the same characters in a grid. 100ˡp̲icture ← { s → ⎕G̲RID (ˡc̲odes s) s̲elect "#F*.12345678" }