Tic-tac-toe win detection — pure board logic
A board is a flat list of 9 cells, each "X", "O", or "" (empty). The winner() handler scans the 8 winning lines and returns the mark of the winner, "draw" if the board is full with no line, or "" if still playing. Run: soma run tic_tac_toe.cell --signal demo Test: soma test tic_tac_toe.cell
A Soma program from the games part of the verified corpus: 67 lines, cells TicTacToe, TicTacToeTests. It passes soma check with soma 3.2.4.
How it is built
The cell TicTacToe declares 3 signals in its face: winner(board: List) -> String, is_full(board: List) -> Bool and demo() -> String. Handlers: is_full (line 28), winner (line 32) and demo (line 47). Private helpers: _lines.
The file also holds 1 test cell (TicTacToeTests) with 6 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 = truetic_tac_toe.cell
// ============================================================================
// Tic-tac-toe win detection — pure board logic
// ============================================================================
//
// A board is a flat list of 9 cells, each "X", "O", or "" (empty). The
// winner() handler scans the 8 winning lines and returns the mark of the
// winner, "draw" if the board is full with no line, or "" if still playing.
//
// Run: soma run tic_tac_toe.cell --signal demo
// Test: soma test tic_tac_toe.cell
cell TicTacToe {
face {
signal winner(board: List) -> String
signal is_full(board: List) -> Bool
signal demo() -> String
}
// The 8 winning triples (row, column, diagonal indices).
on _lines() {
return [
[0, 1, 2], [3, 4, 5], [6, 7, 8], // rows
[0, 3, 6], [1, 4, 7], [2, 5, 8], // cols
[0, 4, 8], [2, 4, 6] // diagonals
]
}
on is_full(board: List) {
return all(board, c => c != "")
}
on winner(board: List) {
let result = ""
for line in _lines() {
let a = board[line[0]]
let b = board[line[1]]
let c = board[line[2]]
if a != "" && a == b && b == c {
result = a
}
}
if result != "" { return result }
if is_full(board) { return "draw" }
return ""
}
on demo() {
let b = ["X", "O", "X", "O", "X", "O", "X", "", ""]
return winner(b)
}
}
cell test TicTacToeTests {
rules {
// X wins the left column (0,3,6)
assert winner(["X", "O", "O", "X", "", "", "X", "", ""]) == "X"
// O wins the main diagonal (0,4,8)
assert winner(["O", "X", "X", "", "O", "", "", "", "O"]) == "O"
// full board, no line → draw
assert winner(["X", "O", "X", "X", "O", "O", "O", "X", "X"]) == "draw"
// game still in progress → ""
assert winner(["X", "", "", "", "O", "", "", "", ""]) == ""
assert is_full(["X", "O", "X", "O", "X", "O", "X", "O", "X"]) == true
assert is_full(["X", "", "X", "O", "X", "O", "X", "O", "X"]) == false
}
}What the toolchain says
$ soma check tic_tac_toe.cell
✓ All checks passed.$ soma test tic_tac_toe.cell
test TicTacToeTests ...
✓ assert winner(["X", "O", "O", "X", "", "", "X", "", ""]) == "X"
✓ assert winner(["O", "X", "X", "", "O", "", "", "", "O"]) == "O"
✓ assert winner(["X", "O", "X", "X", "O", "O", "O", "X", "X"]) == "draw"
✓ assert winner(["X", "", "", "", "O", "", "", "", ""]) == ""
✓ assert is_full(["X", "O", "X", "O", "X", "O", "X", "O", "X"]) == true
✓ assert is_full(["X", "", "X", "O", "X", "O", "X", "O", "X"]) == false
6 tests: 6 passed, 0 failed$ soma verify tic_tac_toe.cell
Verifying tic_tac_toe.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)