Scripting the Simulation - Contents

## ApeScript Notes

Variables and Functions
If, While and Metaphysics
Locks
Xtreme ApeScript - Case/Switch
Xtreme ApeScript - Brain as Memory
Debugging the Monkey
Variables and Scripts
Parsing and Interpreting

Variables and Functions ( Notes Index )

ApeScript is a procedural scripting language developed for use with the ApeSDK. ApeScript is small, memory efficient and highly modular and could easily be ported to other applications. This document describes using both ApeScript and the ApeScript implementation specific to the Simulated Ape.

ApeScript requires an initial execution function which must be located at the physical end of the script file. In the case of the Simulated Ape this function is the being function.

function ( being ) { }

All other functions must be defined above the being function. There is no function ordering other than placing the being function at the end of the file. Similarly, all other functions can be referenced as variables. This will be expanded on later in the document. But it is important to note that the being function can not be referenced as a variable.

Variables and functions in ApeScript can be made up of any letter or underscore. Numbers or other characters can not be included in variables or functions. It may prove easier as you are learning ApeScript to think of functions as a particular kind of variable. In fact, ApeScript will define the function variable as the point in the interpreted scripting code where the function is defined.

Functions (other than being) are called by the keyword run. For example;

function ( second_time ) { run ( first_time ); } function ( first_time) { } function ( being ) { run ( second_time ); }

Similarly, the following would also work;

function ( second_time ) { run ( which_one ); } function ( first_time) { } function ( being ) { which_one = first_time; run ( second_time ); }

Here you see the variable which_one being set at the function location before it is run in second_time. Normal variables can be defined in a single instance between 0 and 65535. This is deemed the normal operating range of definition in ApeScript. You can define a variable;

big_minus = 0 - 65535;

But as you will see this is the limit in a single line.

A variable through use can reach the 32-bit signed min/max of -2147483648 to 2147483647. This can be achieved through cumulative operations.

ApeScript allows a wide variety of mathematical and logical operators. The only limit (currently) in ApeScript is that a line of code can only contain two input variables, one operator, one equality and one output variable.

output_value = input_one + input_two;

The number of operators may be subject to change in the future. Your best source for the current operators is the ApeSDK Manual. Similarly it is possible that the negative definition without the zero subtraction or multiple operators used. All this is possible in the future.

If, While and Metaphysics ( Notes Index )

A little naming convention from international English. Brackets refer to this ( and this ).Braces refer to this { and this }. The only use of brackets is run, function, if and while. Similarly, the only place where braces can be used is function, if and while. The check for if and while are based on the same initial premise;

if ( condition ) { run ( will_be_run ); } while ( condition ) { run ( will_be_run ); }

If condition doesn't equal zero, then will_be_run will be run in the if and while operator. The while operator will continue to execute will_be_run until condition equals zero. If condition is zero, then will_be_run will not be executed.

The following example shows both if and while being used in a practical context.

function ( being ) { while ( loop < number_beings ){ if ( current_being != loop ){ select_being = loop; test_x = location_x; test_y = location_y; select_being = current_being; other_visible = is_visible; } loop = loop + 1; } }

This being function identifies each visible being to the current_being.

It's important to take a step back and ask the question - what is the being function?

Put simply, the being function is run per Simulated Ape. The ape that is currently being run is identified by the variable current_being. Initially - at the start of the being function - select_being is set to current_being. However through the script execution select_being can be set to anything from 0 to number_beings - 1.

Through developing ApeScript in the ApeSDK, you should always reference the Simulation linking variables explained inApeScript and the APeSDK.

This page describes the current version of the Simulation's special ApeScript variables and how they link to the Simulation. If you develop ApeScript, print this page. If you have any questions from the page email them directly to tom at nobleape dot com. To date, the ApeScript and the ApeSDK page has been rewritten based on additional information requests. This digression aside, you need to understand what each of the variables mean. The term, output, means you can't set anything to this variable. It is only useful on the right hand side of an equality assignment.

