Yahtzee — score a roll of five dice in any category counts() buckets the five dice into face frequencies
The category scorers read those buckets: upper boxes, n-of-a-kind, full house, straights, and the 50-point Yahtzee. All pure integer logic. Run: soma run yahtzee.cell --signal demo Test: soma test yahtzee.cell
A Soma program from the games part of the verified corpus: 99 lines, cells Yahtzee, YahtzeeTests. It passes soma check with soma 3.2.4.
How it is built
The cell Yahtzee declares 7 signals in its face: counts(dice: List) -> List, n_of_a_kind(dice: List, n: Int) -> Int, full_house(dice: List) -> Int, small_straight(dice: List) -> Int, yahtzee(dice: List) -> Int, chance(dice: List) -> Int and demo() -> Map. Handlers: counts (line 24), n_of_a_kind (line 33), full_house (line 39), small_straight (line 48), yahtzee (line 59), chance (line 65) and demo (line 69).
The file also holds 1 test cell (YahtzeeTests) with 10 assertions, run by soma test.
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 = trueyahtzee.cell
// ============================================================================
// Yahtzee — score a roll of five dice in any category
// ============================================================================
//
// counts() buckets the five dice into face frequencies. The category scorers
// read those buckets: upper boxes, n-of-a-kind, full house, straights, and
// the 50-point Yahtzee. All pure integer logic.
//
// Run: soma run yahtzee.cell --signal demo
// Test: soma test yahtzee.cell
cell Yahtzee {
face {
signal counts(dice: List) -> List
signal n_of_a_kind(dice: List, n: Int) -> Int
signal full_house(dice: List) -> Int
signal small_straight(dice: List) -> Int
signal yahtzee(dice: List) -> Int
signal chance(dice: List) -> Int
signal demo() -> Map
}
// Frequency of each face 1..6, indexed 0..5.
on counts(dice: List) {
let c = [0, 0, 0, 0, 0, 0]
for d in dice {
c[d - 1] = c[d - 1] + 1
}
return c
}
// Sum of all dice if some face appears >= n times, else 0.
on n_of_a_kind(dice: List, n: Int) {
let c = counts(dice)
if any(c, x => x >= n) { return sum(dice) }
return 0
}
on full_house(dice: List) {
let c = counts(dice)
let has3 = any(c, x => x == 3)
let has2 = any(c, x => x == 2)
if has3 && has2 { return 25 }
return 0
}
// Four consecutive faces present → 30 points.
on small_straight(dice: List) {
let c = counts(dice)
let runs = [
c[0] >= 1 && c[1] >= 1 && c[2] >= 1 && c[3] >= 1,
c[1] >= 1 && c[2] >= 1 && c[3] >= 1 && c[4] >= 1,
c[2] >= 1 && c[3] >= 1 && c[4] >= 1 && c[5] >= 1
]
if any(runs, r => r) { return 30 }
return 0
}
on yahtzee(dice: List) {
let c = counts(dice)
if any(c, x => x == 5) { return 50 }
return 0
}
on chance(dice: List) {
return sum(dice)
}
on demo() {
let d = [3, 3, 3, 5, 5]
return map(
"full_house", full_house(d),
"three_kind", n_of_a_kind(d, 3),
"chance", chance(d)
)
}
}
cell test YahtzeeTests {
rules {
// five of a kind
assert yahtzee([4, 4, 4, 4, 4]) == 50
assert yahtzee([4, 4, 4, 4, 5]) == 0
// full house 3+2
assert full_house([3, 3, 3, 5, 5]) == 25
assert full_house([3, 3, 3, 3, 5]) == 0
// three of a kind scores the whole roll's sum
assert n_of_a_kind([2, 2, 2, 6, 1], 3) == 13
assert n_of_a_kind([2, 2, 3, 6, 1], 3) == 0
// four of a kind
assert n_of_a_kind([5, 5, 5, 5, 1], 4) == 21
// small straight 2-3-4-5
assert small_straight([2, 3, 4, 5, 5]) == 30
assert small_straight([1, 1, 4, 5, 6]) == 0
// chance is just the total
assert chance([1, 2, 3, 4, 5]) == 15
}
}What the toolchain says
$ soma check yahtzee.cell
✓ All checks passed.$ soma test yahtzee.cell
test YahtzeeTests ...
✓ assert yahtzee([4, 4, 4, 4, 4]) == 50
✓ assert yahtzee([4, 4, 4, 4, 5]) == 0
✓ assert full_house([3, 3, 3, 5, 5]) == 25
✓ assert full_house([3, 3, 3, 3, 5]) == 0
✓ assert n_of_a_kind([2, 2, 2, 6, 1], 3) == 13
✓ assert n_of_a_kind([2, 2, 3, 6, 1], 3) == 0
✓ assert n_of_a_kind([5, 5, 5, 5, 1], 4) == 21
✓ assert small_straight([2, 3, 4, 5, 5]) == 30
✓ assert small_straight([1, 1, 4, 5, 6]) == 0
✓ assert chance([1, 2, 3, 4, 5]) == 15
10 tests: 10 passed, 0 failed$ soma verify yahtzee.cell
Verifying yahtzee.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)