- Read the question carefully (twice).
- Break the task into the smallest steps.
- Sketch or write pseudocode before coding.
- Start small — test as you go.
- Check your solution with different cases.
Create an Ant Colony Simulation
Introduction – The Story
In the previous problem set, you modeled one ant wandering randomly. Real ant colonies, however, display complex behavior: they search for food, leave trails, and work together.
In this task, you will create a simulation of a small ant colony. Instead of just one ant, there will be multiple ants, and they can interact with the environment.
This is your first taste of how simple agents (ants) following simple rules can create emergent complex behavior. This concept is widely used in:
- Swarm robotics (drones working together).
- Traffic flow simulation.
- Epidemiology (disease spread).
- Optimization problems (Ant Colony Optimization algorithms).
The Problem
You need to simulate a colony of ants moving on a 2D grid with food sources.
Rules for the system:
- The grid is
n x n. mants are placed at the nest (center of the grid).- Food sources are randomly placed on the grid.
- Each ant can:
- Wander randomly if it hasn’t found food.
- If it finds food, it carries it back to the nest.
- While carrying food, it leaves a pheromone trail on each cell.
- Other ants are more likely to follow cells with stronger pheromone trails.
- The grid tracks pheromone levels (they fade over time).
Your Goal: Run the simulation for steps turns and return:
- How many pieces of food were delivered to the nest.
- How many unique cells the ants visited.
- A summary of pheromone levels (average, max).
Learning Objectives (IB Command Terms)
- Describe how local rules create global system behavior.
- Explain the role of randomness and probability in simulations.
- Construct a multi-agent simulation (colony).
- Evaluate how changing parameters (number of ants, food sources) affects outcomes.
Starter Code
import random
class Ant:
def __init__(self, grid_size, nest):
self.x, self.y = nest
self.has_food = False
self.grid_size = grid_size
def move(self, pheromones):
"""Move randomly, biased by pheromone strength."""
directions = [(0,-1), (1,0), (0,1), (-1,0)] # N,E,S,W
candidates = []
for dx, dy in directions:
nx, ny = self.x+dx, self.y+dy
if 0 <= nx < self.grid_size and 0 <= ny < self.grid_size:
strength = pheromones[ny][nx]
candidates.extend([(dx,dy)] * (1 + strength)) # more pheromone = more likely
dx, dy = random.choice(candidates)
self.x += dx
self.y += dy
class Colony:
def __init__(self, grid_size, num_ants, num_food):
self.grid_size = grid_size
self.nest = (grid_size//2, grid_size//2)
self.ants = [Ant(grid_size, self.nest) for _ in range(num_ants)]
self.pheromones = [[0]*grid_size for _ in range(grid_size)]
self.food = set()
for _ in range(num_food):
fx, fy = random.randint(0,grid_size-1), random.randint(0,grid_size-1)
self.food.add((fx, fy))
self.delivered = 0
def step(self):
for ant in self.ants:
if not ant.has_food:
# Check for food
if (ant.x, ant.y) in self.food:
ant.has_food = True
self.food.remove((ant.x, ant.y))
else:
ant.move(self.pheromones)
else:
# Return to nest
if (ant.x, ant.y) == self.nest:
self.delivered += 1
ant.has_food = False
else:
self.pheromones[ant.y][ant.x] += 1
ant.move(self.pheromones)
# Evaporate pheromones
for y in range(self.grid_size):
for x in range(self.grid_size):
if self.pheromones[y][x] > 0:
self.pheromones[y][x] -= 1
def run(self, steps):
for _ in range(steps):
self.step()
total_pheromones = sum(sum(row) for row in self.pheromones)
max_pheromone = max(max(row) for row in self.pheromones)
return {
"delivered": self.delivered,
"unique_food_left": len(self.food),
"avg_pheromone": total_pheromones / (self.grid_size**2),
"max_pheromone": max_pheromone
}
Test Cases
from colony import Colony
# Small simulation
colony = Colony(grid_size=10, num_ants=5, num_food=3)
result = colony.run(50)
print(result)
# Example: {'delivered': 2, 'unique_food_left': 1, 'avg_pheromone': 0.03, 'max_pheromone': 2}
# Edge case: no food
colony = Colony(grid_size=10, num_ants=5, num_food=0)
result = colony.run(50)
print(result)
# Expected: delivered=0, unique_food_left=0
# Edge case: no ants
colony = Colony(grid_size=10, num_ants=0, num_food=5)
result = colony.run(50)
print(result)
# Expected: delivered=0 (since no ants can carry food)
Student Tasks
- Run the simulation and track how many food pieces are delivered.
- Modify the number of ants, food, or steps — how does it change the outcome?
- Add visualization (text-based or with
matplotlib) to see the ants and pheromone trails. - Reflect: How do simple local rules (turn, move, drop pheromone) create coordinated colony behavior?
- Challenge: Add “energy” to ants (they die if they don’t return to the nest in time).