2023-05-12 12:09:00 +00:00
|
|
|
from soil import FSM, state, default_state, BaseAgent, NetworkAgent, Environment, report, parameters as params
|
2022-10-13 20:43:16 +00:00
|
|
|
import math
|
|
|
|
|
|
|
|
|
2022-10-16 19:57:30 +00:00
|
|
|
class RabbitEnv(Environment):
|
2023-05-12 12:09:00 +00:00
|
|
|
prob_death: params.probability = 1e-3
|
2022-11-13 19:31:05 +00:00
|
|
|
|
2023-04-09 02:19:24 +00:00
|
|
|
def init(self):
|
|
|
|
a1 = self.add_node(Male)
|
|
|
|
a2 = self.add_node(Female)
|
|
|
|
a1.add_edge(a2)
|
|
|
|
self.add_agent(RandomAccident)
|
|
|
|
|
|
|
|
@report
|
2022-10-16 19:57:30 +00:00
|
|
|
@property
|
|
|
|
def num_rabbits(self):
|
|
|
|
return self.count_agents(agent_class=Rabbit)
|
|
|
|
|
2023-04-09 02:19:24 +00:00
|
|
|
@report
|
2022-10-16 19:57:30 +00:00
|
|
|
@property
|
|
|
|
def num_males(self):
|
|
|
|
return self.count_agents(agent_class=Male)
|
|
|
|
|
2023-04-09 02:19:24 +00:00
|
|
|
@report
|
2022-10-16 19:57:30 +00:00
|
|
|
@property
|
|
|
|
def num_females(self):
|
|
|
|
return self.count_agents(agent_class=Female)
|
|
|
|
|
|
|
|
|
2022-10-17 17:29:39 +00:00
|
|
|
class Rabbit(NetworkAgent, FSM):
|
2022-10-13 20:43:16 +00:00
|
|
|
|
|
|
|
sexual_maturity = 30
|
|
|
|
life_expectancy = 300
|
|
|
|
|
|
|
|
@default_state
|
|
|
|
@state
|
|
|
|
def newborn(self):
|
2023-05-12 12:09:00 +00:00
|
|
|
self.debug("I am a newborn.")
|
2022-10-13 20:43:16 +00:00
|
|
|
self.age = 0
|
|
|
|
self.offspring = 0
|
|
|
|
return self.youngling
|
|
|
|
|
|
|
|
@state
|
|
|
|
def youngling(self):
|
|
|
|
self.age += 1
|
|
|
|
if self.age >= self.sexual_maturity:
|
2023-05-12 12:09:00 +00:00
|
|
|
self.debug(f"I am fertile! My age is {self.age}")
|
2022-10-13 20:43:16 +00:00
|
|
|
return self.fertile
|
|
|
|
|
|
|
|
@state
|
|
|
|
def fertile(self):
|
|
|
|
raise Exception("Each subclass should define its fertile state")
|
|
|
|
|
|
|
|
@state
|
|
|
|
def dead(self):
|
|
|
|
self.die()
|
|
|
|
|
|
|
|
|
2022-10-16 19:57:30 +00:00
|
|
|
class Male(Rabbit):
|
2022-10-13 20:43:16 +00:00
|
|
|
max_females = 5
|
2023-05-12 12:09:00 +00:00
|
|
|
mating_prob = 0.005
|
2022-10-13 20:43:16 +00:00
|
|
|
|
|
|
|
@state
|
|
|
|
def fertile(self):
|
|
|
|
self.age += 1
|
|
|
|
|
|
|
|
if self.age > self.life_expectancy:
|
|
|
|
return self.dead
|
|
|
|
|
|
|
|
# Males try to mate
|
2023-05-12 12:09:00 +00:00
|
|
|
for f in self.model.agents.filter(
|
|
|
|
agent_class=Female, state_id=Female.fertile.id).limit(self.max_females):
|
2022-10-17 18:23:57 +00:00
|
|
|
self.debug("FOUND A FEMALE: ", repr(f), self.mating_prob)
|
|
|
|
if self.prob(self["mating_prob"]):
|
2022-10-13 20:43:16 +00:00
|
|
|
f.impregnate(self)
|
|
|
|
break # Take a break
|
|
|
|
|
|
|
|
|
2022-10-16 19:57:30 +00:00
|
|
|
class Female(Rabbit):
|
2022-10-17 17:29:39 +00:00
|
|
|
gestation = 10
|
|
|
|
pregnancy = -1
|
2022-10-13 20:43:16 +00:00
|
|
|
|
|
|
|
@state
|
|
|
|
def fertile(self):
|
|
|
|
# Just wait for a Male
|
|
|
|
self.age += 1
|
|
|
|
if self.age > self.life_expectancy:
|
|
|
|
return self.dead
|
2022-10-17 17:29:39 +00:00
|
|
|
if self.pregnancy >= 0:
|
|
|
|
return self.pregnant
|
2022-10-13 20:43:16 +00:00
|
|
|
|
|
|
|
def impregnate(self, male):
|
2023-05-12 12:09:00 +00:00
|
|
|
self.debug(f"impregnated by {repr(male)}")
|
2022-10-13 20:43:16 +00:00
|
|
|
self.mate = male
|
2022-10-17 17:29:39 +00:00
|
|
|
self.pregnancy = 0
|
2022-10-17 18:23:57 +00:00
|
|
|
self.number_of_babies = int(8 + 4 * self.random.random())
|
2022-10-13 20:43:16 +00:00
|
|
|
|
|
|
|
@state
|
|
|
|
def pregnant(self):
|
2023-05-12 12:09:00 +00:00
|
|
|
self.debug("I am pregnant")
|
2022-10-13 20:43:16 +00:00
|
|
|
self.age += 1
|
|
|
|
|
2022-10-17 17:29:39 +00:00
|
|
|
if self.age >= self.life_expectancy:
|
2022-10-13 20:43:16 +00:00
|
|
|
return self.die()
|
|
|
|
|
2022-10-17 17:29:39 +00:00
|
|
|
if self.pregnancy < self.gestation:
|
|
|
|
self.pregnancy += 1
|
|
|
|
return
|
|
|
|
|
2023-05-12 12:09:00 +00:00
|
|
|
self.debug("Having {} babies".format(self.number_of_babies))
|
2022-10-17 17:29:39 +00:00
|
|
|
for i in range(self.number_of_babies):
|
|
|
|
state = {}
|
|
|
|
agent_class = self.random.choice([Male, Female])
|
2022-10-17 18:23:57 +00:00
|
|
|
child = self.model.add_node(agent_class=agent_class, **state)
|
2022-10-17 17:29:39 +00:00
|
|
|
child.add_edge(self)
|
|
|
|
try:
|
|
|
|
child.add_edge(self.mate)
|
|
|
|
self.model.agents[self.mate].offspring += 1
|
|
|
|
except ValueError:
|
2022-10-17 18:23:57 +00:00
|
|
|
self.debug("The father has passed away")
|
2022-10-17 17:29:39 +00:00
|
|
|
|
|
|
|
self.offspring += 1
|
|
|
|
self.mate = None
|
|
|
|
self.pregnancy = -1
|
|
|
|
return self.fertile
|
2022-10-13 20:43:16 +00:00
|
|
|
|
2022-10-17 17:29:39 +00:00
|
|
|
def die(self):
|
2022-10-17 18:23:57 +00:00
|
|
|
if "pregnancy" in self and self["pregnancy"] > -1:
|
2023-05-12 12:09:00 +00:00
|
|
|
self.debug("A mother has died carrying a baby!!")
|
2022-10-17 17:29:39 +00:00
|
|
|
return super().die()
|
2022-10-13 20:43:16 +00:00
|
|
|
|
|
|
|
|
|
|
|
class RandomAccident(BaseAgent):
|
2023-05-12 12:09:00 +00:00
|
|
|
prob_death = None
|
2022-10-13 20:43:16 +00:00
|
|
|
def step(self):
|
2023-05-12 12:09:00 +00:00
|
|
|
alive = self.get_agents(agent_class=Rabbit, alive=True)
|
2022-10-13 20:43:16 +00:00
|
|
|
|
2023-05-12 12:09:00 +00:00
|
|
|
if not alive:
|
|
|
|
return self.die("No more rabbits to kill")
|
2022-10-13 20:43:16 +00:00
|
|
|
|
2023-05-12 12:09:00 +00:00
|
|
|
num_alive = len(alive)
|
|
|
|
prob_death = min(1, self.prob_death * num_alive/10)
|
2022-10-17 18:23:57 +00:00
|
|
|
self.debug("Killing some rabbits with prob={}!".format(prob_death))
|
2023-05-12 12:09:00 +00:00
|
|
|
|
2023-05-19 14:19:50 +00:00
|
|
|
for i in alive:
|
2022-10-16 15:54:03 +00:00
|
|
|
if i.state_id == i.dead.id:
|
2022-10-13 20:43:16 +00:00
|
|
|
continue
|
|
|
|
if self.prob(prob_death):
|
2023-05-12 12:09:00 +00:00
|
|
|
self.debug("I killed a rabbit: {}".format(i.unique_id))
|
|
|
|
num_alive -= 1
|
2023-05-19 14:19:50 +00:00
|
|
|
self.model.remove_agent(i)
|
|
|
|
i.alive = False
|
|
|
|
i.killed = True
|
2023-05-12 12:09:00 +00:00
|
|
|
self.debug("Rabbits alive: {}".format(num_alive))
|
2022-10-17 18:23:57 +00:00
|
|
|
|
2023-04-09 02:19:24 +00:00
|
|
|
|
2023-05-12 12:09:00 +00:00
|
|
|
RabbitEnv.run(max_time=1000, seed="MySeed", iterations=1)
|