/*  PLANNING_BUG.PL  */


/*
SPECIFICATION
-------------

This is an experimental bug that can read and obey simple commands.
To run it, do
    eden( planning_bug, planning_world ).

Start by giving the listener one of the commands
    open the door
    grab the key
    grab the hammer
    grab the rock

The bug will then parse your command and extract its meaning, explore a
bit so that it can see the objects, and then form and obey a plan to
implement the command.

This is the first version, and is still very experimental. The planner
is of limited power, and I haven't yet decided on a proper
representation for states, action descriptions, and meanings. Those used
at the moment are little more than hacks.
*/


/*
IMPLEMENTATION
--------------

See below.
*/


/*
Setting up
==========

This section loads the necessary libraries and other files, and does
other setting-up operations.
*/


/*
Extend my library list so I can load some of the library files used by
my Logic Programming Tutor. These are included with the distributed
code of this bug, and labelled LPT below.
*/
:- prolog_language(pop11).
[ 'logic$src:' ] <> prologliblist -> prologliblist;
:- prolog_language(prolog).


/*
Load some files. The library list includes my own directory, so
some of these are not, strictly speaking, _library_ files.                   
*/
:- library(utils).              /* LPT - see file header */
:- library(lists).              /* LPT - see file header */
:- library(control).            /* LPT - see file header */
:- library(output).             /* LPT - see file header */
:- library(exec).               /* Eden library - see HELP EXEC */
:- library(gb).                 /* Own code - parser */
:- library(gb_lex).             /* Own code - parser */
:- library(warplan).            /* Own code - planner */
:- library(awm_to_objects).     /* Own code - AWM to propositions */


/*
To stop those irritating messages.
*/
:- quietspy(on).


/*
Load the Pop-11 Eden libraries I need, also my Pop-11 files.
*/
:- prolog_language(pop11).

needs explore;                  /* My own code */
needs awm_path;                 /* Eden library - see HELP AWM_PATH */
needs path_to_moves;            /* Eden library - see HELP PATH_TO_MOVES */

:- prolog_language(prolog).


/*
Define a shorthand for sending output to the 'view' window. output/1
is defined in OUTPUT.PL.
*/


:- op( 250, fx, v ).

v X :-
    bug_view output('PB'...X~).


/*
Interface with Eden
===================

This section defines 'start_thinking' and 'bugdead', and hooks 'think'
to the predicate 'brain'. I run the brain as a continuous process:
see HELP EXEC.
*/


start_thinking :-
    goal_to_process( brain, Co ),
    asserta( co(Co) ).


think( Action ) :-
    co( Co ),
    restart_process( Co, Action ).


bugdead( _, rerun ) :-
    start_thinking.


/*
The brain predicate
===================

This section defines the 'brain' predicate, the brain's top-level. The
brain first waits until you have said something to it. It then parses
this sentence, and extracts its meaning. This is represented in a simple
"logical form language": see GB.PL.

The rest of the brain assumes that you have given a command: it won't be
able to cope with anything else. It translates the meaning
representation into a representation of the command verb. This verb is
assumed to denote an action, and will be submitted to a planner.

The bug then breaks off and explores its world, using EXPLORE.P. It uses
breadth-first search to do this, and wanders for roughly 200 steps, or
until it has reached the boundaries of its surroundings. During this
exploration, it builds up a map of its surroundings. Exploration is
completely independent of planning, and the bug can't do anything else
while it is exploring.

At the end of exploration, we hope that the bug's map contains all the
objects referred to in the command. At the moment, there's no test for
this. The bug then converts the map, which is essentially an analogue
description of the world, into a propositional representation: a list
of propositions which represents the current state. This is used as the
start-state in planning. The command verb is used as the goal-state. The
bug passes these two to the planner (WARPLAN.PL), and gets back a plan.

Some of the actions in this plan are at the right level of abstraction
for the bug to obey directly: use and grab, for example. However, when
planning movement, the bug assumes that it can go from one point to
another, and does not check that there's actually a path between the two.
The planner therefore outputs just a 'go(From,To)' action, not a path.

The next job is to flesh this out with real primitive bug-moves. The bug
submits the entire plan to a "fleshing" predicate. This converts the
'go' actions into sequences of moves between the points, using a
path-planner on its map. Other actions are left more-or-less the same
(apart from stripping off some redundant arguments).

The bug now has a list of actions, and it executes these one by one.
No attempt is made to monitor execution, or to re-plan if an action
fails.
*/


/*  brain:
        The brain.
*/
brain :-
    sentence( [] ),
    !,
    say( ['Please', say, something] ),
    exec( wait ),
    brain.

