/**
* Name: Boids With Flocks
* Author: 
* Description: This model shows the movement of boids following a goal and creating a flock . 
* Tags: gui, skill, 3d, multi_level, clustering
*/
model boids_flock
//Import the boids model
import "Boids.gaml"
global {
	//Size of the boids
	float boids_size <- float(3);
	//Shape of the boids
	geometry boids_shape <- circle(boids_size);
	//Separation between boids
	float boids_separation <- 4 * boids_size;
	//Distance to allow creation of the flock 
	int flock_creation_distance <- int(boids_separation + 1);
	//Minimum number of member among a flock
	int min_group_member <- 3;
	//Frequency of update for the flock
	int update_frequency <- 10;
	//Frequency of merge for the flock
	int merge_frequency <- 10;
	//Allow the creation of flock
	bool create_flocks <- false;
	//Perception range of the boids
	int base_perception_range <- int(xmax / 100) min: 1;
	
	init {
		//Creation of the different agents viewer
		create boids_agents_viewer;
		create flock_agents_viewer;
		create boids_in_flock_viewer;
	}
	//Reflex to create the flocks if it is available
	reflex create_flocks when: create_flocks {
		
		if (length(boids) > 1) {
			//Clustering by distance of the boids to determine the satisfying boids groups
			list> satisfying_boids_groups <- (boids.population simple_clustering_by_distance flock_creation_distance) where ((length(each)) > min_group_member);
			loop one_group over: satisfying_boids_groups {
				
				geometry potential_flock_polygon <- convex_hull(solid(polygon(one_group collect boids(each).location)) + (base_perception_range + 5));
				//If there is no obstacle between the boids of a potential flock, then the flock is created and all the boids become boids in flock
				if (empty(obstacle overlapping potential_flock_polygon)) {
					create flock {
						capture one_group as: boids_in_flock;
					}
				}

			}

		}

	}

}

//Species flock which represent the flock of boids, using the skill moving
species flock skills: [moving] {
	rgb color <- rgb(rnd(255), rnd(255), rnd(255));
	geometry shape <- any_point_in(host);
	//Range of perception of the flock
	float perception_range <- float(base_perception_range + (rnd(5)));
	//Speed of the flock
	float speed update: mean(boids_in_flock collect each.speed);
	//Reflex to disaggregate the flock if there is a obstacle in the flock
	reflex disaggregate {
		geometry buffered_shape <- shape + perception_range;
		if !(empty(obstacle overlapping buffered_shape)) {
			release list(members) as: boids in: world;
			do die;
		}

	}
	//Reflex to capture the boids nearby in the range of perception with an update_frequency
	reflex capture_nearby_boids when: ((cycle mod update_frequency) = 0) {
		geometry buffered_shape <- shape + perception_range;
		list nearby_boids <- (boids overlapping buffered_shape);
		if (!(empty(nearby_boids))) {
			geometry new_polygon <- convex_hull(solid(shape + polygon(nearby_boids collect (each.location))));
			if (empty(obstacle overlapping new_polygon)) {
				capture nearby_boids as: boids_in_flock;
			}

		}

	}
	//Reflex to merge the intersecting flocks
	reflex merge_nearby_flocks when: ((cycle mod merge_frequency) = 0) 
	{
		loop f over: (flock) {
			if (f != self and (shape intersects f.shape)) {
				geometry new_shape <- convex_hull(polygon(shape.points + f.shape.points));
				if empty(obstacle overlapping new_shape) {
					list released_boids;
					ask f {
						release list(members) as: boids in: world returns: released_coms;
						released_boids <- released_coms;
						do die;
					}

					if (!empty(released_boids)) {
						capture released_boids as: boids_in_flock;
					}

					shape <- convex_hull(polygon(members collect (boids_in_flock(each).location)));
				}

			}

		}

	}
	//Reflex to make the flock follow the goal
	reflex chase_goal {
		float direction_to_nearest_ball <- (self towards (first(boids_goal)));
		float step_distance <- speed * step;
		float dx <- step_distance * (cos(direction_to_nearest_ball));
		float dy <- step_distance * (sin(direction_to_nearest_ball));
		geometry envelope <- shape.envelope;
		float min_y <- (envelope.points with_min_of (each.y)).y;
		float min_x <- (envelope.points with_min_of (each.x)).x;
		float max_x <- (envelope.points with_max_of (each.x)).x;
		float max_y <- (envelope.points with_max_of (each.y)).y;
		if (((dx + min_x) < xmin) and min_x > xmin) or (((dx + max_x) > xmax) and max_x < xmax) {
			dx <- 0.0;
		}
		if (((dy + min_y) < ymin) and min_y > ymin) or (((dy + max_y) > ymax) and max_y < ymax) {
			dy <- 0.0;
		}
		
		loop com over: boids_in_flock {
			com.location <- com.location + { dx, dy };
		}

		shape <- convex_hull(polygon(list(boids_in_flock) collect (each.location)));
	}

	aspect default {
		draw shape color: color;
	}
	//Species boids_in_flock which represents the boids agents captured by the flock
	species boids_in_flock parent: boids {
		float my_age <- 1.0 update: my_age + 0.01;
		reflex separation when: apply_separation {
		}

		reflex alignment when: apply_alignment {
		}

		reflex cohesion when: apply_cohesion {
		}

		reflex avoid when: apply_avoid {
		}

		reflex follow_goal  {
		}

		reflex wind when: apply_wind {
		}

		action do_move {
		}

		reflex movement {
			do do_move;
		}

		aspect default {
			draw circle(my_age) color: (host.color).darker;
		}

	}

}
//Species flock agents viewer which draw the flock information
species flock_agents_viewer {
	aspect default {
		draw "Flocks: " + (string(length(list(flock)))) at: { width_and_height_of_environment - 810, (width_and_height_of_environment) - 5 } color: #blue size: 80 ;
	}

}
//Species boids agents viewer which draw the boids information
species boids_agents_viewer {
	aspect default {
		draw "Boids: " + (string(length(list(boids)))) at: { width_and_height_of_environment - 810, (width_and_height_of_environment) - 165 } color: #blue size: 80 ;
	}

}

//Species boids_in_flock_viewer which draw the boids in flock information
species boids_in_flock_viewer {
	aspect default {
		draw "Boids in flocks: " + (string(number_of_agents - (length(list(boids))))) at: { width_and_height_of_environment - 810, width_and_height_of_environment - 85 } color:
		#blue size: 80 ;
	}

}


experiment boids_flocks type: gui {
	parameter "Create flock?" var: create_flocks <- true;
	parameter "Number of boids" var: number_of_agents <- 300;
	parameter "Environment size" var: width_and_height_of_environment <- 1600;
	parameter "Moving obstacles?" var: moving_obstacles <- true;
	parameter "Torus environment?" var: torus_environment <- false;
	parameter "Number of obstacles" var: number_of_obstacles <- 5;
	output {
		display default_display {
			species boids_goal;
			species boids aspect: image;
			species obstacle;
			species flock aspect: default transparency: 0.5 {
				species boids_in_flock aspect: default;
			}

			species flock_agents_viewer;
			species boids_agents_viewer;
			species boids_in_flock_viewer;
		}

	}

}