Scanning down to the Being section of the ApeScript and the ApeSDK page, you will note that three variables aren't based on select_being. Of the variables that can be changed in the being (aside from the brain values) only facing, speed and energy can be changed. This means that you can only change the direction facing, the speed and the energy an ape has. Not the Ape's location directly. The location is changed through the direction facing and the speed set. The brain values relate to current_being. Obviously one being can not effect the brain of another being directly.

Consider the metaphysics of the individual being functions and the being functions when run together in a single time cycle.

Locks ( Notes Index )

As things are currently ApeScript is an interpreted language. It is integrated in the ApeSDK but there are still conditions that will cause ApeScript to substantially slow down. Particular variables take longer time to process than others. The biology_output variable is relatively time consuming. is_visible is extremely time consuming. Getting and setting brain_value may be slightly more time consuming than addition.

If you experience lock conditions, rework the code to create fewer loops over less variables. Be mindful of the variables that are really pseudo variables (like is_visible or biology_output) which in fact result in substantial code in the Simulation being run to produce a single value.

Xtreme ApeScript - Case/Switch ( Notes Index )

There are some properties of ApeScript that may prove useful in practical applications. Whilst the ApeScript specification is simple, the underlying elements of the specification allow a number of features common to more complex languages.

The switch statement is common in many languages - it allows from an input variable a number of possible choices based on that variable. Consider the case with four possibilities.

function ( case_one ){ number_a = number_b * 56; } function ( case_two ){ number_a = number_b - 56; } function ( case_three ){ number_a = number_b + 56; } function ( case_four ){ number_a = number_b; } function ( being ){ number_b = time; which_function = time & 3; offset_function = case_two - case_one; which_function = which_function * offset_function; which_function = which_function + case_one; run( which_function ); number_result = number_a; }

This works because each case function decomposes to the same number of interpreted bytes. This idea of interpreted bytes is rather subtle, but it is the basis of functions as variables representing points in the interpreted bytes. Now suppose there are a variety of lengths in each function. The functions do distinctly different things and can't be guaranteed to have the same number of interpreted bytes.

function ( actual_one ){ run ( actual_two ); number_a = number_a + 26; } function ( actual_two ){ number_a = number_b * 234; } function ( actual_three ){ number_a = number_b - 10; number_a = number_a * 21; number_a = number_a + 127; } function ( actual_four ){ run ( actual_one); number_a = number_a * 20; } function ( direct_one ){ run ( actual_one ); } function ( direct_two ){ run ( actual_two ); } function ( direct_three ){ run ( actual_three ); } function ( direct_four ){ run ( actual_four ); } function ( being ){ number_b = time; which_function = time & 3; offset_function = direct_two - direct_one; which_function = which_function * offset_function; which_function = which_function + direct_one; run( which_function ); number_result = number_a; }

Here the functions of the same number of bytes is put in a tight block. This block has the same number of bytes but they refer to different functions that have different numbers of bytes. Applications of if functions may be preferable;

function ( being ){ number_b = time; which_function = time & 3; if ( which_function == 0){ pick_function = actual_one; } if ( which_function == 1){ pick_function = actual_two; } if ( which_function == 2){ pick_function = actual_three; } if ( which_function == 3){ pick_function = actual_four; } run ( pick_function ); }

Xtreme ApeScript - Brain as Memory ( Notes Index )

For folks looking for traditional array memory, the brain memory provides this for ApeScript. The initial complication is that the Simulated Ape brain memory has a time and space based function to move information through it. To stop this effect the brain constants (that govern this spreading of information through the brain) can be set to zero through the variable file. Looking at the ApeSDK source code;

/* from sim/core/being.c - NOT ApeScript */ n_int local_a = constants<0>; n_int local_c = constants<2>; n_int local_b = constants<1> + local_c; ((((aver)*local_a)+((brain)*local_b)-((old_brain)*local_c))>>10)

Thus the variable files should contain the following brn per being;