brain :-
    v 'Doing next command',               
    sentence( Chars ),
    v 'Characters'...Chars,
    chars_to_items( Chars, S ),
    v 'Words'...S,
    (
        trans( S, Meaning, Type )
    ->
        bug_message output( Meaning ),
        v 'Meaning'...Meaning,
        reshape_meaning( Meaning, Verb ),
        v 'Verb'...Verb,
        bug_message output( Verb ),
        utter( ['About to explore'] ),
        prolog_eval( explore(200) ),
        utter( ['Getting AWM'] ),
        prolog_eval( valof(awm), AWM ),
        v 'AWM'...AWM,
        utter( ['Converting to objects'] ),
        awm_to_objects( AWM, State, _, _, [dir(Dir)] ),
        v 'State'...State,
        utter( ['Planning'] ),
        obey( Verb, [ empty | State ], Plan ),
        v 'Plan'...Plan,
        flesh_plan( AWM, Dir, Plan, FP ),
        v 'Moves'...FP,
        follow_plan( FP ),
        brain
    ;
        say( ['I', 'don\'t', understand, that] ),
        exec( wait ),
        brain
    ).


/*
Interfacing commands and the planner
====================================       

The meaning of a command is returned by 'trans'. 'reshape_meaning',
below, converts this into a term of the form
    Verb( me, Object )
It has to assume that the only commands are sentences like
    open the door
    grab the food
At this stage, that's all this bug can accept. 'reshape_meaning' is
just quick-and-dirty way to communicate with the planner.

The output of 'reshape_meaning' is used as the first argument to 'verb'.
This predicate represents the meaning of verbs using a state-transition
semantics. I took this idea from Steven Vere's and T Bickmore's "Basic
Agent", in "Computational Intelligence", 1990, volume 6. The idea is
that each action verb is represented by its pre- and post-conditions,
giving the difference in state before and after performing the action.
*/


/*  reshape_meaning( Meaning+, VerbSpec- ):
        Convert Meaning into a verb specifier of the form
        Verb(me,Object).
*/
reshape_meaning( ( the(Var,Obj), V2 ), V1 ) :-
    Obj =.. [ Type, Var ],
    V2 =.. [ Verb, me, Var ],
    !,
    V1 =.. [ Verb, Obj ].


/*  verb( Action+, Preconditions-, Postconditions- ):
        Specifies the pre- and post-conditions for each verb.
        Where the postcondition is
            primitive(A)
        then the verb is assumed to command the bug to perform a
        primitive bug-move, rather than planning a composite
        action.               
*/
verb( grab(X),
      [ square(X,S) ],
      [ have(X) ]
    ).

verb( drop(X),
      [ have(X) ],
      primitive(drop)
    ).

verb( smash(rock(Rock)),
      [ square(rock(Rock),S) ]
      [ clear(S) ]
    ).

verb( open(door(Door)),
      [ square(door(Door),S) ],
      [ clear(S) ]
    ).

verb( use(X),
      [ have(X) ],
      primitive(use)
    ).

verb( use(X),
      [ square(X,S) ],
      primitive(use)
    ).


/*  obey( Verb+, State+, Plan- ):
        Looks up the verb specification and checks its preconditions
        against the current state State. These are strong preconditions,
        such as: there must exist a door before you can open it.
        Consequently, if they are not satisfied by State, the bug
        doesn't attempt to make them true, it just gives up. If they are
        satisfied, it puts a plan into Plan.
*/
obey( Verb, State, error ) :-
    not( verb( Verb, _, _ ) ),
    !,
    say( [ Verb, not, recognised ] ).

obey( Verb, State, Plan ) :-
    verb( Verb, Preconditions, ActionSpec ),
    preconditions_satisfied( State, Preconditions ),
    !,
    obey_1( Verb, State, ActionSpec, Plan ).

obey( Verb, _, error ) :-
    say( [preconditions,not,satisfied,for,Verb] ).


/*  obey_1( Verb+, State+, Goal+, Plan- ):
        Verb can be performed in the current state State. The goal
        that must be planned for is Goal; Plan is the resulting
        plan.
*/
obey_1( Verb, _, primitive(Action), [Action] ) :-
    !.

obey_1( Verb, State, Goal, Plan ) :-
    make_plan( State, Goal, Plan ).


/*  preconditions_satisfied( State+, Preconditions+ ):
        True when State is subsumed in Preconditions, i.e.
        when Preconditions are a sublist of State. (Every
        requirement in Preconditions is also in State, and
        therefore satisfied.)
*/
preconditions_satisfied( State, Preconditions ) :-
    sublist0( State, Preconditions ).


