/**
* Name: _soccerbase
* Author: Julien
* Description: This model contains the parent classes of the model
* Tags:
*/
model soccerbase
species soccer_game {
// contains the global informations of the game
rgb back_color_team;
rgb front_color_team;
ball_sp ball; // the ball agent
goal_sp front_goal; // contains the goal at the front of the field (y = 120)
goal_sp back_goal; // contains the goal at the back of the field (y = 0)
list teams; // contains the 2 teams
list players; // contains all the players of the game
base_team team_possession; // the last team which possess the ball. This value is used to determine if the behavior of the team has to be defensive or offensive.
init {
// create the entities ball and the 2 goals
create ball_sp with:[location::world.location] returns:var_ball;
ball <- first(var_ball);
create goal_sp with:[location::{world.location.x,120},position::"front"] returns:var_goal1;
front_goal <- first(var_goal1);
create goal_sp with:[location::{world.location.x,0},position::"back"] returns:var_goal2;
back_goal <- first(var_goal2);
}
action reinit_phase {
// this action is called when a goal has been scored : the players are placed with their initial position, and the ball is reset to the center
ask players {
location <- init_pos;
previous_pos <- init_pos;
}
ball.location <- world.location;
ball.destination <- world.location;
ball.speed <- 0.0;
}
}
species base_player skills:[moving] {
// ATTRIBUTES ////////////////////////////////////////////////
// ATTRIBUTES ONLY USED IN THIS BASE CLASSE, SHOULD NEVER BEEN CALLED IN STRATEGY FILE
float recuperation_ability <- 0.2; // a mark from 0 to 1 to be able to catch the ball if another player has it
float speed_without_ball;
float speed_with_ball;
point previous_pos; // used to apply inertia
bool displacement_effectued<-false update:false; // we can apply only one displacement by step !
// ATTRIBUTE USEFUL TO BE READ IN THE TEAM STRATEGY FILE (READ ONLY !)
base_team team;
soccer_game game;
base_team ennemy_team <- nil update:first(game.teams where (each.position != team.position));
ball_sp ball <- nil update:first(ball_sp);
goal_sp own_goal <- nil update:first(goal_sp where (each.position = team.position));
goal_sp ennemy_goal <- nil update:first(goal_sp where (each.position != team.position));
// ratio of avancement of the ball (from the point of view of the current team)
float ball_advancement <- 0.0 update:(team.position = "back") ? ball.location.y / 120 : 1 - ball.location.y / 120;
bool possess_ball;
point init_pos;
point init_pos_in_percent;
float distance_to_closest_ennemy_player <- 100.0 update:self distance_to closest_ennemy_player;
// the number of ennemy players in a range of 15 meters
int number_of_ennemy_player_in_range <- 0 update:length((game.players where (each.team != team)) where ((each intersects circle(15))=true));
float distance_to_ball <- 100.0 update:(ball = nil) ? 100.0 : self distance_to ball;
float distance_to_goal <- 100.0 update:(ennemy_goal = nil) ? 100.0 : self distance_to ennemy_goal;
// the closest player of this team
base_player closest_friend_player <- nil update:(ball = nil) ? base_player(nil) : first( (game.players where (each.team = team and each != self))
where (each distance_to self = min( (game.players where (each.team = team and each != self)) collect (each distance_to self) ) )
);
// the closest ennemy player
base_player closest_ennemy_player <- nil update:(ball = nil) ? base_player(nil) : first( (game.players where (each.team != team and each != self))
where (each distance_to self = min( (game.players where (each.team != team and each != self)) collect (each distance_to self) ) )
);
// the player of this team wich has the best "position_mark"
base_player best_position_player <- nil update:first((team.players where (each != self))
where (each.position_mark = max((team.players where (each != self)) collect (each.position_mark)))
);
float current_speed<-1.0 update:(possess_ball) ? speed_with_ball : speed_without_ball;
// ATTRIBUTES WICH CAN BE CHANGED FROM THE TEAM STRAGEGY FILE
float position_mark <- 0.0 update:-distance_to_goal; // a mark attributed according to the position of the player (the higher the note is, the best the position is).
// By default, this mark is equal to -distance_to_goal.
string status <- ""; // the current status of the player (can be useful to build the model)
geometry influence_area <- nil update:circle(15); // the area of interest of the player. By default, this area is a circle 15m diameter centered in the player location.
// CONSTRUCTOR /////////////////////////////////////////////
init {
init_pos <- location;
previous_pos <- location;
possess_ball <- false;
speed_with_ball <- 0.4;
speed_without_ball <- 0.5;
}
// ACTIONS ////////////////////////////////////////////////////
// ACTIONS TO CALL FROM THE STRATEGY FILE
// action to run to a particular position
action run_to(point target) {
if (!displacement_effectued) {
do goto target:target speed:current_speed;
if (possess_ball) {
ball.location <- location;
}
displacement_effectued <- true;
}
else {
write "WARNING : only ONE action of displacement is allowed each step";
}
}
// action to run to the ball
action run_to_ball {
point targetPos;
if (ball.ball_direction intersects circle(1)) {
targetPos <- ball.location;
}
else {
targetPos <- (ball.ball_direction closest_points_with self) at 0;
}
do run_to(targetPos);
}
// action to run to the ennemy goal
action run_to_ennemy_goal {
do run_to( ennemy_goal.location );
}
// action to run to its own goal
action run_to_own_goal {
do run_to( own_goal.location );
}
// action to mark a player
action mark_player (base_player player) {
float rnd_area <- 4.0; // the player will choose a position in a square of rnd_area m.
point pos <- (team.position = "front") ? {player.location.x,player.location.y-rnd_area/2} : {player.location.x,player.location.y+rnd_area/2};
do run_to( {pos.x-rnd_area/2+rnd(rnd_area),pos.y-rnd_area/2+rnd(rnd_area)} );
}
// action ot shoot the ball to the ennemy goal
action shoot {
do loose_ball;
ask ball {
do shooted speed_atr:3.0 target_position:myself.ennemy_goal.location;
}
}
// action to pass the ball to an ally
action pass_the_ball (base_player target_player) {
do loose_ball;
ask ball {
do shooted target_position:target_player.location speed_atr:target_player.distance_to_ball/15;
}
team.called_player <- target_player;
}
// action to pass the ball to an ally
action pass_the_ball_ahead (base_player target_player,float number_of_meter_ahead) {
do loose_ball;
ask ball {
float offset <- ((myself.team.position = "back") ? number_of_meter_ahead : -number_of_meter_ahead);
point target_point <- {target_player.location.x,target_player.location.y+offset};
do shooted target_position:target_point speed_atr:target_player.distance_to_ball/15;
}
team.called_player <- target_player;
}
// ACTION AUTOMATICALLY CALLED IN THE BASE CLASSE
// try to take the ball if it is close enough
action try_to_take_ball {
// if no player has the ball
if (!team.possess_ball and !ennemy_team.possess_ball) {
// if the player is the one called (result of a pass)
if (team.called_player = self) {
do take_ball;
}
// if the player is not the one called (interception of the ball), probability to catch the ball inversly proportionnal with the speed of the ball
else {
if (flip(1/(1+2*ball.speed))) {
do take_ball;
}
}
}
// the ball is possessed by the ennemy team
else if (ennemy_team.possess_ball) {
// try to catch the ball from the other player
if flip(recuperation_ability) {
do take_ball;
}
}
}
// action of taking the ball
action take_ball {
if (ennemy_team.possess_ball) {
ask ennemy_team.player_with_ball {
do loose_ball;
}
}
possess_ball <- true;
ball.speed <- 0.0;
ball.destination <- ball.location;
team.called_player <- nil;
team.player_with_ball <- self;
team.possess_ball <- true;
game.team_possession <- team;
}
// action of loosing the ball
action loose_ball {
possess_ball <- false;
team.player_with_ball <- nil;
team.possess_ball <- false;
}
// apply the inertia
action apply_inertia {
point prev_pos <- location;
point inertia_vect <- {(location.x-previous_pos.x)*0.7,(location.y-previous_pos.y)*0.7};
float max_inertia <- current_speed;
if (norm(inertia_vect) > max_inertia) {
float inertia_x <- sqrt(abs(max_inertia*max_inertia-inertia_vect.y*inertia_vect.y));
float inertia_y <- sqrt(abs(max_inertia*max_inertia-inertia_vect.x*inertia_vect.x));
inertia_x <- (inertia_vect.x < 0) ? -inertia_x : inertia_x;
inertia_y <- (inertia_vect.y < 0) ? -inertia_y : inertia_y;
inertia_vect <- {inertia_x,inertia_y};
}
location <- location + inertia_vect;
previous_pos <- prev_pos;
}
// useful functions
// this function returns the real x if we pass a percentage : 0 is the extreme left point, 100 is the extreme right point.
float getXPos(float x_ratio) {
float result;
if (team.position="back") {
result <- 90-x_ratio*90;
}
else {
result <- x_ratio*90;
}
return result;
}
// this function returns the real y if we pass a percentage : 0 is the extreme defensive point, 100 is the extreme attack point.
float getYPos(float y_ratio) {
float result;
if (team.position="back") {
result <- y_ratio*120;
}
else {
result <- 120-y_ratio*120;
}
return result;
}
///////////////////////////////////////////////////////
// The update function, calls the adequate behavior
reflex update when:cycle>1 {
do apply_inertia;
// verify if it is a non-offside position
if ( (((team.position = "back") and (location.y > team.offside_pos))
or ((team.position = "front") and (location.y < team.offside_pos)))
and (!possess_ball) and (self != team.called_player)
) {
// offside position, go back to a correct position
point target_pos <- {location.x,(team.position = "back") ? location.y-current_speed:location.y+current_speed};
do run_to(target_pos);
status <- "offside position !";
}
else if ((distance_to_ball < 2) and !possess_ball) {
do try_to_take_ball;
}
else if (game.team_possession = team) {
do offensive_behavior;
}
else {
do defensive_behavior;
}
}
// defensive behavior, need to be redefined in the strategy file.
// this action is called when the last player who was holding the ball was a player of the ennemy team
action defensive_behavior virtual:true {
}
// defensive behavior, need to be redefined in the strategy file.
// this action is called when the last player who was holding the ball was a player of this team
action offensive_behavior virtual:true {
}
// ASPECT ////////////////////////////////////////////////////////
aspect player {
// the player wich possess the ball is displayed with a square. It is displayed with a circle otherwise.
if (possess_ball) {
draw square(2) color:(team.position = "back") ? game.back_color_team : game.front_color_team;
}
else {
draw circle(1) color:(team.position = "back") ? game.back_color_team : game.front_color_team;
}
}
}
species base_team {
// ATTRIBUTES ////////////////////////////////////////////////
// ATTRIBUTES ONLY USED IN THIS BASE CLASSE, SHOULD NEVER BEEN CALLED IN STRATEGY FILE
float offside_pos <- 0.0 update: (position = "back") ? max((game.players where (each.team != self)) collect (each.location.y))
: min((game.players where (each.team != self)) collect (each.location.y));
// ATTRIBUTES USEFUL TO BE READ IN THE TEAM STRATEGY FILE (READ ONLY !)
string position; // can be "front" or "back".
list players; // all the players of the team.
soccer_game game;
base_player closest_player_to_ball <- first(players) update: first( players with_min_of (each.distance_to_ball ) );
base_player called_player;
bool possess_ball <- false;// update: ! empty ( players where (each.possess_ball=true) );
base_player player_with_ball <- nil;// update: first(players where (each.possess_ball = true));
// ATTRIBUTES WICH CAN BE CHANGED FROM THE TEAM STRATEGY FILE
list player_init_position;
}
species ball_sp skills:[moving] {
// The ball agent.
float speed <- 0.0;
geometry ball_direction; // the direction of the ball is used to be followed by the player
reflex update {
speed <- speed*0.95;
float future_speed <- speed;
point tmpPos<-location;
loop i from:0 to:10 {
tmpPos <- {tmpPos.x+cos(heading)*speed,tmpPos.y+sin(heading)*speed};
future_speed <- future_speed*0.9;
}
ball_direction <- line([location,tmpPos]);
do wander amplitude:1.0;
// anticipation of the ball position to detect a goal
if ((location.y+sin(heading)*speed) > 120) {
write "back team score a goal !!";
ask first(soccer_game) {
do reinit_phase;
}
}
if ((location.y+sin(heading)*speed) < 0) {
write "front team score a goal !!";
ask first(soccer_game) {
do reinit_phase;
}
}
}
action shooted (point target_position, float speed_atr) {
// action called when a player shoots the ball
speed <- speed_atr;
do goto target:target_position;
}
aspect ball {
draw circle(0.5) color:#white;
}
}
species goal_sp {
string position; // can be "front" or "back".
init {
create goal_keeper with:[position::position];
}
aspect goal {
draw rectangle(7.32,1.0) color:#black;
}
}
species goal_keeper {
// the goal has a basic behavior : he tries to catch the ball when it is close enough, and when
string position; // can be "front" or "back".
ball_sp ball <- nil update:first(ball_sp);
reflex update when:cycle>0 {
location <- {ball.location.x/90*12+(90-12)/2,location.y};
if (ball distance_to self < 2) {
if (flip(1/(1+2*ball.speed))) {
first(soccer_game).team_possession <- first(first(soccer_game).teams where (each.position = position));
ask ball {
do shooted ({30+rnd(30),60},5.0);
}
}
}
}
init {
location <- {45,(position="front") ? 117 : 3};
}
action offensive_behavior {
}
action defensive_behavior {
}
aspect goal_keeper {
draw circle(1) color:(position = "back") ? first(soccer_game).back_color_team : first(soccer_game).front_color_team;
}
}