brn = 0, 1024, 0, 0, 1024, 0;

Each memory entry is a single byte. If you want to put larger variables into memory, such as the full range of an ApeScript variable, you will need to write code to spread the variable through the bytes. For example;

function ( offset_to_brain ){ memory_offset = memory_location * 4; brain_x = memory_offset & 31; memory_offset = memory_offset / 32; brain_y = memory_offset & 31; memory_offset = memory_offset / 32; brain_z = memory_offset & 31; } function ( number_to_brain ){ run ( offset_to_brain ); memory_offset = memory_location * 4; memory_offset = memory_offset & 31; negative_one = 0 - 1; if ( memory_number < 0 ){ number_is_negative = 128; absolute_number = negative_one - memory_number; } if ( number > negative_one ){ number_is_negative = 0; absolute_number = memory_number; } brain_value = absolute_number & 255; absolute_number = absolute_number / 256; brain_x = memory_offset + 1; brain_value = absolute_number & 255; absolute_number = absolute_number / 256; brain_x = memory_offset + 2; brain_value = absolute_number & 255; absolute_number = absolute_number / 256; brain_x = memory_offset + 3; brain_value = absolute_number + number_is_negative; } function ( brain_to_number ){ run ( offset_to_brain ); memory_offset = memory_location * 4; memory_offset = memory_offset & 31; negative_one = 0 - 1; brain_x = memory_offset + 3; number_is_negative = 0; absolute_number = brain_value; if ( absolute_number > 127 ){ is_negative = 128; absolute_number = absolute_number - 128; } absolute_number = absolute_number * 256; brain_x = memory_offset + 2; absolute_number = absolute_number + brain_value; absolute_number = absolute_number * 256; brain_x = memory_offset + 1; absolute_number = absolute_number + brain_value; absolute_number = absolute_number * 256; brain_x = memory_offset; absolute_number = absolute_number + brain_value; if ( is_negative == 128 ){ absolute_number = negative_one - absolute_number; } memory_number = absolute_number; }

This script can be further optimized for particular kinds of data you want to put in memory. Not to mention, optimized for the time it takes to get and set a particular number in memory.

Debugging the Monkey ( Notes Index )

ApeScript has a debugger built into it. Currently this provides a file of the Script code being run for a single cycle of the first being being simulated. Using some of the example code from the first set of notes.

function ( second_time ) { run ( which_one ); } function ( first_time ) { } function ( being ) { which_one = first_time; run ( second_time ); }

Is run through the Simulation and the debug output is produced;

function( being ){ which_one = 24 ; run( second_time ){ run( which_one ){ } } }

What this shows is the numerical representation of the first_time function set to which_one. The function second_time is run, then which_one is run successfully. Relatively uneventful. But an accurate indication of the code run.

Looking at this code;

function ( case_one ){ number_a = number_b * 56; } function ( case_two ){ number_a = number_b - 56; } function ( case_three ){ number_a = number_b + 56; } function ( case_four ){ number_a = number_b; } function ( being ){ number_b = time; which_function = time & 3; offset_function = case_two - case_one; which_function = which_function * offset_function; which_function = which_function + case_one; run( which_function ); number_result = number_a; }

This may require a stronger leap of faith for a new ApeScript developer than the first example. Let's look at the debug output.

function( being ){ number_b = 867 ; which_function = 3 ; offset_function = 19 ; which_function = 57 ; which_function = 66 ; run( which_function ){ number_a = 867 ; } number_result = 867 ; }

Quite luckily the time & 3 resulted in 3 for which_function. Relatively uneventful with a straight pass-through for number_a = number_b; But still, the debug output is quite useful to verify the method works and the output is exactly as expected in terms of picking the correct function.

Looking at the slightly more complex version of the switch/case.