/*
Planning - action specification
===============================

I use the Warplan planner, sent to me by David Warren. See the header
comment to WARPLAN.PL for a specification. This section defines the
actions in terms of add-lists, delete-lists and preconditions. The
actions are "abstract actions":
    use(X)      Use X
    grab(X,S)   Grab X in square S
    drop(X,S)   Drop X in square S
    go(S1,S2)   Go from S1 to S2
For the first three, the translation to primitive bug-moves is easy.
For 'go', we need to plan a path. This is done later: the planner here
assumes that all go's are feasible. (Another hack - need to find some
way to integrate path-planning and re-planning if a route is impossible.)

Note that Warplan expects single add- and delete-items to be returned
by 'add' and 'del'. Since my action/4 predicate packs them all into
one list, I use 'member' to return them one at a time.
*/


/*  add( Add-, Action+ ):
        Add is one of the facts that's deleted by Action.
*/
add( Add, Move ) :-
    action( Move, _, _, Adds ),
    member( Add, Adds ).


/*  del( Del-, Action+ ):
        Del is one of the facts that's deleted by Action.
*/
del( Del, Move ) :-
    action( Move, _, _, Dels ),
    member( Del, Dels ).


/*  can( Action+, Preconditions- ):
        Preconditions are the preconditions fr Action.
*/
can( Move, Preconds ) :-
    action( Move, Preconds, _, _ ).


/*  action( Action+, Preconds-, Dels-, Adds- ):
        Specifies the preconditions, delete-list, and add-list for
        Action. Preconditions are written as Goal&Goal&... for
        compatibility with Warplan; delete- and add-lists are
        lists.
*/
action( use(key(Key)),
        have(key(Key))&square(door(Door),S)&square(me,S),
        [ have(key(Key)), square(door(Door),S) ],
        [ empty, clear(S) ]
      ).

action( use(hammer(Hammer)),
        have(hammer(Hammer))&square(rock(Rock),S)&square(me,S),
        [ square(hammer(Hammer),S) ],
        [ clear(S) ]
      ).

action( grab(X,S),
        square(X,S) & empty & square(me,S),
        [ square(X,S), empty ],
        [ have(X) ]
      ).

action( drop(X,S),
        have(X) & square(me,S),
        [ have(X) ],
        [ square(X,S), empty ]
      ).

action( go(Here,S),
        square(me,Here),
        [ square(me,Here) ],
        [ square(me,S) ]
      ).


/*  imposs( State+ ):
        It is impossible for the two goals here to be simultaneously
        true. If we don't tell Warplan this, it gets into an infinite
        loop when planning "open the door".
*/
imposs( have(X)&empty ).


/*
Interface to Warplan
====================

I have not altered Warplan at all. Consequently, I need to change
representations before calling its main predicate plan/4. For details
of what these are, see WARPLAN.PL. Briefly, on calling plan/4, we
need to build up a set of clauses for given/2 representing the start state.
On exit, we need to transform the plan from a sequence of actions
joined by : to a list.
*/


/*  make_plan( CurrentState+, Goal+, Plan- ):
        Constructs a plan which will convert CurrentState to Goal.
        Undefined if one can't be made.
*/
make_plan( CurrentState, Goal, Plan ) :-
    warconj_vs_list( WarGoal, Goal ),
    retractall( given(start,_) ),
    assertlist( CurrentState, given(start,X), X ),
    plan( WarGoal, true, start, WarPlan ),
    warplan_to_list( WarPlan, Plan ),
    retractall( given(start,_) ).


/*  assertlist( List, Term, Var ):
        Term is of the form Pred(Arg,Arg,...) where one of the Args
        is Var. Bind each element of List to Var in turn, and
        assert Term.
*/
assertlist( [], _, _ ) :- !.

assertlist( [H|T], Term, Var ) :-
    non_binding_call(( H=Var, assert(Term) )),
    assertlist( T, Term, Var ).


/*  warconj_vs_list( C?, L? ):
        Converts a Warplan style conjunction of goals, written as
        Goal1&Goal2&... to or from a list of goals.
*/
warconj_vs_list( C, L ) :-
    warconj_vs_list( C, [], L ).


/*  warconj_vs_list( G?, L0+, G? ):
        Auxiliary predicate.
*/
warconj_vs_list( G, L0, [G|L0] ) :-
    G \= (_&_),
    G \= true,
    !.

warconj_vs_list( true, L, L ) :- !.

warconj_vs_list( (A&B), L0, L ) :-
    warconj_vs_list( A, L1, L ),
    warconj_vs_list( B, L0, L1 ).


/*  warplan_to_list( WarPlan+, Plan- ):
        Converts Warplan format plans into lists. Does not alter the
        individual terms therein.
*/
warplan_to_list( WarPlan, Plan ) :-
    warplan_to_list( WarPlan, [], Plan ).


