Documentation for ACT: The Autonomous Creature Toolkit v1.0

WHAT THE TOOLKIT DOES

The toolkit, for any given world defined by a user,
constructs a system which, for each creature in the world at
each timestep, is able to make "reasonable" decisions which
are based on the current state of the world and the current
goals of the creature.

References:
This toolkit implements and supports the action selection
algorithm explained in "How to do the Right Thing" by Pattie
Maes, Connection Science, Vol. 1, No.3, 1989 (pp 291 - 323).
A working knowledge of this paper is not necessary to run
the provided examples, but is essential for writing your own
examples, or understanding how the algorithm works and what
the system is actually doing.

WHAT THE TOOLKIT DOES NOT DO

The toolkit does *not* over constrain the user.  In
particular, design decisions were made which allow a greater
degree of programmer flexibility at the expense of increased
complexity.  This is especially noticeable when developing
simulator files.

The toolkit also does *not* currently support learning in
any form.  Creatures are programmed by the user and do not
change their behavior (or anything else) over time.  ACT
will be changed in the future to support the learning
algorithm proposed by Pattie Maes in "Learning behavior
networks from experience", Proceedings of the 1st European
Artificial Life Conference, MIT Press, 1992.

Finally, goals and sensors in ACT are simplified.  A sensor
can be either off or on.  A goal can either be satisfied or
not satisfied.   Each sensor and goal should be thought of
as being connected to some (arbitrarily large) amount of raw
sensors plus some simple combinatorial logic which returns
either on or off for some (arbitrarily complex) concept.
Numeric weights indicating the strength of the motivation to
satisfy a given goal will be added in a future version of
the toolkit.  (It is unclear at this time if numeric weights
for sensors will be implemented -- they aren't required by
any of our future application plans.)

SIMULATOR AND WORLD FILES

Each ACT example consists of two parts, a simulator and a
world file.

There are three basic things a simulator file must do:
  define all sensors for creatures in the world
  define all actuators for creatures in the world
  keep the state of the world

A world is a simulator plus a group of creatures which
inhabit it.

Both parts are extremely important, and when developing
examples, you will find yourself switching between the two
files often.

The decision to have separate files was based on the notion
that the state of the world should be separate from the
creatures that inhabit it as well as separate from the
algorithm that controls the creatures.

DEFMODULE CONSTRUCT

