diff --git a/CHANGELOG.md b/CHANGELOG.md index e4d59f7..b485cf5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.20.1] +### Fixed +* Agents would run another step after dying. ## [0.20.0] ### Added * Integration with MESA diff --git a/examples/random_delays/random_delays.py b/examples/random_delays/random_delays.py index 257eb82..c3a6961 100644 --- a/examples/random_delays/random_delays.py +++ b/examples/random_delays/random_delays.py @@ -4,20 +4,34 @@ Example of a fully programmatic simulation, without definition files. ''' from soil import Simulation, agents from soil.time import Delta -from networkx import Graph from random import expovariate import logging class MyAgent(agents.FSM): + ''' + An agent that first does a ping + ''' + + defaults = {'pong_counts': 2} @agents.default_state @agents.state - def neutral(self): - self.info('I am running') + def ping(self): + self.info('Ping') + return self.pong, Delta(expovariate(1/16)) + + @agents.state + def pong(self): + self.info('Pong') + self.pong_counts -= 1 + self.info(str(self.pong_counts)) + if self.pong_counts < 1: + return self.die() return None, Delta(expovariate(1/16)) + s = Simulation(name='Programmatic', network_agents=[{'agent_type': MyAgent, 'id': 0}], topology={'nodes': [{'id': 0}], 'links': []}, diff --git a/soil/VERSION b/soil/VERSION index a881cf7..9d26321 100644 --- a/soil/VERSION +++ b/soil/VERSION @@ -1 +1 @@ -0.20.0 \ No newline at end of file +0.20.1 \ No newline at end of file diff --git a/soil/agents/__init__.py b/soil/agents/__init__.py index d260fec..e7c3b6c 100644 --- a/soil/agents/__init__.py +++ b/soil/agents/__init__.py @@ -20,6 +20,10 @@ def as_node(agent): IGNORED_FIELDS = ('model', 'logger') + +class DeadAgent(Exception): + pass + class BaseAgent(Agent): """ A special Agent that keeps track of its state history. @@ -129,13 +133,14 @@ class BaseAgent(Agent): return None def die(self, remove=False): + self.info(f'agent {self.unique_id} is dying') self.alive = False if remove: self.remove_node(self.id) def step(self): if not self.alive: - return time.When('inf') + raise DeadAgent(self.unique_id) return super().step() or time.Delta(self.interval) def log(self, message, *args, level=logging.INFO, **kwargs): @@ -300,7 +305,10 @@ class FSM(NetworkAgent, metaclass=MetaFSM): def step(self): self.debug(f'Agent {self.unique_id} @ state {self.state_id}') - interval = super().step() + try: + interval = super().step() + except DeadAgent: + return time.When('inf') if 'id' not in self.state: # if 'id' in self.state: # self.set_state(self.state['id'])