// -----------------------------------------------------------------
// Crab-Racoon Puzzle (Problem 15.1 from Edwin F. Meier III et al: 
// Guide to Teaching Puzzle-Based Learning, Springer, 2014)
// -----------------------------------------------------------------

// some crab burrows

/* burrow 0 with holes 
   0 - 1 - 2 - 3 - 4
*/ 
type Hole0 = ℕ[4]; 
pred move0(i:Hole0, j:Hole0) ⇔
  (i = 0 ⇒ (j = 1)) ∧
  (i = 1 ⇒ (j = 0 ∨ j = 2)) ∧
  (i = 2 ⇒ (j = 1 ∨ j = 3)) ∧
  (i = 3 ⇒ (j = 2 ∨ j = 4)) ∧
  (i = 4 ⇒ (j = 3))
;

/* burrow 1 with holes 
   0 - 1 - 2 - 3 - 4
           |
           5
           |
           6 
*/     
type Hole1 = ℕ[6]; 
pred move1(i:Hole1, j:Hole1) ⇔
  (i = 0 ⇒ (j = 1)) ∧
  (i = 1 ⇒ (j = 0 ∨ j = 2)) ∧
  (i = 2 ⇒ (j = 1 ∨ j = 3 ∨ j = 5)) ∧
  (i = 3 ⇒ (j = 2 ∨ j = 4)) ∧
  (i = 4 ⇒ (j = 3)) ∧
  (i = 5 ⇒ (j = 2) ∨ (j = 6)) ∧
  (i = 6 ⇒ (j = 5))
;

// the selected crab burrow
type Hole = Hole1;
pred move(i:Hole, j:Hole) ⇔ move1(i, j);

// -----------------------------------------------------------------
// the rules of the game
// -----------------------------------------------------------------

// maximum number of looks considered
val N:ℕ; 

// the numbers and the sequences of holes considered
type Num = ℕ[N];
type Holes = Array[N,Hole];

// m describes a possible sequence of N moves of the crab
pred movement(m:Holes) ⇔
  ∀i:Num with i < N-1. move(m[i],m[i+1]);
  
// -----------------------------------------------------------------
// the precomputation of all moves of the crab
// -----------------------------------------------------------------

// compute the set of all sequences of n moves of the crab
fun moves(n:Num):Set[Holes] 
  requires n ≠ 0;
= if n = 1 then
    { Array[N,Hole](i) | i:Hole }
  else let M = moves(n-1) in
    { m with [n-1]=i | m ∈ M, i:Hole with move(m[n-2],i) };
    
// the set of all sequences of N moves of the crab
val Moves = moves(N);

// we only have correct moves
theorem movesCorrect ⇔ ∀m∈Moves. movement(m);

// we have all correct moves (takes too long to check)
// theorem movesComplete ⇔ ∀m:Holes with movement(m). m∈Moves;

// -----------------------------------------------------------------
// the solution to the problem
// -----------------------------------------------------------------

// sequence "looks" catches crab moving along sequence "moves"
pred catch(looks:Holes, moves:Holes) ⇔
  ∃i:Num with i < N. looks[i] = moves[i];

// sequence "looks" is guaranteed to catch the crab
pred solution(looks:Holes) ⇔
  ∀moves∈Moves. catch(looks, moves);

// compute a solution of the problem
fun solve(): Holes =
  choose looks:Holes with solution(looks);

// also a violation of this theorem determines a solution
// (allows the application of multiple threads to each candidate)
theorem nosolution(looks:Holes) ⇔ ¬solution(looks);

// the computation of a sequence of N "looks"
proc looks(): Holes
{
  var holes:Holes = Array[N,Hole](0);
  var i:Num = 0;
  while i < N do
  {
    choose h:Hole;
    holes[i] ≔ h;
    i ≔ i+1;
  }
  return holes;
}

// also a violation of this theorem determines a solution
theorem nosolution() ⇔ 
  let l = looks() in
  if solution(l) then print l in false else true;

// -----------------------------------------------------------------
// the check of a given solution
// -----------------------------------------------------------------

// a given solution
val looks = Array[10,ℕ[6]](0)
with [0]=1
with [1]=2
with [2]=3
with [3]=2
with [4]=5
with [5]=1
with [6]=2
with [7]=3
with [8]=2
with [9]=5
;

// solution catches crab whatever moves it makes
theorem catch(moves:Holes)
  requires movement(moves);
⇔ ∃i:Num with i < N. looks[i] = moves[i];

// check solution for the precomputed move sequences
proc main(): ()
{
  check catch with Moves;
}

// -----------------------------------------------------------------
// end of file
// -----------------------------------------------------------------





