Dear all,
I hope this message finds you well.
I’m currently working on a snowstorm simulation model using GAMA for a research project. I’m reaching out because I’ve encountered a critical issue that I’ve been struggling to resolve despite extensive debugging and reviewing the documentation.
I’ve attached below a detailed version of my GAML model, but let me briefly explain the main functionality and the issue I’m facing.
Model Overview:- The simulation models a snowstorm in a city using shapefiles for buildings and roads.
- Snow accumulates dynamically over time on roads.
- Rescue stations spawn multiple agents:
- plows (leaders),
- plow (helpers),
- and rescue_vehicle (emergency responders).
- The plows (leaders) are supposed to:
- Detect roads with the most snow (using a scoring system),
- Move to those roads,
- Request help once snow exceeds a threshold,
- Begin clearing snow while waiting for the helper.
- The plow (helper) agents are designed to:
- Detect when a leader (plows) needs help,
- Move to the same road,
- Start clearing alongside the leader.
Problem Description:The issue I need help with is specifically related to the behavior of the leader plow (plows) once it reaches a road with significant snow and requests help.
Initially, the leader correctly detects the snowy road, moves to it, and requests help.
While waiting, it starts clearing the target road or surrounding roads, as expected.
=> However, after some time, it gets stuck, either:
oscillating back and forth between two points,
or completely stopping without clearing any more snow.
This coordination problem is at the core of my project and I urgently need guidance to fix it.
The current logic that is working as described above is within this plan:Â plan clear_target_snow intention: clear_snow
What I’ve TriedI’ve inspected and modified the plan clear_target_snow intention multiple times.
I added conditions to detect road endpoints and attempted to identify dead ends by checking for connections at intersections.
I experimented with reassigning targets or reversing direction when stuck.
I tested alternate routing using path_between, distance checks, and debug logs for heading and road shape points.
Despite all these debugging efforts, the behavior persists. The leader gets stuck repeating a short move or simply halts without engaging in further clearing.
What I Need Help WithI believe the issue lies in how the plow decides what to do after reaching the end of the road or when no alternative routes are found. I would be very grateful if someone could review the clear_target_snow plan logic and suggest how I might refactor it to make the leader plow:
continue clearing effectively while waiting for help,
handle road ends or isolated segments gracefully, and avoid getting stuck in place or repeating movements.
This coordination is the centerpiece of my simulation, and resolving it is vital for the project to progress.
Nothing has resolved the issue, and I’m at a point where I need expert help.
If anyone in the community or development team could spare some time to look at this model or suggest debugging directions, it would mean a lot. This project is part of a larger academic effort, and I truly want to make it functional and impactful.
Thank you very much for your time and for the amazing work you all do with GAMA.
Below is my entire model code that I am using :
/**
- Name: Model
- Author:
- Tags:
*/
model Model
global {
file building_shapefile <- file("../includes/buildings.shp");
file road_shapefile <- file("../includes/roads.shp");
list<map> roads_data <- [];
geometry shape <- envelope(building_shapefile) + envelope(road_shapefile);
float step <- 60 #s;
field<float> snow_field <- field(300, 300, 0.0);
graph road_network ;
reflex collect_snow_data {
// Collect data from current model
add sum(snow_field)/1000.0 to: current_model_snow_levels;
}
map<point, int> intersection_points;
list<point> valid_starting_points;
action analyze_road_network {
intersection_points <- [];
ask road {
point start_point <- first(shape.points);
point end_point <- last(shape.points);
if (intersection_points contains start_point) {
intersection_points[start_point] <- intersection_points[start_point] + 1;
} else {
add 1 at: start_point to: intersection_points;
}
if (intersection_points contains end_point) {
intersection_points[end_point] <- intersection_points[end_point] + 1;
} else {
add 1 at: end_point to: intersection_points;
}
}
valid_starting_points <- intersection_points.keys where (
(intersection_points[each] = 1) or (intersection_points[each] >= 3)
);
// write "Found " + length(valid_starting_points) + " valid starting points";
loop point_to_check over: valid_starting_points {
list<road> connected_roads <- road where (
(first(each.shape.points) = point_to_check) or
(last(each.shape.points) = point_to_check)
);
}
}
bool allow_snow_accumulation <- true;
bool allow_snowplow_action <- false;
bool allow_snow_clear <- true;
bool debug_snow <- true;
int current_cycle <- 0;
int max_cycles <- 50;
int clear_cycles <- 110;
float high_snow_accumulation_rate <- 5.0;
float base_snow_accumulation_rate <- 2.0;
float base_snow_clearing_rate <- 2.0;
float snow_threshold <- 1000.0;
//NUM PLOWS
int num_plows <- 2;
float total_snow <- 0.0;
string snow_at_location <- "snow_at_location";
string clear_road_report <- "clear_road_report";
//CO-MODEL
list<float> current_model_snow_levels <- [];
list<float> imported_model_snow_levels <- [];
predicate road_snow_level <- new_predicate(snow_at_location);
predicate choose_road <- new_predicate ("choose a road to plow");
predicate is_plowing <- new_predicate ("plowing snow");
predicate report_clear_road <- new_predicate("clear_road_report");
predicate snow_emergency <- new_predicate("snow emergency");
predicate respond_to_emergency <- new_predicate("respond to emergency");
predicate analyze_roads <- new_predicate("analyze nearby roads");
predicate analyze_surroundings <- new_predicate("analyze surroundings");
predicate choose_snowy_road <- new_predicate("choose snowy road");
predicate evaluate_connected_roads <- new_predicate("evaluate_connected_roads");
predicate check_intersection <- new_predicate("check_intersection");
predicate assess_snow_levels <- new_predicate("assess_snow_levels");
predicate find_snowiest_road <- new_predicate("find_snowiest_road");
predicate select_snowiest_road <- new_predicate("select_snowiest_road");
predicate clear_selected_road <- new_predicate("clear_selected_road");
predicate clear_snow <- new_predicate("clear_snow");
predicate first_attempt_clear <- new_predicate ("first_attempt_clear");
predicate continue_clearing <- new_predicate ("continue_clearing");
predicate go_to_target <- new_predicate("go_to_target");
predicate request_help <- new_predicate("request_help");
predicate call_for_help <- new_predicate("call_for_help");
predicate waiting_for_help <- new_predicate("waiting_for_help");
predicate helper_arrived <- new_predicate("helper_arrived");
predicate helping_leader <- new_predicate("helping_leader");
predicate returning_home <- new_predicate("returning_home");
predicate at_home <- new_predicate("at_home");
predicate respond_to_call <- new_predicate("respond_to_call");
predicate move_to_leader <- new_predicate("move_to_leadert");
predicate start_clearing <- new_predicate("start_clearing");
predicate helper_clears <- new_predicate("helper_clears");
predicate clearing_in_progress <- new_predicate ("clearing_in_progress");
predicate follow_cleared_path <- new_predicate("follow_cleared_path");
//rescue vehicle
predicate find_emergency <- new_predicate("find_emergency");
predicate go_to_emergency <- new_predicate("go_to_emergency");
predicate compute_travel_time <- new_predicate("compute_travel_time");
//list<point> rescue_station_locations <- [{100,100}, {500,300}, {900,700}];
bool emergency_active <- false;
float emergency_threshold <- 500.0;
list snow_over_time <- [0,0];
//RESCUE VEHICLES
list<point>emergency_locations <- [];
//list<point> emergency_sites <- [];
//RESCUE INTERACTIONS
list<rescue_vehicle> all_rescue_vehicles <- []; // Store all rescue vehicles
map<rescue_vehicle, float> rescue_times <- []; // Stores time taken for each vehicle
bool all_rescue_done <- false;
list<rescue_vehicle> arrived_vehicles <- [];
map<string, float> final_travel_times <- [];
init {
create building from: building_shapefile;
create road from: road_shapefile;
// In your init section
//create OnRoads.storm with: [num_plows::2];
road_network <- as_edge_graph(road);
list<point> station_locations <- [{855.31,2093.16}, {1763.87,336.10},{2313.01,795.34},{342.79,988.35}];
//UNCOMMENT FOR FINAL TESTING:
//list<point> station_locations <- [{855.31,2093.16}, {1763.87,336.10},{2313.01,795.34},{342.79,988.35},{995.09,256.24}];
//using this for loop: loop i from: 0 to:4 {
loop i from: 0 to:3 {
create rescue_station {
location <- station_locations[i];
}
}
ask rescue_station {
point station_location <- location;
//int num_plows <- 2; // Number of plows per station
float angle_step <- 360.0 / num_plows; // Angle difference between plows
// Distance from the station (adjustable)
//CREATES LEADER PLOW
loop i from: 0 to: num_plows - 1 {
float angle <- i * angle_step;
float radius <- 105.0;
float offset_x <- radius * cos(angle);
float offset_y <- radius * sin(angle);
create plows {
location <- {station_location.x + offset_x, station_location.y + offset_y};
}
}
//CREATES HELPER PLOW
loop i from: 0 to: num_plows - 1 {
float angle <- i * angle_step;
float radius <- 85.0;
float offset_x <- radius * cos(angle);
float offset_y <- radius * sin(angle);
create plow {
location <- {station_location.x + offset_x, station_location.y + offset_y};
}
}
//CREATES RESCUE VEHICLES
loop i from: 0 to: num_plows -1 {
float angle <- i * angle_step;
float radius <- 45.0;
float offset_x <- radius * cos(angle);
float offset_y <- radius * sin(angle);
create rescue_vehicle {
location <- {station_location.x + offset_x, station_location.y + offset_y};
}
}
}
all_rescue_vehicles <- list<rescue_vehicle>(rescue_vehicle);
write "Total rescue vehicles: " + length(all_rescue_vehicles);
}
//CREATING EMERGENCY SITES WITH CYCLE CONDITIONS
int emergency_start_cycle <- 20;
int emergency_interval <- 10;
int emergency_end_cycle <- 200;
//list<point> emergency_locations <- [];
reflex generate_emergencies when: (current_cycle >= emergency_start_cycle) and (current_cycle mod emergency_interval = 0 and (current_cycle <= emergency_end_cycle )) {
road selected_road <- one_of(road);
if (selected_road != nil) {
point site_location <- any_location_in(selected_road);
create emergency_site{
location <- site_location;
}
add site_location to: emergency_locations;
//write "sites " + emergency_locations;
}
}
reflex track_cycles {
current_cycle <- current_cycle + 1;
if (current_cycle >= max_cycles){
allow_snow_accumulation <- false;
allow_snowplow_action <- true;
}
}
reflex snow_accumulation when: allow_snow_accumulation {
if (debug_snow){
//write "Accumulating snow in cycle: " + current_cycle;
}
float new_snow <- 0.0;
ask road {
float random_rate <- base_snow_accumulation_rate * (1.0 + rnd(0.2));
geometry road_line <- shape;
list<point>road_points <- points_on(road_line, 5.0);
loop pt over: road_points {
float new_value <- snow_field[pt] + random_rate;
snow_field[pt] <- new_value;
new_snow <- new_snow + random_rate;
total_snow <- total_snow + new_snow;
}
//save [current_cycle, self.name, total_snow] to:"snow_roads.csv" header:true rewrite:false;
}
diffuse var: snow_field on: snow_field proportion: 0.9;
}
reflex track_snow {
total_snow <- sum(snow_field);
}
}
species rescue_station {
aspect default {
draw box(40, 40, 20) color: rgb(255, 95, 21); // Main building
// Front triangle roof
draw polygon([{ -20, -20, 20 }, { 0, 0, 40 }, { 20, -20, 20 }]) color: rgb(200, 50, 10);
// Back triangle roof
draw polygon([{ -20, 20, 20 }, { 0, 0, 40 }, { 20, 20, 20 }]) color: rgb(200, 50, 10);
// Left slanted roof surface
draw polygon([{ -20, -20, 20 }, { 0, 0, 40 }, { -20, 20, 20 }]) color: rgb(170, 40, 10);
// Right slanted roof surface
draw polygon([{ 20, -20, 20 }, { 0, 0, 40 }, { 20, 20, 20 }]) color: rgb(170, 40, 10);
}
//draw circle(20) color: #red border: #black size: 10;
}
species building {
aspect default {
draw shape color: darker(#darkgray).darker depth: rnd(10) + 12;
}
}
species road {
int road_id;
float snow_accumulation <- 0.0;
bool is_being_evaluated <- false;
bool is_selected <- false;
int priority <- -1;
rgb color <- #gray ;
float road_width;
init {
road_width <- shape.perimeter / length(shape.points);
if (road_width > 20.0 ){
priority <- 1;
//color <- #orange;
}else if (road_width > 10.0){
priority <- 2;
//color <- #green;
}else {
priority <- 3;
//color <- #yellow;
}
}
aspect default {
draw shape color: color width: (1 + (4 - priority) );
}
}
species plows skills: [moving] control: simple_bdi {
float view_dist <- 200.0;
float speed <- 900 #m/#h;
point target;
road current_road;
//list<road> connected_roads;
road selected_road;
road target_road;
path shortest_path;
list<road> zone_roads <- [];
//Help coordination
plow helper_plow;
list<plow> helper_plows <- [];
float snow_threshold <- 1.0;
bool needs_help <- false;
//Help clearing
bool is_waiting <-false;
bool helper_has_arrived <- false;
//Clear while waiting
road current_road_to_clear <- nil;
point current_target <- nil;
list<road> roads_to_clear <- [];
//Variables for movements
bool moving_forward <- true;
point movement_target <- nil;
bool moving_to_end <- true;
//Road Points Variables
list<point> cleared_points <- [];
list<point> points_to_clear <- [];
//Clearing variables
road clearing_road <- nil;
road current_clearing_road <- nil;
bool initialized_clearing <- false;
point current_snow_target <- nil;
int current_point_index <- 0;
map<road, float> road_lengths <- [];
//Data extraction variables
// Amount of snow cleared per plow
map<plows, float> snow_cleared <- [];
// Corresponding simulation cycle numbers
list<int> time_series <- [];
// Stores lists: [cycle, plow name, snow cleared]
list<list<string>> snow_cleared_log <- [];
// Running total Snow
float total_snow_cleared <- 0.0;
//list<road> road_queue <- [];
float distance_traveled <- 0.0;
point last_position <- 0.0;
//ROAD EVALUATION FUNCTION
// float calculate_road_score (road rd){
//
// float snow_amount <- snow_field[rd.location];
// float priority_score <- 1.5 - (rd.priority * 0.5);
//
// //Evaluate the distance to the road
// float distance_to_road <- self.location distance_to rd.location;
// float max_distance <- 1000.0;
//
// //Compute distance factor to penalize far roads
// float distance_factor <- exp(-distance_to_road / 500) * 0.1;
// float distance_penalty <- distance_to_road * 0.09;
//
// //Snow factor
// float snow_factor <- (1- exp(-snow_amount / 50)) * 0.7;
//
// //Final Score
// float final_score <- snow_factor + (distance_factor * 0.2) + priority_score - distance_penalty;
//
// return final_score;
//
// }
init {
}
reflex start_plow_movement when: current_cycle = 60 {
ask plows {
do add_desire(find_snowiest_road);
}
}
//Scoring function
float calculate_road_score(road rd) {
float snow_weight <- 0.6;
float priority_weight <- 0.4;
float snow_score <- snow_field[rd.location]/100;
float priority_score <- (4 - rd.priority)/3;
return (snow_score * snow_weight) + (priority_score * priority_weight);
}
init {
//Step 1
list<road> priority_1_roads <- road where (each.priority = 1);
current_road <- one_of(priority_1_roads);
location <- any_location_in(current_road);
write "Plow started on road: " + current_road + " with priority: " + current_road.priority;
do add_desire(find_snowiest_road);
}
//Step 2
plan find_snow intention: find_snowiest_road {
list<list> snow_road_pairs <-[];
ask road {
float snow_amount <- snow_field[location];
if (snow_amount >0.01){
string road_num <- copy_between (self.name, 6, length(self.name));
// Add a small value based on road ID to break ties
float unique_snow <- snow_amount + float(road_num)*5 / 2.0;
snow_road_pairs <- snow_road_pairs + [[unique_snow,self]];
//road_assignments <- road_assignments + [[self,snow_amount]];
//write "Road " + self + " has snow level: " + snow_amount; // Debug info
}
}
snow_road_pairs <- snow_road_pairs sort_by (-float(each[0]));
//write "Road is sorted has snow level: " + road_assignments; // Debug info
list<plows> all_plows <- plows sort_by each.name;
int total_plows <- length(all_plows);
int my_index <- all_plows index_of self;
if (my_index <length(snow_road_pairs)){
//float assigned_road <- snow_road_pairs[my_index];
list my_pair <- snow_road_pairs[my_index];
float snow_level <- float(my_pair[0]);
road my_road <- road(my_pair[1]);
target_road <- my_road;
target <- any_location_in(target_road);
}
// Create weights based on snow levels
map<road, float> road_weights;
ask road {
put (1.0 + snow_field[location]) at: self in: road_weights; // Add 1 to avoid zero weights
}
// Compute path considering snow weights
shortest_path <- path_between(road_network, location, target);
write "Path length: " + shortest_path.shape.perimeter;
//After finding the target, add a desire to move there
do remove_intention(find_snowiest_road,true);
do add_desire(go_to_target);
}
//Plan to move to target
plan move_to_target intention: go_to_target {
//do goto(target: target, on: road_network, speed: speed);
path followed_path <- goto(target: target, on: road_network, speed: speed, return_path: true);
if (followed_path != nil and followed_path.shape !=nil) {
float current_snow <- snow_field[followed_path.shape.location];
if (current_snow) > 0 {
snow_field[followed_path.shape.location] <- -6.0;
}
}
if(location distance_to target <2.0){ //log : //write "Reached target road!";
do remove_intention(go_to_target,true);
do add_desire(call_for_help);
// do add_desire(clear_snow);
}
}
perceive target: road {
float current_snow <- snow_field[location];
//write "Road ID: " + self + " Current snow: " + current_snow + " Has target?: " + (myself.target_road != nil);
if (myself.target_road != nil
and current_snow > myself.snow_threshold
and not myself.needs_help
){
ask myself {
do add_belief(clear_snow);
//needs_help <- true;
}
}
}
//Plan to handle the request for help
plan handle_help_request intention: call_for_help {
if (target_road = nil) {
return;
}
// needs_help <- true;
// is_waiting <- true;
// do add_belief(waiting_for_help);
// do add_desire(request_help);
float target_snow_level <- snow_field[target_road.location];
if (target_snow_level > snow_threshold or empty(helper_plows)) {
needs_help <- true;
is_waiting <- true;
do add_belief(waiting_for_help);
//do add_desire(request_help);
//write name + " is requesting help at " + target_road + " due to snow level: " + target_snow_level;
}
do remove_intention(call_for_help);
do add_desire(clear_snow);
}
//Plan for clearing the snow
plan clear_target_snow intention: clear_snow {
float current_snow <- snow_field[location];
//--------------------- Movement working ---------------------------------------
// Get the road we're currently on
road current_road <- road closest_to(location);
// Create a path that follows the current road's shape
path road_path <- path_between(road_network, first(current_road.shape.points), last(current_road.shape.points));
// Check if we have reached the end point of the road
bool at_endpoint <- location distance_to last(current_road.shape.points) < 2.0;
bool is_dead_end <- false;
if(at_endpoint){
//Find roads that share this endpoint
point end_point <- last(current_road.shape.points);
list<road> connected_roads <- road where (each != current_road and end_point in each.shape.points);
is_dead_end <- empty(connected_roads);
if(is_dead_end){
write name + " has reached a dead end at " + end_point;
point new_target <- first(current_road.shape.points);
do goto(target: new_target, on: road_network, speed: speed/2);
//write name + " has reached a dead end at " + end_point;
}
}
// Move along the road while clearing snow
do goto(target: last(current_road.shape.points), on: road_network, speed: speed/2);
// Clear snow as we move - using your existing snow field logic
if (current_snow > 0) {
snow_field[location] <- -25;
}
}
aspect default {
draw rectangle(6, 12) rotated_by (heading + 90) color: #red depth: 1;
draw circle(view_dist) color: #red border: #red depth: 1 wireframe: true;
if (target != nil) {
draw line([location, target]) color: #blue;
}
}
}
species plow skills: [moving] control: simple_bdi {
point home_location;
//plows leader;
point target;
road current_road;
float speed <- 1000 #m/#h;
float view_dist <- 30.0;
plows assigned_leader <- nil;
rescue_vehicle assigned_rescue_vehicle <- nil;
//Conditions
bool is_available <- true;
bool is_at_home <-true;
bool is_at_target <-true;
perceive target: plows when: is_at_home {
if (myself.is_at_home) {
if(myself.assigned_leader =nil){
plows unassisted_leader <- plows first_with(
each.needs_help = true and
each.helper_plows = []
);
if (unassisted_leader!=nil and unassisted_leader.is_waiting){
write "Helper plow " + name + " detected leader: " + unassisted_leader.name;
ask myself {
target <- unassisted_leader.target;
assigned_leader <- unassisted_leader;
ask assigned_leader {
add myself to: helper_plows;
write "Leader " + name + " assigned helper: " + myself.name;
}
do remove_belief(at_home);
do add_desire(start_clearing);
//is_at_home <- false;
}
}
}
}
}
plan respond_to_call intention: start_clearing{
path followed_path <- goto(target: target, on: road_network, speed: speed, return_path: true);
if (followed_path != nil and followed_path.shape !=nil) {
float current_snow <- snow_field[followed_path.shape.location];
if (current_snow) > 0 {
snow_field[followed_path.shape.location] <- -10.0;
}
}
if (location distance_to target < 2.0) {
do remove_intention(start_clearing, true);
do add_desire(helper_clears);
//write name + " has intention helper_clears?: " ;
}
}
plan clear_at_target intention: helper_clears{
is_at_target <-true;
float current_snow <- snow_field[location];
road current_road <- road closest_to(location);
// Create a path that follows the current road's shape
path road_path <- path_between(road_network, first(current_road.shape.points), last(current_road.shape.points));
// Move along the road while clearing snow
do goto(target: last(current_road.shape.points), on: road_network, speed: speed/2);
// Clear snow as we move - using your existing snow field logic
if (current_snow > 0) {
snow_field[location] <- -25;
}
}
aspect default {
draw circle(20) color: #black size:15;
draw circle(view_dist) color: #orange border: #black depth: 1 wireframe: true;
}
}
species emergency_site {
//point location;
aspect default {
draw circle(15) color: #red size:20;
}
}
species rescue_vehicle skills: [moving] control: simple_bdi {
point target;
point home_location;
road current_road;
float view_dist <- 100.0;
bool is_at_home <- true;
bool is_on_mission <- false;
bool sites_available <- false;
float snow_level <- snow_field[location];
float speed <- 400.0 #m/#h;
//list<point> emergency_locations -> emergency_locations;
map<rescue_vehicle, point> assigned_sites <- []; // Stores which vehicle is assigned to which site
map<rescue_vehicle, float> rescue_times <- []; // Stores time taken for each vehicle
int travel_time;
float travel_times <- 0.0;
list<int> time_series <- []; // Stores time at each cycle
list<float> travel_series <- []; // Stores corresponding travel times
bool has_arrived <- false;
int start_time;
int end_time;
init {
start_time <- -1;
home_location <- location;
do add_belief(at_home);
//do add_desire(find_emergency);
//all_rescue_vehicles <- rescue_vehicle;
}
reflex start_rescue_perception when: current_cycle = 72 {
ask rescue_vehicle {
do add_desire(find_emergency);
}
}
// Track time evolution of travel time
reflex track_travel_time when: (start_time > -1) {
add current_cycle to: time_series;
add (current_cycle - start_time) to: travel_series; // Compute elapsed travel time
}
// PLAN: Find the closest emergency and respond
plan find_emergency intention: find_emergency {
//write "DEBUG: Current emergency locations: " + emergency_locations;
if (empty(emergency_locations)) {
//write name + " No emergency sites available!";
return;
}
// Find closest emergency site
point closest_site <- nil;
float min_distance <- #max_float;
loop site over: emergency_locations {
float dist <- location distance_to site;
if (dist < min_distance) {
min_distance <- dist;
closest_site <- site;
}
}
target <- closest_site;
put target at: self in: assigned_sites;
remove target from: emergency_locations;
if (not is_on_mission) {
is_on_mission <- true;
do add_desire(go_to_emergency);
}
do remove_intention(find_emergency, true);
}
//MOVE TO EMERGENCY
plan go_to_emergency intention: go_to_emergency {
int start_time <- current_cycle; // Capture the time when the vehicle starts
//rescue_times[self] <- start_time; // Store the starting time
//write name + " attempting to move to emergency at: " + target;
// Move using road network
path rescue_path <- goto(target: target, on: road_network, speed: speed , return_path: true);
// Check if arrived
if (location distance_to target < 5 and not (self in arrived_vehicles)) {
has_arrived <- true;
int end_time <- current_cycle;
float travel_time <- end_time - rescue_times[self];
rescue_times[self] <- travel_time;
add self to: arrived_vehicles;
map<string, float> final_travel_times <- []; // Stores each vehicle and its travel time
put travel_time at: name in: final_travel_times; // Map vehicle name to time
write name + " arrived. Total: " + length(arrived_vehicles);
}
if (length(arrived_vehicles) = length(all_rescue_vehicles) and not all_rescue_done) {
write "FINAL RESCUE VEHICLE TRAVEL TIMES ";
write rescue_times; // Prints entire map of vehicle -> travel time
all_rescue_done <- true; // Prevent multiple prints
}
}
aspect default {
draw triangle(20) color: #green;
}
}
experiment storm type: gui autorun: true {
parameter "Number of Plows per station" var: num_plows category: "Simulation" min: 1 max: 10 step: 1;
//file snow_data_file <- file("snow_data.csv");
float minimum_cycle_duration <- 0.1;
float max_observed_snow <- 0.0;
float simulation_start_time <- #ms;
float max_total_snow <- 0.0;
list<rgb> pal <- palette([rgb(200, 221, 232), rgb(0, 0, 128)]);
float total_time_to_clear <- 0.0;
int total_snow_cleared <- 0;
int total_clear_events <- 0;
list<float> snow_clear_times <- [];
list<float> total_snow_over_time <- [];
list<float> snow_cleared_over_time <- [];
list<float> percent_snow_cleared_over_time <- [];
int total_plows <- 0;
int active_plows <- 0;
reflex track_snow_performance {
float current_total_snow <- sum(snow_field) / 1000.0;
float total_snow_raw <- sum(snow_field);
if (empty(total_snow_over_time)) {
add current_total_snow to: total_snow_over_time;
//write "First Snow Measurement: " + current_total_snow;
return;
}
float initial_snow <- total_snow_over_time[0];
//write "Initial snow is: " + initial_snow;
add current_total_snow to: total_snow_over_time;
float snow_cleared <- max(initial_snow - current_total_snow , 0.0);
add snow_cleared to: snow_cleared_over_time;
float percent_cleared <- 0.0;
if (initial_snow > 0) {
percent_cleared <- max(0.0, ((initial_snow - current_total_snow) / initial_snow) * 100.0);
}
if (percent_cleared > 0) {
percent_cleared <- round(percent_cleared * 100.0) / 100.0;
}
add percent_cleared to: percent_snow_cleared_over_time;
}
float total_snow <- sum(snow_field);
//Parameter number of plows
map<rescue_station, map<plows, list>> snow_cleared_by_station <- [];
int last_plow_count <- num_plows;
reflex track_snow_per_plow {
ask plows {
float snow_cleared <- sum(snow_field[location]);
// Find which station the plow belongs to
rescue_station my_station <- rescue_station closest_to(location);
if (my_station != nil) {
if (not (my_station in myself.snow_cleared_by_station)) {
myself.snow_cleared_by_station[my_station] <- [];
}
if (not (self in myself.snow_cleared_by_station[my_station])) {
myself.snow_cleared_by_station[my_station][self] <- [];
}
// Append cleared snow
add snow_cleared to: myself.snow_cleared_by_station[my_station][self];
}
}
}
output synchronized: true {
display storm_simulation type: 3d axes: false background: rgb(245, 245, 245) fullscreen: false toolbar: false {
light #ambient intensity: 200;
camera 'default' location: {1254.041, 2938.6921, 1792.4286} target: {1258.8966, 1547.6862, 0.0};
species road refresh: true;
species building refresh: false;
species plows;
species rescue_station;
species plow;
species rescue_vehicle;
species emergency_site;
mesh snow_field scale: 30 triangulation: true transparency: 0.5 smooth: 3 above: 0.5 color: pal;
graphics "Info" position: {10, 10} size: {180, 20} {
}
}
}
}