Skip to content
Snippets Groups Projects
Commit 74d43c05 authored by xo30xoqa's avatar xo30xoqa
Browse files

Moved some functions from populations.jl to new file individuals.jl

parent 2db97204
Branches
Tags
Loading
......@@ -141,6 +141,7 @@ include("nature/energy.jl")
include("nature/nature.jl")
include("nature/macros.jl")
include("nature/populations.jl")
include("nature/individuals.jl")
include("nature/ecologicaldata.jl")
include("nature/species/skylark.jl")
include("nature/species/wolpertinger.jl")
......
### Persefone.jl - a model of agricultural landscapes and ecosystems in Europe.
###
### This file contains life-history and other ecological functions that apply to
### all animal individuals, such reproduction, death, and movement.
###
"""
reproduce!(animal, model, mate, n=1)
Produce one or more offspring for the given animal at its current location.
The `mate` argument gives the ID of the reproductive partner.
"""
function reproduce!(animal::Animal, model::SimulationModel,
n::Int64=1, mate::Int64=-1)
(animal.sex == male) && @warn "Male $(animalid(animal)) is reproducing."
for i in 1:n
if animal.sex == hermaphrodite
sex = hermaphrodite
else
sex = @rand([male, female])
end
bphase = populationparameters(typeof(animal)).birthphase
#TODO add DEB?
child = typeof(animal)(length(model.animals)+1, sex, (animal.id, mate), animal.pos, bphase)
push!(model.animals, child)
push!(animal.offspring, child.id)
mate > 0 && push!(model.animals[mate].offspring, child.id)
push!(model.landscape[child.pos...].animals, child.id)
create!(child, model)
end
@debug "$(animalid(animal)) has reproduced."
end
"""
kill!(animal, model, probability=1.0, cause="")
Kill this animal, optionally with a given percentage probability.
Returns true if the animal dies, false if not.
"""
function kill!(animal::Animal, model::SimulationModel,
probability::Float64=1.0, cause::String="")
if @rand() < probability
# remove the animal's location and territory pointers from the landscape
filter!(x -> x!=animal.id, model.landscape[animal.pos...].animals)
for pos in animal.territory
filter!(x -> x!=animal.id, model.landscape[pos...].territories)
end
# print the epitaph and remove the animal from the model
postfix = isempty(cause) ? "." : " from $cause."
@debug "$(animalid(animal)) has died$(postfix)"
model.animals[animal.id] = nothing
return true
end
return false
end
"""
migrate!(animal, model, arrival)
Remove this animal from the map and add it to the migrant species pool.
It will be returned to its current location at the specified `arrival` date.
"""
function migrate!(animal::Animal, model::SimulationModel, arrival::Date)
# keep model.migrants sorted by inserting the new migrant after migrants
# that will return earlier than it
i = findfirst(m -> m.second >= arrival, model.migrants)
if isnothing(i)
push!(model.migrants, Pair(animal, arrival))
else
insert!(model.migrants, i, Pair(animal, arrival))
end
filter!(x -> x!=animal.id, model.landscape[animal.pos...].animals)
model.animals[animal.id] = nothing
@debug "$(animalid(animal)) has migrated."
end
"""
occupy!(animal, model, position)
Add the given location to the animal's territory.
"""
function occupy!(animal::Animal, model::SimulationModel, position::Tuple{Int64,Int64})
if isoccupied(model, speciesof(animal), position) #XXX should this be an error?
@warn "Position $position is already occupied by a $(speciesof(animal))."
end
push!(animal.territories, position)
push!(model.landscape[position...], animal.id)
end
"""
vacate!(animal, model, position)
Remove this position from the animal's territory.
"""
function vacate!(animal::Animal, model::SimulationModel, position::Tuple{Int64,Int64})
filter!(x -> x!=position, animal.territory)
filter!(x -> x!=animal.id, model.landscape[position...].territories)
end
"""
vacate!(animal, model)
Remove the animal's complete territory.
"""
function vacate!(animal::Animal, model::SimulationModel)
for pos in animal.territory
filter!(x -> x!=animal.id, model.landscape[pos...].territories)
end
animal.territory = Vector{Tuple{Int64,Int64}}()
end
"""
followanimal!(follower, leader, model, distance=0)
Move the follower animal to a location near the leading animal.
"""
function followanimal!(follower::Animal, leader::Animal, model::SimulationModel,
distance::Length=0m)
#TODO test function
dist = Int(floor(distance / @param(world.mapresolution)))
spacing = Tuple(@rand(-dist:dist, 2))
targetposition = safebounds(spacing .+ leader.pos, model)
move!(follower, model, targetposition)
end
"""
move!(animal, model, position)
Move the animal to the given position, making sure that this is in-bounds.
If the position is out of bounds, the animal stops at the map edge.
"""
function move!(animal::Animal, model::SimulationModel, position::Tuple{Int64,Int64})
#XXX should this function give some sort of warning (e.g. return false)
# if the original position is not reachable?
filter!(x -> x != animal.id, model.landscape[animal.pos...].animals)
animal.pos = safebounds(position, model)
push!(model.landscape[animal.pos...].animals, animal.id)
end
"""
walk!(animal, model, direction, distance=1pixel)
Let the animal move a given number of steps in the given direction ("north", "northeast",
"east", "southeast", "south", "southwest", "west", "northwest", "random").
"""
function walk!(animal::Animal, model::SimulationModel, direction::String, distance::Length=-1m)
distance < 0m ? steps = 1 : steps = Int(floor(distance/@param(world.mapresolution)))
if direction == "north"
shift = (0,-steps)
elseif direction == "northeast"
shift = (steps,-steps)
elseif direction == "east"
shift = (steps,0)
elseif direction == "southeast"
shift = (steps,steps)
elseif direction == "south"
shift = (0,steps)
elseif direction == "southwest"
shift = (-steps,steps)
elseif direction == "west"
shift = (-steps,0)
elseif direction == "northwest"
shift = (-steps,-steps)
elseif direction == "random"
shift = Tuple(@rand([-steps,0,steps], 2))
else
@error "Invalid direction in @walk: "*direction
end
walk!(animal, model, shift)
end
"""
walk!(animal, model, direction, distance=-1)
Let the animal move in the given direction, where the direction is
defined by an (x, y) tuple to specify the shift in coordinates.
If maxdist >= 0, move no further than the specified distance.
"""
function walk!(animal::Animal, model::SimulationModel, direction::Tuple{Int64,Int64},
maxdist::Length=-1m)
#TODO test
distance = Int(floor(maxdist/@param(world.mapresolution)))
if distance >= 0
direction[1] > distance && (direction[1] = distance)
direction[2] > distance && (direction[2] = distance)
direction[1] < -distance && (direction[1] = -distance)
direction[2] < -distance && (direction[2] = -distance)
end
newpos = animal.pos .+ direction
move!(animal, model, newpos)
end
#TODO add a walk function with a habitat descriptor
##TODO add walktoward or similar function (incl. pathfinding?)
### Persefone.jl - a model of agricultural landscapes and ecosystems in Europe.
###
### This file contains a set of utility functions for species, including initialisation,
### reproduction, and mortality.
### This file contains functions that apply to all animal populations, such as for
### initialisation, or querying for neighbours.
###
##TODO move the life-history functions into a new file `individuals.jl`
"""
PopInitParams
......@@ -141,89 +139,6 @@ end
#XXX initpopulation with dispersal from an original source?
#XXX initpopulation based on known occurences in real-life?
"""
reproduce!(animal, model, mate, n=1)
Produce one or more offspring for the given animal at its current location.
The `mate` argument gives the ID of the reproductive partner.
"""
function reproduce!(animal::Animal, model::SimulationModel,
n::Int64=1, mate::Int64=-1)
(animal.sex == male) && @warn "Male $(animalid(animal)) is reproducing."
for i in 1:n
if animal.sex == hermaphrodite
sex = hermaphrodite
else
sex = @rand([male, female])
end
bphase = populationparameters(typeof(animal)).birthphase
#TODO add DEB?
child = typeof(animal)(length(model.animals)+1, sex, (animal.id, mate), animal.pos, bphase)
push!(model.animals, child)
push!(animal.offspring, child.id)
mate > 0 && push!(model.animals[mate].offspring, child.id)
push!(model.landscape[child.pos...].animals, child.id)
create!(child, model)
end
@debug "$(animalid(animal)) has reproduced."
end
"""
kill!(animal, model, probability=1.0, cause="")
Kill this animal, optionally with a given percentage probability.
Returns true if the animal dies, false if not.
"""
function kill!(animal::Animal, model::SimulationModel,
probability::Float64=1.0, cause::String="")
if @rand() < probability
# remove the animal's location and territory pointers from the landscape
filter!(x -> x!=animal.id, model.landscape[animal.pos...].animals)
for pos in animal.territory
filter!(x -> x!=animal.id, model.landscape[pos...].territories)
end
# print the epitaph and remove the animal from the model
postfix = isempty(cause) ? "." : " from $cause."
@debug "$(animalid(animal)) has died$(postfix)"
model.animals[animal.id] = nothing
return true
end
return false
end
"""
migrate!(animal, model, arrival)
Remove this animal from the map and add it to the migrant species pool.
It will be returned to its current location at the specified `arrival` date.
"""
function migrate!(animal::Animal, model::SimulationModel, arrival::Date)
# keep model.migrants sorted by inserting the new migrant after migrants
# that will return earlier than it
i = findfirst(m -> m.second >= arrival, model.migrants)
if isnothing(i)
push!(model.migrants, Pair(animal, arrival))
else
insert!(model.migrants, i, Pair(animal, arrival))
end
filter!(x -> x!=animal.id, model.landscape[animal.pos...].animals)
model.animals[animal.id] = nothing
@debug "$(animalid(animal)) has migrated."
end
"""
occupy!(animal, model, position)
Add the given location to the animal's territory.
"""
function occupy!(animal::Animal, model::SimulationModel, position::Tuple{Int64,Int64})
if isoccupied(model, speciesof(animal), position) #XXX should this be an error?
@warn "Position $position is already occupied by a $(speciesof(animal))."
end
push!(animal.territories, position)
push!(model.landscape[position...], animal.id)
end
"""
isoccupied(model, position, species)
......@@ -236,28 +151,6 @@ function isoccupied(model::SimulationModel, species::String, position::Tuple{Int
return false
end
"""
vacate!(animal, model, position)
Remove this position from the animal's territory.
"""
function vacate!(animal::Animal, model::SimulationModel, position::Tuple{Int64,Int64})
filter!(x -> x!=position, animal.territory)
filter!(x -> x!=animal.id, model.landscape[position...].territories)
end
"""
vacate!(animal, model)
Remove the animal's complete territory.
"""
function vacate!(animal::Animal, model::SimulationModel)
for pos in animal.territory
filter!(x -> x!=animal.id, model.landscape[pos...].territories)
end
animal.territory = Vector{Tuple{Int64,Int64}}()
end
"""
isalive(id, model)
......@@ -346,87 +239,3 @@ function distanceto(pos::Tuple{Int64,Int64}, model::SimulationModel, animal::Ani
#XXX this is very imprecise because diagonal distances are not calculated trigonometrically
maximum(abs.(animal.pos .- pos)) * @param(world.mapresolution)
end
"""
followanimal!(follower, leader, model, distance=0)
Move the follower animal to a location near the leading animal.
"""
function followanimal!(follower::Animal, leader::Animal, model::SimulationModel,
distance::Length=0m)
#TODO test function
dist = Int(floor(distance / @param(world.mapresolution)))
spacing = Tuple(@rand(-dist:dist, 2))
targetposition = safebounds(spacing .+ leader.pos, model)
move!(follower, model, targetposition)
end
"""
move!(animal, model, position)
Move the animal to the given position, making sure that this is in-bounds.
If the position is out of bounds, the animal stops at the map edge.
"""
function move!(animal::Animal, model::SimulationModel, position::Tuple{Int64,Int64})
#XXX should this function give some sort of warning (e.g. return false)
# if the original position is not reachable?
filter!(x -> x != animal.id, model.landscape[animal.pos...].animals)
animal.pos = safebounds(position, model)
push!(model.landscape[animal.pos...].animals, animal.id)
end
"""
walk!(animal, model, direction, distance=1pixel)
Let the animal move a given number of steps in the given direction ("north", "northeast",
"east", "southeast", "south", "southwest", "west", "northwest", "random").
"""
function walk!(animal::Animal, model::SimulationModel, direction::String, distance::Length=-1m)
distance < 0m ? steps = 1 : steps = Int(floor(distance/@param(world.mapresolution)))
if direction == "north"
shift = (0,-steps)
elseif direction == "northeast"
shift = (steps,-steps)
elseif direction == "east"
shift = (steps,0)
elseif direction == "southeast"
shift = (steps,steps)
elseif direction == "south"
shift = (0,steps)
elseif direction == "southwest"
shift = (-steps,steps)
elseif direction == "west"
shift = (-steps,0)
elseif direction == "northwest"
shift = (-steps,-steps)
elseif direction == "random"
shift = Tuple(@rand([-steps,0,steps], 2))
else
@error "Invalid direction in @walk: "*direction
end
walk!(animal, model, shift)
end
"""
walk!(animal, model, direction, distance=-1)
Let the animal move in the given direction, where the direction is
defined by an (x, y) tuple to specify the shift in coordinates.
If maxdist >= 0, move no further than the specified distance.
"""
function walk!(animal::Animal, model::SimulationModel, direction::Tuple{Int64,Int64},
maxdist::Length=-1m)
#TODO test
distance = Int(floor(maxdist/@param(world.mapresolution)))
if distance >= 0
direction[1] > distance && (direction[1] = distance)
direction[2] > distance && (direction[2] = distance)
direction[1] < -distance && (direction[1] = -distance)
direction[2] < -distance && (direction[2] = -distance)
end
newpos = animal.pos .+ direction
move!(animal, model, newpos)
end
#TODO add a walk function with a habitat descriptor
##TODO add walktoward or similar function (incl. pathfinding?)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment