Variables and attributes represent named data that can be used in an expression. They can be accessed depending on their scope:
When an agent wants to use either one of the variables declared locally, one of the attributes declared in its species (or parent species), one of the attributes declared in the macro-species of its species, it can directly invoke its name and the compiler will do the rest (i.e. finding the variable or attribute in the right scope). For instance, we can have a look at the following example:
species animal {
float energy <- 1000 min: 0 max: 2000 update: energy - 0.001;
int age_in_years <- 1 update: age_in_years + int (time / 365);
action eat (float amount <- 0) {
float gain <- amount / age_in_years;
energy <- energy + gain;
}
reflex feed {
int food_found <- rnd(100);
do eat (amount: food_found);
}
}
Everywhere in the species declaration, we are able to directly name and use:
time
, a global built-in variable,energy
and age_in_years
, the two species attributes.
Nevertheless, in the species declaration, but outside of the action
eat
and the reflexfeed
, we cannot name the variables:
amount
, the argument of eat
action,gain
, a local variable defined into the eat
action,food_found
, the local variable defined into the feed
reflex.Eat
action declaration
In the
eat
action declaration, we can directly name and use:
time
, a global built-in variable,energy
and age_in_years
, the two species attributes,amount
, which is an argument to the action eat
,gain
, a temporary variable within the action.
We cannot name and use the variables:
food_found
, the local variable defined into the feed
reflex.feed
reflex declaration
Similarly, in the
feed
reflex declaration, we can directly name and use:
time
, a global built-in variable,energy
and age_in_years
, the two species variables,food_found
, the local variable defined into the reflex.
But we cannot access to variables:
amount
, the argument of eat
action,gain
, a local variable defined into the eat
action.When an expression needs to get access to the attribute of an agent which does not belong to its scope of execution, a special notation (similar to that used in Java) has to be used:
remote_agent.variable
where remote_agent can be the name of an agent, an expression returning an agent, self, myself or each. For instance, if we modify the previous species by giving its agents the possibility to feed another agent found in its neighbourhood, the result would be:
species animal {
float energy <- 1000 min: 0 max: 2000 update: energy - 0.001;
int age_in_years <- 1 update: age_in_years + int (time / 365);
action eat (float amount <- 0.0) {
float gain <- amount / age_in_years;
energy <- energy + gain;
}
action feed (animal target){
if (agent_to_feed != nil) and (agent_to_feed.energy < energy { // verifies that the agent exists and that it need to be fed
ask agent_to_feed {
do eat amount: myself.energy / 10; // asks the agent to eat 10% of our own energy
}
energy <- energy - (energy / 10); // reduces the energy by 10%
}
}
reflex {
animal candidates <- agents_overlapping (10 around agent.shape); gathers all the neighbours
agent_to_feed value: candidates with_min_of (each.energy); //grabs one agent with the lowest energy
do feed target: agent_to_feed; // tries to feed it
}
}
In this example, agent_to_feed.energy, myself.energy and each.energy show different remote accesses to the attribute energy. The dotted notation used here can be employed in assignments as well. For instance, an action allowing two agents to exchange their energy could be defined as:
action random_exchange {//exchanges our energy with that of the closest agent
animal one_agent <- agent_closest_to (self)/>
float temp <-one_agent.energy; // temporary storage of the agent's energy
one_agent.energy <- energy; // assignment of the agent's energy with our energy
energy <- temp;
}