Number guessing game — hi/lo feedback and a binary-search solver feedback() compares a guess to the secret and returns "low" / "high" / "correct"
auto_solve() proves the search space is exhausted in at most ceil(log2(range)) guesses by playing the optimal binary-search strategy. Run: soma run guessing_game.cell --signal demo Test: soma test guessing_game.cell
A Soma program from the games part of the verified corpus: 67 lines, cells GuessingGame, GuessingTests. It passes soma check with soma 3.2.4.
How it is built
The cell GuessingGame declares 3 signals in its face: feedback(secret: Int, guess: Int) -> String, auto_solve(secret: Int, lo: Int, hi: Int) -> Map and demo() -> Map. Handlers: feedback (line 19), auto_solve (line 28) and demo (line 49).
The file also holds 1 test cell (GuessingTests) 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 = trueguessing_game.cell
// ============================================================================
// Number guessing game — hi/lo feedback and a binary-search solver
// ============================================================================
//
// feedback() compares a guess to the secret and returns "low" / "high" /
// "correct". auto_solve() proves the search space is exhausted in at most
// ceil(log2(range)) guesses by playing the optimal binary-search strategy.
//
// Run: soma run guessing_game.cell --signal demo
// Test: soma test guessing_game.cell
cell GuessingGame {
face {
signal feedback(secret: Int, guess: Int) -> String
signal auto_solve(secret: Int, lo: Int, hi: Int) -> Map
signal demo() -> Map
}
on feedback(secret: Int, guess: Int) {
return match true {
_ if guess < secret -> "low"
_ if guess > secret -> "high"
_ -> "correct"
}
}
// Binary search the [lo, hi] interval; count guesses until "correct".
on auto_solve(secret: Int, lo: Int, hi: Int) {
let l = lo
let h = hi
let guesses = 0
let found = -1
while l <= h {
let mid = floor((l + h) / 2)
guesses = guesses + 1
let f = feedback(secret, mid)
if f == "correct" {
found = mid
l = h + 1
} else if f == "low" {
l = mid + 1
} else {
h = mid - 1
}
}
return map("found", found, "guesses", guesses)
}
on demo() {
return auto_solve(73, 1, 100)
}
}
cell test GuessingTests {
rules {
assert feedback(50, 30) == "low"
assert feedback(50, 80) == "high"
assert feedback(50, 50) == "correct"
// binary search always finds the secret
assert auto_solve(73, 1, 100).found == 73
// 1..100 is solved in at most 7 guesses (ceil(log2(100)) = 7)
assert auto_solve(1, 1, 100).guesses <= 7
assert auto_solve(100, 1, 100).guesses <= 7
assert auto_solve(64, 1, 100).found == 64
}
}What the toolchain says
$ soma check guessing_game.cell
✓ All checks passed.$ soma test guessing_game.cell
test GuessingTests ...
✓ assert feedback(50, 30) == "low"
✓ assert feedback(50, 80) == "high"
✓ assert feedback(50, 50) == "correct"
✓ assert auto_solve(73, 1, 100).found == 73
✓ assert auto_solve(1, 1, 100).guesses <= 7
✓ assert auto_solve(100, 1, 100).guesses <= 7
✓ assert auto_solve(64, 1, 100).found == 64
7 tests: 7 passed, 0 failed$ soma verify guessing_game.cell
Verifying guessing_game.cell...
GuessingGame/termination:
⚠ handler `auto_solve`: while-loop without provable termination bound (write `while [loop_bound(N)] cond { … }`, or a for-loop over a bounded range)
⚠ `demo` calls `GuessingGame.auto_solve`, which may not terminate
0 passed, 2 warnings, 0 failures
Temporal: 0 passed, 0 failed
VERIFY OK