More Samples
{

Three cannibals and three missionaries want to cross a river in a canoe which
only holds two people. Someone has to bring the boat back. The trouble is, if 
there are ever more cannibals than missionaries the cannibals will eat the 
missionaries. How can the six get safely across? 

The following code solves the problem by calling the query
    "all Cannibals()"

There are infinitely many solutions, as it is possible to have one guy paddling
across and back as many times as we want, but we are only interested in solutions
that do not contain repeating sequences. For this purpose we keep two history lists,
one for each river bank. If a trip would result in the same distribution of 
missionaries and cannibals as we already encountered previously (i.e. is in the history list),
trip is discarted. Otherwise the trip is added to the list of trips and also the
history list is updated to reflect the new trip. And, of coures, we make sure 
cannibals never outnumber missionaries.

}


People = cannibals:I[0..3], missionaries:I[0..3]
Direction = A | B
Trips = direction:Direction,cannibals:I[0..3], missionaries:I[0..3]
 
History = list People
Trip = list Trips

pred Cannibals() iff
    // Specify the initial conditions:
    left  :. People & left  := (3,3) & // all 6 people on the left bank
    right :. People & right := (0,0) & // nobody on the right bank
    hl :.History & hl := left,Nil &    // initial history of the left bank state
    hr :.History & hr := right,Nil &   // initial history of the right bank state
    trip :. Trip & trip := Nil &
    NextTrip(left,hl,right,hr,trip) & PrintOneSolution(trip) 

local pred NextTrip(left:.People,hl:.History,right:.People,hr :.History,trip :.Trip) iff   
    Accross(left,right,hl,trip) &  
    if right <> (3,3) then 
        Back(left,right,hr,trip) & NextTrip(left,hl,right,hr,trip)
    end 

// Going accross: moving people from the left river bnk to right river bank.
  
local pred Accross(left:.People, right:.People, history:.History,trip :.Trip) iff
    OneTrip(cannibals, missionaries) &
    left.cannibals     := left.cannibals - cannibals & 
    right.cannibals    := right.cannibals + cannibals & 
    left.missionaries  := left.missionaries - missionaries & 
    right.missionaries := right.missionaries + missionaries &
    SafeMissionaries(left,right) &
    ~left in history & history := left,history & 
    trip := (A,cannibals,missionaries),trip

// Going back: moving people from the right to the left river bank

local pred Back(left:.People, right:.People, history :. History,trip :.Trip) iff
    OneTrip(cannibals, missionaries) &
    left.cannibals     := left.cannibals + cannibals & 
    right.cannibals    := right.cannibals - cannibals & 
    left.missionaries  := left.missionaries + missionaries & 
    right.missionaries := right.missionaries - missionaries &
    SafeMissionaries(left,right) &
    ~right in history & history := right,history & 
    trip := (B,cannibals,missionaries),trip  

// We don't differentiate between trips, after all, the canoe can transport up to
// two people regardless of the direction...
// So we just generate all possible occupancies.

local pred OneTrip(cannibals :>I, missionaries :> I) iff
    (
    (cannibals = 1 & missionaries = 0)  |
    (cannibals = 0 & missionaries = 1)  |
    (cannibals = 2 & missionaries = 0)  |
    (cannibals = 1 & missionaries = 1)  |
    (cannibals = 0 & missionaries = 2)  
    ) 

// Helper routine to make sure cannibals don't outnumber any missionaries present

local proc SafeMissionaries(left:<People,right:<People)  iff
    if left.missionaries > 0 then
        left.cannibals <= left.missionaries 
    end &
    if right.missionaries > 0 then
        right.cannibals <= right.missionaries 
    end

// ----------------------------------------------------------------------------
// The following is just code to display the resulting list of the trips
// in a format that is more user friendly
// ----------------------------------------------------------------------------

local proc PrintOneSolution(trip :< Trip) iff
    Print('\n\n        Left  Right  Trip          Left  Right') &
    left :.  People & left  := (3,3) &
    right :. People & right := (0,0) &
    // Our list of trips is in order tripLast...,trip7,trip6,...trip1,Nil
    // We want to print the solution in order trip1,trip2,....,tripLast
    revtrip :. Trip & revtrip := Nil  & ListReverse(trip,revtrip) &
    // Traverse the reversed list and print states before and after each trip
    PrintOneSolution1(revtrip,left,right)

local proc PrintOneSolution1(trip :< Trip,left:.People,right:.People) iff
    if trip = h,t then 
        Print ('\nStart: ') & PrintPeople(left) & PrintPeople(right) &
        crew:.People & crew:= (h.cannibals,h.missionaries) &
        if h.direction = A then 
            PrintPeople(crew) & Print(' -> ') & 
            left.missionaries  := left.missionaries - h.missionaries &
            left.cannibals     := left.cannibals - h.cannibals &
            right.missionaries := right.missionaries + h.missionaries &
            right.cannibals    := right.cannibals + h.cannibals
        else Print(' <- ') & PrintPeople(crew) &
            left.missionaries  := left.missionaries + h.missionaries &
            left.cannibals     := left.cannibals + h.cannibals &
            right.missionaries := right.missionaries - h.missionaries &
            right.cannibals    := right.cannibals - h.cannibals 
        end & Print(' End:') & PrintPeople(left) & PrintPeople(right) & PrintOneSolution1(t,left,right)
    else
        Print('\n\n')
    end

// Formatted printout of People. Don't print zeros

local proc PrintPeople(p:<People) iff
    Print(' ') &
    if p.cannibals = 0 then Print('  ') 
    else Print(p.cannibals,'c')
    end &
    if p.missionaries = 0 then Print('   ')
    else Print(p.missionaries,'m ')
    end

// Code to create a reversed list
local proc ListReverse(inlist:< Trip, outlist:.Trip) iff
    if inlist = h,t then outlist := h,outlist & ListReverse(t,outlist) end




This page was created by F1toHTML