added live-server visualization to project
This commit is contained in:
parent
a5e03b38ac
commit
214135d063
11
agent.py
11
agent.py
@ -14,14 +14,14 @@ from typing import overload
|
|||||||
|
|
||||||
class RandomWalkerAnt(Agent):
|
class RandomWalkerAnt(Agent):
|
||||||
def __init__(self, unique_id, model, look_for_chemical=None,
|
def __init__(self, unique_id, model, look_for_chemical=None,
|
||||||
energy_0=1, chemical_drop_rate_0=1, sensitvity_0=1, alpha=0.5) -> None:
|
energy_0=1, chemical_drop_rate_0=1, sensitvity_0=1, alpha=0.5, drop_chemical=None) -> None:
|
||||||
super().__init__(unique_id=unique_id, model=model)
|
super().__init__(unique_id=unique_id, model=model)
|
||||||
|
|
||||||
self._next_pos : None | Coordinate = None
|
self._next_pos : None | Coordinate = None
|
||||||
|
|
||||||
self.prev_pos : None | Coordinate = None
|
self.prev_pos : None | Coordinate = None
|
||||||
self.look_for_chemical = look_for_chemical
|
self.look_for_chemical = look_for_chemical
|
||||||
self.drop_chemical = None
|
self.drop_chemical = drop_chemical
|
||||||
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
|
||||||
@ -34,6 +34,10 @@ class RandomWalkerAnt(Agent):
|
|||||||
|
|
||||||
def step(self):
|
def step(self):
|
||||||
# follow positive gradient
|
# follow positive gradient
|
||||||
|
if self.prev_pos is None:
|
||||||
|
i = np.random.choice(range(6))
|
||||||
|
self._next_pos = self.neighbors()[i]
|
||||||
|
return
|
||||||
if self.look_for_chemical is not None:
|
if self.look_for_chemical is not None:
|
||||||
front_concentration = [self.model.grid.fields[self.look_for_chemical][cell] for cell in self.front_neighbors ]
|
front_concentration = [self.model.grid.fields[self.look_for_chemical][cell] for cell in self.front_neighbors ]
|
||||||
gradient = front_concentration - np.repeat(self.model.grid.fields[self.look_for_chemical][self.pos], 3)
|
gradient = front_concentration - np.repeat(self.model.grid.fields[self.look_for_chemical][self.pos], 3)
|
||||||
@ -61,8 +65,7 @@ class RandomWalkerAnt(Agent):
|
|||||||
def advance(self) -> None:
|
def advance(self) -> None:
|
||||||
self.drop_chemicals()
|
self.drop_chemicals()
|
||||||
self.prev_pos = self.pos
|
self.prev_pos = self.pos
|
||||||
self.pos = self._next_pos
|
self.model.grid.move_agent(self, self._next_pos)
|
||||||
|
|
||||||
|
|
||||||
# TODO: find out how to decorate with property properly
|
# TODO: find out how to decorate with property properly
|
||||||
def neighbors(self, pos=None, include_center=False):
|
def neighbors(self, pos=None, include_center=False):
|
||||||
|
8
model.py
8
model.py
@ -33,12 +33,12 @@ class ActiveWalkerModel(Model):
|
|||||||
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,
|
self.decay_rates : dict[str, float] = {"A" :0.1,
|
||||||
"B": 1,
|
"B": 0.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, look_for_chemical="A")
|
agent = RandomWalkerAnt(unique_id=agent_id, model=self, look_for_chemical="A", drop_chemical="A")
|
||||||
self.schedule.add(agent)
|
self.schedule.add(agent)
|
||||||
self.grid.place_agent(agent, pos=nest_position)
|
self.grid.place_agent(agent, pos=nest_position)
|
||||||
|
|
||||||
@ -64,8 +64,6 @@ class ActiveWalkerModel(Model):
|
|||||||
field = self.grid.fields[key]
|
field = self.grid.fields[key]
|
||||||
self.grid.fields[key] = field - self.decay_rates[key]*field
|
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:
|
||||||
|
109
server.py
Executable file
109
server.py
Executable file
@ -0,0 +1,109 @@
|
|||||||
|
#!/bin/python
|
||||||
|
"""
|
||||||
|
server.py - Part of ants project
|
||||||
|
|
||||||
|
This file sets up the mesa built-in visualization server
|
||||||
|
and runs it on file execution.
|
||||||
|
For now it displays ant locations as well as pheromone A
|
||||||
|
concentrations on two seperate grids
|
||||||
|
|
||||||
|
License: AGPL 3 (see end of file)
|
||||||
|
(C) Alexander Bocken, Viviane Fahrni, Grace Kragho
|
||||||
|
"""
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from mesa.visualization.modules import CanvasHexGrid, ChartModule, CanvasGrid
|
||||||
|
from mesa.visualization.ModularVisualization import ModularServer
|
||||||
|
from mesa.visualization.UserParam import UserSettableParameter
|
||||||
|
from model import ActiveWalkerModel
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
def setup():
|
||||||
|
# Set the model parameters
|
||||||
|
params = {
|
||||||
|
"width": 50, "height": 50,
|
||||||
|
"num_max_agents" : 100,
|
||||||
|
"nest_position" : (25,25),
|
||||||
|
"num_initial_roamers" : 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CanvasHexGridMultiAgents(CanvasHexGrid):
|
||||||
|
"""
|
||||||
|
A modification of CanvasHexGrid to not run visualization functions on all agents
|
||||||
|
but on all grid positions instead
|
||||||
|
"""
|
||||||
|
package_includes = ["HexDraw.js", "CanvasHexModule.js", "InteractionHandler.js"]
|
||||||
|
portrayal_method = None # Portrayal function
|
||||||
|
canvas_width = 500
|
||||||
|
canvas_height = 500
|
||||||
|
|
||||||
|
def __init__(self, portrayal_method, grid_width, grid_height, canvas_width=500, canvas_height=500,):
|
||||||
|
super().__init__(portrayal_method, grid_width, grid_height, canvas_width, canvas_height)
|
||||||
|
|
||||||
|
def render(self, model):
|
||||||
|
grid_state = defaultdict(list)
|
||||||
|
for x in range(model.grid.width):
|
||||||
|
for y in range(model.grid.height):
|
||||||
|
portrayal = self.portrayal_method(model, (x, y))
|
||||||
|
if portrayal:
|
||||||
|
portrayal["x"] = x
|
||||||
|
portrayal["y"] = y
|
||||||
|
grid_state[portrayal["Layer"]].append(portrayal)
|
||||||
|
|
||||||
|
return grid_state
|
||||||
|
|
||||||
|
|
||||||
|
def get_color(level, normalization):
|
||||||
|
"""
|
||||||
|
level: level to calculate color between white and black (linearly)
|
||||||
|
normalization: value for which we want full black color
|
||||||
|
"""
|
||||||
|
rgb = int(255 - level * 255 / normalization)
|
||||||
|
mono = f"{rgb:0{2}x}" # hex value of rgb value with fixed length 2
|
||||||
|
rgb = f"#{3*mono}"
|
||||||
|
return rgb
|
||||||
|
|
||||||
|
def portray_ant_density(model, pos):
|
||||||
|
return {
|
||||||
|
"Shape": "hex",
|
||||||
|
"r": 1,
|
||||||
|
"Filled": "true",
|
||||||
|
"Layer": 0,
|
||||||
|
"x": pos[0],
|
||||||
|
"y": pos[1],
|
||||||
|
"Color": get_color(level=len(model.grid[pos]), normalization=5)
|
||||||
|
}
|
||||||
|
|
||||||
|
def portray_pheromone_density(model, pos):
|
||||||
|
return {
|
||||||
|
"Shape": "hex",
|
||||||
|
"r": 1,
|
||||||
|
"Filled": "true",
|
||||||
|
"Layer": 0,
|
||||||
|
"x": pos[0],
|
||||||
|
"y": pos[1],
|
||||||
|
"Color": get_color(level=model.grid.fields["A"][pos], normalization=3)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
width = params['width']
|
||||||
|
height = params['height']
|
||||||
|
pixel_ratio = 10
|
||||||
|
grid_ants = CanvasHexGridMultiAgents(portray_ant_density, width, height, width*pixel_ratio, height*pixel_ratio)
|
||||||
|
grid_pheromones = CanvasHexGridMultiAgents(portray_pheromone_density, width, height, width*pixel_ratio, height*pixel_ratio)
|
||||||
|
return ModularServer(ActiveWalkerModel, [grid_ants, grid_pheromones],
|
||||||
|
"Active Random Walker Ants", params)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
server = setup()
|
||||||
|
server.launch()
|
||||||
|
|
||||||
|
"""
|
||||||
|
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 distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||||
|
"""
|
Loading…
Reference in New Issue
Block a user