mirror of
https://github.com/gsi-upm/soil
synced 2024-11-24 11:52:29 +00:00
a7c51742f6
Fixed several bugs Added convenience methods in soil.analysis
121 lines
3.9 KiB
Python
121 lines
3.9 KiB
Python
from soil.agents import FSM, state, default_state, BaseAgent
|
|
from enum import Enum
|
|
from random import random, choice
|
|
from itertools import islice
|
|
import logging
|
|
import math
|
|
|
|
|
|
class Genders(Enum):
|
|
male = 'male'
|
|
female = 'female'
|
|
|
|
|
|
class RabbitModel(FSM):
|
|
|
|
level = logging.INFO
|
|
|
|
defaults = {
|
|
'age': 0,
|
|
'gender': Genders.male.value,
|
|
'mating_prob': 0.001,
|
|
'offspring': 0,
|
|
}
|
|
|
|
sexual_maturity = 4*30
|
|
life_expectancy = 365 * 3
|
|
gestation = 33
|
|
pregnancy = -1
|
|
max_females = 5
|
|
|
|
@default_state
|
|
@state
|
|
def newborn(self):
|
|
self['age'] += 1
|
|
|
|
if self['age'] >= self.sexual_maturity:
|
|
return self.fertile
|
|
|
|
@state
|
|
def fertile(self):
|
|
self['age'] += 1
|
|
if self['age'] > self.life_expectancy:
|
|
return self.dead
|
|
|
|
if self['gender'] == Genders.female.value:
|
|
return
|
|
|
|
# Males try to mate
|
|
females = self.get_agents(state_id=self.fertile.id, gender=Genders.female.value, limit_neighbors=False)
|
|
for f in islice(females, self.max_females):
|
|
r = random()
|
|
if r < self['mating_prob']:
|
|
self.impregnate(f)
|
|
break # Take a break
|
|
|
|
def impregnate(self, whom):
|
|
if self['gender'] == Genders.female.value:
|
|
raise NotImplementedError('Females cannot impregnate')
|
|
whom['pregnancy'] = 0
|
|
whom['mate'] = self.id
|
|
whom.set_state(whom.pregnant)
|
|
self.debug('{} impregnating: {}. {}'.format(self.id, whom.id, whom.state))
|
|
|
|
@state
|
|
def pregnant(self):
|
|
self['age'] += 1
|
|
if self['age'] > self.life_expectancy:
|
|
return self.dead
|
|
|
|
self['pregnancy'] += 1
|
|
self.debug('Pregnancy: {}'.format(self['pregnancy']))
|
|
if self['pregnancy'] >= self.gestation:
|
|
number_of_babies = int(8+4*random())
|
|
self.info('Having {} babies'.format(number_of_babies))
|
|
for i in range(number_of_babies):
|
|
state = {}
|
|
state['gender'] = choice(list(Genders)).value
|
|
child = self.env.add_node(self.__class__, state)
|
|
self.env.add_edge(self.id, child.id)
|
|
self.env.add_edge(self['mate'], child.id)
|
|
# self.add_edge()
|
|
self.debug('A BABY IS COMING TO LIFE')
|
|
self.env['rabbits_alive'] = self.env.get('rabbits_alive', self.global_topology.number_of_nodes())+1
|
|
self.debug('Rabbits alive: {}'.format(self.env['rabbits_alive']))
|
|
self['offspring'] += 1
|
|
self.env.get_agent(self['mate'])['offspring'] += 1
|
|
del self['mate']
|
|
self['pregnancy'] = -1
|
|
return self.fertile
|
|
|
|
@state
|
|
def dead(self):
|
|
self.info('Agent {} is dying'.format(self.id))
|
|
if 'pregnancy' in self and self['pregnancy'] > -1:
|
|
self.info('A mother has died carrying a baby!!')
|
|
self.die()
|
|
return
|
|
|
|
|
|
class RandomAccident(BaseAgent):
|
|
|
|
level = logging.DEBUG
|
|
|
|
def step(self):
|
|
rabbits_total = self.global_topology.number_of_nodes()
|
|
rabbits_alive = self.env.get('rabbits_alive', rabbits_total)
|
|
prob_death = self.env.get('prob_death', 1e-100)*math.floor(math.log10(max(1, rabbits_alive)))
|
|
self.debug('Killing some rabbits with prob={}!'.format(prob_death))
|
|
for i in self.env.network_agents:
|
|
if i.state['id'] == i.dead.id:
|
|
continue
|
|
r = random()
|
|
if r < prob_death:
|
|
self.debug('I killed a rabbit: {}'.format(i.id))
|
|
rabbits_alive = self.env['rabbits_alive'] = rabbits_alive -1
|
|
self.log('Rabbits alive: {}'.format(self.env['rabbits_alive']))
|
|
i.set_state(i.dead)
|
|
self.log('Rabbits alive: {}/{}'.format(rabbits_alive, rabbits_total))
|
|
if self.count_agents(state_id=RabbitModel.dead.id) == self.global_topology.number_of_nodes():
|
|
self.die()
|