(defmodule module-name
  :conditions '(proposition1 ... propositionn)
  :expected-positive-effects '(proposition1 ... propositionn)
  :expected-negative-effects '(proposition1 ... propositionn)
  :actions '(action1 ... actionn))

The conditions clause specifies the propositions which must
be on before the module will be considered for activation.

The expected-effects clauses indicate which propositions
should be on or off after the module is activated.

The symbols in the actions clause map to actuators in the
simulator which are called sequentially from  left to right
when the module is activated.  The actuators are responsible
for changing the simulator state in response to the
activation of the module.  The mapping from action (symbol)
to actuator (function) is a property of the current
simulator and is defined through the defsimulator construct.
Each symbol referenced in an actions clause in a module is
expected to appear on the left hand side of the action to
actuator mapping in the appropriate defsimulator construct.


Example:

(defmodule pick-up-sprayer
  :conditions '(sprayer-somewhere hand-is-empty)
  :expected-positive-effects '(sprayer-in-hand)
  :expected-negative-effects '(sprayer-somewhere hand-is-empty)
  :actions '(sim::pick-up-sprayer))

(This format is clearly very similar to that described in
the paper.)

DEFCREATURE CONSTRUCT

(defcreature creature-name
  :modules '(module-name1 ... module-namen)
  :proposition-to-sensor-mapping
    (list '(proposition1 simulator-sensor1)
          ...
          '(propositionn simulator-sensorn))
  :once-only-goals '(proposition1 ... propositionn)
  :permanent-goals '(proposition1 ... propositionn))

The modules clause specifies all the modules which the
creature can activate -- and must choose between at each
time step

The proposition to sensor mapping implements a simple brain
concept to raw physical sensor (proposition to sensor)
mapping.  When the creature is preparing to execute the
action selection algorithm, it quickly checks each of its
sensors -- if a particular sensor function on the right hand
side of this mapping returns the value t, the corresponding
proposition on the left hand side is considered to be on.
Similarly for nil and off.  This construct (along with the
action-to-actuator mapping in the simulator file) helps
support distinct multiple creatures which are identical in
architecture and do not share sensors.  In addition, all
references within a given module are to propositions -- and
therefore are creature-relative references to sensors.  This
enables a number of creatures to share the same modules
without having to rewrite them for each creature.

The two goal clauses indicate the propositions which the
creature desires to turn on.  A once-only goal is a goal
which needs to be satisfied only once.  A permanent goal is
a goal which needs to be satisfied continuously.  The
charniak creature's desire to sand the board is a good
example of a once only goal.  The minsky creature's desire
to build towers is a good example of a permanent goal -- the
minsky creature wants to continuously build towers.  Note
that each of these goals must be a proposition (i.e. appear
on the left hand side of the proposition-to-sensor-mapping).

Example:

(defcreature charniak-creature
  :modules         '(place-board-in-vise
                     spray-paint-self
                     sand-board-in-hand
                     sand-board-in-vise
                     pick-up-sander
                     pick-up-sprayer
                     pick-up-board
                     put-down-sprayer
                     put-down-sander
                     put-down-board)
  :proposition-to-sensor-mapping
  (list '(sprayer-somewhere #'sim::onp-sprayer-somewhere)
        '(sander-somewhere  #'sim::onp-sander-somewhere)
        '(board-somewhere   #'sim::onp-board-somewhere)
        '(hand-is-empty     #'sim::onp-hand-is-empty)
        '(sander-in-hand    #'sim::onp-sander-in-hand)
        '(sprayer-in-hand   #'sim::onp-sprayer-in-hand)
        '(board-in-hand     #'sim::onp-board-in-hand)
        '(board-in-vise     #'sim::onp-board-in-vise)
        '(board-sanded      #'sim::onp-board-sanded)
        '(self-painted      #'sim::onp-self-painted)
        '(operational       #'sim::onp-operational))
  :once-only-goals '(board-sanded self-painted)
  :permanent-goals '())

DEFWORLD CONSTRUCT

(defworld world-name
  :creatures '(creature-name1 ... creature-namen)
  :simulator sim::simulator-name)

The creatures clause specifies all the creatures living (or
for robots, residing) in this world.  At each time step,
each creature, in order from left to right, is given the
opportunity to sense the current state of the simulator and
select a module to activate.  Note that each creature can
appear in this list only once.

The simulator clause points to the simulator for this
particular world.  Note that everything pertaining to the
simulator is in the "SIM" package, so the name of the
simulator (as defined using the defsimulator construct) must
be prefaced by "sim::".

Example:

(defworld charniak-world
  :creatures             (charniak-creature)
  :simulator             sim::charniak-simulator)

WORLD FILE DETAILS

The world file:

Every world file must begin with "(in-package "ACT")".
Toolkit files expect to find certain structures in this
package.

The defworld construct should appear at the end of the file.
All the defmodule and defcreature constructs in a world file
should be designed to be "put together" into a world
definition at the end of the file.

Each defcreature construct must occur *after* all the
defmodule constructs for the modules in it's :modules
clause.

THE SIMULATOR

There are three basic things a simulator must do:
  define all sensors for creatures in the world
  define all actuators for creatures in the world
  keep the state of the world

Sensors are represented as LISP functions which take the
name of a creature as an argument (represented as a string
in all capitals, for example "CHARNIAK CREATURE") and return
either t or nil.

Actuators are represented as LISP functions which again take
the name of a creature as an argument, and are expected to
modify the current state directly.  They do not return
anything usable.

