soma.cell

Minesweeper — compute the neighbour-mine board from a mine layout mines is a flat row-major r×c list of 0/1

annotate() returns a board where each non-mine cell holds its count of adjacent mines (0..8) and each mine cell holds -1. reveal_safe() reports whether a click is safe. Run: soma run minesweeper.cell --signal demo Test: soma test minesweeper.cell

A Soma program from the games part of the verified corpus: 77 lines, cells Minesweeper, MinesweeperTests. It passes soma check with soma 3.2.4.

How it is built

The cell Minesweeper declares 4 signals in its face: count_adjacent(mines: List, r: Int, c: Int, y: Int, x: Int) -> Int, annotate(mines: List, r: Int, c: Int) -> List, is_safe(mines: List, c: Int, y: Int, x: Int) -> Bool and demo() -> List. Handlers: count_adjacent (line 20), annotate (line 36), is_safe (line 50) and demo (line 54).

The file also holds 1 test cell (MinesweeperTests) with 9 assertions, run by soma test.

Generated from soma describe on this file.

The source

soma.toml

[package]
name = "games-corpus"
version = "0.1.0"
description = "Training corpus: games & simulations"

# The turn/phase machine is intentionally cyclic (a game loops through
# phases each round) with a `* -> over` escape, so we verify only that no
# reachable state deadlocks — liveness to a terminal is not the contract here.
[verify]
deadlock_free = true

minesweeper.cell

// ============================================================================
// Minesweeper — compute the neighbour-mine board from a mine layout
// ============================================================================
//
// mines is a flat row-major r×c list of 0/1. annotate() returns a board where
// each non-mine cell holds its count of adjacent mines (0..8) and each mine
// cell holds -1. reveal_safe() reports whether a click is safe.
//
// Run:  soma run minesweeper.cell --signal demo
// Test: soma test minesweeper.cell

cell Minesweeper {
    face {
        signal count_adjacent(mines: List, r: Int, c: Int, y: Int, x: Int) -> Int
        signal annotate(mines: List, r: Int, c: Int) -> List
        signal is_safe(mines: List, c: Int, y: Int, x: Int) -> Bool
        signal demo() -> List
    }

    on count_adjacent(mines: List, r: Int, c: Int, y: Int, x: Int) {
        let n = 0
        for dy in range(-1, 2) {
            for dx in range(-1, 2) {
                if dy != 0 || dx != 0 {
                    let ny = y + dy
                    let nx = x + dx
                    if ny >= 0 && ny < r && nx >= 0 && nx < c {
                        n = n + mines[ny * c + nx]
                    }
                }
            }
        }
        return n
    }

    on annotate(mines: List, r: Int, c: Int) {
        let out = []
        for y in range(0, r) {
            for x in range(0, c) {
                let v = -1
                if mines[y * c + x] == 0 {
                    v = count_adjacent(mines, r, c, y, x)
                }
                out = push(out, v)
            }
        }
        return out
    }

    on is_safe(mines: List, c: Int, y: Int, x: Int) {
        return mines[y * c + x] == 0
    }

    on demo() {
        // 3x3 with a single mine in the centre
        return annotate([0,0,0, 0,1,0, 0,0,0], 3, 3)
    }
}

cell test MinesweeperTests {
    rules {
        // single centre mine → every border cell counts 1, centre is -1
        assert annotate([0,0,0, 0,1,0, 0,0,0], 3, 3)[0] == 1
        assert annotate([0,0,0, 0,1,0, 0,0,0], 3, 3)[4] == -1
        assert annotate([0,0,0, 0,1,0, 0,0,0], 3, 3)[1] == 1
        // a corner mine touches exactly 3 cells
        assert count_adjacent([1,0,0, 0,0,0, 0,0,0], 3, 3, 1, 1) == 1
        assert annotate([1,0,0, 0,0,0, 0,0,0], 3, 3)[4] == 1
        assert annotate([1,0,0, 0,0,0, 0,0,0], 3, 3)[8] == 0
        // two adjacent mines
        assert count_adjacent([1,1,0, 0,0,0, 0,0,0], 3, 3, 1, 0) == 2
        // safety check
        assert is_safe([0,0,0, 0,1,0, 0,0,0], 3, 1, 1) == false
        assert is_safe([0,0,0, 0,1,0, 0,0,0], 3, 0, 0) == true
    }
}

What the toolchain says

Run on a fresh copy of the program at build time; the outputs are pasted unedited.

$ soma check minesweeper.cell
✓ All checks passed.
$ soma test minesweeper.cell
test MinesweeperTests ...
  ✓ assert annotate([0,0,0, 0,1,0, 0,0,0], 3, 3)[0] == 1
  ✓ assert annotate([0,0,0, 0,1,0, 0,0,0], 3, 3)[4] == -1
  ✓ assert annotate([0,0,0, 0,1,0, 0,0,0], 3, 3)[1] == 1
  ✓ assert count_adjacent([1,0,0, 0,0,0, 0,0,0], 3, 3, 1, 1) == 1
  ✓ assert annotate([1,0,0, 0,0,0, 0,0,0], 3, 3)[4] == 1
  ✓ assert annotate([1,0,0, 0,0,0, 0,0,0], 3, 3)[8] == 0
  ✓ assert count_adjacent([1,1,0, 0,0,0, 0,0,0], 3, 3, 1, 0) == 2
  ✓ assert is_safe([0,0,0, 0,1,0, 0,0,0], 3, 1, 1) == false
  ✓ assert is_safe([0,0,0, 0,1,0, 0,0,0], 3, 0, 0) == true

9 tests: 9 passed, 0 failed
$ soma verify minesweeper.cell
Verifying minesweeper.cell...
No state machine in this program: nothing to prove beyond `soma check` (invariants and lifecycles are what verify proves).
VERIFY OK (vacuous — no state machine)

Builtins used

push, range

Source file: /corpus/games/minesweeper.cell · examples/corpus/games/minesweeper.cell