?he 'MANDC''Page %'
?fo 'Steven Hardy'-%-'21st January, 1977'
.mh
The Missionaries and Cannibals Problem
======================================
.hm
.in+6
Three missionaries and three cannibals seek to cross a
river from the left bank to the right bank. A boat is
available which will hold two people and which can be
navigated by any combination of missionaries and cannibals
involving one or two people. If the missionaries on either bank of
the river at any one time are outnumbered by cannibals, the cannibals
will indulge in their anthropophagic tendences and do away with the
missionaries. When the boat is moored at a bank, it is counted as
part of the bank for these purposes. Find the simplest schedule of
crossings that will permit all the missionaries and cannibals to
cross the river safely.
.in-6
.sp
.tp3
Introduction
.br
============
.sp
Consider the M and C problem, doing it yourself. As you do so
think about the following points:
.in+6
(a) Can you give a more precise statement of the problem in
terms of an initial 'state of the world', a desired 'state of the
world' and 'allowable changes to the state of the world' (moves).
.br
(b) How would you precisely describe a state, for example, will the
following do:
 	: [LEFT [M M M BOAT] RIGHT [C C C]]
.br
(c) How would you describe a move?
.br
(d) Can you view the problem as searching a tree?
.br
(e) Did you conduct a 'depth first' search of the tree?
.in-6
.sp
.tp3
Precise Recipe
.br
==============
.br
Can we design a 'precise recipe' for finding a solution to 
this problem? In order to guarantee the precision of our receipe
let us aim at making it a computer program from the start. Let us
weaken the task, initially, to that of writing a computer program that
merely checks our solution and then develop it into a program that
finds the solution unaided. On the way we will introduce numerous ideas
about programming and problem solving.
.sp
.tp3
What will we need?
.br
==================
.br
1. A formalization of the problem, for example in terms of states
and moves. How to get from natural language to this formalization
is an issue we delay tackling until we get to the natural language
part of the course.
.sp
2. We need to represent 'states' inside the computer. We will also
need to be able to apply 'moves' to these states to produce new
states. It seems natural to represent the states and the objects
moved as a data structure (say a list) and the move-maker as a
function to manipulate this data structure. For example, if we
have declared two variables
LEFTBANK
and
RIGHTBANK,
thus:
 	: VARS LEFTBANK RIGHTBANK;
.br
we can represent the state of the world in which two missionaries,
two cannibals and the boat are on the left bank and the other
missionary and cannibal are on the right bank as:
.me2
 	: [M M C C BOAT] -> LEFTBANK;
 	: [M C] -> RIGHTBANK;
.br
.sp
We could represent the movement of a missionary and cannibal from
left to right by calling the function
MOVELTOR
with a list of things to be moved as argument, thus:
.tp4
 	: MOVELTOR([M C BOAT]);
 	: LEFTBANK=>
 	** [M C]
 	: RIGHTBANK=>
 	** [M M C C BOAT]
.br
A suitable framework for this function is:
.tp5
 	: FUNCTION MOVELTOR(MOVE);
 	:	<OLD LEFTBANK WITHOUT MOVE> -> LEFTBANK;
 	:	<OLD RIGHTBANK WITH MOVE> -> RIGHTBANK;
 	: END;
.br
We will also need a function to initialise the variables
LEFTBANK
and
RIGHTBANK,
thus:
.tp5
 	: FUNCTION STARTMANDC();
 	:	[M M M C C C BOAT] -> 
 	:	[] -> RIGHTBANK;
 	: END;
.br
.sp
.tp3
Building the program
.br
====================
.br
Let us suppose that we already have functions called
WITH
and 
WITHOUT;
the functions to effect a movement from left to right and from right
to left may then be defined thus:
.tp5
 	: FUNCTION MOVELTOR(MOVE);
 	:	LEFTBANK WITHOUT MOVE -> LEFTBANK;
 	:	RIGHTBANK WITH MOVE -> RIGHTBANK;
 	: END;
