Source code for Environment

from collections import defaultdict
from random import shuffle


[docs]class Environment(object): """ Initializes a generic environment instance. Assigns the name and binds it to the model instance. This class would **not** be instantiated itself. It would be extended by a concrete environment extension. Attributes ---------- name : string The name of the environment, this will be used when referring to it throughout the code. (Eg: AgentEnv, OxygenEnv, etc.) model : model The instance of the model class to which the environment will be attached. """ def __init__(self, name, model): self.name = name model.environments[name] = self
[docs]class Grid3D(Environment, object): """ Initializes a 3D Grid object. Assigns the name, size and binds it to a model instance. This class would **not** be instantiated itself as it lacks any concrete extension of the underlying grid. Rather, it is used to check whether position in the grid is valid and provide moore neighbourhoods and additional information based on the geometry of a 3D grid. Attributes ---------- name : string The name of the environment, this will be used when referring to it throughout the code. (Eg: AgentEnv, OxygenEnv, etc.) xsize : int The number of positions along the x-axis. In other words, the width of the environment. ysize : int The number of positions along the y-axis. In other words, the height of the environment. zsize : int The number of positions along the z-axis. In other words, the depth of the environment. model : model The instance of the model class to which the environment will be attached. """ def __init__(self, name, xsize, ysize, zsize, model): super(Grid3D, self).__init__(name, model) self.xsize = xsize self.ysize = ysize self.zsize = zsize
[docs] def valid_position(self, position): """ Checks whether a coordinate is valid with regards to the size of the grid instance. Checks if none of the coordinate values are negative or out of bounds. Parameters ---------- position : tuple A tuple consisting of exactly three element, each of them being an integer. Returns ------- bool True if the position is a valid one, false otherwise. """ return self.xsize > position[0] >= 0 and self.ysize > position[ 1] >= 0 and self.zsize > position[2] >= 0
[docs] def get_moore_neighbourhood(self, position, shuffle_neigh=True): """ Returns a list of moore neighbours for a given position. A moore neighbourhood is intended as all positions immediately adjacent to a target one. Parameters ---------- position : tuple A tuple consisting of exactly three element, each of them being an integer. shuffle_neigh : bool Optional, if set to true the list of neighbours will be shuffled and returned in a random order. Returns ------- list A list of moore neighbours """ neigh = [ (position[0] + 1, position[1] + 1, position[2] + 1), (position[0] + 1, position[1] + 1, position[2] - 1), (position[0] + 1, position[1] + 1, position[2]), (position[0] + 1, position[1] - 1, position[2] + 1), (position[0] + 1, position[1] - 1, position[2] - 1), (position[0] + 1, position[1] - 1, position[2]), (position[0] + 1, position[1], position[2] + 1), (position[0] + 1, position[1], position[2] - 1), (position[0] + 1, position[1], position[2]), (position[0], position[1] + 1, position[2] + 1), (position[0], position[1] + 1, position[2] - 1), (position[0], position[1] + 1, position[2]), (position[0], position[1] - 1, position[2] + 1), (position[0], position[1] - 1, position[2] - 1), (position[0], position[1] - 1, position[2]), (position[0], position[1], position[2] + 1), (position[0], position[1], position[2] - 1), (position[0] - 1, position[1] + 1, position[2] + 1), (position[0] - 1, position[1] + 1, position[2] - 1), (position[0] - 1, position[1] + 1, position[2]), (position[0] - 1, position[1] - 1, position[2] + 1), (position[0] - 1, position[1] - 1, position[2] - 1), (position[0] - 1, position[1] - 1, position[2]), (position[0] - 1, position[1], position[2] + 1), (position[0] - 1, position[1], position[2] - 1), (position[0] - 1, position[1], position[2]), ] neigh = [n for n in neigh if self.valid_position(n)] if shuffle_neigh: shuffle(neigh) return neigh
[docs]class Grid2D(Environment, object): """ Initializes a 2D Grid object. Assigns the name, size and binds it to a model instance. This class would **not** be instantiated itself as it lacks any concrete extension of the underlying grid. Rather, it is used to check whether position in the grid is valid and provide moore neighbourhoods and additional information based on the geometry of a 2D grid. Attributes ---------- name : string The name of the environment, this will be used when referring to it throughout the code. (Eg: AgentEnv, OxygenEnv, etc.) xsize : int The number of positions along the x-axis. In other words, the width of the environment. ysize : int The number of positions along the y-axis. In other words, the height of the environment. model : model The instance of the model class to which the environment will be attached. """ def __init__(self, name, xsize, ysize, model): super(Grid2D, self).__init__(name, model) self.xsize = xsize self.ysize = ysize
[docs] def valid_position(self, position): """ Checks whether a coordinate is valid with regards to the size of the grid instance. Checks if none of the coordinate values are negative or out of bounds. Parameters ---------- position : tuple A tuple consisting of exactly two element, each of them being an integer. Returns ------- bool True if the position is a valid one, false otherwise. """ return self.xsize > position[0] >= 0 and self.ysize > position[1] >= 0
[docs] def get_moore_neighbourhood(self, position, shuffle_neigh=True): """ Returns a list of moore neighbours for a given position. A moore neighbourhood is intended as all positions immediately adjacent to a target one. Parameters ---------- position : tuple A tuple consisting of exactly two element, each of them being an integer. shuffle_neigh : bool Optional, if set to true the list of neighbours will be shuffled and returned in a random order. Returns ------- list A list of moore neighbours """ neigh = [ (position[0] + 1, position[1] - 1), (position[0] + 1, position[1]), (position[0] + 1, position[1] + 1), (position[0], position[1] - 1), (position[0], position[1] + 1), (position[0] - 1, position[1] - 1), (position[0] - 1, position[1]), (position[0] - 1, position[1] + 1), ] neigh = [n for n in neigh if self.valid_position(n)] if shuffle_neigh: shuffle(neigh) return neigh
[docs]class ObjectGrid(object): """ Initializes an ObjectGrid. Object grids at each position hold collections of objects. Most likely these would be instances of agents. This class provides common methods to and, move and remove agents from the grid. It also exposes methods to get agent densities at various positions. This class would **not** be itself instantiated, but would be extended by another class that would implement it. """ def __init__(self): self.grid = defaultdict(set)
[docs] def move_agent(self, agent, position_old, position_new): """ Moves an agent from a grid position to another grid position. This class does *not* update the internal state of the agent. So, if the agent also keeps its own record of its position in the grid, this should be updated separately. It is up to the developer to check that the old position did indeed contain the agent. If an invalid position is provided as a new position, the agent will not be moved. Positions should be given as tuples of two or three values, depending if this object grid is associated to a 2D or 3D environment. Parameters ---------- agent : Agent The instance of the agent we wish to update. position_old : tuple The old position of the agent. position_new : tuple The new position of the agent. """ if self.valid_position(position_new): self.grid[position_old].remove(agent) self.grid[position_new].add(agent)
[docs] def remove_agent(self, agent, position): """ Removes an agent from a position. This class does *not* update the internal state of the agent. So, if the agent also keeps its own record of its position in the grid, this should be updated separately. Positions should be given as tuples of two or three values, depending if this object grid is associated to a 2D or 3D environment. Parameters ---------- agent : Agent The instance of the agent we wish to update. position: tuple The position from which we wish to remove the agent. """ self.grid[position].remove(agent)
[docs] def get_most_populated_moore_neigh(self, position): """ Gets the coordinates of the moore neighbour with the most agents. If multiple neighbours meet the criteria, any may be returned. A moore neighbourhood is defined as all positions immediately adjacent the one we are searching. It does not include the target position itself. Positions should be given and are returned as tuples of two or three values, depending if this object grid is associated to a 2D or 3D environment. Parameters ---------- position: tuple The position whose neighbourhood we wish to search. Returns ------- tuple The moore position with the highest number of agents. """ neigh = self.get_moore_neighbourhood(position) if len(neigh) == 0: return None shuffle(neigh) max_pop = 0 most_populated = neigh[0] for n in neigh: pop = self.grid[n].__len__() if pop > max_pop: max_pop = pop most_populated = n return most_populated
[docs] def get_least_populated_moore_neigh(self, position): """ Gets the coordinates of the moore neighbour with the fewest agents. If multiple neighbours meet the criteria, any may be returned. A moore neighbourhood is defined as all positions immediately adjacent the one we are searching. It does not include the target position itself. Positions should be given and are returned as tuples of two or three values, depending if this object grid is associated to a 2D or 3D environment. Parameters ---------- position: tuple The position whose neighbourhood we wish to search. Returns ------- tuple The moore position with the fewest number of agents. """ neigh = self.get_moore_neighbourhood(position) if len(neigh) == 0: return None n = neigh[0] shuffle(neigh) min_pop = (self.grid[n]).__len__() min_populated = n for n in neigh: pop = (self.grid[n]).__len__() if int(pop) < min_pop: min_pop = pop min_populated = n return min_populated
[docs] def add_agent(self, agent, position): """ Adds an agent to a position in the environment. This does *not* check if an agent already exists at another position. This should be checked separately. This class does *not* update the internal state of the agent. So, if the agent also keeps its own record of its position in the grid, this should be updated separately. If an invalid position is provided, then the agent will not be added. Parameters ---------- agent : Agent The instance of the agent we wish to update. position: tuple The position to which we wish to add the agent. """ if self.valid_position(position): self.grid[position].add(agent)
[docs]class ObjectGrid3D(Grid3D, ObjectGrid, object): """ Instantiates a 3D Object Grid. This extends Grid3D and ObjectGrid, allowing to place objects in a three-dimensional grid exposing all methods offered by the aforementioned classes. Attributes ---------- name : string The name of the environment, this will be used when referring to it throughout the code. (Eg: AgentEnv, OxygenEnv, etc.) xsize : int The number of positions along the x-axis. In other words, the width of the environment. ysize : int The number of positions along the y-axis. In other words, the height of the environment. zsize : int The number of positions along the z-axis. In other words, the depth of the environment. model : model The instance of the model class to which the environment will be attached. """ def __init__(self, name, xsize, ysize, zsize, model): Grid3D.__init__(self, name, xsize, ysize, zsize, model) ObjectGrid.__init__(self)
[docs]class ObjectGrid2D(Grid2D, ObjectGrid, object): """ Instantiates a 2D Object Grid. This extends Grid2D and ObjectGrid, allowing to place objects in a twp-dimensional grid exposing all methods offered by the aforementioned classes. Attributes ---------- name : string The name of the environment, this will be used when referring to it throughout the code. (Eg: AgentEnv, OxygenEnv, etc.) xsize : int The number of positions along the x-axis. In other words, the width of the environment. ysize : int The number of positions along the y-axis. In other words, the height of the environment. model : model The instance of the model class to which the environment will be attached. """ def __init__(self, name, xsize, ysize, model): Grid2D.__init__(self, name, xsize, ysize, model) ObjectGrid.__init__(self)
[docs]class NumericalGrid(object): """ Initializes a NumericalGrid object. A NumericalGrid holds a single number value at each position. This class exposes methods to explore a position's neighbourhood. This class would **not** be itself instantiated, but would be extended by another class that would implement it. """ def __init__(self): self.grid = defaultdict(int)
[docs] def get_max_in_neigh(self, position): """ Gets the coordinates of the moore neighbour with the largest value. If multiple neighbours meet the criteria, any may be returned. A moore neighbourhood is defined as all positions immediately adjacent the one we are searching. It does not include the target position itself. Positions should be given and are returned as tuples of two or three values, depending if this object grid is associated to a 2D or 3D environment. Parameters ---------- position: tuple The position whose neighbourhood we wish to search. Returns ------- tuple The moore position with the largest value. """ neigh = self.get_moore_neighbourhood(position) max_pos = neigh[0] max_value = 0 for n in neigh: valAtPos = self.grid[n] if valAtPos > max_value: max_value = valAtPos max_pos = n return max_pos
[docs] def get_least_in_neigh(self, position): """ Gets the coordinates of the moore neighbour with the smallest value. If multiple neighbours meet the criteria, any may be returned. A moore neighbourhood is defined as all positions immediately adjacent the one we are searching. It does not include the target position itself. Positions should be given and are returned as tuples of two or three values, depending if this object grid is associated to a 2D or 3D environment. Parameters ---------- position: tuple The position whose neighbourhood we wish to search. Returns ------- tuple The moore position with the smallest value. """ neigh = self.get_moore_neighbourhood(position) min_pos = neigh[0] min_value = self.grid[neigh[0]] for n in neigh: valAtPos = self.grid[n] if valAtPos < min_value: min_value = valAtPos min_pos = n return min_pos
[docs]class NumericalGrid2D(Grid2D, NumericalGrid, object): """ Instantiates a 2D Numerical Grid. This extends Grid2D and NumericalGrid, allowing to store values in a two-dimensional grid exposing all methods offered by the aforementioned classes. Attributes ---------- name : string The name of the environment, this will be used when referring to it throughout the code. (Eg: AgentEnv, OxygenEnv, etc.) xsize : int The number of positions along the x-axis. In other words, the width of the environment. ysize : int The number of positions along the y-axis. In other words, the height of the environment. model : model The instance of the model class to which the environment will be attached. """ def __init__(self, name, xsize, ysize, model): Grid2D.__init__(self, name, xsize, ysize, model) NumericalGrid.__init__(self)
[docs]class NumericalGrid3D(Grid3D, NumericalGrid, object): """ Instantiates a 3D Numerical Grid. This extends Grid3D and NumericalGrid, allowing to store values in a three-dimensional grid exposing all methods offered by the aforementioned classes. Attributes ---------- name : string The name of the environment, this will be used when referring to it throughout the code. (Eg: AgentEnv, OxygenEnv, etc.) xsize : int The number of positions along the x-axis. In other words, the width of the environment. ysize : int The number of positions along the y-axis. In other words, the height of the environment. model : model The instance of the model class to which the environment will be attached. """ def __init__(self, name, xsize, ysize, zsize, model): Grid3D.__init__(self, name, xsize, ysize, zsize, model) NumericalGrid.__init__(self)