/*  warplan_to_list( WarPlan+, SoFar+, Plan- ):
        Auxiliary predicate.  
*/
warplan_to_list( (A:B), SoFar, L ) :-
    !,
    warplan_to_list( A, [B|SoFar], L ).

warplan_to_list( A, SoFar, [A|SoFar] ).


/*
Fleshing
========

This section fleshes out Warplan plans with primitive bug-moves. This
was explained above when describing the brain.
*/


/*  flesh_plan( AWM+, Dir0+, Plan+, Fleshed- ):
        Fleshed is the fleshed-out plan corresponding to Plan. AWM
        is used for path-planning, and Dir0 is Bug's initial heading.
*/
flesh_plan( AWM, Dir0, Plan, Fleshed ) :-
    flesh_plan( AWM, Dir0, Plan, Fleshed, _ ).


/*  flesh_plan( AWM+, Dir0+, Plan+, Fleshed-, Dir- ):
        We need to keep track of Bug's heading after each path, and
        pass it as the initial heading when another segment of
        path is generated. The last argument of this predicate is the
        new heading.
*/
flesh_plan( AWM, Dir0, [A|RestPlan], Fleshed, Dir ) :-
    !,
    flesh_action( AWM, Dir0, A, FA, Dir1 ),
    flesh_plan( AWM, Dir1, RestPlan, RestFPlan, Dir ),
    append( FA, RestFPlan, Fleshed ).

flesh_plan( _, Dir, [], [], Dir ) :-
    !.


/*  flesh_action( AWM+, Dir0+, PlannerAction+, Moves-, Dir- ):
        Fleshes out one action. Note that go(_,_) assumes there
        is always a path.
*/
flesh_action( _, Dir, start, [], Dir ) :- !.

flesh_action( _, Dir, use(_), [use], Dir ) :- !.

flesh_action( _, Dir, grab(_,_), [grab], Dir ) :- !.

flesh_action( _, Dir, drop(_,_), [drop], Dir ) :- !.

flesh_action( AWM, Dir0, go(A,B), Moves, Dir ) :-
    move( AWM, Dir0, A, B, Moves, Dir ).

flesh_action( _, Dir, A, ['FLESH'(A)], Dir ).
/*  This should report an error.  I left it like this for testing.  */        


/*  move( AWM+, Dir0+, A+, B+, Moves-, Dir- ):
        Plan a path from A to B within AWM, and convert it into a list
        Moves of bug-actions. Dir0 and Dir are Bug's initial and final
        heading.
*/
move( AWM, Dir0, A, B, Moves, Dir ) :-
    prolog_eval( safe_path( AWM, A, B ), Path ),
    prolog_eval( path_moves( Dir0, Path ), [Moves,Dir] ).


:- prolog_language(pop11).


/*  safe_path( awm, a, b ):
        Return a path from a to b. It is allowed to pass through blank
        squares, and those containing keys, hammers, food, doors,
        and rocks. The latter two are because the planning actions
        are wrongly expressed. They should say that Bug has to be
        next to a door/rock in order to open it; in fact, they say
        he has to be in the same square. But if he plans a path which
        includes the door or rock, then the final move into the
        door or rock square will just fail, so such a path is OK.
        This is another hack.
*/
define safe_path( awm, a, b );
    define safe(awm,x,y);
        lvars awm, x, y;
        lvars c=awm(x,y);
        c = ` ` or c = `k` or c = `T` or c = `+` or c = `#` or c = `@`
    enddefine;
    awm_path( awm, a, b, safe );
enddefine;


/*  path_moves( dir, path ):
        Converts path to a list of moves. dir is Bug's initial heading.
        Call into PATH_TO_MOVES.P to do this.
*/
define path_moves( dir, path );
    lvars dir, path;
    [% path_to_moves( dir, path.prolog_full_deref ) %]
enddefine;


:- prolog_language(prolog).


/*
Executing plans 
===============

Once a plan has been generated, Bug just follows it action by action.
There is no execution monitoring. See comment for safe_path above
in this regard.       
*/


/*  follow_plan( Moves+ ):
        Moves is a list of bug actions. Obey each in turn.
*/
follow_plan( [] ) :- !.

follow_plan( [H|T] ) :-
    exec( H ),
    follow_plan( T ).


/*
Saying
======

'utter' allows Bug to say a sentence and then wait. This ensures that the
sentence gets displayed imediately: Eden doesn't display the results of 'say'
until the current turn has finished. The brain uses 'utter' to write some
of its progress reports.                   
*/


/*  utter( S+ ):
        Say sentence S and then wait.
*/
utter( S ) :-
    say( S ),
    exec( wait ).