The state of the world can be kept in any convenient format
which allows for creature-independent sensors and actuators.

DEFSIMULATOR CONSTRUCT

(defsimulator simulator-name
  :initialize-function #'initalize-function-name
  :action-to-actuator-mapping
  (list '(action1 #'actuator-function-namen)
        ...
        '(actionn #'actuator-function-namen)))

The initialize-function is a LISP function which takes no
arguments and resets the state of the world to the initial
settings.

The action-to-actuator-mapping implements a simple brain
concept to raw physical activity (action to actuator).  When
the creature activates a given module, it wants to perform
each *action* sequentially in order.  This mapping exists
almost solely so that the user can modify the actuators in
the simulator without having to modify the module or
creature definitions.  The new modification will work
correctly as long as the function on the right still
represents the physical activity indicated by the symbol on
the left.

USING SIMVARS IN THE SIMULATOR

Many ACT examples have states in the world which directly
correspond to certain sensors being on or off.  Defsimvars,
a special construct, was created to pick off the majority of
the simple world state bookkeeping, as a majority of the
state of the world can be kept by using Boolean values.

(defsimvars '(simvar-name1 ... simvar-namen))

For each simvar-name, four functions are defined in the
"SIM" package:
  set-on-simvar-name   sets the given simvar to on
  set-off-simvar-name  sets the given simvar to off
  onp-simvar-name      returns t if the simvar is on
  offp-simvar-name     returns t if the simvar is off

For example, the first simvar hand-one-empty has the
functionality:
  (set-on-hand-one-empty)    sets hand-one-empty simvar on
  (set-off-hand-one-empty)   sets hand-one-empty simvar off
  (onp-hand-one-empty)       t if hand-one-empty is on
  (offp-hand-one-empty)      t if hand-one-empty is off

Each of these functions are defined in the SIM package and
are not exported.  Similar versions of each of these four
functions are created for each simvar in the defsimvar
construct.  Collectively, these functions provide a clean
way of handling the state of the simulator.

The usefulness of the defsimvars construct is apparent when
looking at the included ACT examples.

Note: simvar is shorthand for SIMulator VARiable.

Aside: All of these functions are actually defined so they
can take an optional argument, a string, which is the print
name of the current creature.  Functions defined through the
defsimvars construct *do not* use this argument even if it
is provided -- the state of a given simvar is the same
regardless of which creature is currently active.  However,
complex examples may have observers, such as "onp-food-in-
view" which can be on or off depending both on the state of
the simulator *and* the current creature.  Because of this,
a convention is followed where every one of these functions
is expected to be able to take the creature name string as
an argument.  Of course the observer function is free to
ignore this argument, which is precisely what the functions
defined by defsimvars do.  If you don't understand this
paragraph, feel free to ignore it entirely, and come back to
it later when you are trying to construct an example with
more than one creature.

SIMULATOR FILE SUMMARY

Simulator files always start with (in-package "SIM").

The defsimvars construct allows for quick and easy
definition of Boolean simulator state variables and
functions to manipulate them.  There can only be one in any
simulator file, and it must occur before any references to
functions which are created as a result of its evaluation.
The safest thing to do is to have the defsimvars construct
as the second LISP expression in the simulator file
(immediately after the in-package declaration).

If a creature or module needs to be able to change something
about the state of the simulator, you need to define an
actuator function for it.

Furthermore, you need to make a symbol for each actuator
function via the :action-to-actuator-mapping clause of the
defsimulator construct.

You also need to make a function which initializes the
simulator, and reference it in the :initialize-function
clause of the defsimulator construct.  It is a good rule of
thumb to check over your code and make *sure* that all
special variables and simvars are reset to the appropriate
values.

Simulator files should always end with the defsimulator
construct.

WORLD FILE SUMMARY

World files always start with (in-package "ACT").

For every module you wish to define, you must include a
defmodule construct.  The :conditions clause lists those
predicates which must be on in order to activate the module.
The :expected-positive-effects and :expected-negative-
effects clauses list those predicates which are expected to
change state as a result of activation of the module. The
:actions clause is a list of actions to be executed (in
order from left to right) when the module is selected for
activation.  Each element of the action list must be
included in the :action-to-actuator-mapping clause (on the
left hand side) of the current simulator .  Each element of
the other three lists must be included in the :proposition-
to-sensor-mapping clause (on the left hand side) of any
creature which uses the module .

For every creature you wish to define, you must include a
defcreature construct.  The :modules clause lists all the
modules which can be activated by the creature.  The
:proposition-to-sensor-mapping clause defines a mapping
between the name of a proposition (on the left hand side)
and a simulator function which defines the state of the
sensor for the creature.

The world is simply a list of the creatures in it, plus a
simulator which keeps the state of the system.

To avoid forward reference errors, it required that all
modules be defined before they are referenced by a
defcreature.  All creatures must be defined before they are
referenced by a defworld.

Any action referenced in a module must appear on the left
hand side of the :action-to-actuator-mapping clause of the
current simulator.  Any sensor referenced in a module must
appear on the left hand side of the:proposition-to-sensor-
mapping clause of any creature which includes it in its
:modules clause.

HOW TO GET STARTED

Make sure you have a good copy of Lucid Common LISP or
Macintosh Common LISP.  These two LISPs are currently the
only ones supported.

Copy the toolkit files into a separate directory or folder.
IMPORTANT: make a note of this location.

Start up your favorite editor (perhaps the editor that
comes with your LISP) and edit the "actstart.lisp" file.
Put the pathname which points to your copy of the toolkit in
either *lucid-path* or *mac-path* (depending on which LISP
and platform you are using).  Save this version of
"actstart.lisp".

Start LISP if you haven't already.

Load "actpackage.lisp".  This file defines all the packages
for the toolkit.

Load "actstart.lisp".  This file defines many important top
level functions.

Do an "(in-package "ACT")".  This allows you to access the
toolkit functions.

If the files aren't already compiled, do a "(rebuild)".
The toolkit files need only be compiled the first time the
system is used.  The compilation process takes roughly 15
minutes on a Macintosh IIfx with MCL, roughly 25 minutes on
a DECStation 5000/120 with Lucid LISP.  NOTE: the files need
to be compiled in a certain order and in a certain way, so
please use this function and do NOT try to "wing it".
Thanks.

Do a "(start)".  This loads all of the toolkit files.

You are now ready to try running some of the examples.  If
you would just like to get the toolkit doing *something*, do
"(charniak-start), "(blocks-start)" or "(minsky-start)".
These three functions (plus a handful of others) execute one
of the built in examples and save the output as
charniak.out, blocks.out and minsky.out respectively.

If you want to play with the examples more extensively, take
a look at the supplied execution trace, most of the
important interactive functions are used there.

INTERACTIVELY RUNNING EXAMPLES

If running your own examples, you must first compile and
resolve any errors in your simulator and world files.  Do
the following steps:
  (load-compile-and-load (source-filename "foo-simulator")
  (compile-file (source-filename "foobar-world")

Once your files are presumed error free, you can try to run
them.  As before, when running the provided examples, load
actpackage.lisp, actstartup.lisp, get into the ACT package
and type (startup).

(new-experiment
  :simulator-filename
  (compiled-filename "foobar-simulator")
  :world-filename
  (compiled-filename "foobar-world"))
will set things up appropriately for a new experiment.  The
compiled versions of these files are looked for in the
directory or folders where the ACT toolkit lives.  They are
loaded and some initialization takes place.  This function
takes an optional keyword argument of :output-filename which
defaults to "default.out" in the currently defined
directory.  See the end of actstart.lisp for some examples
of how to use this optional argument.  The output file is
superseded (i.e. if it already exists, it is overwritten).

(init-experiment) resets the current experiment so it can be
started again from the beginning.  The current output file
is erased and started over with a header line and the
initial parameter settings for each of the creatures in the
world.

(run-experiment integer) runs the current experiment for
integer timesteps.  Integer defaults to 1 if omitted.  The
trace output is appended to the current output file if file
output is on.

OUTPUT CONTROL

(file-off) suspends trace output to the output file.  Note
that some things are still written to the output file --
changes in the simulator state, creature parameters and
creature goals.  In addition, the old output file is still
superseded if (init-experiment) is called.

(file-on) resumes trace output to the output file.

(screen-off) turns screen output off.  Minimal information
is still displayed.

(screen-on) turns screen output on.

(screen-brief) whenever screen output is enabled, prints
just the final activation levels for each creature rather
than the whole trace.

(screen-verbose) whenever screen output is enabled, print
the whole trace for each creature.

The file output and screen output are entirely independent.

SUPPORT

ACT is currently available via FTP from
josquin.media.mit.edu.  login anonymous, userid as password,
and cd ACT.  Grab all the files there.  The binaries are
from Lucid version 4.0 and run on a DECStation 5000/120.

ACT is also available from the Macintosh Breaking Glass if
you have Ethertalk and are inside the MIT Media Lab.  Go to
the Chooser, select Appleshare and Ethertalk.  Select
Breaking Glass.  Connect as a guest and access the folder
ACT Public.  Copy the contents of this folder over to your
local Macintosh.  The binaries are from MCL 2.0B4c5 and run
on a Macintosh IIfx.

There is a mailing list for users of this toolkit.  Send your 
request to be added to the list to 
act-request@media-lab.media.mit.edu.

Finally, if you have questions about ACT, feel free to send
email to Bob Ramstad boccibob@media-lab.media.mit.edu or
Pattie Maes pattie@media-lab.media.mit.edu.  Please also
contact us if you have any neat examples, or suggestions for
improvements!

SAMPLE SIMULATOR

The charniak-simulator is a good example of a standard
simple simulator.

Every simulator file must begin with (in-package "SIM").
The toolkit expects to find certain constructs in the SIM
package.

Look at the end of the file.  Everything in a simulator file
is designed to be "put together" into a simulator
definition.  The name of the simulator is "charniak-
simulator".

Each simulator is defined by two things.  The initialize-
function is called whenever a new experiment is started.
This function is supposed to reset any state which the
simulator keeps to its appropriate initial value.  It should
expect no arguments.  Look at the function definition for
init-simulator for an example.  We'll come back to this
function later.

A creature in a world must be able to affect the state of
the simulator.  It does this by activating a module --
thereby performing a series of actions.  Look back at the
defsimulator construct.  The :action-to-actuator-mapping
clause requires a list with a very special format.  When the
defsimulator is evaluated, each of the action symbols on the
left hand side is bound to the actuator functions on the
right hand side.  There are two good reasons for this.
First, the functions and function names on the right hand
side can be modified without having to change the world
file, as modules in the world file only reference the
symbols on the left hand side.  Second, this format allows
aliasing where two different left hand symbols both refer to
the same right hand function -- this can be useful in
certain situations, especially during development of
complicated examples.

Look at the function definitions starting with "actuator".
Each function referred to on the right hand side of the
action-to-actuator-mapping clause is expected to take a
creature print name (a string) as an argument.  This is for
support for functions such as "get-food" where, in a
multiple creature world, it is essential to know which
creature is getting the food (and the position of the
creature) before modifying the state of the simulator.  The
charniak example has only one creature, so we can ignore the
argument (the declaration keeps the compiler from
complaining about bound but not referenced variables).
As you may have noticed, there are many function calls to
functions starting with "onp", "offp", "set-on" and "set-
off" throughout the actuator functions, as well as elsewhere
in the file.  These functions are defined by the defsimvars
construct, which is explained elsewhere in this document.

Look at the function definition for one of the actuator
functions, actuator-pick-up-sprayer.  If the defsimvars
construct is understood, this function should make sense.
It checks to see if the sprayer is somewhere and a hand is
empty.  If so, it puts the sprayer in the hand, and
indicates that the sprayer is no longer available to be
picked up and that hand is now occupied.  All of the
actuator definitions follow the same type of structure.

To sum up, the action-to-actuator-mapping clause of the
defsimulator construct can be thought of as a lookup table:
whenever an action is referenced by a module in the world
file, it is translated into a call to the appropriate
simulator function by finding the action symbol on the left
hand side and calling the corresponding actuator function.

Most of the stuff in the charniak-simulator.lisp file should
now be understood.  There is one elegant hack which accounts
for the remaining LISP expressions.  Right below the
defsimvars construct there are four functions, which
together seem to manually define a predicate hand-is-empty.

The charniak creature, which is modeled in the world file,
has two hands.  The charniak creature, furthermore, is
perfectly ambidextrous.  If the charniak creature wants to
pick something up, it can pick it up as long as either its
left or right hand is empty. Moreover, if it does pick
something up, the object only occupies one of its hands --
if the other hand is empty, it can still pick something else
up as well.

These four functions, which follow the same conventions as
the functions defined through the defsimvars construct,
state:

hand-is-empty is on if either one or two hands are empty
hand-is-empty is off if no hands are empty
if setting something down:
  if currently one hand is empty, drop the object, and
    indicate that two hands are now empty
  otherwise indicate that now one hand is empty
if picking something up:
  if currently two hands are empty, pick up the object, and
    indicate that one hand is now empty
  if currently one hand is empty, pick up the object, and
    indicate that there are no longer any empty hands

The state of the two hands, each of which is either empty or
full, could be easily modeled by special variables.
However, by defining one-hand-empty and two-hand-empty as
simvars, we can use the functions defined for each of these
simvars without having to deal with the grunge of special
variables.

A major reason for programming in this fashion is that, by
default, when running experiments the state of all simvars
is outputted automatically.  By having one-hand-empty and
two-hand-empty defined as simvars, their status will
automatically be outputted, allowing for easier debugging
and a more complete trace of the experiment.

This elegant hack may look inefficient, and it is possibly
slightly less efficient than the obvious implementation
using special variables (it depends on the LISP you are
using), but it succeeds in simplifying the simulator file
and in reducing development time.

FOR ADVANCED USERS

Defsimulator has an optional clause -- :print-state-
function.  The print-state-function is a function which
outputs the current state of the simulator (preferably in a
neat, human readable format).  It is expected to take an
output stream as an argument, and should default to the
standard-output stream.  It defaults to a function which
outputs the names of all simvars which are currently on.  It
can be customized when doing complicated simulations.

Defcreature takes a number of optional arguments.  Each
creature maintains its own parameters as described in the
paper.  :influence-from-goals, :influence-from-state,
:influence-from-protected-goals, :mean-activation-level and
:initial-threshold are clauses which override the default
parameter values.  For an example of this, see blocks-
world.lisp.

When writing large examples, it is possible you may run up
against some hardwired maximums.  The maximum number of
simvars in a simulator is 20.  The maximum number of modules
defined in a world is 20.  The maximum number of creatures
defined in a world is 10.  The maximum number of sensors for
a creature is 20.

These maximums can be changed, but require modification of
files, and then using the (rebuild) command to recompile all
of the files as you may have done when first preparing the
system.

To change the maximum number of simvars in a world, search
for all occurrences of "hardwired simvars maximum" in the
simvars.lisp file, and follow the directions given.

To change the maximum number of modules in a world, search
for all occurrences of "hardwired module maximum" in the
world.lisp file, and follow the directions given.

To change the maximum number of creatures in a world, search
for all occurrences of "hardwired creature maximum" in the
world.lisp file, and follow the directions given.

To change the maximum number of sensors in a creature,
search for all occurrences of "hardwired module maximum" in
the world.lisp file, and follow the directions given.

