/**
* Name: Ant Foraging (Complex)
* Author:
* Description: Toy Model ant using the question of how ants search food and use pheromons to return to their
* nest once they did find food.
* Tags: gui, fsm, grid, diffusion
*/
model ants
global {
//Utilities
bool use_icons <- true;
bool display_state <- false;
//Evaporation value per cycle
float evaporation_per_cycle <- 5.0 min: 0.0 max: 240.0 parameter: 'Evaporation of the signal (unit/cycle):' category: 'Signals';
//Diffusion rate of the pheromon among the grid
float diffusion_rate <- 1.0 min: 0.0 max: 1.0 parameter: 'Rate of diffusion of the signal (%/cycle):' category: 'Signals';
//Size of the grid
int gridsize <- 100 min: 30 parameter: 'Width and Height of the grid:' category: 'Environment and Population';
//Number of ants
int ants_number <- 200 min: 1 parameter: 'Number of ants:' category: 'Environment and Population';
//Frequency of update of the grid
int grid_frequency <- 1 min: 1 max: 100 parameter: 'Grid updates itself every:' category: 'Environment and Population';
//Number of food places among the grid
int number_of_food_places <- 5 min: 1 parameter: 'Number of food depots:' category: 'Environment and Population';
float grid_transparency <- 1.0;
image_file ant_shape const: true <- file('../images/ant.png');
svg_file ant_shape_svg const: true <- svg_file("../images/ant.svg");
obj_file ant3D_shape const: true <- obj_file('../images/fire-ant.obj', '../images/fire-ant.mtl', -90::{1, 0, 0});
//The center of the grid that will be considered as the nest location
point center const: true <- {round(gridsize / 2), round(gridsize / 2)};
int food_gathered <- 1;
int food_placed <- 1;
rgb background const: true <- rgb(99, 200, 66);
rgb food_color const: true <- rgb(31, 22, 0);
rgb nest_color const: true <- rgb(0, 0, 0);
geometry shape <- square(gridsize);
image_file terrain <- image_file("../images/soil.jpg");
matrix grid_values <- matrix(as_matrix(terrain, {gridsize, gridsize}));
init {
// Normalization of the grid values
float min <- min(grid_values);
float max <- max(grid_values);
float range <- (max - min) / 2.5;
loop i from: 0 to: gridsize - 1 {
loop j from: 0 to: gridsize - 1 {
grid_values[i, j] <- (grid_values[i, j] - min) / range;
}
}
//Creation of the food places placed randomly with a certain distance between each
loop times: number_of_food_places {
point loc <- {rnd(gridsize - 10) + 5, rnd(gridsize - 10) + 5};
list food_places <- (ant_grid where ((each distance_to loc) < 5));
ask food_places {
if food = 0 {
food <- 5;
food_placed <- food_placed + 5;
color <- food_color;
}
}
}
//Creation of the ants that will be placed in the nest
create ant number: ants_number with: (location: center);
}
//Reflex to diffuse the pheromon among the grid
reflex diffuse {
diffuse var: road on: ant_grid proportion: diffusion_rate radius: 3 propagation: gradient method: convolution;
} }
//Grid used to discretize the space to place food
grid ant_grid width: gridsize height: gridsize neighbors: 8 frequency: grid_frequency use_regular_agents: false use_individual_shapes: false {
bool is_nest const: true <- (topology(ant_grid) distance_between [self, center]) < 4;
float road <- 0.0 max: 240.0 update: (road <= evaporation_per_cycle) ? 0.0 : road - evaporation_per_cycle;
rgb color <- is_nest ? nest_color : ((food > 0) ? food_color : ((road < 0.001) ? background : rgb(0, 99, 0) + int(road * 5))) update: is_nest ? nest_color : ((food > 0) ?
food_color : ((road < 0.001) ? background : rgb(0, 99, 0) + int(road * 5)));
int food <- 0;
}
//Species ant that will move and follow a final state machine
species ant skills: [moving] control: fsm {
float speed <- 1.0;
bool has_food <- false;
//Reflex to place a pheromon stock in the cell
reflex diffuse_road when: has_food = true {
ant_grid(location).road <- ant_grid(location).road + 100.0;
}
//Action to pick food
action pick (int amount) {
has_food <- true;
ant_grid place <- ant_grid(location);
place.food <- place.food - amount;
}
//Action to drop food
action drop {
food_gathered <- food_gathered + 1;
has_food <- false;
heading <- heading - 180;
}
//Action to find the best place in the neighborhood cells
point choose_best_place {
container list_places <- ant_grid(location).neighbors;
if (list_places count (each.food > 0)) > 0 {
return point(list_places first_with (each.food > 0));
} else {
list_places <- (list_places where ((each.road > 0) and ((each distance_to center) > (self distance_to center)))) sort_by (each.road);
return point(last(list_places));
}
}
//Reflex to drop food once the ant is in the nest
reflex drop when: has_food and (ant_grid(location)).is_nest {
do drop();
}
//Reflex to pick food when there is one at the same location
reflex pick when: !has_food and (ant_grid(location)).food > 0 {
do pick(1);
}
//Initial state to make the ant wander
state wandering initial: true {
do wander(amplitude: 90.0);
float pr <- (ant_grid(location)).road;
transition to: carryingFood when: has_food;
transition to: followingRoad when: (pr > 0.05) and (pr < 4);
}
//State to carry food once it has been found
state carryingFood {
do goto(target: center);
transition to: wandering when: !has_food;
}
//State to follow a pheromon road if once has been found
state followingRoad {
point next_place <- choose_best_place();
float pr <- (ant_grid(location)).road;
location <- next_place;
transition to: carryingFood when: has_food;
transition to: wandering when: (pr < 0.05) or (next_place = nil);
}
aspect info {
if (use_icons) {
draw ant_shape size: {7, 5} rotate: my heading + 1;
} else {
draw circle(1) wireframe: !has_food color: #red;
}
if (destination != nil) {
draw line([location + {0, 0, 0.5}, {location.x + 5 * cos(heading), location.y + 5 * sin(heading)} + {0, 0, 0.5}]) + 0.1 color: #white border: false end_arrow: 1.2;
}
if (state != "wandering") {
//draw circle(4) wireframe: true color: #white;
if (display_state) {
draw string(self as int) color: #white font: font("Helvetica", 14, #bold) at: my location - {1, 1, -0.5};
draw state color: #yellow font: font("Helvetica", 18, #bold) at: my location + {1, 1, 0.5};
}
}
}
aspect threeD {
draw ant3D_shape size: {7, 5} at: (location + {0, 0, 1}) rotate: heading;
}
aspect icon {
draw ant_shape size: {7, 5} rotate: my heading + 1 wireframe: true;
}
aspect icon_svg {
draw ant_shape_svg size: {5, 7} rotate: my heading + 270 color: #black;
} }
//Complete experiment that will inspect all ants in a table
experiment "With Inspector" type: gui {
parameter 'Number:' var: ants_number init: 100 unit: 'ants' category: 'Environment and Population';
parameter 'Grid dimension:' var: gridsize init: 100 unit: '(number of rows and columns)' category: 'Environment and Population';
parameter 'Number of food depots:' var: number_of_food_places init: 5 min: 1 category: 'Environment and Population';
output {
layout #split editors: false;
display Ants type: opengl {
image terrain position: {0.05, 0.05} size: {0.9, 0.9} refresh: false;
agents "agents" transparency: 0.7 position: {0.05, 0.05} size: {0.9, 0.9} value: (ant_grid as list) where ((each.food > 0) or (each.road > 0) or (each.is_nest));
species ant position: {0.05, 0.05, 0.05} size: {0.9, 0.9} aspect: icon_svg;
overlay transparency: 0.3 background: rgb(99, 85, 66, 255) position: {50 °px, 50 °px} size: {250 °px, 150 °px} border: rgb(99, 85, 66, 255) rounded: true {
draw ant_shape at: {60 °px, 70 °px} size: {140 °px, 100 °px} rotate: -60;
draw ('Food foraged: ' + (((food_placed = 0 ? 0 : food_gathered / food_placed) * 100) with_precision 2) + '%') at: {40 °px, 70 °px} font: font("Arial", 18, #bold) color:
#white;
draw ('Carrying ants: ' + (((100 * ant count (each.has_food or each.state = "followingRoad")) / length(ant)) with_precision 2) + '%') at: {40 °px, 100 °px} font:
font("Arial", 18, #bold) color: #white;
}
}
inspect "All ants" type: table value: ant attributes: ['name', 'state'];
}
}
experiment "Classic" type: gui {
parameter 'Number of ants:' var: ants_number category: 'Model';
parameter 'Evaporation of the signal (unit/cycle):' var: evaporation_per_cycle category: 'Model';
parameter 'Rate of diffusion of the signal (%/cycle):' var: diffusion_rate category: 'Model';
parameter 'Use icons for the agents:' var: use_icons category: 'Display';
parameter 'Display state of agents:' var: display_state category: 'Display';
output {
display Ants antialias: false type: opengl {
light #ambient intensity: 127;
light #default intensity: 127;
image terrain refresh: true;
agents "Grid" transparency: 0.4 value: ant_grid where ((each.food > 0) or (each.road > 0) or (each.is_nest));
species ant aspect: info;
}
}
}
//Complete experiment that will inspect all ants in a table
experiment "3D View" type: gui {
parameter 'Number:' var: ants_number init: 30 unit: 'ants' category: 'Environment and Population';
parameter 'Grid dimension:' var: gridsize init: 100 unit: '(number of rows and columns)' category: 'Environment and Population';
parameter 'Number of food depots:' var: number_of_food_places init: 5 min: 1 category: 'Environment and Population';
output {
display Ants3D type: opengl show_fps: true antialias: false{
grid ant_grid elevation: grid_values triangulation: true texture: terrain refresh: false;
agents "Trail" transparency: 0.7 position: {0.05, 0.05, 0.02} size: {0.9, 0.9} value: (ant_grid as list) where ((each.food > 0) or (each.road > 0) or (each.is_nest));
species ant position: {0.05, 0.05, 0.025} size: {0.9, 0.9} aspect: threeD;
}
}
}
//Experiment to show how to make multi simulations
experiment "3 Simulations" type: gui {
parameter 'Number:' var: ants_number init: 100 unit: 'ants' category: 'Environment and Population';
parameter 'Grid dimension:' var: gridsize init: 100 unit: '(number of rows and columns)' category: 'Environment and Population';
parameter 'Number of food depots:' var: number_of_food_places init: 5 min: 1 category: 'Environment and Population';
// We create 2 supplementary simulations using the species name 'ants_model' (automatically created from the name of the model + '_model')
init {
create ants_model with: [ants_number::200, evaporation_per_cycle::100, diffusion_rate::0.2];
create ants_model with: [ants_number::10, evaporation_per_cycle::72, diffusion_rate::0.6];
}
permanent {
display Comparison background: #white {
chart "Food Gathered" type: series {
loop s over: simulations {
data "Food " + int(s) value: s.food_gathered color: s.color marker: false style: line thickness: 5;
}
}
}
}
output {
layout #split editors: false consoles: false toolbars: true tabs: false tray: false;
display Ants background: color type: opengl toolbar: color axes: false {
image terrain position: {0.05, 0.05} size: {0.9, 0.9} refresh: false;
agents "agents" transparency: 0.5 position: {0.05, 0.05} size: {0.9, 0.9} value: (ant_grid as list) where ((each.food > 0) or (each.road > 0) or (each.is_nest));
species ant position: {0.05, 0.05} size: {0.9, 0.9} aspect: icon;
}
}
}