|
| 1 | +------------------------------- MODULE Hanoi ------------------------------- |
| 2 | +EXTENDS Naturals, Bits, FiniteSets, TLC |
| 3 | + |
| 4 | +(***************************************************************************) |
| 5 | +(* TRUE iff i is a power of two *) |
| 6 | +(***************************************************************************) |
| 7 | +PowerOfTwo(i) == i & (i-1) = 0 |
| 8 | + |
| 9 | +(***************************************************************************) |
| 10 | +(* A set of all powers of two up to n *) |
| 11 | +(***************************************************************************) |
| 12 | +SetOfPowerOfTwo(n) == {x \in 1..(2^n-1): PowerOfTwo(x)} |
| 13 | + |
| 14 | +(***************************************************************************) |
| 15 | +(* Copied from TLA+'s Bags standard library. The sum of f[x] for all x in *) |
| 16 | +(* DOMAIN f. *) |
| 17 | +(***************************************************************************) |
| 18 | +Sum(f) == LET DSum[S \in SUBSET DOMAIN f] == |
| 19 | + LET elt == CHOOSE e \in S : TRUE |
| 20 | + IN IF S = {} THEN 0 |
| 21 | + ELSE f[elt] + DSum[S \ {elt}] |
| 22 | + IN DSum[DOMAIN f] |
| 23 | + |
| 24 | +(***************************************************************************) |
| 25 | +(* D is number of disks and N number of towers *) |
| 26 | +(***************************************************************************) |
| 27 | +CONSTANT D, N |
| 28 | + |
| 29 | +(***************************************************************************) |
| 30 | +(* Towers of Hanoi with N towers *) |
| 31 | +(***************************************************************************) |
| 32 | +VARIABLES towers |
| 33 | +vars == <<towers>> |
| 34 | + |
| 35 | +(***************************************************************************) |
| 36 | +(* The total sum of all towers must amount to the disks in the system *) |
| 37 | +(***************************************************************************) |
| 38 | +Inv == Sum(towers) = 2^D - 1 |
| 39 | + |
| 40 | +(* Towers are naturals in the interval (0, 2^D] *) |
| 41 | +TypeOK == /\ \A i \in DOMAIN towers : /\ towers[i] \in Nat \* Towers are represented by natural numbers |
| 42 | + /\ towers[i] < 2^D |
| 43 | + |
| 44 | +(***************************************************************************) |
| 45 | +(* Now we define of the initial predicate, that specifies the initial *) |
| 46 | +(* values of the variables. *) |
| 47 | +(***************************************************************************) |
| 48 | +Init == /\ towers = [i \in 1..N |-> IF i = 1 THEN 2^D - 1 ELSE 0] \* all towers are empty except all disks are on the first Tower |
| 49 | + |
| 50 | +(***************************************************************************) |
| 51 | +(* TRUE iff the tower is empty *) |
| 52 | +(***************************************************************************) |
| 53 | +IsEmptyTower(tower) == tower = 0 |
| 54 | + |
| 55 | +(***************************************************************************) |
| 56 | +(* TRUE iff the disk is located on the given tower *) |
| 57 | +(***************************************************************************) |
| 58 | +IsOnTower(tower, disk) == /\ tower & disk = disk |
| 59 | + |
| 60 | +(***************************************************************************) |
| 61 | +(* TRUE iff disk is the smallest disk on tower *) |
| 62 | +(***************************************************************************) |
| 63 | +IsSmallestDisk(tower, disk) == /\ IsOnTower(tower, disk) |
| 64 | + /\ tower & (disk - 1) = 0 \* All less significant bits are 0 |
| 65 | + |
| 66 | +(***************************************************************************) |
| 67 | +(* TRUE iff disk can be moved off of tower *) |
| 68 | +(***************************************************************************) |
| 69 | +CanMoveOff(tower, disk) == /\ IsOnTower(tower, disk) |
| 70 | + /\ IsSmallestDisk(tower, disk) |
| 71 | + |
| 72 | +(***************************************************************************) |
| 73 | +(* TRUE iff disk can be moved to the tower *) |
| 74 | +(***************************************************************************) |
| 75 | +CanMoveTo(tower, disk) == \/ tower & (disk - 1) = 0 |
| 76 | + \/ IsEmptyTower(tower) |
| 77 | + |
| 78 | +(***************************************************************************) |
| 79 | +(* *) |
| 80 | +(***************************************************************************) |
| 81 | +Move(from, to, disk) == /\ CanMoveOff(towers[from], disk) |
| 82 | + /\ CanMoveTo(towers[to], disk) |
| 83 | + /\ towers' = [towers EXCEPT ![from] = towers[from] - disk, \* Remaining tower does not change |
| 84 | + ![to] = towers[to] + disk] |
| 85 | + |
| 86 | +(***************************************************************************) |
| 87 | +(* Define all possible actions that disks can perform. *) |
| 88 | +(***************************************************************************) |
| 89 | +Next == \E d \in SetOfPowerOfTwo(D): \E idx1, idx2 \in DOMAIN towers: |
| 90 | + /\ idx1 # idx2 \* No need to try to move onto the same tower (Move(...) prevents it too) |
| 91 | + /\ Move(idx1, idx2, d) |
| 92 | + |
| 93 | +(***************************************************************************) |
| 94 | +(* We define the formula Spec to be the complete specification, asserting *) |
| 95 | +(* of a behavior that it begins in a state satisfying Init, and that every *) |
| 96 | +(* step either satisfies Next or else leaves the pair <<big, small>> *) |
| 97 | +(* unchanged. *) |
| 98 | +(***************************************************************************) |
| 99 | +Spec == Init /\ [][Next]_vars |
| 100 | +----------------------------------------------------------------------------- |
| 101 | + |
| 102 | +(***************************************************************************) |
| 103 | +(* The final configuration has all disks on the right tower. If TLC is set *) |
| 104 | +(* to run with an invariant rightTower # 2^N-1, it will search for *) |
| 105 | +(* configurations in which this invariant is violated. If violation can be *) |
| 106 | +(* found, the stack trace shows the steps to solve the Hanoi problem. *) |
| 107 | +(***************************************************************************) |
| 108 | +NotSolved == towers[N] # 2^D - 1 |
| 109 | + |
| 110 | +(***************************************************************************) |
| 111 | +(* We find a solution by having TLC check if NotSolved is an invariant, *) |
| 112 | +(* which will cause it to print out an "error trace" consisting of a *) |
| 113 | +(* behavior ending in a state where NotSolved is false. With three disks, *) |
| 114 | +(* and three towers the puzzle can be solved in seven moves. *) |
| 115 | +(* The minimum number of moves required to solve a Tower of Hanoi puzzle *) |
| 116 | +(* generally is 2n - 1, where n is the number of disks. Thus, the counter- *) |
| 117 | +(* examples shown by TLC will be of length 2n-1 *) |
| 118 | +(***************************************************************************) |
| 119 | +============================================================================= |
| 120 | +\* Modification History |
| 121 | +\* Last modified Tue May 17 14:55:33 CEST 2016 by markus |
| 122 | +\* Created Sun Jul 17 10:20:57 CEST 2011 by markus |
0 commit comments