ErunticLab 0.95 Lee Spector, 1995 lspector@hampshire.edu Latest revision: November 2, 1995 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; OVERVIEW ErunticLab is an artificial life environment designed for experimentation at the intersection of animal behavior studies and computer science. It was written as course software for the Spring 1995 Animals and Animats course at Hampshire College. Animals in ErunticLab have lisp-like behavior programs and produce offspring using the tree crossover operation developed by Koza [J. Koza, Genetic Programming, MIT Press, 1992]. Development of ErunticLab stopped at the end of the Spring 1995 semester. The system is not polished (hence the 0.95 version number) and it is not being actively supported. It may nonetheless be of interest to experimentalists and to researchers working on combinations of genetic programming and artificial life technologies. A few of ErunticLab's salient features: - genetic-programming-like reproduction - a "code-stepper" utility that allows animals to execute their programs with function-call-level pseudo-parallelism - a wide range of animal sensors/actions/features including local indexed memory, the ability to drop numbered "markers" and to raise flags visible to other animals, etc. - data-logging functions that allow one to produce tables that can be imported into graphing packages - simple experiment set-up procedures; one can easily change a wide range of parameters, the primordial "soup" out of which animal programs are constructed, the initial state of the world, etc. The core elements of ErunticLab are written in Common Lisp with CLOS and should be portable to any CLtL2-compliant system. The current version of ErunticLab was developed in Macintosh Common Lisp (v2.0.1) and includes a Macintosh user interface. ErunticLab was tested on beta versions of Macintosh Common Lisp 3.0 -- it ran correctly but some of the user interface elements were improperly displayed due to changes in default appearances of dialog items. It may be necessary to tweak the dialog specifications if you wish to use the MCL user interface in current versions of MCL. See "SPECIAL INSTRUCTIONS FOR NON-MACINTOSH ENVIRONMENTS" below if you want to run ErunticLab in a non-macintosh environment. This document presents only basic concepts and a few guidelines on the use of the system. Unfortunately, a real "user's guide" doesn't exist, but you should be able to figure things out from this document and from the documentation in the various configuration files (mentioned below). The rest of this document is organized as follows: - CREDITS - BASIC CONCEPTS - LOADING AND RUNNING ERUNTICLAB - SPECIAL INSTRUCTIONS FOR NON-MACINTOSH ENVIRONMENTS - HOW NOT TO CRASH THE MAC INTERFACE - REVISION HISTORY - TO DO LIST ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CREDITS A few functions in ErunticLab were taken from John Koza's Genetic Programming Lisp code or from optimized versions of Koza's functions produced by KAnderson@bbn.com. The world plot code in the Macintosh user interface uses code by Paul McCartney. The name for ErunticLab comes from a piece by Stanislaw Lem [S. Lem, Imaginary Magnitude, Harcourt Brace Jovanovich, 1981]. Adam Alpern provided the about-box for the Macintosh version and the associated code. Mark Feinstein and students in our Animals and Animats course at Hampshire College helped to develop and refine several of the ideas that lead to the current system. All code not credited to others above is (c) 1995 Lee Spector. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; BASIC CONCEPTS An ErunticLab world is a toroidal grid of world locations. The dimensions of the world are *max-x* by *max-y*, where *max-x* and *max-y* are parameters that may be changed by the user (see Parameters.lisp). By default both *max-x* and *max-y* are 10. Each location may contain a variety of objects; ErunticLab currently supports five kinds of objects: rocks, plants, animals, markers, and corpses. Rocks are inert; they do nothing but take up room (possibly limiting the amount of plant growth, etc.). Plants grow and can be eaten by animals. Animals are what ErunticLab is all about; they have a variety of features including behavior programs. Markers are objects that can be "dropped" by animals as signals to themselves or to other animals; they decay over time. When animals are killed they leave corpses behind; these may be eaten by other animals and they also decay over time. Animals that starve do not leave corpses, they just wither into nothingness. Each world location may contain a maximum of 9 units of "size" of any one type of thing (rock, plant, animal, marker, corpse). The total "size" of all types combined cannot exceed the parameter *max-size-per-location*. This parameter, along with all others mentioned in this document, may be changed by the user by editing the file Parameters.lisp. Most types of things have default initial sizes; these are set via the parameters *initial-rock-size*, *initial-plant-size*, *initial-animal-size*, and *initial-marker-size*. Corpses are created with the size of the animal that was killed. Rocks never change in size. Plants grow in size; every growing season a certain amount is added to each plant's size. The length of the growing season (in behavior-function-call cycles -- this is the unit of time measurement for everything in ErunticLab) is set via the parameter *cycles-per-growing-season*. The amount to be added to each plant each growing season is set via the parameter *plant-growth-rate*. Animals never change in size -- this may seem a little odd and it is certainly something that might be changed in future releases. Animals do, however, have stomachs that change in fullness as they eat and expend energy. Each animal is born with an amount of energy (or "stomach fullness" -- these terms are equivalent) obtained from the parameter *initial-animal-energy*. Markers decay over time; they lose *marker-decay-rate* units of size every cycle, and they are deleted when they have no remaining size. Corpses also decay over time; they lose *corpse-decay-rate* units of size every cycle, and they are deleted when they have no remaining size. Note that the size limits allow the creation of "niches" of various sorts. For example, it is possible to create locations that are so rocky that only one animal can inhabit the location at a time, or locations that can support only minimal plant growth. At each unit of time the following things happen: - the *world-time* clock is incremented - the world is randomly repopulated if the population has fallen below a set threshold (see the parameters in Parameters.lisp) - a small number of new random individuals may be added regardless of the settings of the repopulation parameters, for the sake of diversity (again, see Parameters.lisp) - each animal performs one function call of its behavior program - if it's the right time in the growing season then plants grow - markers decay - corpses decay A wide range of functions are generally available for use in animal programs. A user may selectively include/exclude specific functions from the "primordial soup" or, with more lisp coding, add new functions. A few general comments about the functions are included here; to find out more about the included functions read the comments in Soup.lisp. The "closure" of the function and terminal sets for animal programs is the range of integers [0-9]. This means that every function used in an animal program must return a number in this range, that these numbers or symbols that evaluate to these numbers are the only allowed terminal atoms, and that every function used in animal programs must take numbers in this range as the values for all parameters. A user need not be concerned with this fact unless she is extending the function set. Many behavior functions have associated energy costs. In fact, each animal is "charged" an energy cost, obtained from the parameter *existence-cost*, on each cycle regardless of what actions are performed. Other costs, obtained from the parameters *movement-cost*, *sensing-cost*, *attacking-cost*, *defending-cost*, and *mating-cost* are charged for the execution of particular actions; see Soup.lisp for details. Costs for specific actions may also be specified so that, for example, a particular sensing action may be charged at a higher (or lower) rate than other sensing actions; see Soup.lisp for an example. When an agent attempts to do something for which it has insufficient energy it dies of starvation and is removed from the world. Animals will also die of old age; see the parameter *max-animal-age*. No corpse is left when an animal dies of starvation or of old age. Animals can attack each other. An animal executing the ATTACK function attacks the weakest other animal in its location; the energy cost to the attacker is *attacking-cost* and the energy cost to the defender is *defending-cost*. If the defender has insufficient energy to defend then it dies and becomes a corpse. The attacker and other live animals may eat the corpse. The nutritional value of meat relative to plants is obtained from the parameter *meat-scaling-factor*. The sizes of plant and meat meals (the amounts of energy obtained from meals and the amounts of size removed from foodstuffs) are obtained from *veg-meal-size* and *meat-meal-size* respectively. If a defender survives an attack then it remembers the attack for *attacked-memory-duration* cycles; it may access this memory via the ATTACKED function. Manipulation of the attack and meat-meal parameters allows for the exploration of various theories of animal predation. Most sensing actions have one of the following forms: - sense how much of something is in my present location, rounded if necessary to the range [0..9]. - sense the direction in which the maximum (or minimum) amount of something can be found in my immediate neighborhood. In this case a return value of 0 means "where I am," a return value of 1 means "directly in front of me," return values of 2 through 8 mean in front of me after progressively higher numbers of clockwise turns, and a return value of 9 means something like "nowhere". The details vary for particular functions; see Soup.lisp. Animals may not generally refer to other animals as individuals. Most functions that require reference to an animal other than the actor select the target animal according to some pre-defined criterion. For example, an attacking animal will always attack the weakest other animal in its location. Similarly, a mating animal will always attack the strongest other animal in its location. There are three communication mechanisms. First, animals can drop and sense numbered markers (described above). Second, animals can raise and sense numbered flags. Third, up to 10 action functions may be made "visible" -- memories of the performances of these actions are maintained for *action-memory-duration* cycles and may be sensed by other animals using the ACTION-MIN, ACTION-MAX, and ACTION-HERE functions. The PRETEND function can be used to simulate the performance of an action to any action-sensing neighbor. It is hoped that the visible action memories and the PRETEND function will allow for the development of communication strategies through "ritualization." Animals have local indexed memories (see the STORE and RETRIEVE functions). A variety of conditional action functions (e.g. EAT-IF=) as well as conditional and looping pseudo-macros (e.g., IF=, WHILE=) are also provided; see Soup.lisp. Data collection is facilitated by a "data-zone" mechanism. Each data-zone defines a rectangular area of the world about which data will be collected. By default there are three data-zones, and by default data-zone #1 covers the entire world, while data-zones #1 and #2 cover only the location <0,0>. One can change the boundaries of these data-zones via the Data dialog in the Macintosh user interface, or via changes to the calls in StartupScript.lisp. By editing ErunticLab.lisp one can also create a larger number of data-zones. The accumulated data from a data-zone can be saved to a file via the "Save..." buttons on the Data dialog or via the save-data function. Three types of data are collected for each data-zone. The first, "snapshot data," is recorded and collected at the end of each data collection period. The length of data-collection periods is obtained from the parameter *collect-data-every* in Parameters.lisp. The second type of data, "runtime data," is recorded as actions take place in the world. For example, one can keep a running tally of all births as they happen. At the end of each data collection period all of the runtime data is collected and all tallies are reset to zero. The third type of data, "gene-pool data," is a special type of snapshot data that indicates, for each atom in the primordial soup, the number of animal programs in the given data-zone that contain that contain that atom. For example, a gene-pool datum such as (ATTACK 234) for data-zone 0 means that there are 234 animals in data-zone 0 that have programs that include the atom ATTACK. Gene pool information can also be obtained at runtime by clicking on the Gene Pool button in the Info dialog. Note that the phrase "gene pool" here means the number of programs containing each atom. The phrase "aggregate gene pool" is used for the total number of instances of a given atom, summed over all animals. One can look at the aggregate gene pool at runtime by clicking on the Aggregate gene pool button in the Info dialog (or by calling aggregate-gene-pool). ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; LOADING AND RUNNING ERUNTICLAB Under MCL: launch MCL and then load the file "@load-elab.lisp" by selecting "Load..." from the Eval menu. This will load several files and create user interface elements. Loading is complete when you see "Welcome to ErunticLab!" in the Listener window. The general pattern for ErunticLab use is something like this: - set up for a run - run for a while, watching & poking around as it goes - save data to files - analyze/graph data within other applications SETTING UP FOR A RUN The main things you'll probably want to do to set up for a run are: - possibly change parameters; see instructions in Parameters.lisp - possibly change the primordial soup; see instructions in Soup.lisp - populate the world with rocks, plants, animals via the Populate dialog - set up data zones via the Data dialog You'll also want to take notes on everything you do to set up the run so that you can make sense of the results later; the program does not currently log all of this information, although future versions might. RUNNING Click the "Run" button on the control windoid. More runtime information can be displayed, at the cost of system performance, via the "Continuous Display" and "Verbose Trace" options. You can stop the run by clicking on "Stop," examine various things via the Info dialog (also try clicking on the world plot), and then restart the world. SAVING DATA TO FILES Click on the "Save..." button for the appropriate data-zone in the Data dialog. Note that no data is saved to files until you click on one of these buttons or call save-data directly. ANALYZING/GRAPHING DATA The data files are tab-delimited text files. The first row is column headers; all other rows are numerical values. Cricket Graph III (available for use in the Animals and Animats course) will open the data files correctly if you check the check box for "Load first row into column titles" in the "Text File IO" dialog that can be selected from the "Preferences" submenu of the "Options" menu. You'll probably want to make line graphs with TIME as the X axis and various other things on the Y axis. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SPECIAL INSTRUCTIONS FOR NON-MACINTOSH ENVIRONMENTS The system has been tested under CMU lisp with PCL and should work under any common lisp that is either CLtL2 compliant or as close as CMU/PCL. To use the system in a non-Macintosh environment copy all of the .lisp files from the top-level ErunticLab folder to your non-Macintosh machine. Also copy any animal-program files that you want to use in your initial populations. The loader file is conditionalized and should work properly as-is. You'll probably want to re-compile the system for the new environment: after loading do (compile-elab) and then either do (load-elab) or quit and reload the system. Compiling under CMU lisp may generate a lot of warnings if you change the extensions:inhibit-warnings setting; ignore them (or change the code to reduce them and send me the changes!). The file StartupScript.lisp contains calls to set up the world and the data zones without the Populate or Data dialogs; edit the file appropriately and then load it. Run the world with (run-world ) and set *trace-verbose* to T or NIL to see more or less information as the run progresses. Calling (run-world) with no arguments causes the world to run until you abort or break; to leave the world in a consistent state after such a call you must break, set *stop-run* to T, and resume -- the system will then halt in a consistent state after the completion of the current cycle. It's probably easier just to provide a number of cycles in the call to run-world. Use (save-data ) to save data to a file. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; HOW NOT TO CRASH THE MAC INTERFACE The world plot window, while cute, may crash your machine if the world is running while you switch between applications (and perhaps in some other circumstances). I haven't yet isolated the problem, but for the moment I suggest that you close the world plot window during long runs. You can always get it back by clicking on "Refresh Display." Use the world plot to see how you are populating the world and for visualization at various times during a run, but close the window if you'll be switching between applications, allowing a screen-saver to kick in, or anything else funny. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; REVISION HISTORY 2/5/95: fixed acting zombie bug in run-world 2/5/95: added natural-born-gene-pool and added related TO DO items 2/6/95: added save-programs and save-parameters 2/7/95: fixed type declarations in run-world 2/8/95: changed declarations throughout for port to CMU lisp 2/8/95: reorganized files and conditionalized loader for mac/non-mac 2/8/95: added Adam Alpern's about-box for mac 2/8/95: added populate-X in place of make-random-X 2/8/95: added a conditionalized (mac/non-mac) get-new-file-name 2/8/95: added StartupScript.lisp for non-mac configuration 2/9/95: added animal-program-from-file for use in StartupScript.lisp 2/9/95: added SPECIAL INSTRUCTIONS FOR NON-MACINTOSH ENVIRONMENTS 2/11/95: added inhibit-warnings declaration & purify for CMU to loader 2/11/95: renamed energy to my-energy 2/11/95: added my-age to facilitate ontogeny 2/11/95: implemented rock sensing (rock-min, rock-max, rock-here) 2/11/95: implemented corpses; this involved many changes including: added *corpse-decay-rate*, added corpse class & associated fns, changed *meal-size* to *veg-meal-size*, added *meat-meal-size*. added *meat-scaling-factor*, changed eat to eat-veg, eat-if= to eat-veg-if=, and eat-if< to eat-veg-if<, added eat-meat, eat-meat-if=, and eat-meat-if<, removed *kill-bonus*, changed attack to create a corpse, added corpse-max, corpse-min, and corpse-here, added display-corpses to ErunticPlotInterface.lisp, added display-corpse dialog item in ControlWindoid.lisp, changed saved animal programs to use eat-veg/meat, changed README doc in several places 2/13/95: added force-output to non-MCL get-new-file-name 2/13/95: added *auto-save-data* and auto-save-data function 2/14/95: changed (safety 0) declarations to (safety 1) 2/14/95: added hormones TO DO item 2/17/95: added all-natural-born-animal-programs 2/18/95: added auto-save-programs, *auto-save-programs*, *fraction-of-programs-to-save* & related changes 2/19/95: added number-of-corpses-in-zone to *snapshot-data-to-track* 2/19/95: changed size-at-location to handle mark-numbers and changed marker-min, marker-max and marker-here to work w/size rather than number of markers -- this fixed a bug whereby marker sensors could return more than 9 2/20/95: added natural-born-p arg to all-animal-programs and made associated changes 2/20/95: added natural-born-p arg to gene-pool and made associated changes 2/20/95: saved gene-pool data now only includes natural-born animals 2/23/95: actually made the about-box work! 2/23/95: added free-space-at-location, space-max, space-min, space-here 2/23/95: changed reset-world to reinitialize random number generator and to take an optional random-state argument 3/6/95: added multiple-animal-programs-from-file 3/6/95: added multiple-programs option to populate-animals 3/6/95: updated mac populate dialog for multiple program files 3/12/95: added number-of-flag--in-zone functions and included them in *snapshot-data-to-track* 3/13/95: added *program-to-repopulate* 3/13/95: added find-any-flag & added it to soup 3/22/95: added specific-cost mechanism (see calls in Soup.Lisp) 3/22/95: added action-memories and related functions to encourage the development of communication via ritualization. This involved changes to the act method, the addition of a action-memory slot for animals, a new parameter *action-memory-duration*, a new variable *action-memory-indices* (maintained via clear-action-memory-indices and action-memory-index -- see calls in Soup.Lisp), a record-action-memory function that was added to all soup functions, and the sensor functions action-max, action-min, and action-here. A "pretend" function, which causes the current actor to falsely record the performance of an action, was also added. 11/2/95: changed this document for 0.95 release. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; TO DO LIST (not listed in any particular order -- let me know if you've implemented any of these!) - add dialog for saving programs by zone - hormones: levels controlled (in part?) by animals; affect number of actions per cycle AND modify costs. - reduce amount of duplicated code (more OOP techniques) & consing - enhance animal ontogeny - save/restore world states (with parameters) - log window for info about experiments not in data files: parameter settings, populate dialog interactions, data zone descriptions, etc. - auto recompile of changed files (perhaps w/defsystem) - more informative and less crash-prone macintosh world display - enhancements for social behavior, suggested by Ray Coppinger - chance element in attack - factor by which attacking efficiency decreases as a function of the size of the attacking group - attack (& defense?) efficiency proportionate to energy - kill bonus proportionate to (previous?) defender size - sense age of others - sense relatedness of others (either via program comparison or ancestor calculations; do for most-energetic and least-energetic) - relatedness-sensitive mating (e.g., disallow if not related enough) - actions for feeding others (young w/certain flags, or lowest energy at loc) - pickup/putdown actions for rocks/plants - poison plants - user manual