librarygames/minesweeper/Mines.xtl
Minesweeper's rules, a library: minesweeper.xtl (scripted), play.xtl (at the terminal) and the web page all use these. A 9 by 9 field hides 10 mines; open a square to see how many mines touch it, flag the ones you think are mines, and open every safe square to win.
The state is one vector: the status (0 playing, 1 won, 2 lost), then the mines, the opened squares and the flags, three 9 by 9 0/1 boards.
ˡm̲ines : a -> a
Where the mines are, n by n (1 a mine).
ˡm̲ines ← { s → (ˡn c̲at ˡn) r̲eshape 81 t̲ake 1 d̲rop s }
ˡo̲pen : a -> a
Which squares are open, n by n.
ˡo̲pen ← { s → (ˡn c̲at ˡn) r̲eshape 81 t̲ake 82 d̲rop s }
ˡf̲lags : a -> a
Which squares are flagged, n by n.
ˡf̲lags ← { s → (ˡn c̲at ˡn) r̲eshape -81 t̲ake s }
ˡs̲tate : a -> a -> a
The state again from a status and the three boards.
ˡs̲tate ← { st m → st c̲at r̲avel m }
ˡn̲ew : (Any a, Num b, Truthy b) => a -> b
A new field: 10 mines on the first 10 squares of a random order.
ˡn̲ew ← { x → m ← (r̲ange 81) m̲ember? 10 t̲ake g̲rade r̲oll! 81 r̲eshape 1000000 0 c̲at m c̲at 162 r̲eshape 0 }
ˡa̲round : Num a => a -> a
The eight neighbors of every square at once, as Life counts them (lib/Board: the board in a border of zeros rotated by every offset and summed, less itself).
ˡa̲round ← { b → ᵇa̲round b }
ˡw̲ith : a -> a -> a
The state again from a status and the three boards' parts.
ˡw̲ith ← { s so → (f̲irst so) c̲at (r̲avel ˡm̲ines s) c̲at 1 d̲rop so }
ˡr̲eveal : (Num a, Truthy a) => a -> Int -> a
Opening square rc (row, column from 1): a mine loses; otherwise the opened region grows by flood fill: every opened square with no mine around it opens its neighbors, again and again until nothing changes. Squares next to a mine have a count, so the fill never reaches a mine. Opened squares lose their flags.
ˡr̲eveal ← { s rc → k ← ˡn ᵇc̲ell rc − 1 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 start ← (ˡo̲pen s) ∨ (ˡn c̲at ˡn) r̲eshape k = r̲ange 81 o ← s ˡg̲row start won ← 0 = '+ r̲/ r̲avel (n̲ot o) ∧ n̲ot ˡm̲ines s s ˡw̲ith won c̲at (r̲avel o) c̲at r̲avel (ˡf̲lags s) ∧ n̲ot o }
ˡg̲row : (Num a, Truthy a) => a -> a -> a
The opened region grown by flood fill: every opened square with no mine around it opens its neighbors, until nothing changes.
ˡg̲row ← { s o → zero ← o ∧ 0 = ˡc̲ounts s more ← (o ∨ 0 < ˡa̲round zero) ∧ n̲ot ˡm̲ines s more m̲atch o ? o s ˡg̲row more }
ˡf̲lag : (Num a, Truthy a) => a -> Int -> a
Flag (or unflag) square rc, if it is not open.
ˡf̲lag ← { s rc → k ← ˡn ᵇc̲ell rc − 1 1 = k s̲elect r̲avel ˡo̲pen s ? s f ← (r̲avel ˡf̲lags s) ≠ k = r̲ange 81 s ˡw̲ith (ˡs̲tatus s) c̲at (r̲avel ˡo̲pen s) c̲at f }
ˡc̲odes : (Num a, Truthy a) => a -> a
The field as text: # unopened, F flagged, . an opened square with no mine around, 1 to 8 its count, * a mine (shown once the game is lost); row and column numbers around it, spaced (lib/Board). Every square's code at once.
ˡc̲odes ← { s → o ← ˡo̲pen s shown ← o ∧ n̲ot ˡm̲ines s boom ← o ∧ ˡm̲ines s 1 + ((n̲ot o) ∧ ˡf̲lags s) + (2 × boom) + shown × 3 + ˡc̲ounts s }
ˡv̲iew : Int -> Char
The field as text: # unopened, F flagged, * a mine (when lost), . an opened square with no mine around, else its count.
ˡv̲iew ← { s → lab ← (ˡn c̲at 2) r̲eshape "1 2 3 4 5 6 7 8 9 " (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" }