.br
.tp5
 	: FUNCTION MOVELTOR(MOVE);
 	:	RIGHTBANK WITHOUT MOVE -> RIGHTBANK;
 	:	LEFTBANK WITH MOVE -> LEFTBANK;
 	: END;
.br
The functions
WITH
and
WITHOUT
have been defined as 'infix' functions, like "+" and "-". This is solely
for 'readibility' (as you will know if you have read the handout
describing ways of defining functions).
.sp
We do not declare
LEFTBANK
and 
RIGHTBANK
as 'locals' of these two functions as we want to access them 'globally'
that is their value is to remain accessible after the
functions have finished execution. We still have to write the
functions
WITH
and
WITHOUT.
WITH
is relatively easy; it takes two lists and returns a new list containing
all the elements of the first
AND
all the elements of the second. The operation <> will do this task,
thus:
.tp5
 	: OPERATION WITH LISTONE LISTTWO;
 	:	LISTONE <> LISTTWO
 	: END;
.br
The use of the word
OPERATION
rather than
FUNCTION
signals to the 
POP11 
compiler that when we call this function we will use infix
notation.
.sp
.tp9
.tp3
WITHOUT
.br
=======
.br
WITHOUT
is harder to write so if you don't understand the following
ignore it for now and come back to it later.
.tp4
 	: OPERATION WITHOUT LIST UNWANTED;
 	:	IF	UNWANTED = []
 	:	THEN	LIST
 	:	ELSE	DELETE(HD(UNWANTED),
 	:			LIST WITHOUT TL(UNWANTED))
 	:	CLOSE
 	: END;
.br
Here is an English translation of the above function:
.in+8
To build a list that has the elements of 
LIST
without those of
UNWANTED
first see if 
UNWANTED
is
[]
(i.e. there are no elements of
LIST
that we don't want). If this is the case then the answer is the
original
LIST.
Otherwise things get hard! We suppose that we have a function
DELETE
which takes an object and a list, it returns a new list which is a
copy of the original with the first occurrence of the object deleted.
At some stage we are going to have to 
DELETE
the first element of the
UNWANTED
list, but what are we to
DELETE
it from? I have chosen to
DELETE
it from the list we get by deleting all but the first of
UNWANTED
from the
LIST,
that is:
.tp5
 	: DELETE(HD(UNWANTED), LIST WITHOUT TL(UNWANTED))
.br
.sp
.in-8
.tp3
DELETE
.br
======
.br
Unfortunately we now have to write
DELETE!
Here is an English description of a suitable function:
.in+8
To delete an
OBJECT
from a
LIST
see if the
OBJECT
is the same as the first element of the
LIST;
it is is then 
TL(LIST)
is a suitable result (by definition this is a list of all the things
in
LIST
but the first). If the 
OBJECT
is not the first element of
LIST
we will get a recursive call of
DELETE 
to remove the
OBJECT FROM THE
TL
of the
LIST AND THEN ADD THE FIRST ELEMENT OF THE 
LIST
onto this list
.sp
.in-8
.tp3
Exercise
.br
========
.br
Complete the following function definition:
.tp5
 	: FUNCTION DELETE(OBJECT,LIST);
 	:	IF	HD(LIST) = OBJECT
 	:	THEN	...
 	:	ELSE	[% ... %] <> DELETE(OBJECT, ...)
.br
Remember, decorated list brackets ("[%" and "%]") build a list of
the intervening expressions and <> concatenates two lists.
.sp
.tp3
PRINTSTATE
.br
==========
.br
Let us also define a procedure to tell us the current state,
otherwise we will find it difficult to remember how we are doing:
.tp5
 	: FUNCTION PRINTSTATE();
 	:	PRINTLEFTBANK();
 	:	PRINTRIGHTBANK();
 	: END;
 	: FUNCTION PRINTLEFTBANK();
 	:	[%"LEFTBANK", "IS", LEFTBANK%] =>
 	: END;
.br
.tp5
 	: FUNCTION PRINTRIGHTBANK();
 	:	[%"RIGHTBANK", "IS", RIGHTBANK%] =>
 	: END;
.br
.sp
.tp3
Exercises
.br
=========
.br
1) 
PRINTLEFTBANK
and
PRINTRIGHTBANK
are very similar.
Can you write a procedure with one input which can do the work of 
both?
.br
2) These functions are in the system library, read them in by typing:
 	: LIB MANDC1;
