soma.cell

Pig — the press-your-luck dice game, scored from a roll log

In Pig a player rolls a d6 repeatedly, banking the running total — but a 1 busts the turn and scores 0. turn_score() folds a list of rolls into the banked points. play() runs the "hold at 20" strategy over a deterministic roll stream and reports the turn outcomes. Run: soma run pig_dice.cell --signal demo Test: soma test pig_dice.cell

A Soma program from the games part of the verified corpus: 66 lines, cells PigDice, PigTests. It passes soma check with soma 3.2.4.

How it is built

The cell PigDice declares 3 signals in its face: turn_score(rolls: List) -> Int, hold_at(rolls: List, target: Int) -> Map and demo() -> Map. Handlers: turn_score (line 21), hold_at (line 27) and demo (line 45).

The file also holds 1 test cell (PigTests) with 7 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

pig_dice.cell

// ============================================================================
// Pig — the press-your-luck dice game, scored from a roll log
// ============================================================================
//
// In Pig a player rolls a d6 repeatedly, banking the running total — but a 1
// busts the turn and scores 0. turn_score() folds a list of rolls into the
// banked points. play() runs the "hold at 20" strategy over a deterministic
// roll stream and reports the turn outcomes.
//
// Run:  soma run pig_dice.cell --signal demo
// Test: soma test pig_dice.cell

cell PigDice {
    face {
        signal turn_score(rolls: List) -> Int
        signal hold_at(rolls: List, target: Int) -> Map
        signal demo() -> Map
    }

    // Sum the rolls; a single 1 anywhere busts the whole turn to 0.
    on turn_score(rolls: List) {
        if any(rolls, r => r == 1) { return 0 }
        return sum(rolls)
    }

    // Stop as soon as the running total reaches `target` (or a 1 busts).
    on hold_at(rolls: List, target: Int) {
        let total = 0
        let busted = false
        let used = 0
        for r in rolls {
            if total < target && busted == false {
                used = used + 1
                if r == 1 {
                    total = 0
                    busted = true
                } else {
                    total = total + r
                }
            }
        }
        return map("score", total, "rolls_used", used, "busted", busted)
    }

    on demo() {
        // rolls 5,4,6,3 → 18, then 5 → 23 ≥ 20, hold
        return hold_at([5, 4, 6, 3, 5, 2], 20)
    }
}

cell test PigTests {
    rules {
        // clean run, no 1
        assert turn_score([3, 4, 5]) == 12
        // a 1 busts the turn to 0
        assert turn_score([6, 6, 1, 6]) == 0
        assert turn_score([]) == 0
        // hold-at-20: 5+4+6+3+5 = 23, stops after 5 rolls
        assert hold_at([5, 4, 6, 3, 5, 2], 20).score == 23
        assert hold_at([5, 4, 6, 3, 5, 2], 20).rolls_used == 5
        // busting before target
        assert hold_at([5, 1, 6, 6], 20).busted == true
        assert hold_at([5, 1, 6, 6], 20).score == 0
    }
}

What the toolchain says

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

$ soma check pig_dice.cell
✓ All checks passed.
$ soma test pig_dice.cell
test PigTests ...
  ✓ assert turn_score([3, 4, 5]) == 12
  ✓ assert turn_score([6, 6, 1, 6]) == 0
  ✓ assert turn_score([]) == 0
  ✓ assert hold_at([5, 4, 6, 3, 5, 2], 20).score == 23
  ✓ assert hold_at([5, 4, 6, 3, 5, 2], 20).rolls_used == 5
  ✓ assert hold_at([5, 1, 6, 6], 20).busted == true
  ✓ assert hold_at([5, 1, 6, 6], 20).score == 0

7 tests: 7 passed, 0 failed
$ soma verify pig_dice.cell
Verifying pig_dice.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

any, map, sum

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