Skip to content

Commit 1310cc9

Browse files
committed
2 parents a2da6ae + 30e1c61 commit 1310cc9

File tree

2 files changed

+148
-141
lines changed

2 files changed

+148
-141
lines changed

docs/src/lecture_02/hw.md

Lines changed: 20 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,147 +1,23 @@
11
# Homework 2: Predator-Prey Agents
22

33
In this lab you will continue working on your agent simulation. If you did not
4-
manage to finish the homework, do not worry, you can use the code below which
5-
contains all the functionality we developed in the lab.
6-
7-
```@raw html
8-
<details class = "solution-body">
9-
<summary class = "solution-header">Solution of Lab 2:</summary><p>
4+
manage to finish the homework, do not worry, you can use [this
5+
script](https://github.com/JuliaTeachingCTU/Scientific-Programming-in-Julia/blob/master/src/Lab02Ecosystem.jl)
6+
which contains all the functionality we developed in the lab.
7+
```@setup hw02
8+
projdir = dirname(Base.active_project())
9+
include(joinpath(projdir,"..","src","Lab02Ecosystem.jl"))
1010
```
11-
```@example hw02
12-
using StatsBase
1311

14-
abstract type Agent end
15-
abstract type Animal <: Agent end
16-
abstract type Plant <: Agent end
12+
## How to submit?
1713

18-
mutable struct World{A<:Agent}
19-
agents::Dict{Int,A}
20-
max_id::Int
21-
end
22-
function World(agents::Vector{<:Agent})
23-
World(Dict(id(a)=>a for a in agents), maximum(id.(agents)))
24-
end
14+
Put all your code (including your or the provided solution of lab 2) in a
15+
script named `hw.jl` alongside with the `Project.toml` and `Manifest.toml` of
16+
the environment. Please only include packages in the environment that are
17+
necessary for the homework. Create a `.zip` archive of the three files and
18+
send it to the lab instructor, who has assigned the task, via email (contact
19+
emails are located on the [homepage](@ref emails) of the course).
2520

