In [2]:
using Bokeh
setupnotebook()


BokehJS successfully loaded.


In [3]:
abstract Animal

midrand() = rand() - 0.5
prednames = [:Tiger, :Lion, :Eagle]
type Preditor <: Animal
    breed::Symbol
    alive::Bool
    x::Float64; y::Float64
    heading::Float64; speed::Float64
    route::Vector{(Float64, Float64)}
    eaten::Int
    Preditor(breed::Symbol) = new(breed, true, 5midrand(), 5midrand(), 
        rand()*2pi, 0, Array((Float64, Float64), 0), 0)
end
function Preditor()
    Preditor(prednames[rand(1:length(prednames))])
end

preynames = [:Goat, :Sheep, :Horse, :Deer, :Chicken]
type Prey <: Animal
    breed::Symbol
    alive::Bool
    x::Float64; y::Float64
    heading::Float64; speed::Float64
    route::Vector{(Float64, Float64)}
    Prey(breed::Symbol) = new(breed, true, 5midrand(), 5midrand(),
                    rand()*2pi, 0, Array((Float64, Float64), 0))
end
function Prey()
    Prey(preynames[rand(1:length(preynames))])
end

function Base.show(io::IO, an::Animal)
    coords = @sprintf "%0.3f, %0.3f" an.x an.y
    print(io, "$(an.breed) ($coords)")
end

type Meeting
    event::Symbol
    x::Float64
    y::Float64
end

In [4]:
const area = 6
function move!(an::Animal)
    an.heading += midrand()
    an.speed += 0.01 + 0.001midrand()
    an.x += an.speed * sin(an.heading)
    an.y += an.speed * cos(an.heading)
    push!(an.route, (an.x, an.y))
    if abs(an.x) > area || abs(an.y) > area
        an.speed *= 0.1
    end
end

function move!(animals::Array{Animal, 1})
    for animal in animals
        move!(animal)
    end
end

distance(a1::Animal, a2::Animal) = sqrt((a1.x-a2.x)^2 + (a1.y-a2.y)^2)
const meetdist = 0.3
function meet!(animals::Array{Animal, 1}, meetings::Array{Meeting, 1})
    newanimals = Animal[]
    for (i, a1) in enumerate(animals)
        !a1.alive && continue
        for a2 in animals[(i+1):end]
            !a2.alive && continue
            distance(a1, a2) > meetdist && continue
            if a1.breed == a2.breed && length(animals) < 1000 && rand() > 0.9
                println("$a1 breeds with $a2")
                isa(a1, Preditor) ? push!(newanimals, Preditor(a1.breed)) : 
                                    push!(newanimals, Prey(a1.breed))
                push!(meetings, Meeting(:breed, a1.x, a1.y))
            elseif isa(a1, Preditor) && isa(a2, Prey)
                println("$a1 eats $a2")
                a2.alive = false
                a1.eaten += 1
                push!(meetings, Meeting(:kill, a2.x, a2.y))
            elseif isa(a1, Prey) && isa(a2, Preditor)
                println("$a2 eats $a1")
                a1.alive = false
                a2.eaten += 1
                push!(meetings, Meeting(:kill, a1.x, a1.y))
            end
        end
    end
#     filter!(p -> p.alive, animals)
    append!(animals, newanimals)
end

animals = Animal[Preditor() for _ in 1:4]
meetings = Meeting[]
append!(animals, [Prey() for _ in 1:40])
steps = 200
println("starting number of animals:", length(animals))
for step in 1:steps
    move!(animals)
    meet!(animals, meetings)
end
println("final number of animals:", length(animals))


starting number of animals:44
Tiger (0.916, -1.510) eats Sheep (0.676, -1.566)
Tiger (0.916, -1.510) eats Chicken (0.779, -1.628)
Tiger (1.040, -1.359) eats Sheep (1.264, -1.402)
Eagle (-0.096, 0.310) eats Horse (-0.060, 0.516)
Tiger (1.052, -0.902) eats Chicken (0.975, -1.096)
Eagle (0.335, 1.115) eats Goat (0.052, 1.195)
Tiger (1.186, -1.357) eats Horse (1.127, -1.538)
Tiger (1.186, -1.357) eats Goat (1.054, -1.537)
Eagle (1.777, 3.883) eats Goat (1.621, 4.110)
Tiger (-0.408, 4.248) eats Sheep (-0.679, 4.217)
Tiger (-2.901, -2.873) eats Chicken (-2.959, -2.909)
Tiger (6.226, -1.172) eats Horse (6.089, -1.274)
Tiger (-6.404, -1.606) eats Deer (-6.409, -1.725)
Tiger (-6.249, 4.385) eats Chicken (-6.015, 4.490)
Tiger (6.406, -1.115) eats Goat (6.214, -1.118)
Tiger (-6.741, 4.079) eats Horse (-6.699, 3.794)
Tiger (-7.103, -1.317) eats Goat (-6.824, -1.235)
final number of animals:44

In [5]:
xs = zeros(steps, length(animals))
ys = zeros(steps, length(animals))
for (i, p) in enumerate(animals)
    length(p.route) == 0 && continue
    # (steps - length(p.route) + 1)
    xs[:, i] = p.route[end][1]
    ys[:, i] = p.route[end][2]
    xs[1:length(p.route), i] = Float64[r[1] for r in p.route]
    ys[1:length(p.route), i] = Float64[r[2] for r in p.route]
end
hold(false)
plot(xs, ys, width=1000, height=800)
hold(true)
killx = Float64[m.x for m in filter(m->m.event == :kill, meetings)]
killy = Float64[m.y for m in filter(m->m.event == :kill, meetings)]
plot(killx, killy, "ok")
breedx = Float64[m.x for m in filter(m->m.event == :breed, meetings)]
breedy = Float64[m.y for m in filter(m->m.event == :breed, meetings)]
plot(breedx, breedy, "ob")


Out[5]:

Plot("Bokeh Plot" with 46 datacolumns)