/*  EDEN_CORE.PL  */


:- module eden.

:- public
    eden/0,
    eden/1,
    eden/2,
    eden/3,
    eden/4,
    retina/3,
    energy/1,
    inventory/1,
    smell/1,
    sentence/1,
    say/1,
    (bug_message)/1,
    bug_using_ved/0,
    (bug_view)/1,
    bug_view_on/0,
    bug_view_off/0.


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

This is the main module defining Eden for Prolog. It has to be loaded by
EDEN.PL. For details of Eden itself, see HELP EDEN. The specifications
given below assume you have read that.


Running Eden:
=============

PUBLIC eden( <arguments> ):

'eden' starts Eden running, putting a bug into a world and setting it
off. The arguments are
    eden( <bug-file>, <world-file>, <options> )
    eden( <options>  )
up to a limit of four arguments. <options> stands for any number
(including none), upto the argument limit, of the following arguments,
in any order:
    noved
    batch
    [ save, <save-file> ]

<bug-file> and <world-file> must be atoms, denoting files. The bug-file
must be a file of Prolog code, and its extension defaults to .PL. The
world-file contains a saved world, as saved by the Ved 'saveworld'
command, and its extension defaults to .W.

If a file is specified without a directory, Eden looks first in the
current directory. If it can't find the file there, it then searches the
library files, as defined by 'prologliblist'. See HELP LIBRARY for more
details. This assumes Eden and the standard worlds and bugs will have
been set up as libraries, which I hope will be true on most systems.

The noved and batch options are atoms. The save option is a list whose
first element should be the atom 'save' and whose second element should
be a filename.

Example calls of eden are:
    eden( noved ).
    eden( 'MYBUG', tw1, [ save, mycases ] ).
    eden( manual_bug, 'MyWorld',
          [ save, 'CASES' ], batch ).


How to avoid repeating filenames
--------------------------------

If the world and bug you want to run are exactly the same as last time,
you can omit them. You can still specify the other options to 'eden', for
instance:
    eden.
    eden( noved );
    eden( [ save, 'CASES' ], batch ).


Saving cases
------------

By using the save option, you can direct that perceptions and actions
are to be stored in a file. You can then read this back later, and use
it (for example) for training a neural net.

In the present version of of Prolog Eden, you only have one option,
    [ save, <file> ]
where <file> is a filename, as an atom. This saves the perceptions to
<file> in the order
    retina sentence smell inventory energy

You will need to enter Pop-11 to read them back, and you should use LIB
STOW to do this. For further details, see HELP EDEN.


Running without Ved
-------------------

To run outside Ved, give the 'noved' option to eden(). Eden will run,
displaying the world and status information for each cycle in teletype
mode, as a sequence of lines. The prompts are the same, but you won't be
able to edit sentences passed to the listener. Examples are:
    eden( noved ).
    eden( talk_bug, tw3, noved ).


Batch mode
----------

In normal use, whether Ved or non-Ved mode, Eden interacts with the
user, prompting for the number of cycles to run next. This would make it
tedious to run a bug which needs to run through several hundred or even
thousand worlds. By giving the batch option, you can specify that
Eden is to run lives until bugdead finally returns 'stop', without
any prompting or display of the world. In batch mode, Eden prints three
lines to the standard output for each life, like this:
    Starting life 1.
    Life 1 succeeded on energy 494.
    Result from bugdead: rerun.


The senses:
===========

PUBLIC retina( X+, Y+, Obj? ):

Unifies Obj with the atom representing the object at location (X,Y)
in Bug's retina. If there is no object, the atom returned is ' '.


PUBLIC smell( Smell? ):

The direction of the food. This will be one of the atoms 'forward',
'right', 'back', 'left', 'here', 'carried'.


PUBLIC sentence( S? ):

Unifies S with the last sentence ``heard'' by Bug. [] if none, otherwise
a list of characters.


PUBLIC inventory( I? ):

Unifies I with the contents of Bug's inventory. This is an atom
representing an object: ' ' if none.


PUBLIC energy( E? ):

Unifies E with an integer representing Bug's current energy. This will
always be between 0 and its initial energy, inclusive.


Special output:
===============

