diff --git a/mesa/examples/advanced/wolf_sheep/agents.py b/mesa/examples/advanced/wolf_sheep/agents.py index 93f4b4eb77e..000b160f44b 100644 --- a/mesa/examples/advanced/wolf_sheep/agents.py +++ b/mesa/examples/advanced/wolf_sheep/agents.py @@ -40,7 +40,6 @@ def step(self): """Execute one step of the animal's behavior.""" # Move to random neighboring cell self.move() - self.energy -= 1 # Try to feed @@ -125,11 +124,15 @@ def fully_grown(self, value: bool) -> None: if not value: # If grass was just eaten self.model.simulator.schedule_event_relative( setattr, - self.grass_regrowth_time, + int( + self.model.grid.grass_regrowth_time.data[self.cell.coordinate[0]][ + self.cell.coordinate[1] + ] + ), function_args=[self, "fully_grown", True], ) - def __init__(self, model, countdown, grass_regrowth_time, cell): + def __init__(self, model, countdown, cell): """Create a new patch of grass. Args: @@ -140,7 +143,6 @@ def __init__(self, model, countdown, grass_regrowth_time, cell): """ super().__init__(model) self._fully_grown = countdown == 0 - self.grass_regrowth_time = grass_regrowth_time self.cell = cell # Schedule initial growth if not fully grown diff --git a/mesa/examples/advanced/wolf_sheep/model.py b/mesa/examples/advanced/wolf_sheep/model.py index 73b9c26a833..faefd143302 100644 --- a/mesa/examples/advanced/wolf_sheep/model.py +++ b/mesa/examples/advanced/wolf_sheep/model.py @@ -11,10 +11,14 @@ import math +import numpy as np +from scipy.ndimage import gaussian_filter + from mesa import Model from mesa.datacollection import DataCollector from mesa.examples.advanced.wolf_sheep.agents import GrassPatch, Sheep, Wolf from mesa.experimental.cell_space import OrthogonalVonNeumannGrid +from mesa.experimental.cell_space.property_layer import PropertyLayer from mesa.experimental.devs import ABMSimulator @@ -38,7 +42,7 @@ def __init__( wolf_reproduce=0.05, wolf_gain_from_food=20, grass=True, - grass_regrowth_time=30, + max_grass_regrowth_time=30, sheep_gain_from_food=4, seed=None, simulator: ABMSimulator = None, @@ -71,7 +75,10 @@ def __init__( # Create grid using experimental cell space self.grid = OrthogonalVonNeumannGrid( - [self.height, self.width], + ( + self.height, + self.width, + ), # use tuple instead of list, otherwise it would fail the dimension check in add_property_layer torus=True, capacity=math.inf, random=self.random, @@ -89,6 +96,33 @@ def __init__( self.datacollector = DataCollector(model_reporters) + def generate_grass_regrowth_time_array(): # Using Gaussian filter to make it look like spatial distribution + rows, cols = height, width + + seeds = np.zeros((rows, cols)) + num_seeds = max_grass_regrowth_time + + for _ in range(num_seeds): + x, y = np.random.randint(0, rows), np.random.randint(0, cols) + seeds[x, y] = np.random.randint(1, num_seeds) + + # Smooth the array to create clustered patterns using SciPy's Gaussian filter + filtered_array = gaussian_filter(seeds, sigma=10) + + # Normalize the array to the range [1, num_seeds] + filtered_array = (filtered_array - np.min(filtered_array)) / ( + np.max(filtered_array) - np.min(filtered_array) + ) * (num_seeds - 1) + 1 + filtered_array = filtered_array.astype(int) + + return filtered_array + + grass_regrowth_time_array = generate_grass_regrowth_time_array() + + self.grid.add_property_layer( + PropertyLayer.from_data("grass_regrowth_time", grass_regrowth_time_array) + ) + # Create sheep: Sheep.create_agents( self, @@ -114,9 +148,16 @@ def __init__( for cell in self.grid: fully_grown = self.random.choice(possibly_fully_grown) countdown = ( - 0 if fully_grown else self.random.randrange(0, grass_regrowth_time) + 0 + if fully_grown + else self.random.randrange( + 0, + grass_regrowth_time_array[cell.coordinate[0]][ + cell.coordinate[1] + ], + ) ) - GrassPatch(self, countdown, grass_regrowth_time, cell) + GrassPatch(self, countdown, cell) # Collect initial data self.running = True