Created an example for the "Towers of Hanoi" logic puzzle, but ran out of time before I could add any error checking using invariants.
Anybody got a good suggestion for what I should have verified in
(*
TypeInvariant == /\ peg1 ???
/\ peg2 ???
/\ peg3 ???
*)
Hanoi.tla
------------------------------ MODULE Hanoi ---------------------------------
(***************************************************************************)
(* "Towers of Hanoi" puzzle solved using TLA+ 12/12/2010-12/13/2010 *)
(* Transfer a stack of disks from one peg to another, with the constraint *)
(* that a larger disk can never be placed on a smaller disk. A third peg *)
(* makes the puzzle possible. *)
(***************************************************************************)
(***************************************************************************)
(* Since we care about the SIZE of the disks we represent them as integers *)
(* We will represent each peg as a sequence of those disks, with the Head *)
(* of each Sequence representing the TOP of that stack of disks. *)
(***************************************************************************)
EXTENDS Integers, Sequences
CONSTANTS NumberOfDisks
VARIABLE peg1, peg2, peg3
-----------------------------------------------------------------------------
\* I never did find a way to specify the initial tuple in the .cfg file...
Disks == [i \in 1..NumberOfDisks |-> i]
Init == /\ peg1 = Disks
/\ peg2 = <<>>
/\ peg3 = <<>>
(*
TypeInvariant == /\ peg1 ???
/\ peg2 ???
/\ peg3 ???
*)
(***************************************************************************)
(* Helper function, needed because the destination peg might be empty *)
(* (You cannot take the Head() of an empty Sequence!) *)
(* What we do in the "empty peg" case is return a number bigger than ANY *)
(* disk. *)
(***************************************************************************)
Top(peg) == IF peg = <<>> THEN NumberOfDisks+1 ELSE Head(peg)
(***************************************************************************)
(* A legal move in Hanoi, genericized in terms of src peg, dest peg, and *)
(* the spare peg *)
(***************************************************************************)
MoveDisk(src, dest, spare) == /\ src # <<>>
/\ Head(src) < Top(dest)
/\ src' = Tail(src)
/\ dest' = SubSeq(src,1,1) \o dest
/\ UNCHANGED <<spare>>
(***************************************************************************)
(* The legal moves in Hanoi, specified in terms of MoveDisk. *)
(***************************************************************************)
MoveTopDiskFromPeg1ToPeg2 == MoveDisk(peg1, peg2, peg3)
MoveTopDiskFromPeg1ToPeg3 == MoveDisk(peg1, peg3, peg2)
MoveTopDiskFromPeg2ToPeg1 == MoveDisk(peg2, peg1, peg3)
MoveTopDiskFromPeg2ToPeg3 == MoveDisk(peg2, peg3, peg1)
MoveTopDiskFromPeg3ToPeg1 == MoveDisk(peg3, peg1, peg2)
MoveTopDiskFromPeg3ToPeg2 == MoveDisk(peg3, peg2, peg1)
Next == \/ MoveTopDiskFromPeg1ToPeg2
\/ MoveTopDiskFromPeg1ToPeg3
\/ MoveTopDiskFromPeg2ToPeg1
\/ MoveTopDiskFromPeg2ToPeg3
\/ MoveTopDiskFromPeg3ToPeg1
\/ MoveTopDiskFromPeg3ToPeg2
Spec == Init /\ [][Next]_<<peg1, peg2, peg3>>
-----------------------------------------------------------------------------
Solved == /\ peg1 = <<>>
/\ peg2 = <<>>
/\ peg3 = Disks
NotSolved == ~Solved
=============================================================================
Hanoi.cfg
CONSTANTS
NumberOfDisks = 3
SPECIFICATION Spec
\*INVARIANTS TypeInvariant NotSolved
INVARIANTS NotSolved