Card deck — build, deterministic Fisher-Yates shuffle, and deal
A 52-card deck is built as "Rank of Suit" strings. shuffle() uses a seeded linear-congruential PRNG so the permutation is reproducible (no random()). deal() splits the deck into n hands of k cards round-robin. Run: soma run card_deck.cell --signal demo Test: soma test card_deck.cell
A Soma program from the games part of the verified corpus: 85 lines, cells CardDeck, CardDeckTests. It passes soma check with soma 3.2.4.
How it is built
The cell CardDeck declares 4 signals in its face: new_deck() -> List, shuffle(deck: List, seed: Int) -> List, deal(deck: List, hands: Int, cards: Int) -> List and demo() -> Map. Handlers: new_deck (line 20), shuffle (line 33), deal (line 50) and demo (line 62).
The file also holds 1 test cell (CardDeckTests) with 7 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 = truecard_deck.cell
// ============================================================================
// Card deck — build, deterministic Fisher-Yates shuffle, and deal
// ============================================================================
//
// A 52-card deck is built as "Rank of Suit" strings. shuffle() uses a seeded
// linear-congruential PRNG so the permutation is reproducible (no random()).
// deal() splits the deck into n hands of k cards round-robin.
//
// Run: soma run card_deck.cell --signal demo
// Test: soma test card_deck.cell
cell CardDeck {
face {
signal new_deck() -> List
signal shuffle(deck: List, seed: Int) -> List
signal deal(deck: List, hands: Int, cards: Int) -> List
signal demo() -> Map
}
on new_deck() {
let suits = ["Clubs", "Diamonds", "Hearts", "Spades"]
let ranks = ["2","3","4","5","6","7","8","9","10","J","Q","K","A"]
let deck = []
for s in suits {
for r in ranks {
deck = push(deck, "{r} of {s}")
}
}
return deck
}
// Fisher-Yates driven by a seeded LCG → deterministic permutation.
on shuffle(deck: List, seed: Int) {
let d = deck
let state = seed
let i = len(d) - 1
while i > 0 {
// Numerical Recipes LCG, kept in 31 bits.
state = band(state * 1103515245 + 12345, 2147483647)
let j = state % (i + 1)
let tmp = d[i]
d[i] = d[j]
d[j] = tmp
i = i - 1
}
return d
}
// Round-robin deal: hand h gets cards h, h+hands, h+2*hands, ...
on deal(deck: List, hands: Int, cards: Int) {
let out = []
for h in range(0, hands) {
let hand = []
for k in range(0, cards) {
hand = push(hand, deck[h + k * hands])
}
out = push(out, hand)
}
return out
}
on demo() {
let d = new_deck()
let s = shuffle(d, 42)
let hands = deal(s, 4, 5)
return map("size", len(d), "top", s[0], "hands", len(hands))
}
}
cell test CardDeckTests {
rules {
assert len(new_deck()) == 52
// a shuffle is a permutation — same length, all cards retained
assert len(shuffle(new_deck(), 7)) == 52
// determinism: same seed → identical top card
assert shuffle(new_deck(), 99)[0] == shuffle(new_deck(), 99)[0]
// dealing 4 hands of 5 yields 4 hands of 5 cards each
assert len(deal(new_deck(), 4, 5)) == 4
assert len(deal(new_deck(), 4, 5)[0]) == 5
// an unshuffled deck starts with "2 of Clubs"
assert new_deck()[0] == "2 of Clubs"
assert new_deck()[51] == "A of Spades"
}
}What the toolchain says
$ soma check card_deck.cell
✓ All checks passed.$ soma test card_deck.cell
test CardDeckTests ...
✓ assert len(new_deck()) == 52
✓ assert len(shuffle(new_deck(), 7)) == 52
✓ assert shuffle(new_deck(), 99)[0] == shuffle(new_deck(), 99)[0]
✓ assert len(deal(new_deck(), 4, 5)) == 4
✓ assert len(deal(new_deck(), 4, 5)[0]) == 5
✓ assert new_deck()[0] == "2 of Clubs"
✓ assert new_deck()[51] == "A of Spades"
7 tests: 7 passed, 0 failed$ soma verify card_deck.cell
Verifying card_deck.cell...
CardDeck/termination:
⚠ handler `shuffle`: while-loop without provable termination bound (write `while [loop_bound(N)] cond { … }`, or a for-loop over a bounded range)
⚠ `demo` calls `CardDeck.shuffle`, which may not terminate
0 passed, 2 warnings, 0 failures
Temporal: 0 passed, 0 failed
VERIFY OK