function ( actual_one ){ run ( actual_two ); number_a = number_a + 26; } function ( actual_two ){ number_a = number_b * 234; } function ( actual_three ){ number_a = number_b - 10; number_a = number_a * 21; number_a = number_a + 127; } function ( actual_four ){ run ( actual_one ); number_a = number_a * 20; } function ( direct_one ){ run ( actual_one ); } function ( direct_two ){ run ( actual_two ); } function ( direct_three ){ run ( actual_three ); } function ( direct_four ){ run ( actual_four ); } function ( being ){ number_b = time; which_function = time & 3; offset_function = direct_two - direct_one; which_function = which_function * offset_function; which_function = which_function + direct_one; run( which_function ); number_result = number_a; }

Looking at the debug output.

function( being ){ number_b = 942 ; which_function = 2 ; offset_function = 15 ; which_function = 30 ; which_function = 151 ; run( which_function ){ run( actual_three ){ number_a = 932 ; number_a = 19572 ; number_a = 19699 ; } } number_result = 19699 ; }

This is as to be expected. Whilst the switch case of 2 is relatively uneventful, it clearly follows the correct function actual_three and the result comes through.

Let's try something interesting... a clear error case.

function ( buffer_backstep ){ number_z = number_y + number_a; number_a = number_z * 3; number_a = number_y; number_a = 3; } function ( case_one ){ number_a = number_b * 56; } function ( case_two ){ number_a = number_b - 56; } function ( being ){ number_b = time; which_function = time & 1; offset_function = case_two - case_one; which_function = which_function * offset_function; which_function = which_function + case_one; /* this is clear function abuse, but what will happen? */ random_error = random & 7; random_error = random_error + 1; which_function = which_function - random_error; run( which_function ); number_result = number_a; }

This is similar to the switch case, but we are mucking up the function to be run by subtracting a random error from 1 to 8.

function( being ){ number_b = 757 ; which_function = 1 ; offset_function = 19 ; which_function = 19 ; which_function = 72 ; random_error = 3 ; random_error = 4 ; which_function = 68 ; run( which_function ){ < ERROR : Input variable without equals >

As you can see, an error is fired and recorded in the debug output. This is very useful for novice and expert ApeScript authors alike. You will find yourself writing long bits of code and the debugger may be the only indication that the code won't run correctly.

ApeScript allows you to create variables on the fly. Please consider including additional debug values that immediately identify the section of the script information.

Variables and Scripts ( Notes Index )

The ApeSDK currently has two types of files it reads and one type of file it writes. The Simulation reads and writes variable files. These contain the immediate variables bar the 64k of brain values. These values may be included with future variable input and output files, but at this time, there is little practical use for such large additions to the relatively minimalist variable files. The variable file format is described in great detail here.

In addition to variable files used for input and output, there are script files. These script files are the subject of the ApeScript Notes documents. It is important to think about how the variable files and the scripting files can work together - both in terms of debugging and testing.

For example, if you want a particular part of a script to run with a specific ape, the apes ID number (id_number based on select_being) should be the best test for running specific code for a specific ape.

Parsing and Interpreting ( Notes Index )

It is important to note that the current version of ApeScript has two parts - a parser and an interpreter. The parser takes the text information present in the script file and converts it to ApeScript interpretable byte codes. Aside from actual code, these byte codes contain the constant number values used through the script.

It is possible to generate a parser error such as using an unrecognizable character or a set of symbols that are not recognized, ++ for example, that won't result in an error that will be shown in a debug file. Please be mindful of this. If you require this information to feature in a debug file, please make contact (tom at nobleape dot com).

To explain the logic of why parser errors are not treated as interpreter errors comes in part from the handling of variable files. If a variable file fails to load, it shouldn't display an error. Users may load dud files accidentally and it should effect the Simulation's running in any way. Similarly, a parser error typically indicates that the file loaded is not a script file and not that the logic of the script file will be compromised.

In the case of genuine errors, ApeScript will either produce an error or in the case of extreme logic errors - like an infinite loop for example - the interpreter will allow the user to quit or create a new simulation environment.

Tom Barbalet, 6 October 2005.

Scripting the Simulation - Contents