add scalarfields on grid
This commit is contained in:
parent
808dc6f78c
commit
1952198fad
28
agent.py
28
agent.py
@ -18,38 +18,52 @@ class RandomWalkerAnt(Agent):
|
|||||||
|
|
||||||
self._next_pos : None | Coordinate = None
|
self._next_pos : None | Coordinate = None
|
||||||
|
|
||||||
self.prev_pos = None
|
self.prev_pos : None | Coordinate = None
|
||||||
self.do_follow_chemical_A : bool = True # False -> follow_chemical_B = True
|
self.do_follow_chemical_A : bool = True # False -> follow_chemical_B = True
|
||||||
self.energy : float = energy_0
|
self.energy : float = energy_0
|
||||||
self.sensitvity : float = sensitvity_0
|
self.sensitvity : float = sensitvity_0
|
||||||
self.chemical_drop_rate : float = chemical_drop_rate_0 #TODO: check whether needs to be separated into A and B
|
self.chemical_drop_rate : float = chemical_drop_rate_0 #TODO: check whether needs to be separated into A and B
|
||||||
self.alpha = alpha
|
self.alpha = alpha
|
||||||
|
|
||||||
|
|
||||||
|
def sensitvity_to_concentration(self, prop : float) -> float:
|
||||||
|
# TODO
|
||||||
|
return prop
|
||||||
|
|
||||||
|
|
||||||
def step(self):
|
def step(self):
|
||||||
|
# Calculate where next ant location should be and store in _next_pos
|
||||||
|
# TODO
|
||||||
|
pass
|
||||||
|
|
||||||
|
def drop_chemicals(self):
|
||||||
|
# drop chemicals (depending on current state) on concentration field
|
||||||
|
# TODO
|
||||||
|
# use self.model.grid.add_to_field(key, value, pos) to not interfere with other ants
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def advance(self) -> None:
|
def advance(self) -> None:
|
||||||
|
self.drop_chemicals()
|
||||||
self.pos = self._next_pos
|
self.pos = self._next_pos
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def front_neighbors(self):
|
def front_neighbors(self):
|
||||||
if self.prev_pos is not None:
|
if self.prev_pos is not None:
|
||||||
|
assert(self.pos is not None)
|
||||||
x, y = self.pos
|
x, y = self.pos
|
||||||
x_prev, y_prev = self.prev_pos
|
x_prev, y_prev = self.prev_pos
|
||||||
dx, dy = x - x_prev, y - y_prev
|
dx, dy = x - x_prev, y - y_prev
|
||||||
front = [
|
front = np.array([
|
||||||
(x, y + dy),
|
(x, y + dy),
|
||||||
(x + dx, y),
|
|
||||||
(x + dx, y + dy),
|
(x + dx, y + dy),
|
||||||
]
|
(x + dx, y),
|
||||||
|
])
|
||||||
return front #TODO: verify (do we need to sperate into even/odd?)
|
return front #TODO: verify (do we need to sperate into even/odd?)
|
||||||
else:
|
else:
|
||||||
# TODO: return all neighbors or raise Exception?
|
# TODO: return all neighbors or raise Exception?
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3.
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3.
|
||||||
|
|
||||||
|
1
main.py
1
main.py
@ -32,6 +32,7 @@ def main():
|
|||||||
agent.do_follow_chemical_A = False
|
agent.do_follow_chemical_A = False
|
||||||
agent.prev_pos = (9,10)
|
agent.prev_pos = (9,10)
|
||||||
print(agent.front_neighbors)
|
print(agent.front_neighbors)
|
||||||
|
|
||||||
print(agent.pos, agent.unique_id, agent.do_follow_chemical_A)
|
print(agent.pos, agent.unique_id, agent.do_follow_chemical_A)
|
||||||
|
|
||||||
|
|
||||||
|
30
model.py
30
model.py
@ -11,25 +11,37 @@ License: AGPL 3 (see end of file)
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from mesa.model import Model
|
from mesa.model import Model
|
||||||
from mesa.space import Coordinate, HexGrid, Iterable
|
from mesa.space import Coordinate, HexGrid, Iterable
|
||||||
from multihex import MultiHexGrid
|
from multihex import MultiHexGrid, MultiHexGridScalarFields
|
||||||
from mesa.time import SimultaneousActivation
|
from mesa.time import SimultaneousActivation
|
||||||
from mesa.datacollection import DataCollector
|
from mesa.datacollection import DataCollector
|
||||||
from agent import RandomWalkerAnt
|
from agent import RandomWalkerAnt
|
||||||
|
from agent import Pheromone
|
||||||
|
|
||||||
class ActiveWalkerModel(Model):
|
class ActiveWalkerModel(Model):
|
||||||
|
# TODO: separate food and source into new agents?
|
||||||
|
# TODO: pheromone concentrations as well as agents?
|
||||||
def __init__(self, width : int, height : int , num_max_agents : int,
|
def __init__(self, width : int, height : int , num_max_agents : int,
|
||||||
num_initial_roamers : int,
|
num_initial_roamers : int,
|
||||||
nest_position : Coordinate,
|
nest_position : Coordinate,
|
||||||
max_steps:int=1000) -> None:
|
max_steps:int=1000) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
fields={"A" : True, # key : also have _next prop (for no interference in step)
|
||||||
|
"B": True,
|
||||||
|
"nests": False,
|
||||||
|
"food" : False,
|
||||||
|
}
|
||||||
self.schedule = SimultaneousActivation(self)
|
self.schedule = SimultaneousActivation(self)
|
||||||
self.grid = MultiHexGrid(width=width, height=height, torus=True) # TODO: replace with MultiHexGrid
|
self.grid = MultiHexGridScalarFields(width=width, height=height, torus=True, fields=fields)
|
||||||
self._unique_id_counter : int = -1 # only touch via get_unique_id() or get_unique_ids(num_ids)
|
self._unique_id_counter = -1
|
||||||
|
|
||||||
self.max_steps = max_steps
|
self.max_steps = max_steps
|
||||||
self.nest_position : Coordinate = nest_position
|
self.nest_position : Coordinate = nest_position
|
||||||
self.num_max_agents = num_max_agents
|
self.num_max_agents = num_max_agents
|
||||||
|
|
||||||
|
self.decay_rates = {"A" :1,
|
||||||
|
"B": 1,
|
||||||
|
}
|
||||||
|
|
||||||
for agent_id in self.get_unique_ids(num_initial_roamers):
|
for agent_id in self.get_unique_ids(num_initial_roamers):
|
||||||
agent = RandomWalkerAnt(unique_id=agent_id, model=self, do_follow_chemical_A=True)
|
agent = RandomWalkerAnt(unique_id=agent_id, model=self, do_follow_chemical_A=True)
|
||||||
self.schedule.add(agent)
|
self.schedule.add(agent)
|
||||||
@ -41,8 +53,18 @@ class ActiveWalkerModel(Model):
|
|||||||
)
|
)
|
||||||
self.datacollector.collect(self) # keep at end of __init___
|
self.datacollector.collect(self) # keep at end of __init___
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def step(self):
|
def step(self):
|
||||||
self.schedule.step()
|
self.schedule.step() # step() and advance() all agents
|
||||||
|
|
||||||
|
# apply decay rate on pheromone levels
|
||||||
|
for key in ("A", "B"):
|
||||||
|
field = self.grid.fields[key]
|
||||||
|
self.grid.fields[key] = field - self.decay_rates[key]*field
|
||||||
|
|
||||||
|
self.grid.step() # actually apply deposits on fields
|
||||||
|
|
||||||
self.datacollector.collect(self)
|
self.datacollector.collect(self)
|
||||||
|
|
||||||
if self.schedule.steps >= self.max_steps:
|
if self.schedule.steps >= self.max_steps:
|
||||||
|
27
multihex.py
27
multihex.py
@ -12,6 +12,7 @@ License: AGPL 3 (see end of file)
|
|||||||
from mesa.space import HexGrid
|
from mesa.space import HexGrid
|
||||||
from mesa.agent import Agent
|
from mesa.agent import Agent
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import numpy.typing as npt
|
||||||
from mesa.space import Coordinate, accept_tuple_argument
|
from mesa.space import Coordinate, accept_tuple_argument
|
||||||
import itertools
|
import itertools
|
||||||
from typing import (
|
from typing import (
|
||||||
@ -91,6 +92,32 @@ class MultiHexGrid(HexGrid):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MultiHexGridScalarFields(MultiHexGrid):
|
||||||
|
def __init__(self, fields: dict[str, bool], width : int, height : int, torus : bool, scalar_initial_value : float=0) -> None:
|
||||||
|
super().__init__(width=width, height=height, torus=torus)
|
||||||
|
self._field_props = fields
|
||||||
|
|
||||||
|
self.fields : dict[str, npt.NDArray[np.float_]] = {}
|
||||||
|
|
||||||
|
for key, is_step_field in fields.items():
|
||||||
|
self.fields[key] = np.ones((width, height)).astype(float) * scalar_initial_value
|
||||||
|
if is_step_field:
|
||||||
|
self.fields[f"_next_{key}"] = np.zeros((width, height)).astype(float)
|
||||||
|
|
||||||
|
def reset_field(self, key : str) -> None:
|
||||||
|
self.fields[key] = np.zeros((self.width, self.height))
|
||||||
|
|
||||||
|
def add_to_field(self, field_key : str, value : float, pos : Coordinate) -> None:
|
||||||
|
if self._field_props[field_key]:
|
||||||
|
self.fields[f"_next_{field_key}"][pos] += value
|
||||||
|
else:
|
||||||
|
self.fields[field_key][pos] += value
|
||||||
|
|
||||||
|
def step(self) -> None:
|
||||||
|
for key, is_step_field in self._field_props.items():
|
||||||
|
if is_step_field:
|
||||||
|
self.fields[key] += self.fields[f"_next_{key}"]
|
||||||
|
self.reset_field(f"_next_{key}")
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3.
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3.
|
||||||
|
Loading…
Reference in New Issue
Block a user