PUBLIC bug_using_ved:

Succeeds if Ved is being used, i.e. if 'eden' was called without the
'noved' option. Fails otherwise.


PUBLIC bug_message Goal+:

bug_message is used to write to the Ved status line. It is a unary
operator of precedence 250, which takes a goal as argument. In Ved mode,
it calls its argument, which should produce some output. Rather than
displaying this immediately, it collects the output in a string, and
then displays that string on the status line.

In non-Ved mode, it just calls its argument, and then writes a newline.               

An example is:
    bug_message output( Meaning )

This calls the goal output(Meaning), and displays the output it would
send on the status line (in Ved mode). In non-Ved mode, it just calls
output(Meaning).      


The view window:
================

The bug_view predicate allows you to write output that will get sent to a
separate Ved window. There are also predicates for turning view output on
and off.


PUBLIC bug_view Goal+:

bug_view is a prefix operator of precedence 250, whose argument should
be a goal which generates some output. In Ved mode, before obeying the
goal, bug_view opens a new Ved window for a buffer called VIEW, and
gives it the bottom half of the screen, leaving the bug in the top half.
bug_view then arranges for the output from the goal to be sent to the
next location in the window. Once bug_view has finished, the window
remains open until the end of the run. Subsequent calls to bug_view will
write their output at the end of the same window.

In non-Ved mode, bug_view Goal is equivalent to Goal.

Examples are:
    bug_view write('Demonstrating my new bug\n')
    bug_view ( smell(S), write(S), nl )


PUBLIC bug_view_on:
PUBLIC bug_view_off:

The predicates bug_view_on and bug_view_off turn view on and off. By
default, 'eden' turns it on. If it's turned off, calls to bug_view will
have no effect. This is useful if you want to leave bug_view statements
in, but disable them on some runs.
*/


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

See notes in EDEN_CORE.P, and also below.
*/


:- library(chars_to_items).
/*  Load this so the user can access it without needing to load
    it.
*/


/*
The top-level predicate: eden
----------------------------

These predicates just pass their arguments to the Pop-11 'eden', together
with the special option "prolog". This enables Eden to adapt itself to
the needs of Prolog, e.g. in calling predicates to interface the brain.
*/


eden :-
    prolog_eval( eden(prolog) ).


eden( Arg ) :-
    prolog_eval( eden( Arg, prolog ) ).


eden( Arg1, Arg2 ) :-
    prolog_eval( eden( Arg1, Arg2, prolog ) ).


eden( Arg1, Arg2, Arg3 ) :-
    prolog_eval( eden( Arg1, Arg2, Arg3, prolog ) ).


eden( Arg1, Arg2, Arg3, Arg4 ) :-
    prolog_eval( eden( Arg1, Arg2, Arg3, Arg4, prolog ) ).


/*
Brains and bugdead
------------------          

In Pop-11 mode, Eden calls user-supplied routines think, start_thinking
and bugdead. In Prolog mode, it calls interface routines, defined below,
which are named prolog_think, prolog_start_thinking, and prolog_bugdead.
These call the user's Prolog predicates. They translate any arguments
from Pop representation to Prolog, and then translate any results in the
other direction.

We define the interface routines to lie in section $-eden, by enclosing
the definitions below in $-eden section brackets. The user's predicates
will always be assumed to lie in the top-level module. At present,
Prolog Eden doesn't use modules, so this is true by default; but if
I ever do make it use them, I'll keep it true. This means that the
routines have to switch section context to pop_section before calling
the user's predicates.

If you amend Eden to call any other user predicates, the interface
routines should go here.
*/


:- prolog_language(pop11).


section $-eden;


define global prolog_think();
    lvars action;
    prolog_newvar() -> action;
    call_in_section(
        pop_section,
        prolog_invoke(% prolog_maketerm(action,"think",1) %)
    ).erase;
    action.prolog_deref;
enddefine;


define global prolog_start_thinking();
    call_in_section(
        pop_section,
        prolog_invoke(% prolog_maketerm("start_thinking",0) %)
    ).erase;
enddefine;


