From 5dc7db403fca12417f64552810ed825cea95ae7f Mon Sep 17 00:00:00 2001 From: Muhammad Gema Akbar Date: Sun, 22 Dec 2024 19:04:49 +0100 Subject: [PATCH 01/12] modify wolf sheep example to include PropertyLayer --- mesa/examples/advanced/wolf_sheep/model.py | 24 +++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/mesa/examples/advanced/wolf_sheep/model.py b/mesa/examples/advanced/wolf_sheep/model.py index 73b9c26a833..0a89a5a47a9 100644 --- a/mesa/examples/advanced/wolf_sheep/model.py +++ b/mesa/examples/advanced/wolf_sheep/model.py @@ -16,6 +16,9 @@ from mesa.examples.advanced.wolf_sheep.agents import GrassPatch, Sheep, Wolf from mesa.experimental.cell_space import OrthogonalVonNeumannGrid from mesa.experimental.devs import ABMSimulator +from mesa.experimental.cell_space.property_layer import PropertyLayer +import numpy as np +import random class WolfSheep(Model): @@ -71,7 +74,7 @@ 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 +92,21 @@ def __init__( self.datacollector = DataCollector(model_reporters) + obstructionArr = [[False]*self.width for i in range(self.height)] + + obstructionCoord = set([(random.randrange(self.height), random.randrange(self.width)) for i in range(10)] ) # set is used because the random number gen might return the same coordinate + for i,j in obstructionCoord: + obstructionArr[i][j] = True + + obstructionArr = np.array(obstructionArr) + + self.grid.add_property_layer(PropertyLayer.from_data("obstruction", obstructionArr)) + + possibleCells = [] + for cell in self.grid.all_cells.cells: + if (cell.coordinate[0], cell.coordinate[1]) not in obstructionCoord: # so we don't create wolf or sheep on obstructed cells + possibleCells.append(cell) + # Create sheep: Sheep.create_agents( self, @@ -96,7 +114,7 @@ def __init__( energy=self.rng.random((initial_sheep,)) * 2 * sheep_gain_from_food, p_reproduce=sheep_reproduce, energy_from_food=sheep_gain_from_food, - cell=self.random.choices(self.grid.all_cells.cells, k=initial_sheep), + cell=self.random.choices(possibleCells, k=initial_sheep), ) # Create Wolves: Wolf.create_agents( @@ -105,7 +123,7 @@ def __init__( energy=self.rng.random((initial_wolves,)) * 2 * wolf_gain_from_food, p_reproduce=wolf_reproduce, energy_from_food=wolf_gain_from_food, - cell=self.random.choices(self.grid.all_cells.cells, k=initial_wolves), + cell=self.random.choices(possibleCells, k=initial_wolves), ) # Create grass patches if enabled From 292f4d854702e7a518db553b74e6af461fd30f3f Mon Sep 17 00:00:00 2001 From: Muhammad Gema Akbar Date: Sun, 22 Dec 2024 20:06:37 +0100 Subject: [PATCH 02/12] use PropertyLayer to store cliff cell information --- mesa/examples/advanced/wolf_sheep/model.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mesa/examples/advanced/wolf_sheep/model.py b/mesa/examples/advanced/wolf_sheep/model.py index 0a89a5a47a9..df12f112c92 100644 --- a/mesa/examples/advanced/wolf_sheep/model.py +++ b/mesa/examples/advanced/wolf_sheep/model.py @@ -92,19 +92,19 @@ def __init__( self.datacollector = DataCollector(model_reporters) - obstructionArr = [[False]*self.width for i in range(self.height)] + cliff_arr = [[False]*self.width for i in range(self.height)] - obstructionCoord = set([(random.randrange(self.height), random.randrange(self.width)) for i in range(10)] ) # set is used because the random number gen might return the same coordinate - for i,j in obstructionCoord: - obstructionArr[i][j] = True + cliff_coord = set([(random.randrange(self.height), random.randrange(self.width)) for i in range((width*height)//3)] ) # set is used because the random number gen might return the same coordinate + for i,j in cliff_coord: + cliff_arr[i][j] = True - obstructionArr = np.array(obstructionArr) + cliff_arr = np.array(cliff_arr) - self.grid.add_property_layer(PropertyLayer.from_data("obstruction", obstructionArr)) + self.grid.add_property_layer(PropertyLayer.from_data("cliff", cliff_arr)) possibleCells = [] for cell in self.grid.all_cells.cells: - if (cell.coordinate[0], cell.coordinate[1]) not in obstructionCoord: # so we don't create wolf or sheep on obstructed cells + if (cell.coordinate[0], cell.coordinate[1]) not in cliff_coord: # so we don't create wolf or sheep on cliff cells possibleCells.append(cell) # Create sheep: From 469d0aefd255df4b37b03db2fcdc91014869e3e0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Dec 2024 19:17:33 +0000 Subject: [PATCH 03/12] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/examples/advanced/wolf_sheep/model.py | 28 +++++++++++++++------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/mesa/examples/advanced/wolf_sheep/model.py b/mesa/examples/advanced/wolf_sheep/model.py index df12f112c92..8a1ef5001c0 100644 --- a/mesa/examples/advanced/wolf_sheep/model.py +++ b/mesa/examples/advanced/wolf_sheep/model.py @@ -10,15 +10,16 @@ """ import math +import random + +import numpy as np 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.devs import ABMSimulator from mesa.experimental.cell_space.property_layer import PropertyLayer -import numpy as np -import random +from mesa.experimental.devs import ABMSimulator class WolfSheep(Model): @@ -74,7 +75,10 @@ def __init__( # Create grid using experimental cell space self.grid = OrthogonalVonNeumannGrid( - (self.height, self.width), # use tuple instead of list, otherwise it would fail the dimension check in add_property_layer + ( + 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, @@ -92,10 +96,13 @@ def __init__( self.datacollector = DataCollector(model_reporters) - cliff_arr = [[False]*self.width for i in range(self.height)] - - cliff_coord = set([(random.randrange(self.height), random.randrange(self.width)) for i in range((width*height)//3)] ) # set is used because the random number gen might return the same coordinate - for i,j in cliff_coord: + cliff_arr = [[False] * self.width for i in range(self.height)] + + cliff_coord = { + (random.randrange(self.height), random.randrange(self.width)) + for i in range((width * height) // 3) + } # set is used because the random number gen might return the same coordinate + for i, j in cliff_coord: cliff_arr[i][j] = True cliff_arr = np.array(cliff_arr) @@ -104,7 +111,10 @@ def __init__( possibleCells = [] for cell in self.grid.all_cells.cells: - if (cell.coordinate[0], cell.coordinate[1]) not in cliff_coord: # so we don't create wolf or sheep on cliff cells + if ( + cell.coordinate[0], + cell.coordinate[1], + ) not in cliff_coord: # so we don't create wolf or sheep on cliff cells possibleCells.append(cell) # Create sheep: From 1e5c948fc61159306f93370462bbd2bfc5e43c0d Mon Sep 17 00:00:00 2001 From: Muhammad Gema Akbar Date: Sun, 22 Dec 2024 20:43:28 +0100 Subject: [PATCH 04/12] Change variable name to snake case --- mesa/examples/advanced/wolf_sheep/model.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mesa/examples/advanced/wolf_sheep/model.py b/mesa/examples/advanced/wolf_sheep/model.py index 8a1ef5001c0..bffc891667c 100644 --- a/mesa/examples/advanced/wolf_sheep/model.py +++ b/mesa/examples/advanced/wolf_sheep/model.py @@ -109,13 +109,13 @@ def __init__( self.grid.add_property_layer(PropertyLayer.from_data("cliff", cliff_arr)) - possibleCells = [] + possible_cells = [] for cell in self.grid.all_cells.cells: if ( cell.coordinate[0], cell.coordinate[1], ) not in cliff_coord: # so we don't create wolf or sheep on cliff cells - possibleCells.append(cell) + possible_cells.append(cell) # Create sheep: Sheep.create_agents( @@ -124,7 +124,7 @@ def __init__( energy=self.rng.random((initial_sheep,)) * 2 * sheep_gain_from_food, p_reproduce=sheep_reproduce, energy_from_food=sheep_gain_from_food, - cell=self.random.choices(possibleCells, k=initial_sheep), + cell=self.random.choices(possible_cells, k=initial_sheep), ) # Create Wolves: Wolf.create_agents( @@ -133,7 +133,7 @@ def __init__( energy=self.rng.random((initial_wolves,)) * 2 * wolf_gain_from_food, p_reproduce=wolf_reproduce, energy_from_food=wolf_gain_from_food, - cell=self.random.choices(possibleCells, k=initial_wolves), + cell=self.random.choices(possible_cells, k=initial_wolves), ) # Create grass patches if enabled From 6b799c543c2cec457b196e5dacc257789f8d18c7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 22 Dec 2024 19:47:22 +0000 Subject: [PATCH 05/12] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/examples/advanced/wolf_sheep/model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesa/examples/advanced/wolf_sheep/model.py b/mesa/examples/advanced/wolf_sheep/model.py index bffc891667c..f80fc19a8b6 100644 --- a/mesa/examples/advanced/wolf_sheep/model.py +++ b/mesa/examples/advanced/wolf_sheep/model.py @@ -99,8 +99,8 @@ def __init__( cliff_arr = [[False] * self.width for i in range(self.height)] cliff_coord = { - (random.randrange(self.height), random.randrange(self.width)) - for i in range((width * height) // 3) + (random.randrange(self.height), random.randrange(self.width)) + for i in range((width * height) // 3) } # set is used because the random number gen might return the same coordinate for i, j in cliff_coord: cliff_arr[i][j] = True From dce455cc5f5abe67a2419d1e2627cc2198e030b9 Mon Sep 17 00:00:00 2001 From: Muhammad Gema Akbar Date: Mon, 23 Dec 2024 14:01:28 +0100 Subject: [PATCH 06/12] Make the animal dies if it steps into a cliff cell --- mesa/examples/advanced/wolf_sheep/agents.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mesa/examples/advanced/wolf_sheep/agents.py b/mesa/examples/advanced/wolf_sheep/agents.py index 93f4b4eb77e..07fdf34a78e 100644 --- a/mesa/examples/advanced/wolf_sheep/agents.py +++ b/mesa/examples/advanced/wolf_sheep/agents.py @@ -40,6 +40,12 @@ def step(self): """Execute one step of the animal's behavior.""" # Move to random neighboring cell self.move() + is_cliff = self.grid.model.cliff.data[self.cell.coordinate[0]][ + self.cell.coordinate[1] + ] + if is_cliff: # if the cell is a cliff, then the animal dies + self.remove() + return self.energy -= 1 From cb35e2f28a69daacc5eaaac1746463e21d9b114b Mon Sep 17 00:00:00 2001 From: Muhammad Gema Akbar Date: Mon, 23 Dec 2024 14:10:05 +0100 Subject: [PATCH 07/12] Fix error on is_cliff --- mesa/examples/advanced/wolf_sheep/agents.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/examples/advanced/wolf_sheep/agents.py b/mesa/examples/advanced/wolf_sheep/agents.py index 07fdf34a78e..fc22fbf829c 100644 --- a/mesa/examples/advanced/wolf_sheep/agents.py +++ b/mesa/examples/advanced/wolf_sheep/agents.py @@ -40,7 +40,7 @@ def step(self): """Execute one step of the animal's behavior.""" # Move to random neighboring cell self.move() - is_cliff = self.grid.model.cliff.data[self.cell.coordinate[0]][ + is_cliff = self.model.grid.cliff.data[self.cell.coordinate[0]][ self.cell.coordinate[1] ] if is_cliff: # if the cell is a cliff, then the animal dies From 7e7348d12fc6a4d0f4980eda795082111e2ff82b Mon Sep 17 00:00:00 2001 From: Muhammad Gema Akbar Date: Wed, 25 Dec 2024 22:15:29 +0100 Subject: [PATCH 08/12] Change cliff -> wall. Animals cannot pass cells with a wall --- mesa/examples/advanced/wolf_sheep/agents.py | 16 ++++--- mesa/examples/advanced/wolf_sheep/model.py | 47 ++++++++++++++++----- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/mesa/examples/advanced/wolf_sheep/agents.py b/mesa/examples/advanced/wolf_sheep/agents.py index fc22fbf829c..a2505c2e436 100644 --- a/mesa/examples/advanced/wolf_sheep/agents.py +++ b/mesa/examples/advanced/wolf_sheep/agents.py @@ -40,13 +40,6 @@ def step(self): """Execute one step of the animal's behavior.""" # Move to random neighboring cell self.move() - is_cliff = self.model.grid.cliff.data[self.cell.coordinate[0]][ - self.cell.coordinate[1] - ] - if is_cliff: # if the cell is a cliff, then the animal dies - self.remove() - return - self.energy -= 1 # Try to feed @@ -80,6 +73,15 @@ def move(self): if len(cells_without_wolves) == 0: return + target_cells_not_walls = cells_without_wolves.select( + lambda cell: not self.model.grid.wall.data[cell.coordinate[0]][ + cell.coordinate[1] + ] + ) + + if len(target_cells_not_walls) == 0: + return + # Among safe cells, prefer those with grown grass cells_with_grass = cells_without_wolves.select( lambda cell: any( diff --git a/mesa/examples/advanced/wolf_sheep/model.py b/mesa/examples/advanced/wolf_sheep/model.py index f80fc19a8b6..739b465772c 100644 --- a/mesa/examples/advanced/wolf_sheep/model.py +++ b/mesa/examples/advanced/wolf_sheep/model.py @@ -22,6 +22,29 @@ from mesa.experimental.devs import ABMSimulator +def is_trapped_in_wall( + cell, wall_coord, width, height +): # true if cell is trapped of walls + north = (cell.coordinate[0] - 1, cell.coordinate[1]) + south = (cell.coordinate[0] + 1, cell.coordinate[1]) + west = (cell.coordinate[0], cell.coordinate[1] - 1) + east = (cell.coordinate[0], cell.coordinate[1] + 1) + + coord = (cell.coordinate[0], cell.coordinate[1]) + + # 'corner' cases (pun intended) + if coord == (0, 0): # top left corner + return {east, south}.issubset(wall_coord) + if coord == (height - 1, 0): # bottom left corner + return {north, east}.issubset(wall_coord) + if coord == (0, width - 1): # top right corner + return {west, south}.issubset(wall_coord) + if coord == (height - 1, width - 1): # bottom right corner + return {north, west}.issubset(wall_coord) + + return {north, south, west, east}.issubset(wall_coord) + + class WolfSheep(Model): """Wolf-Sheep Predation Model. @@ -96,25 +119,29 @@ def __init__( self.datacollector = DataCollector(model_reporters) - cliff_arr = [[False] * self.width for i in range(self.height)] + wall_arr = [[False] * self.width for i in range(self.height)] - cliff_coord = { + wall_coord = { (random.randrange(self.height), random.randrange(self.width)) - for i in range((width * height) // 3) + for i in range((width * height) // 10) } # set is used because the random number gen might return the same coordinate - for i, j in cliff_coord: - cliff_arr[i][j] = True + for i, j in wall_coord: + wall_arr[i][j] = True - cliff_arr = np.array(cliff_arr) + wall_arr = np.array(wall_arr) - self.grid.add_property_layer(PropertyLayer.from_data("cliff", cliff_arr)) + self.grid.add_property_layer(PropertyLayer.from_data("wall", wall_arr)) possible_cells = [] for cell in self.grid.all_cells.cells: if ( - cell.coordinate[0], - cell.coordinate[1], - ) not in cliff_coord: # so we don't create wolf or sheep on cliff cells + ( + cell.coordinate[0], + cell.coordinate[1], + ) + not in wall_coord + and not is_trapped_in_wall(cell, wall_coord, width, height) + ): # so we don't create an animal at wall cells. and make sure the animal is not trapped in walls possible_cells.append(cell) # Create sheep: From 50b2d9363697633f50772920823b146d072894ba Mon Sep 17 00:00:00 2001 From: Muhammad Gema Akbar Date: Thu, 26 Dec 2024 16:06:07 +0100 Subject: [PATCH 09/12] Fix is_trapped_in_wall --- mesa/examples/advanced/wolf_sheep/model.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/mesa/examples/advanced/wolf_sheep/model.py b/mesa/examples/advanced/wolf_sheep/model.py index 739b465772c..c92214f6cc9 100644 --- a/mesa/examples/advanced/wolf_sheep/model.py +++ b/mesa/examples/advanced/wolf_sheep/model.py @@ -27,8 +27,8 @@ def is_trapped_in_wall( ): # true if cell is trapped of walls north = (cell.coordinate[0] - 1, cell.coordinate[1]) south = (cell.coordinate[0] + 1, cell.coordinate[1]) - west = (cell.coordinate[0], cell.coordinate[1] - 1) east = (cell.coordinate[0], cell.coordinate[1] + 1) + west = (cell.coordinate[0], cell.coordinate[1] - 1) coord = (cell.coordinate[0], cell.coordinate[1]) @@ -41,6 +41,14 @@ def is_trapped_in_wall( return {west, south}.issubset(wall_coord) if coord == (height - 1, width - 1): # bottom right corner return {north, west}.issubset(wall_coord) + if coord[0] == 0: # for cells at top row + return {south, west, east}.issubset(wall_coord) + if coord[1] == 0: # for cells at leftmost column + return {north, south, east}.issubset(wall_coord) + if coord[0] == height - 1: # for cells at the bottom row + return {north, east, west}.issubset(wall_coord) + if coord[1] == width - 1: # for cells at rightmost column + return {north, south, west}.issubset(wall_coord) return {north, south, west, east}.issubset(wall_coord) From 61a70a67570b997abaefa1077fc9b671b57413b0 Mon Sep 17 00:00:00 2001 From: Muhammad Gema Akbar Date: Sun, 29 Dec 2024 19:01:37 +0100 Subject: [PATCH 10/12] Simplify is_trapped_in_walls --- mesa/examples/advanced/wolf_sheep/model.py | 61 +++++++--------------- 1 file changed, 19 insertions(+), 42 deletions(-) diff --git a/mesa/examples/advanced/wolf_sheep/model.py b/mesa/examples/advanced/wolf_sheep/model.py index c92214f6cc9..0d06a707cf3 100644 --- a/mesa/examples/advanced/wolf_sheep/model.py +++ b/mesa/examples/advanced/wolf_sheep/model.py @@ -22,37 +22,6 @@ from mesa.experimental.devs import ABMSimulator -def is_trapped_in_wall( - cell, wall_coord, width, height -): # true if cell is trapped of walls - north = (cell.coordinate[0] - 1, cell.coordinate[1]) - south = (cell.coordinate[0] + 1, cell.coordinate[1]) - east = (cell.coordinate[0], cell.coordinate[1] + 1) - west = (cell.coordinate[0], cell.coordinate[1] - 1) - - coord = (cell.coordinate[0], cell.coordinate[1]) - - # 'corner' cases (pun intended) - if coord == (0, 0): # top left corner - return {east, south}.issubset(wall_coord) - if coord == (height - 1, 0): # bottom left corner - return {north, east}.issubset(wall_coord) - if coord == (0, width - 1): # top right corner - return {west, south}.issubset(wall_coord) - if coord == (height - 1, width - 1): # bottom right corner - return {north, west}.issubset(wall_coord) - if coord[0] == 0: # for cells at top row - return {south, west, east}.issubset(wall_coord) - if coord[1] == 0: # for cells at leftmost column - return {north, south, east}.issubset(wall_coord) - if coord[0] == height - 1: # for cells at the bottom row - return {north, east, west}.issubset(wall_coord) - if coord[1] == width - 1: # for cells at rightmost column - return {north, south, west}.issubset(wall_coord) - - return {north, south, west, east}.issubset(wall_coord) - - class WolfSheep(Model): """Wolf-Sheep Predation Model. @@ -140,17 +109,25 @@ def __init__( self.grid.add_property_layer(PropertyLayer.from_data("wall", wall_arr)) - possible_cells = [] - for cell in self.grid.all_cells.cells: - if ( - ( - cell.coordinate[0], - cell.coordinate[1], - ) - not in wall_coord - and not is_trapped_in_wall(cell, wall_coord, width, height) - ): # so we don't create an animal at wall cells. and make sure the animal is not trapped in walls - possible_cells.append(cell) + def is_wall(row, col): + return ( + True + if row < 0 or col < 0 or row >= height or col >= width # corner case + else wall_arr[row][col] + ) + + def is_trapped_in_walls(row, col): + return ( + is_wall(row + 1, col) + and is_wall(row - 1, col) + and is_wall(row, col + 1) + and is_wall(row, col - 1) + ) + + possible_cells = self.grid.all_cells.select( + lambda cell: not wall_arr[cell.coordinate[0]][cell.coordinate[1]] + and not is_trapped_in_walls(cell.coordinate[0], cell.coordinate[1]) + ).cells # so we don't create an animal at wall cells. and make sure the animal is not trapped in walls # Create sheep: Sheep.create_agents( From cad61310cfacd21df76c2e2c64bc127d7e1b52ba Mon Sep 17 00:00:00 2001 From: Muhammad Gema Akbar Date: Sun, 12 Jan 2025 22:25:46 +0100 Subject: [PATCH 11/12] Move grass regrowth time to PropertyLayer --- mesa/examples/advanced/wolf_sheep/agents.py | 18 ++---- mesa/examples/advanced/wolf_sheep/model.py | 63 +++++++++++---------- 2 files changed, 38 insertions(+), 43 deletions(-) diff --git a/mesa/examples/advanced/wolf_sheep/agents.py b/mesa/examples/advanced/wolf_sheep/agents.py index a2505c2e436..000b160f44b 100644 --- a/mesa/examples/advanced/wolf_sheep/agents.py +++ b/mesa/examples/advanced/wolf_sheep/agents.py @@ -73,15 +73,6 @@ def move(self): if len(cells_without_wolves) == 0: return - target_cells_not_walls = cells_without_wolves.select( - lambda cell: not self.model.grid.wall.data[cell.coordinate[0]][ - cell.coordinate[1] - ] - ) - - if len(target_cells_not_walls) == 0: - return - # Among safe cells, prefer those with grown grass cells_with_grass = cells_without_wolves.select( lambda cell: any( @@ -133,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: @@ -148,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 0d06a707cf3..dfeea974908 100644 --- a/mesa/examples/advanced/wolf_sheep/model.py +++ b/mesa/examples/advanced/wolf_sheep/model.py @@ -10,9 +10,9 @@ """ import math -import random import numpy as np +from scipy.ndimage import gaussian_filter from mesa import Model from mesa.datacollection import DataCollector @@ -96,38 +96,32 @@ def __init__( self.datacollector = DataCollector(model_reporters) - wall_arr = [[False] * self.width for i in range(self.height)] + def generate_grass_regrowth_time_array(): # Using Gaussian filter to make it look like spatial distribution + rows, cols = height, width - wall_coord = { - (random.randrange(self.height), random.randrange(self.width)) - for i in range((width * height) // 10) - } # set is used because the random number gen might return the same coordinate - for i, j in wall_coord: - wall_arr[i][j] = True + seeds = np.zeros((rows, cols)) + num_seeds = grass_regrowth_time - wall_arr = np.array(wall_arr) + 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) - self.grid.add_property_layer(PropertyLayer.from_data("wall", wall_arr)) + # Smooth the array to create clustered patterns using SciPy's Gaussian filter + filtered_array = gaussian_filter(seeds, sigma=10) - def is_wall(row, col): - return ( - True - if row < 0 or col < 0 or row >= height or col >= width # corner case - else wall_arr[row][col] - ) + # 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) - def is_trapped_in_walls(row, col): - return ( - is_wall(row + 1, col) - and is_wall(row - 1, col) - and is_wall(row, col + 1) - and is_wall(row, col - 1) - ) + return filtered_array - possible_cells = self.grid.all_cells.select( - lambda cell: not wall_arr[cell.coordinate[0]][cell.coordinate[1]] - and not is_trapped_in_walls(cell.coordinate[0], cell.coordinate[1]) - ).cells # so we don't create an animal at wall cells. and make sure the animal is not trapped in walls + 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( @@ -136,7 +130,7 @@ def is_trapped_in_walls(row, col): energy=self.rng.random((initial_sheep,)) * 2 * sheep_gain_from_food, p_reproduce=sheep_reproduce, energy_from_food=sheep_gain_from_food, - cell=self.random.choices(possible_cells, k=initial_sheep), + cell=self.random.choices(self.grid.all_cells.cells, k=initial_sheep), ) # Create Wolves: Wolf.create_agents( @@ -145,7 +139,7 @@ def is_trapped_in_walls(row, col): energy=self.rng.random((initial_wolves,)) * 2 * wolf_gain_from_food, p_reproduce=wolf_reproduce, energy_from_food=wolf_gain_from_food, - cell=self.random.choices(possible_cells, k=initial_wolves), + cell=self.random.choices(self.grid.all_cells.cells, k=initial_wolves), ) # Create grass patches if enabled @@ -154,9 +148,16 @@ def is_trapped_in_walls(row, col): 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 From 7589b6c56c69caba842f85eb829602cf8eebf73e Mon Sep 17 00:00:00 2001 From: Muhammad Gema Akbar Date: Sun, 12 Jan 2025 22:41:39 +0100 Subject: [PATCH 12/12] Rename wolf sheep constructor parameter name --- mesa/examples/advanced/wolf_sheep/model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesa/examples/advanced/wolf_sheep/model.py b/mesa/examples/advanced/wolf_sheep/model.py index dfeea974908..faefd143302 100644 --- a/mesa/examples/advanced/wolf_sheep/model.py +++ b/mesa/examples/advanced/wolf_sheep/model.py @@ -42,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, @@ -100,7 +100,7 @@ def generate_grass_regrowth_time_array(): # Using Gaussian filter to make it lo rows, cols = height, width seeds = np.zeros((rows, cols)) - num_seeds = grass_regrowth_time + num_seeds = max_grass_regrowth_time for _ in range(num_seeds): x, y = np.random.randint(0, rows), np.random.randint(0, cols)