.br
(don't forget the "1") and try using them to solve the M & C
problem at the terminal.
.sp
.tp3
Solution Checking
.br
=================
.br
Using the functions introduced, we can try solving the problem
"by hand", but using the computer to keep track of where we are, 
thus:
.tp12
 	: STARTMANDC();
 	: PRINTSTATE();
 	** [LEFTBANK IS [M M M C C C BOAT]]
 	** [RIGHTBANK IS []]
 	: MOVELTOR([M C BOAT]);
 	: PRINTSTATE();
 	** [LEFTBANK IS [M M C C M BOAT]]
 	** [RIGHTBANK IS [C]]
 	: MOVELTOL([M C BOAT]);
 	: PRINTSTATE();
 	** [LEFTBANK IS [M C M]]
 	** [RIGHTBANK IS [C M C BOAT]]
 	: ;;; WHOOPS - THE MISSIONARY ON
 	: ;;; THE RIGHT BANK HAS BEEN EATEN!
 	: ;;; SO - START OVER AGAIN.
 	: STARTMANDC();
 	: MOVELTOR([C C BOAT]);
 	: PRINTSTATE();
 	** [LEFTBANK IS [M M M C BOAT]]
 	** [RIGHTBANK IS [C C BOAT]]
.br
.tp5
.sp
.tp3
DOMOVE
.br
======
.br
It seems a bit clumsy to have to specify 
MOVELTOR
or
MOVERTOL
each time,
and also unnecessary. The computer itself ought to be able to
figure out which way to move next. How? Suppose we are in this
situation:
.tp5
 	: PRINTSTATE();
 	** [LEFTBANK IS [M C BOAT]]
 	** [RIGHTBANK IS [M C M C]]
.br
Which way should the next move be? Obviously since the boat
is on the 
LEFTBANK
we have to
MOVELTOR.
.br
So if we could get the computer to see which bank the boat is on then
we ought to be able to write a
.ul
single
procedure,
say
DOMOVE, 
which can decide to
MOVERTOR
or 
MOVERTOL
as appropriate.
.sp
.tp3
Writing
DOMOVE
.br
==============
.br
We now try writing
DOMOVE.
Like
MOVELTOR
and
MOVERTOL
it takes a single input, a list of what is to be moved across the
river. 
What do we want 
DOMOVE
to do? Well, if the boat is at the
LEFTBANK
we want it to
MOVELTOR
otherwise the boat must be on the
RIGHTBANK
and we want it to
MOVERTOL.
.sp
We are going to need a function called, say,
MEMBER
whose arguments are an object and a list which returns
TRUE
if the object is in the list and
FALSE
otherwise,
for example:
.tp5
 	: MEMBER("BOAT",[M C M C])=>
** <true>
 	: MEMBER("BOAT",[M C M C])=>
** <false>
 	: MEMBER("CAT",[BOY GIRL CAT DOG])=>
** <true>
 	: MEMBER("GOAT",[BOY GIRL CAT DOG])=>
** <false>
 	: MEMBER(3,[BOY GIRL 94 17 APPLE 3])=>
** <true>
.br
We defer programming 
MEMBER
until we have written
DOMOVE
(since we can only do one thing at a time).
.tp5
 	: FUNCTION DOMOVE(MOVE);
 	:	IF	MEMBER("BOAT", LEFTBANK)
 	:	THEN	...
 	:	ELSE	...
 	:	CLOSE
 	: END;
.br
So far so good; let's try using the new function:
.tp13
.tp5
 	: STARTMANDC();
 	: DOMOVE([C BOAT]);
 	: PRINTSTATE();
 	** [LEFTBANK IS [M M M C C]]
 	** [RIGHTBANK IS [C BOAT]]
 	: DOMOVE([C BOAT]);
 	: PRINTSTATE();
 	** [LEFTBANK IS [M M M C C C BOAT]]
 	** [RIGHTBANK IS []]
 	: DOMOVE([C C BOAT]);
 	: PRINTSTATE();
 	** [LEFTBANK IS [M M M C]]
 	** [RIGHTBANK IS [C C BOAT]]
.br
.sp
.tp3
MEMBER
.br
======
.br
One approach to writing functions and categorize the types of
arguments it can get into several cases and to deal with each case
separately. Usually all but one of these cases will be very
simple to handle and the last difficult case can be dealt with by
splitting the task into a number of simpler tasks. For the
MEMBER 
function two simple cases are when the given
LIST
is empty (in which case the
OBJECT
clearly isn't in the
LIST)
and when the given
OBJECT
is the first thing on the
LIST
(when it clearly is in the
LIST
and the result is
TRUE).
The hard case is when it's neither of these! In the hard case we
know that the
OBJECT
isn't the first so if it is in the
LIST
at all it must be in the
TL
of the
LIST;
thus the simpler problem is seeing if
MEMBER(OBJECT,TL(LIST))
is
TRUE -
and only if it is
TRUE
is the
OBJECT
in the original
LIST.
.sp
.tp3
Exercise
.br
========
.br
Complete the following function definition.
.tp5
 	: FUNCTION MEMBER(OBJECT,LIST);
 	:	IF	LIST = []
 	:	THEN	...
 	:	ELSEIF	OBJECT = ...
 	:	THEN	...
 	:	ELSE	MEMBER(...,TL(...))
 	:	CLOSE
 	: END;
.br
Remember the
'ELSE MEMBER(OBJECT,TL(...))'
is telling the POP11 system that the result of
MEMBER(OBJECT,LIST)
is, in this case, the same as the result of
MEMBER(OBJECT,TL(...)).
.br
What would you expect the following to print out?
.tp6
 	: MEMBER([CAT],[[CAT] [DOG] [GOAT]])=>
 	: MEMBER("CAT", [[CAT DOG GOAT]])=>
.br
It may help you to understand these questions if you understand
what
HD
and
TL
do and also if you understand the difference between "=" and
EQUAL.
(see the 
SYSVARS
demo).
.sp
.tp3
Simple interaction
.br
==================
.br
Even with
DOMOVE
we have to do a lot of unnecessary typing. Why not write a
simple program which
.ul
knows
that we want to
STARTMANDC
and then specify a sequence of move, with a
PRINTSTATE
after each?
Let's try:
.tp5
 	: FUNCTION MANDC();
 	:	STARTMANDC();
 	:	MAKEMOVE();
 	: END;
.br
and
.tp5
 	: FUNCTION MAKEMOVES();
 	:	VARS MOVE;
 	:	REQUESTAMOVE() -> MOVE;
 	:	DOMOVE(MOVE);
 	:	MAKEMOVES();
 	: END;
.br
where we use
.tp5
 	: FUNCTION REQUESTAMOVE();
 	:	PRINTSTATE();
 	:	[TYPE A MOVE] =>
 	:	LISTREAD();
 	: END;
.br
This makes things much easier. For example:
.tp15
.tp5
 	: MANDC();
 	** [LEFTBANK IS [M M M C C C BOAT]]
 	** [RIGHTBANK IS []]
 	** [TYPE A MOVE]
 	: [C C BOAT]
 	** [LEFTBANK IS [M M M C]]
 	** [RIGHTBANK IS [C C BOAT]]
 	** [TYPE A MOVE]
 	: [C BOAT]
 	** [LEFTBANK IS [M M M C C BOAT]
 	** [RIGHTBANK IS [C]]
 	** [TYPE A MOVE]
 	: [C C BOAT]
 	** [LEFTBANK IS [M M M]]
 	** [RIGHTBANK IS [C C C BOAT]]
.br
.sp
.tp3
ALLOWABLEMOVE
.br
==============
.br
Of course, this program is not a very good solution checker since it
will allow us to create situations in which missionaries are eaten,
allow us to move more than two people and allow us to move people
without moving the boat! One improvement to the program would be
to edit
MAKEMOVES
so that it rejects impossible moves, thus:
.tp5
 	: FUNCTION MAKEMOVES();
 	:	VARS MOVE;
 	:	REQUESTAMOVE() -> MOVE;
 	:	IF	ALLOWABLEMOVE(MOVE)
 	:	THEN	DOMOVE(MOVE)
 	:	ELSE	[MOVE NOT ACCEPTABLE] =>
 	:	CLOSE;
 	:	MAKEMOVES();
 	: END;
.br
The function
ALLOWABLEMOVE
must now be defined to check on a move's legality. How can it do this?
Well - to be acceptable a move must:
.in+5
(a) Include one, and only one, boat.
.br
(b) Include at least one, but not more than two, people.
.br
(c) Not include people who are not the side from which the boat is
to depart.
.br
(d) Not leave more cannibals than missionaries on either bank unless
there are no missionaries at all on that bank.
.br
.in-5
ALLOWABLEMOVE
is definable thus:
.tp5
 	: FUNCTION ALLOWABLEMOVE(MOVE);
 	:	BOATOKAY(MOVE)
 	:	AND	FROMBANKOKAY(MOVE)
 	:	AND	TOBANKOKAY(MOVE)
 	: END;
.br
.sp
.tp3
Exercise
.br
========
.br
Let us suppose we have a function called
COUNT
which takes an
OBJECT
and a 
LIST
and counts the number of times
OBJECT
occurs in the
LIST;
further suppose we have a function called
FROMSIDE
(of no arguments) which returns as its value the list
describing the side from which the boat is departing and a
corresponding function called
TOPSIDE.
.br
(These functions 
are all in
LIB MANDC1).
Now, complete the following function definitions:
.tp5
 	: FUNCTION BOATOKAY(MOVE);
 	:	COUNT("BOAT",MOVE) = 1
 	:	AND (COUNT("M",MOVE) + COUNT("C",MOVE)) >= ...
 	:	AND ... =< 2
 	:	AND COUNT("M",FROMSIDE()) >= ...
 	:	AND ...
 	: END;
.br
.tp5
 	: FUNCTION FROMBANKOKAY(MOVE);
 	:	COUNT("M",FROMSIDE()) - COUNT("M",MOVE) = 0
 	:	OR	(COUNT("M",FROMSIDE()) - COUNT("M",MOVE)
 	: END;
.br
.tp5
 	: FUNCTION TOBANKOKAY(MOVE);
 	:	...
 	: END;
.br
The function
MANDC 
is available in the library file
MANDC1,
so try typing:
.tp4
 	: LIB MANDC1;
 	: MANDC();
.br
After playing with the program for a while try 
TRACing
some of the functions in it, for example:
.tp3
 	: TRACE MAKEMOVES REQUESTAMOVE DOMOVE ALLOWABLEMOVE;
 	: TRACE BOATOKAY FROMBANKOKAY TOBANKOKAY;
 	: MANDC();
.br
You should, of course, write your own definitions of
COUNT, FROMSIDE
and 
TOPSIDE.
.sp
.tp3
SUCCEEDED
.br
=========
.br
When using the above package I experienced a sense of anti-climax
after successfully conveying everybody to the right hand bank - the
computer just went ahead and asked me for another move! How can we get
the computer to recognize that the puzzle has been solved? One way
would be to alter
MAKEMOVES
so that when everybody has been moved it congratulates you -
rather than
REQUESTing another 
MOVE.
Thus we will need a function called, say
SUCCEEDED
which is called in
MAKEMOVES.
.sp
.tp3
Exercise
.br
========
.br
Write
SUCCEEDED
and alter
MAKEMOVES
to use it. You may find it helpful to have a file like this:
.tp5
 	: LIB MANDC1;
 	: FUNCTION SUCCEEDED();
 	:	...
 	: END;
.br
.tp4
 	: FUNCTION MAKEMOVES();
 	:	<YOUR NEW DEFINITION OF MAKEMOVES>
 	: END;
.br
If you called this file 
MYMANDC.P
you could type:
.tp5
 	: LOAD MYMANDC;
 	: MANDC();
.br
to use your new, improved program.
.sp
.tp3
Keeping a record
.br
================
.br
As a further refinement make your program keep a record of all the
moves you have made and get it to print out the moves you made
after you have successfully solved the puzzle.
.sp
.tp3
Allowing backup
.br
===============
.br
One fault in the program so far developed is that it does not allow
us to back up from a poor move. A way of getting round this problem
is to save the 'state' of the puzzle before each move and alter the
program so that if we give the move 
BACKUP
the system 'undoes' the effect of the most recent move. A way of
achieving this effect is to alter
MAKEMOVES
so that instead of calling
DOMOVE
it instead calls a function
TRYMOVE.
TRYMOVE
would save the state of the puzzle, do the move and call
MAKEMOVES.
We also alter
MAKEMOVES
so that, when given the move
BACKUP,
it simply returns rather than continues the move making process, thus:
.tp5
 	: FUNCTION MAKEMOVES();
 	:	VARS MOVE;
 	:	IF	SUCCEEDED()
 	:	THEN	[WELL DONE - YOU HAVE DONE IT] =>
 	:		SETPOP();
 	:	CLOSE;
 	:	REQUESTAMOVE() -> MOVE;
 	:	IF	EQUAL(MOVE,[BACKUP])
 	:	THEN	[BACKING UP] =>
 	:	ELSEIF	ALLOWABLEMOVE(MOVE)
 	:	THEN	TRYMOVE(MOVE);
 	:		MAKEMOVES();
 	:	ELSE	[MOVE NOT ACCEPTABLE] =>
 	:	CLOSE
 	: END;
 	: FUNCTION TRYMOVE(MOVE);
 	:	VARS LEFTBANK RIGHTBANK;
 	:	DOMOVE(MOVE);
 	:	MAKEMOVES();
 	: END;
.br
.sp
.tp3
Looping
.br
=======
.br
Now that the above program allows us to
BACKUP
it makes no sense for it to allow us to go round in a loop.
One way this can happen is if we keep on specifying the same move,
for example 
[C C BOAT].
This suggests that all the program need do is ensure that we don't
type the same move in twice in succession. Unfortunately some
loops are more complicated - see if you can find such a loop for the
M and C problem. The only way I can think of preventing looping
is for
TRYMOVES
to add the current state of the puzzle to some list of
previous states and for
ALLOWABLEMOVE
to check that the proposed move will not take us to a state we have
already passed through. Since the state of the puzzle is
completely determined by who (and what) is on the
LEFTBANK
I chose to keep a list of previous 
LEFTBANKS
and altered 
ALLOWABLE
move to include a call of
LEADSTOALOOP,
which is defined thus:
.tp5
 	: FUNCTION LEADSTOALOOP(MOVE);
 	:	VARS LEFTBANK RIGHTBANK;
 	:	DOMOVE(MOVE);
 	:	NOT(OCCURS(LEFTBANK,STATELIST))
 	: END;
.br
where:
.tp5
 	: FUNCTION OCCURS(STATE, LISTOFSTATES);
 	:	IF	LISTOFSTATES = []
 	:	THEN	FALSE
 	:	ELSEIF	SAMESTATE(STATE,HD(LISTOFSTATES))
 	:	THEN	TRUE
 	:	ELSE	OCCURS(STATE,TL(LISTOFSTATES))
 	:	CLOSE
 	: END;
.br
SAMESTATE
is to return
TRUE 
if the two
LEFTBANKS
it is given describe the same state. Obviously this is the
case if they
are
EQUAL,
but it is also the case if they are merely in a different order -
[C BOAT C]
and 
[C C BOAT] ARE ESSENTIALLY THE SAME. SEE IF YOU CAN WRITE
SAMESTATE.
(Hint: I used
COUNT
in my definition of 
SAMESTATE.)
.sp
.tp3
Exercise
.br
========
.br
A version of the above program is in the system library, to use it
type:
.tp2
 	: LIB MANDC2;
 	: MANDC();
.br
Notice that this version of the program keeps a list of the
moves you have made.
.sp
.tp3
Automatically solving the problem
.br
=================================
.br
You will recall that the original intention of this exercise was to
develop a program capable of finding the solution itself. To convert
the program we already have into such a program we need only alter
REQUESTAMOVE
so that instead of
.ul
reading
a move from the user it instead selects a move from some stored list
of possible moves, thus:
.tp6
 	: FUNCTION REQUESTAMOVE => MOVE;
 	:	HD(POSSIBLEMOVES) -> MOVE;
 	:	TL(POSSIBLEMOVES) -> POSSIBLEMOVES;
 	:	PRINTSTATE();
 	:	[ABOUT TO TRY] <> [%MOVE%] =>
 	: END;
.br
.tp8
 	: FUNCTION TRYMOVE(MOVE);
 	:	VARS LEFTBANK RIGHTBANK POSSIBLEMOVES;
 	:	[[M M BOAT] [M BOAT] [M C BOAT]
 	:	[C BOAT] [C C BOAT] [BACKUP]]
 	:	-> POSSIBLEMOVES;
 	:	DOMOVE(MOVE);
 	:	MAKEMOVES();
 	: END;
.br
.sp
.tp3
Exercise
.br
========
.br
A version of the above program is in the system library, use it by
typing:
.tp2
 	: LIB MANDC3;
 	: MANDC();
.br
What difference would it make to the problem solver's speed if you
altered the order in which moves are stored on
POSSIBLEMOVES?
You can experiment with this since the program in
LIB MANDC3 
uses the list
GLOBALPOSSIBLEMOVES
which you can alter by assignment, for example:
.tp3
 	: [[C C BOAT] [ C BOAT] [M C BOAT] [M BOAT] [M M BOAT]
 	:	[BACKUP]] ->GLOBALPOSSIBLEMOVES;
 	: MANDC();
.br
.sp
.tp3
Exercise
.br
========
.br
Write a program similar to that described above to solve the
'sliding blocks puzzle'. One form of this puzzle is to have a
three by three plastic frame in which there are eight pieces of 
smaller plastic, which have the numers one to eight on them.
A move consists of sliding one of the numbers (either up, down,
left or right) into the space (since there are only eight
numbers there is one space). The goal is to get the numbers into
the configuration:
.in+10
.tp13
 	*************
 	*   *   *   *
 	* 1 * 2 * 3 *
 	*   *   *   *
 	*************
 	*   *   *   *
 	* 4 * 5 * 6 *
 	*   *   *   *
 	*************
 	*   *   *   *
 	* 7 * 8 * X *
 	*   *   *   *
 	*************
.br
.in-10
.br
where the "X" denotes the empty space.
.sp
You may wish to look at the
MANDC
programs in the library, to do so give the command:
 	% TY /USR/LIB/MANDC?.P