26-
# optional: you can overload the `show` method to get custom
27-
# printing of your World
28-
function Base.show(io::IO, w::World)
29-
println(io, typeof(w))
30-
for (_,a) in w.agents
31-
println(io," $a")
32-
end
33-
end
34-
35-
function world_step!(world::World)
36-
# make sure that we only iterate over IDs that already exist in the
37-
# current timestep this lets us safely add agents
38-
ids = deepcopy(keys(world.agents))
39-
40-
for id in ids
41-
# agents can be killed by other agents, so make sure that we are
42-
# not stepping dead agents forward
43-
!haskey(world.agents,id) && continue
44-
45-
a = world.agents[id]
46-
agent_step!(a,world)
47-
end
48-
end
49-
50-
function agent_step!(a::Plant, w::World)
51-
if size(a) != max_size(a)
52-
grow!(a)
53-
end
54-
end
55-
56-
function agent_step!(a::Animal, w::World)
57-
incr_energy!(a,-1)
58-
if rand() <= foodprob(a)
59-
dinner = find_food(a,w)
60-
eat!(a, dinner, w)
61-
end
62-
if energy(a) <= 0
63-
kill_agent!(a,w)
64-
return
65-
end
66-
if rand() <= reprprob(a)
67-
reproduce!(a,w)
68-
end
69-
return a
70-
end
71-
72-
mutable struct Grass <: Plant
73-
id::Int
74-
size::Int
75-
max_size::Int
76-
end
77-
78-
mutable struct Sheep <: Animal
79-
id::Int
80-
energy::Float64
81-
Δenergy::Float64
82-
reprprob::Float64
83-
foodprob::Float64
84-
end
85-
86-
mutable struct Wolf <: Animal
87-
id::Int
88-
energy::Float64
89-
Δenergy::Float64
90-
reprprob::Float64
91-
foodprob::Float64
92-
end
93-
94-
id(a::Agent) = a.id # every agent has an ID so we can just define id for Agent here
95-
96-
Base.size(a::Plant) = a.size
97-
max_size(a::Plant) = a.max_size
98-
grow!(a::Plant) = a.size += 1
99-
100-
# get field values
101-
energy(a::Animal) = a.energy
102-
Δenergy(a::Animal) = a.Δenergy
103-
reprprob(a::Animal) = a.reprprob
104-
foodprob(a::Animal) = a.foodprob
105-
106-
# set field values
107-
energy!(a::Animal, e) = a.energy = e
108-
incr_energy!(a::Animal, Δe) = energy!(a, energy(a)+Δe)
109-
110-
function eat!(a::Sheep, b::Grass, w::World)
111-
incr_energy!(a, size(b)*Δenergy(a))
112-
kill_agent!(b,w)
113-
end
114-
function eat!(wolf::Wolf, sheep::Sheep, w::World)
115-
incr_energy!(wolf, energy(sheep)*Δenergy(wolf))
116-
kill_agent!(sheep,w)
117-
end
118-
eat!(a::Animal,b::Nothing,w::World) = nothing
119-
120-
kill_agent!(a::Plant, w::World) = a.size = 0
121-
kill_agent!(a::Animal, w::World) = delete!(w.agents, id(a))
122-
123-
function find_food(a::Animal, w::World)
124-
as = filter(x->eats(a,x), w.agents |> values |> collect)
125-
isempty(as) ? nothing : sample(as)
126-
end
127-
128-
eats(::Sheep,::Grass) = true
129-
eats(::Wolf,::Sheep) = true
130-
eats(::Agent,::Agent) = false
131-
132-
function reproduce!(a::A, w::World) where A<:Animal
133-
energy!(a, energy(a)/2)
134-
a_vals = [getproperty(a,n) for n in fieldnames(A) if n!=:id]
135-
new_id = w.max_id + 1
136-
â = A(new_id, a_vals...)
137-
w.agents[id(â)] = â
138-
w.max_id = new_id
139-
end
140-
nothing # hide
141-
```
142-
```@raw html
143-
</p></details>
144-
```
14521

14622
## Counting Agents
14723

@@ -153,7 +29,7 @@ quantity.
15329

15430
```@raw html
15531
<div class="admonition is-category-homework">
156-
<header class="admonition-header">Homework (2 points)</header>
32+
<header class="admonition-header">Compulsory Homework (2 points)</header>
15733
<div class="admonition-body">
15834
```
15935
1. Implement a function `agent_count` that can be called on a single
@@ -187,12 +63,15 @@ end
18763

18864
```@repl hw02
18965
grass1 = Grass(1,5,5);
66+
agent_count(grass1)
67+
19068
grass2 = Grass(2,1,5);
69+
agent_count([grass1,grass2]) # one grass is fully grown; the other only 20% => 1.2
70+
19171
sheep = Sheep(3,10.0,5.0,1.0,1.0);
19272
wolf = Wolf(4,20.0,10.0,1.0,1.0);
19373
world = World([grass1, grass2, sheep, wolf]);
194-
195-
agent_count(world) # one grass is fully grown; the other only 20% => 1.2
74+
agent_count(world)
19675
```
19776

19877
Hint: You can get the *name* of a type by using the `nameof` function:
@@ -208,7 +87,7 @@ Use as much dispatch as you can! ;)
20887

20988
```@raw html
21089
<div class="admonition is-category-exercise">
211-
<header class="admonition-header">Exercise (voluntary)</header>
90+
<header class="admonition-header">Voluntary Exercise (voluntary)</header>
21291
<div class="admonition-body">
21392
```
21493
Using the world below, run few `world_step!`s. Plot trajectories of the agents

src/Lab02Ecosystem.jl

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
using StatsBase
2+
3+
abstract type Agent end
4+
abstract type Animal <: Agent end
5+
abstract type Plant <: Agent end
6+
7+
mutable struct World{A<:Agent}
8+
agents::Dict{Int,A}
9+
max_id::Int
10+
end
11+
function World(agents::Vector{<:Agent})
12+
World(Dict(id(a)=>a for a in agents), maximum(id.(agents)))
13+
end
14+
15+
# optional: you can overload the `show` method to get custom
16+
# printing of your World
17+
function Base.show(io::IO, w::World)
18+
println(io, typeof(w))
19+
for (_,a) in w.agents
20+
println(io," $a")
21+
end
22+
end
23+
24+
function world_step!(world::World)
25+
# make sure that we only iterate over IDs that already exist in the
26+
# current timestep this lets us safely add agents
27+
ids = deepcopy(keys(world.agents))
28+
29+
for id in ids
30+
# agents can be killed by other agents, so make sure that we are
31+
# not stepping dead agents forward
32+
!haskey(world.agents,id) && continue
33+
34+
a = world.agents[id]
35+
agent_step!(a,world)
36+
end
37+
end
38+
39+
function agent_step!(a::Plant, w::World)
40+
if size(a) != max_size(a)
41+
grow!(a)
42+
end
43+
end
44+
45+
function agent_step!(a::Animal, w::World)
46+
incr_energy!(a,-1)
47+
if rand() <= food_prob(a)
48+
dinner = find_food(a,w)
49+
eat!(a, dinner, w)
50+
end
51+
if energy(a) <= 0
52+
kill_agent!(a,w)
53+
return
54+
end
55+
if rand() <= reproduction_prob(a)
56+
reproduce!(a,w)
57+
end
58+
return a
59+
end
60+
61+
mutable struct Grass <: Plant
62+
id::Int
63+
size::Int
64+
max_size::Int
65+
end
66+
67+
mutable struct Sheep <: Animal
68+
id::Int
69+
energy::Float64
70+
Δenergy::Float64
71+
reproduction_prob::Float64
72+
food_prob::Float64
73+
end
74+
75+
mutable struct Wolf <: Animal
76+
id::Int
77+
energy::Float64
78+
Δenergy::Float64
79+
reproduction_prob::Float64
80+
food_prob::Float64
81+
end
82+
83+
id(a::Agent) = a.id # every agent has an ID so we can just define id for Agent here
84+
85+
Base.size(a::Plant) = a.size
86+
max_size(a::Plant) = a.max_size
87+
grow!(a::Plant) = a.size += 1
88+
89+
# get field values
90+
energy(a::Animal) = a.energy
91+
Δenergy(a::Animal) = a.Δenergy
92+
reproduction_prob(a::Animal) = a.reproduction_prob
93+
food_prob(a::Animal) = a.food_prob
94+
95+
# set field values
96+
energy!(a::Animal, e) = a.energy = e
97+
incr_energy!(a::Animal, Δe) = energy!(a, energy(a)+Δe)
98+
99+
function eat!(a::Sheep, b::Grass, w::World)
100+
incr_energy!(a, size(b)*Δenergy(a))
101+
kill_agent!(b,w)
102+
end
103+
function eat!(wolf::Wolf, sheep::Sheep, w::World)
104+
incr_energy!(wolf, energy(sheep)*Δenergy(wolf))
105+
kill_agent!(sheep,w)
106+
end
107+
eat!(a::Animal,b::Nothing,w::World) = nothing
108+
109+
kill_agent!(a::Plant, w::World) = a.size = 0
110+
kill_agent!(a::Animal, w::World) = delete!(w.agents, id(a))
111+
112+
function find_food(a::Animal, w::World)
113+
as = filter(x->eats(a,x), w.agents |> values |> collect)
114+
isempty(as) ? nothing : sample(as)
115+
end
116+
117+
eats(::Sheep,::Grass) = true
118+
eats(::Wolf,::Sheep) = true
119+
eats(::Agent,::Agent) = false
120+
121+
function reproduce!(a::A, w::World) where A<:Animal
122+
energy!(a, energy(a)/2)
123+
a_vals = [getproperty(a,n) for n in fieldnames(A) if n!=:id]
124+
new_id = w.max_id + 1
125+
= A(new_id, a_vals...)
126+
w.agents[id(â)] =
127+
w.max_id = new_id
128+
end

0 commit comments

Comments
 (0)