ForagerRL_step2 - gama-platform/gama GitHub Wiki

The Smart Forager — Step 2: The Forager Agent

By Killian Trouillet


Step 2: The Forager Agent

Content

This second step adds a forager agent that moves randomly on the grid. The forager is displayed as a blue circle and moves one cell per simulation step in a random direction (up, right, down, or left). It cannot walk through obstacles or outside the grid boundaries.

Formulation

  • Definition of the forager species with a my_cell attribute linking it to the grid
  • Definition of a random_move reflex for movement
  • Display of the forager as a blue circle
  • Boundary and obstacle collision checking

Model Definition

Forager species

We define a new species called forager. The key attribute is my_cell, which stores a reference to the grid cell the forager currently occupies. This links the agent to the grid topology.

species forager {
    world_cell my_cell;
  • world_cell my_cell: A variable of type world_cell (our grid species). This acts as the forager's "position" on the grid.

Random movement reflex

A reflex is a behavior that is automatically executed at every simulation step. Here, we pick a random direction (0–3), compute the target coordinates, check boundaries and obstacles, and move.

    reflex random_move {
        int direction <- rnd(3);
        int new_x <- my_cell.grid_x;
        int new_y <- my_cell.grid_y;

        switch direction {
            match 0 { new_y <- new_y - 1; } // up
            match 1 { new_x <- new_x + 1; } // right
            match 2 { new_y <- new_y + 1; } // down
            match 3 { new_x <- new_x - 1; } // left
        }

        if (new_x >= 0 and new_x < grid_size and new_y >= 0 and new_y < grid_size) {
            world_cell target <- world_cell grid_at {new_x, new_y};
            if (not target.is_obstacle) {
                my_cell <- target;
                location <- my_cell.location;
            }
        }
    }
  • rnd(3): Returns a random integer between 0 and 3 (inclusive).
  • switch/match: A control structure to execute different code depending on the value. This is cleaner than a chain of if/else.
  • my_cell.grid_x / my_cell.grid_y: Built-in attributes of grid cells giving their column and row indices.
  • location <- my_cell.location: Updates the forager's geometric position to the center of the target cell, so it is displayed correctly.

Aspect

An aspect defines how the agent is displayed.

    aspect default {
        draw circle(0.8) color: #blue;
    }
}
  • draw circle(0.8): Draws a circle with radius 0.8, large enough to be clearly visible within a grid cell.
  • The default aspect is used automatically when no specific aspect is requested.

Creating the forager

In the global init block, we create one forager and place it at position (0, 0):

create forager number: 1 {
    my_cell <- world_cell grid_at {0, 0};
    location <- my_cell.location;
}

Updated display

We add the forager species to the display. Order matters: agents drawn later appear on top.

display "Grid World" {
    grid world_cell border: #lightgray;
    species forager;
    graphics "food" {
        ask world_cell where each.is_food {
            draw circle(5) color: rgb(50, 180, 50);
        }
    }
}

Complete Model

/**
* Name: SmartForager - Step 2: The Forager Agent
* Author: Killian Trouillet
* Description: This second step adds a forager agent that moves randomly on the grid.
*              The forager cannot walk into obstacles or outside the grid boundaries.
* Tags: reinforcement-learning, grid, agent, movement, tutorial
*/

model SmartForager

global {
	int grid_size <- 10;
	int food_x <- 9;
	int food_y <- 9;
	list<point> obstacle_positions <- [{2,2}, {3,2}, {2,3}, {6,4}, {7,4}, {7,5}];
	
	init {
		ask world_cell grid_at {food_x, food_y} {
			is_food <- true;
		}
		loop pos over: obstacle_positions {
			ask world_cell grid_at pos {
				is_obstacle <- true;
			}
		}
		// Create the forager at position (0, 0)
		create forager number: 1 {
			my_cell <- world_cell grid_at {0, 0};
			location <- my_cell.location;
		}
	}
}

grid world_cell width: 10 height: 10 neighbors: 4 {
	bool is_food <- false;
	bool is_obstacle <- false;
	rgb color <- #white update: is_obstacle ? rgb(60, 60, 60) : #white;
}

species forager {
	world_cell my_cell;
	
	reflex random_move {
		// Pick a random direction: 0=up, 1=right, 2=down, 3=left
		int direction <- rnd(3);
		int new_x <- my_cell.grid_x;
		int new_y <- my_cell.grid_y;
		
		switch direction {
			match 0 { new_y <- new_y - 1; } // up
			match 1 { new_x <- new_x + 1; } // right
			match 2 { new_y <- new_y + 1; } // down
			match 3 { new_x <- new_x - 1; } // left
		}
		
		// Check boundaries and obstacles
		if (new_x >= 0 and new_x < grid_size and new_y >= 0 and new_y < grid_size) {
			world_cell target <- world_cell grid_at {new_x, new_y};
			if (not target.is_obstacle) {
				my_cell <- target;
				location <- my_cell.location;
			}
		}
	}
	
	aspect default {
		draw circle(0.4) color: #blue;
	}
}

experiment smart_forager type: gui {
	output {
		display "Grid World" {
			grid world_cell border: #lightgray;
			species forager;
			graphics "food" {
				ask world_cell where each.is_food {
					draw circle(0.4) color: rgb(50, 180, 50);
				}
			}
		}
	}
}
⚠️ **GitHub.com Fallback** ⚠️