soma.cell

Maze movement — one step with wall collision on a grid

The maze is a flat row-major list: 0 = open, 1 = wall. move() attempts a step in a compass direction; the player stays put if the target is a wall or off the grid. walk() applies a sequence of moves and returns the final position plus whether the exit was reached. Run: soma run maze_step.cell --signal demo Test: soma test maze_step.cell

A Soma program from the games part of the verified corpus: 82 lines, cells MazeStep, MazeTests. It passes soma check with soma 3.2.4.

How it is built

The cell MazeStep declares 4 signals in its face: can_enter(maze: List, r: Int, c: Int, y: Int, x: Int) -> Bool, move(maze: List, r: Int, c: Int, y: Int, x: Int, dir: String) -> Map, walk(maze: List, r: Int, c: Int, sy: Int, sx: Int, moves: List) -> Map and demo() -> Map. Handlers: can_enter (line 21), move (line 26), walk (line 40) and demo (line 55).

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

maze_step.cell

// ============================================================================
// Maze movement — one step with wall collision on a grid
// ============================================================================
//
// The maze is a flat row-major list: 0 = open, 1 = wall. move() attempts a
// step in a compass direction; the player stays put if the target is a wall
// or off the grid. walk() applies a sequence of moves and returns the final
// position plus whether the exit was reached.
//
// Run:  soma run maze_step.cell --signal demo
// Test: soma test maze_step.cell

cell MazeStep {
    face {
        signal can_enter(maze: List, r: Int, c: Int, y: Int, x: Int) -> Bool
        signal move(maze: List, r: Int, c: Int, y: Int, x: Int, dir: String) -> Map
        signal walk(maze: List, r: Int, c: Int, sy: Int, sx: Int, moves: List) -> Map
        signal demo() -> Map
    }

    on can_enter(maze: List, r: Int, c: Int, y: Int, x: Int) {
        if y < 0 || y >= r || x < 0 || x >= c { return false }
        return maze[y * c + x] == 0
    }

    on move(maze: List, r: Int, c: Int, y: Int, x: Int, dir: String) {
        let ny = y
        let nx = x
        if dir == "N" { ny = y - 1 }
        if dir == "S" { ny = y + 1 }
        if dir == "W" { nx = x - 1 }
        if dir == "E" { nx = x + 1 }
        if can_enter(maze, r, c, ny, nx) {
            return map("y", ny, "x", nx, "moved", true)
        }
        return map("y", y, "x", x, "moved", false)
    }

    // Apply a list of direction strings; report final cell and exit reach.
    on walk(maze: List, r: Int, c: Int, sy: Int, sx: Int, moves: List) {
        let y = sy
        let x = sx
        let blocked = 0
        for d in moves {
            let res = move(maze, r, c, y, x, d)
            y = res.y
            x = res.x
            if res.moved == false { blocked = blocked + 1 }
        }
        // exit is the bottom-right cell
        let at_exit = y == r - 1 && x == c - 1
        return map("y", y, "x", x, "blocked", blocked, "at_exit", at_exit)
    }

    on demo() {
        // 3x3 maze, wall at (1,1):
        //  . . .
        //  . # .
        //  . . .
        let m = [0,0,0, 0,1,0, 0,0,0]
        return walk(m, 3, 3, 0, 0, ["E", "E", "S", "S"])
    }
}

cell test MazeTests {
    rules {
        // maze: [0,0,0, 0,1,0, 0,0,0] — wall at (1,1)
        // moving into the wall at (1,1) from (0,1) going S is blocked
        assert move([0,0,0, 0,1,0, 0,0,0], 3, 3, 0, 1, "S").moved == false
        // moving E from (0,0) is open
        assert move([0,0,0, 0,1,0, 0,0,0], 3, 3, 0, 0, "E").x == 1
        // stepping off the top edge stays put
        assert move([0,0,0, 0,1,0, 0,0,0], 3, 3, 0, 0, "N").moved == false
        // walk around the wall to the exit (2,2)
        assert walk([0,0,0, 0,1,0, 0,0,0], 3, 3, 0, 0, ["E", "E", "S", "S"]).at_exit == true
        // a path that bumps the wall records a block
        assert walk([0,0,0, 0,1,0, 0,0,0], 3, 3, 0, 1, ["S"]).blocked == 1
        assert can_enter([0,0,0, 0,1,0, 0,0,0], 3, 3, 1, 1) == false
        assert can_enter([0,0,0, 0,1,0, 0,0,0], 3, 3, 2, 2) == true
    }
}

What the toolchain says

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

$ soma check maze_step.cell
✓ All checks passed.
$ soma test maze_step.cell
test MazeTests ...
  ✓ assert move([0,0,0, 0,1,0, 0,0,0], 3, 3, 0, 1, "S").moved == false
  ✓ assert move([0,0,0, 0,1,0, 0,0,0], 3, 3, 0, 0, "E").x == 1
  ✓ assert move([0,0,0, 0,1,0, 0,0,0], 3, 3, 0, 0, "N").moved == false
  ✓ assert walk([0,0,0, 0,1,0, 0,0,0], 3, 3, 0, 0, ["E", "E", "S", "S"]).at_exit == true
  ✓ assert walk([0,0,0, 0,1,0, 0,0,0], 3, 3, 0, 1, ["S"]).blocked == 1
  ✓ assert can_enter([0,0,0, 0,1,0, 0,0,0], 3, 3, 1, 1) == false
  ✓ assert can_enter([0,0,0, 0,1,0, 0,0,0], 3, 3, 2, 2) == true

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

map

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