define global prolog_bugdead( success );
    lvars success;
    lvars result;
    if success then
        "succeeded"
    else
        "failed"
    endif -> success;
    prolog_newvar() -> result;
    call_in_section(
        pop_section,
        prolog_invoke(% prolog_maketerm(success,result,"bugdead",2) %)
    ).erase;
    result.prolog_full_deref;
enddefine;


endsection;


:- prolog_language(prolog).


/*
Interface to perceptions
------------------------

We can call some of the Pop-11 perceptual routines directly. Others have
to be encapsulated, via interface routines defined below.
*/


retina( X, Y, Obj ) :-
    nonvar(X), nonvar(Y),
    !,
    prolog_eval( consword( apply(X,Y,apply(valof(retina))),1), Obj ).

retina( X, Y, Obj ) :-
    retina_xbounds( X, XLow, XHigh ),
    retina_ybounds( Y, YLow, YHigh ),
    retina_sweep( X, XLow, XHigh, Y, YLow, YHigh, Obj ).


retina_xbounds( X, X, X ) :-
    nonvar(X),
    !.

retina_xbounds( _, 1, 5 ).


retina_ybounds( Y, Y, Y ) :-
    nonvar(Y),
    !.

retina_ybounds( _, 1, 7 ).


retina_sweep( X, XLow, XHigh, Y, YLow, YHigh, Obj ) :-
    retina_between( X, XLow, XHigh ),
    retina_between( Y, YLow, YHigh ),
    prolog_eval( consword( apply(X,Y,apply(valof(retina))),1), Obj ).


retina_between( _, Low, High ) :-
    Low > High,
    !, fail.

retina_between( Low, Low, _ ).

retina_between( X, Low, High ) :-
    Next is Low + 1,
    retina_between( X, Next, High ).              


inventory( I ) :-
    prolog_eval( consword(apply(valof(inventory)),1), I ).


smell( S ) :-
    prolog_eval( apply(valof(smell)), S ).


energy( E ) :-
    prolog_eval( apply(valof(energy)), E ).


sentence( S ) :-
    prolog_eval( apply(valof(sentence)), S ).


say( S ) :-
    prolog_eval( say(prolog_full_deref(quote(S))) ).


/*
bug_message and bug_using_ved
-----------------------------

bug_message Goal passes Goal to the interface routine to_message,
defined below. This switches cucharout so that all characters output are
dumped into a string, which is then passed to the Pop-11 bug_messsage.
We use prolog_invoke to call Goal: see next section.   
*/


:- op( 250, fx, bug_message ).


bug_message( Goal ) :-
    prolog_eval(
                apply(
                      quote(Goal),
                      valof(word_identifier(to_message_PL,
                                            section_subsect(eden,valof(pop_section),false),
                                            true
                                           )
                           )
                     )
               ).


:- prolog_language(pop11).

section $-eden;

define to_message_PL( goal );                  
    lvars goal;
    vars cucharout;
    procedure (c); lvars c; c endprocedure -> cucharout;
    cons_with consstring {% prolog_invoke(goal).erase %}.bug_message
enddefine;

endsection;

:- prolog_language(prolog).


bug_using_ved :-
    prolog_evaltrue( apply(valof(bug_using_ved)) ).


/*
Viewing thoughts
----------------

bug_view Goal passes the constructed routine
    prolog_invoke(Goal)<>erase
to the Pop-11 routine to_view, which underlies the Pop-11 bug_view.
The constructed routine invokes Prolog on Goal, and then erases the
result which will indicate success or failure. See HELP POPTOPLOG.
*/


:- op( 250, fx, bug_view ).


bug_view( Goal ) :-
    prolog_eval(
                apply(
                      quote(Goal),
                      valof(word_identifier(view_PL,
                                            section_subsect(eden,valof(pop_section),false),
                                            true
                                           )
                           )
                     )
               ).


bug_view_on :-
    prolog_eval( apply(valof(bug_view_on)) ).


bug_view_off :-
    prolog_eval( apply(valof(bug_view_off)) ).


:- prolog_language(pop11).

section $-eden;

define view_PL( Goal );
    $-eden$-to_view( prolog_invoke(%Goal%)<>erase )
enddefine;

endsection;

:- prolog_language(prolog).


:- endmodule.
