Module crowd-control.classes.cell
Expand source code
import numpy as np
import random
from numpy import ndarray
from helpers import get_distance_array, is_empty_vectorized
class Cell:
"""
A cell represents a square on a lattice.
It may be empty or populated and has a fixed position in the lattice.
Attributes:
value (int): Defines if cell is populated (+/- 1) or empty (0).
x (int): Row number of the cell
y (int): Column number of the cell
left_exit_distance (float): Distance to leftt side of corridor
right_exit_distance (float): Distance to right side of corridor
neighbors (list[Cell]): List of neighboring cells in the lattice.
"""
def __init__(self, x, y, left_exit_distance, right_exit_distance) -> None:
"""
Initializes empty Cell.
Args:
value (int): Defines if cell is populated (+/- 1) or empty (0).
x (int): Row number of the cell.
y (int): Column number of the cell.
left_exit_distance (float): Distance to leftt side of corridor.
right_exit_distance (float): Distance to right side of corridor.
"""
self.value = 0
self.left_exit_distance = left_exit_distance
self.right_exit_distance = right_exit_distance
self.x = x
self.y = y
self.neighbors: list[Cell] = []
def is_empty(self) -> bool:
"""
Return True if cell is unpopulated, False otherwise.
"""
return True if self.value == 0 else False
def is_leaving_left(self):
"""
Returns True if agent is about to leave lattice on the left.
"""
return self.y == 0 and self.value == -1
def is_leaving_right(self, len_y):
"""
Returns True if agent is about to leave lattice on the right.
"""
return self.y == len_y - 1 and self.value == 1
def populate(self, value) -> None:
"""
Populate a cell if it is not empty.
Args:
value (int): The movement direction (-1 or +1)
"""
assert value in [-1, 1], 'value must be 1 or -1'
assert self.value == 0, 'can not populate un-empty cell'
self.value = value
def clear(self) -> None:
"""
Empty the cell.
"""
self.value = 0
def add_neighbor(self, cell) -> None:
"""
Add neighbouring cell object.
Args:
cell (object): Neighboring cell to be added.
"""
assert len(self.neighbors) < 9, 'max number of neighbors exceeded.'
self.neighbors.append(cell)
def get_distance_value(self, value) -> int:
"""
Get the correct distance value based on who is asking.
Args:
value (int): The movement direction (-1 or +1)
"""
assert value != 0, 'value is ambiguous, must be -1 or 1'
return self.right_exit_distance if value > 0 else self.left_exit_distance
def get_empty_neighbors(self) -> ndarray[object]:
"""
Return empty neighbors in array.
"""
boolean_array = is_empty_vectorized(self.neighbors)
return np.array(self.neighbors)[boolean_array]
def get_best_neighbor(self, p) -> object | None:
"""
Return empty neighbor cell with smallest distance to the exit.
Return None if no empty options found.
Args:
p (float): Probability of choosing a horizontal neighbor
"""
assert p >= 0 and p <= 1, 'p is a probability'
# only consider empty neighbors
empty_neighbors = self.get_empty_neighbors()
# every neighbor is occupied
if len(empty_neighbors) == 0:
return None
# find distance values of all neighbors
distances = get_distance_array(empty_neighbors, self.value)
current_distance = self.get_distance_value(self.value)
# if no better cells, stay where you are
if distances.min() >= current_distance:
return None
# only consider neighbors with smaller distance to exit
empty_neighbors = empty_neighbors[distances < current_distance]
if len(empty_neighbors) == 1:
return empty_neighbors[0]
# check for horizontal neighbors, move there with given probability
for i, neighbor in enumerate(empty_neighbors):
if neighbor.x == self.x:
if random.random() < p:
return neighbor
np.delete(empty_neighbors, i)
# otherwise target one of remaining cells
return np.random.choice(empty_neighbors)
def lower_distance_to_exit(self):
"""
Dynamically lower the distance to the exit.
Only call this method when agent is leaving the cell.
"""
if self.value < 0:
self.left_exit_distance -= 0.00001
elif self.value > 0:
self.right_exit_distance -= 0.00001
Classes
class Cell (x, y, left_exit_distance, right_exit_distance)
-
A cell represents a square on a lattice. It may be empty or populated and has a fixed position in the lattice.
Attributes
value
:int
- Defines if cell is populated (+/- 1) or empty (0).
x
:int
- Row number of the cell
y
:int
- Column number of the cell
left_exit_distance
:float
- Distance to leftt side of corridor
right_exit_distance
:float
- Distance to right side of corridor
neighbors
:list[Cell]
- List of neighboring cells in the lattice.
Initializes empty Cell.
Args
value
:int
- Defines if cell is populated (+/- 1) or empty (0).
x
:int
- Row number of the cell.
y
:int
- Column number of the cell.
left_exit_distance
:float
- Distance to leftt side of corridor.
right_exit_distance
:float
- Distance to right side of corridor.
Expand source code
class Cell: """ A cell represents a square on a lattice. It may be empty or populated and has a fixed position in the lattice. Attributes: value (int): Defines if cell is populated (+/- 1) or empty (0). x (int): Row number of the cell y (int): Column number of the cell left_exit_distance (float): Distance to leftt side of corridor right_exit_distance (float): Distance to right side of corridor neighbors (list[Cell]): List of neighboring cells in the lattice. """ def __init__(self, x, y, left_exit_distance, right_exit_distance) -> None: """ Initializes empty Cell. Args: value (int): Defines if cell is populated (+/- 1) or empty (0). x (int): Row number of the cell. y (int): Column number of the cell. left_exit_distance (float): Distance to leftt side of corridor. right_exit_distance (float): Distance to right side of corridor. """ self.value = 0 self.left_exit_distance = left_exit_distance self.right_exit_distance = right_exit_distance self.x = x self.y = y self.neighbors: list[Cell] = [] def is_empty(self) -> bool: """ Return True if cell is unpopulated, False otherwise. """ return True if self.value == 0 else False def is_leaving_left(self): """ Returns True if agent is about to leave lattice on the left. """ return self.y == 0 and self.value == -1 def is_leaving_right(self, len_y): """ Returns True if agent is about to leave lattice on the right. """ return self.y == len_y - 1 and self.value == 1 def populate(self, value) -> None: """ Populate a cell if it is not empty. Args: value (int): The movement direction (-1 or +1) """ assert value in [-1, 1], 'value must be 1 or -1' assert self.value == 0, 'can not populate un-empty cell' self.value = value def clear(self) -> None: """ Empty the cell. """ self.value = 0 def add_neighbor(self, cell) -> None: """ Add neighbouring cell object. Args: cell (object): Neighboring cell to be added. """ assert len(self.neighbors) < 9, 'max number of neighbors exceeded.' self.neighbors.append(cell) def get_distance_value(self, value) -> int: """ Get the correct distance value based on who is asking. Args: value (int): The movement direction (-1 or +1) """ assert value != 0, 'value is ambiguous, must be -1 or 1' return self.right_exit_distance if value > 0 else self.left_exit_distance def get_empty_neighbors(self) -> ndarray[object]: """ Return empty neighbors in array. """ boolean_array = is_empty_vectorized(self.neighbors) return np.array(self.neighbors)[boolean_array] def get_best_neighbor(self, p) -> object | None: """ Return empty neighbor cell with smallest distance to the exit. Return None if no empty options found. Args: p (float): Probability of choosing a horizontal neighbor """ assert p >= 0 and p <= 1, 'p is a probability' # only consider empty neighbors empty_neighbors = self.get_empty_neighbors() # every neighbor is occupied if len(empty_neighbors) == 0: return None # find distance values of all neighbors distances = get_distance_array(empty_neighbors, self.value) current_distance = self.get_distance_value(self.value) # if no better cells, stay where you are if distances.min() >= current_distance: return None # only consider neighbors with smaller distance to exit empty_neighbors = empty_neighbors[distances < current_distance] if len(empty_neighbors) == 1: return empty_neighbors[0] # check for horizontal neighbors, move there with given probability for i, neighbor in enumerate(empty_neighbors): if neighbor.x == self.x: if random.random() < p: return neighbor np.delete(empty_neighbors, i) # otherwise target one of remaining cells return np.random.choice(empty_neighbors) def lower_distance_to_exit(self): """ Dynamically lower the distance to the exit. Only call this method when agent is leaving the cell. """ if self.value < 0: self.left_exit_distance -= 0.00001 elif self.value > 0: self.right_exit_distance -= 0.00001
Methods
def add_neighbor(self, cell) ‑> None
-
Add neighbouring cell object.
Args
cell
:object
- Neighboring cell to be added.
Expand source code
def add_neighbor(self, cell) -> None: """ Add neighbouring cell object. Args: cell (object): Neighboring cell to be added. """ assert len(self.neighbors) < 9, 'max number of neighbors exceeded.' self.neighbors.append(cell)
def clear(self) ‑> None
-
Empty the cell.
Expand source code
def clear(self) -> None: """ Empty the cell. """ self.value = 0
def get_best_neighbor(self, p) ‑> object | None
-
Return empty neighbor cell with smallest distance to the exit. Return None if no empty options found.
Args
p
:float
- Probability of choosing a horizontal neighbor
Expand source code
def get_best_neighbor(self, p) -> object | None: """ Return empty neighbor cell with smallest distance to the exit. Return None if no empty options found. Args: p (float): Probability of choosing a horizontal neighbor """ assert p >= 0 and p <= 1, 'p is a probability' # only consider empty neighbors empty_neighbors = self.get_empty_neighbors() # every neighbor is occupied if len(empty_neighbors) == 0: return None # find distance values of all neighbors distances = get_distance_array(empty_neighbors, self.value) current_distance = self.get_distance_value(self.value) # if no better cells, stay where you are if distances.min() >= current_distance: return None # only consider neighbors with smaller distance to exit empty_neighbors = empty_neighbors[distances < current_distance] if len(empty_neighbors) == 1: return empty_neighbors[0] # check for horizontal neighbors, move there with given probability for i, neighbor in enumerate(empty_neighbors): if neighbor.x == self.x: if random.random() < p: return neighbor np.delete(empty_neighbors, i) # otherwise target one of remaining cells return np.random.choice(empty_neighbors)
def get_distance_value(self, value) ‑> int
-
Get the correct distance value based on who is asking.
Args
value
:int
- The movement direction (-1 or +1)
Expand source code
def get_distance_value(self, value) -> int: """ Get the correct distance value based on who is asking. Args: value (int): The movement direction (-1 or +1) """ assert value != 0, 'value is ambiguous, must be -1 or 1' return self.right_exit_distance if value > 0 else self.left_exit_distance
def get_empty_neighbors(self) ‑> numpy.ndarray[object]
-
Return empty neighbors in array.
Expand source code
def get_empty_neighbors(self) -> ndarray[object]: """ Return empty neighbors in array. """ boolean_array = is_empty_vectorized(self.neighbors) return np.array(self.neighbors)[boolean_array]
def is_empty(self) ‑> bool
-
Return True if cell is unpopulated, False otherwise.
Expand source code
def is_empty(self) -> bool: """ Return True if cell is unpopulated, False otherwise. """ return True if self.value == 0 else False
def is_leaving_left(self)
-
Returns True if agent is about to leave lattice on the left.
Expand source code
def is_leaving_left(self): """ Returns True if agent is about to leave lattice on the left. """ return self.y == 0 and self.value == -1
def is_leaving_right(self, len_y)
-
Returns True if agent is about to leave lattice on the right.
Expand source code
def is_leaving_right(self, len_y): """ Returns True if agent is about to leave lattice on the right. """ return self.y == len_y - 1 and self.value == 1
def lower_distance_to_exit(self)
-
Dynamically lower the distance to the exit. Only call this method when agent is leaving the cell.
Expand source code
def lower_distance_to_exit(self): """ Dynamically lower the distance to the exit. Only call this method when agent is leaving the cell. """ if self.value < 0: self.left_exit_distance -= 0.00001 elif self.value > 0: self.right_exit_distance -= 0.00001
def populate(self, value) ‑> None
-
Populate a cell if it is not empty.
Args
value
:int
- The movement direction (-1 or +1)
Expand source code
def populate(self, value) -> None: """ Populate a cell if it is not empty. Args: value (int): The movement direction (-1 or +1) """ assert value in [-1, 1], 'value must be 1 or -1' assert self.value == 0, 'can not populate un-empty cell' self.value = value