ClassDefinition_step5 - gama-platform/gama GitHub Wiki

Advanced Features

Objects as Agent Attributes

Objects can be stored in any GAML variable — global, species, local, etc.

species node_agent skills: [moving] {
	
	/** Incoming messages queued for this agent. */
	list<message_object> inbox <- [];
	
	/** Current velocity vector. */
	vec2 velocity <- vec2(dx: rnd(2.0) - 1.0, dy: rnd(2.0) - 1.0);
}

global {
	geometry shape <- square(100);
	
	init {
		create node_agent number: 10;
	}
}

Passing Objects Between Agents

Objects follow GAML value semantics — assigning to a variable copies the reference, not the object. Both agents hold a reference to the same object in memory:

species node_agent skills: [moving] {
	
	list<message_object> inbox <- [];
	
	/** Receives a message object. */
	action receive_message(message_object msg) {
		inbox <+ msg;
	}
	
	/**
	 * Creates and returns a new message object.
	 * Returning an object from an agent action is identical to returning any other value.
	 */
	message_object compose(string msg_content, int msg_priority) {
		return message_object(sender_name: name,
		               content: msg_content,
		               priority: msg_priority);
	}
}

global {
	init {
		create node_agent number: 5;
	}
}

global {
	init {
		node_agent sender <- first(node_agent);
		message_object m1 <- sender.compose("Hello world", 2);
		
		node_agent recipient <- last(node_agent);
		ask recipient { do receive_message(m1); }
		write recipient.name + " inbox size: " + length(recipient.inbox);
		
		// Both sender and recipient hold a reference to THE SAME object
		write "Same object? " + (m1 = recipient.inbox[0]);   // true: reference equality
	}
}

Objects as Return Values

Actions in any class can return objects — including actions in agents:

vec2 add(vec2 other) {
	return vec2(dx: dx + other.dx, dy: dy + other.dy);
}

This enables chaining:

vec2 combined <- v1.add(v2).scale(0.5);
// Creates a sum, then scales it — zero intermediate variables

Objects as Experiment Parameters

experiment Advanced_Features title: "Advanced Features" type: gui {
	
	/** Number of nodes — also used as a parameter in the GUI. */
	parameter "Number of nodes" var: nb_nodes min: 2 max: 50 category: "Setup";
	
	output {
		display Nodes title: "Nodes" type: 2d {
			species node_agent;
		}
	}
}

The nb_nodes integer parameter can be used in the model to control species population size.

Polymorphic Objects in Agent Collections

Objects inside agent collections can be any type — including polymorphic lists:

// Send messages between random pairs
loop i from: 1 to: 3 {
	node_agent s <- any(node_agent);
	node_agent r <- any(node_agent where (each != s));
	if r != nil {
		message_object msg <- s.compose("ping #" + i, i);
		ask r { do receive_message(msg); }
	}
}

// Accumulate all messages across all agents
list<message_object> all_msgs <- node_agent accumulate each.inbox;
list<message_object> sorted <- all_msgs sort_by (-each.priority);

Object Identity

The equality operator = on objects tests reference identity by default — two objects are the same iff they are the exact same instance:

vec2 a <- vec2(dx: 3.0, dy: 4.0);
vec2 b <- vec2(dx: 3.0, dy: 4.0);   // different object, same values
vec2 c <- a;                          // same reference as a

write "a = b (same reference)? " + (a = b);          // false (different instances)
write "a = c (same reference)? " + (a = c);          // true (same instance)

Objects as Map Keys

Objects can be used as keys in maps since they support identity-based hashing:

map<vec2, string> labels;
labels[a] <- "origin vector";
labels[b] <- "copy vector";

write "labels[a] = " + labels[a];       // "origin vector"
write "labels[b] = " + labels[b];       // "copy vector"
write "labels[c] = " + labels[c];       // "origin vector" — c shares ref with a

Objects + Species: Full Integration

The advanced features model shows how objects and species work together in a complete simulation:

// Supporting classes
class vec2 {
	float dx <- 0.0;
	float dy <- 0.0;
	
	float norm() { return sqrt(dx ^ 2 + dy ^ 2); }
	vec2 add(vec2 other) { return vec2(dx: dx + other.dx, dy: dy + other.dy); }
	vec2 scale(float factor) { return vec2(dx: dx * factor, dy: dy * factor); }
	string to_string() { return "(" + (dx with_precision 2) + ", " + (dy with_precision 2) + ")"; }
}

class message_object {
	string sender_name <- "unknown";
	string content <- "";
	int priority <- 1;
	bool read_flag <- false;
	
	string consume() { read_flag <- true; return content; }
	string to_string() { return "[msg p=" + priority + (read_flag ? " ✓" : "  ")
	                            + "] from=" + sender_name + " : " + content; }
}

// Species with objects as attributes
species node_agent skills: [moving] {
	
	list<message_object> inbox <- [];
	vec2 velocity <- vec2(dx: rnd(2.0) - 1.0, dy: rnd(2.0) - 1.0);
	float energy <- 100.0;
	
	action receive_message(message_object msg) {
		inbox <+ msg;
	}
	
	int process_inbox() {
		int count <- length(inbox);
		loop msg over: inbox {
			string text <- msg.consume();
			energy <- energy - msg.priority * 0.5;
		}
		inbox <- [];
		return count;
	}
	
	message_object compose(string msg_content, int msg_priority) {
		return message_object(sender_name: name, content: msg_content, priority: msg_priority);
	}
	
	reflex drift {
		location <- {(location.x + velocity.dx) mod world.shape.width,
		             (location.y + velocity.dy) mod world.shape.height};
	}
	
	reflex check_mail when: !empty(inbox) {
		int n <- process_inbox();
	}
	
	reflex recharge {
		energy <- min(100.0, energy + 0.2);
	}
	
	aspect default {
		float r <- max(1.0, energy / 20.0);
		draw circle(r) color: rgb(int(energy * 2.55), 100, 50) border: #white;
		if !empty(inbox) {
			draw string(length(inbox)) at: location + {0, -r - 0.5}
				color: #yellow size: 2.0;
		}
	}
}

global {
	geometry shape <- square(100);
	int nb_nodes <- 10;
	
	init { create node_agent number: nb_nodes; }
	
	message_object create_random_message() {
		return message_object(sender_name: "system", content: "test", priority: rnd(5));
	}
}

experiment Advanced_Features title: "Advanced Features" type: gui {
	parameter "Number of nodes" var: nb_nodes min: 2 max: 50 category: "Setup";
	output {
		display Nodes title: "Nodes" type: 2d {
			species node_agent;
		}
	}
}

Key Takeaways

Feature Description
Objects as agent attributes list<message> inbox <- []; inside species
Passing objects between agents Action arguments — reference is shared
Returning objects from actions Same syntax as returning any value
Chaining action returns v1.add(v2).scale(0.5) — no intermediate vars
Object identity a = b returns true only if same instance
Objects as map keys map<ClassName, T> m; m[an_object] <- value;
Experiment parameters parameter "Label" var: class_instance_type;
Aggregating objects Species accumulate each.obj_attr

See Also

⚠️ **GitHub.com Fallback** ⚠️