datatype Player = P1 | P2
function Other(): Player {
if this == P1 then P2 else P1
datatype Variables = Variables(piles: seq<nat>, turn: Player)
ghost predicate Init(v: Variables) {
| TurnStep(take: nat, p: nat)
ghost predicate Turn(v: Variables, v': Variables, step: Step)
&& v' == v.(piles := v.piles[p := v.piles[p] - take]).(turn := v.turn.Other())
ghost predicate NextStep(v: Variables, v': Variables, step: Step) {
case TurnStep(_, _) => Turn(v, v', step)
case NoOpStep() => v' == v // we don't really need to define predicate NoOp
lemma NextStepDeterministicGivenStep(v: Variables, v': Variables, v'': Variables, step: Step)
requires NextStep(v, v', step)
requires NextStep(v, v'', step)
ghost predicate Next(v: Variables, v': Variables) {
exists step :: NextStep(v, v', step)
lemma ExampleBehavior() returns (b: seq<Variables>)
ensures forall i:nat | i + 1 < |b| :: Next(b[i], b[i+1])
var state0 := Variables(piles := [3, 5, 7], turn := P1);
Variables(piles := [3, 1, 7], turn := P2),
Variables(piles := [3, 1, 0], turn := P1)
assert NextStep(b[0], b[1], TurnStep(take := 4, p := 1));
assert NextStep(b[1], b[2], TurnStep(take := 7, p := 2));