Hawk-Dove game in Julia

Years ago, I would took a bunch of CS courses with C++ as the language of interest. One of the main projects I worked on completely outside of any course was an implementation of the Hawk-Dove game. I recently tried to find the files from this, and came up empty-handed. Instead of getting bummed that I lost all that work, I’m taking it as an opportunity to get my feet wet with Julia.

The Hawk-Dove Game

There’s a population of birds. A bird has two features: a strategy and a pool of points. These birds compete with each other for resources (more points). This competition takes the form of duels. A duel is just a comparison of strategies. The two strategies available are called “Hawk” and “Dove”. If two hawks meet, they duke it out. Both get some of the points, but they also lose some from the fight. If two doves meet, they share the points. If a hawk meets a dove, the dove bails, and the hawk gets all the points.

I’m being intentionally vague in these descriptions because there’s naturally a lot of wiggle room to interpret these things and make up different specific point assignments. These choices result in qualitatively different dynamics.

Implementation

When I was doing this stuff in C++, I spent a lot of my time tweaking my linked list class, and the other custom classes I made to handle the bird populations. I just didn’t have to do any of that in Julia. The type flexibility and the robustness of vectors meant that I could focus mostly on just thinking about the game, not the infrastructure.

mutable struct Bird 
    health 
    strategy
end

The word “mutable” is here because I want to be able to adjust a bird’s health. It’s strange to me coming from C++ that I’m basically only saying “a bird is two things”, and that’s it. I don’t even tell you the types of the two things.

the next block is my dueling function. It picks two random birds, compares their strategies, and adjusts the points accordingly. The current values of these point changes is not as carefully thought out as in my old C++ version. I’ll explain more later. After that, it checks both birds’ health to see if they should die (health<1) or if they should duplicate (health >199).

function fight(p::Vector{Bird})
    n = length(p)
    if n < 2
        println("Nobody to fight.")
    else
        fighter = sample(1:n, 2, replace = false)
        if p[fighter[1]].strategy == "Hawk"
            if p[fighter[2]].strategy == "Hawk"
                # violent fight
                p[fighter[1]].health -=50
                p[fighter[2]].health -=50

            elseif p[fighter[2]].strategy == "Dove"
                p[fighter[1]].health +=20 # gets the food no problem
                # Dove loses/gains nothing
            else
                println("Someone has a rogue strategy.")
            end
        elseif p[fighter[1]].strategy == "Dove"
            if p[fighter[2]].strategy == "Hawk"
                # Dove loses/gains nothing
                p[fighter[2]].health +=20 # gets the food no problem
            elseif p[fighter[2]].strategy == "Dove"
                p[fighter[1]].health +=10 #share the food
                p[fighter[2]].health +=10 #share the food
            else
                println("Someone has a rogue strategy.")
            end
        else
            println("Someone has a rogue strategy.")
        end

        #birth/death checks
        for i ∈ 1:2
            if p[fighter[i]].health < 1
                splice!(p, fighter[i])
            end
            if p[fighter[i]].health > 199
                push!(p, Bird[100, p[fighter[i]].strategy])
                p[fighter[i]].health -=100
            end
        end
    end
end

Maybe I just didn’t know as much as I should have about the tools available to me in C++, but one of the things I’m most jazzed about is how easy it was to have Julia draw pictures for me. The following function runs “fight” as many times as you like, collects the change in total points within each population for each fight, and then plots these over time (“time” being measured in duels).

function BigFight(p::Vector{Bird}, n::Int)
    population = [] # initialize the time series for pairs of population counts
    for i ∈ 1:n #fill population array with the time series data
        fight(p)
        push!(population, CountBird(p))
    end
    # use array comprehension to extract the time series for each population
    hawks = [birds[1] for birds ∈ population]
    doves = [birds[2] for birds ∈ population]
    gr() # backend for plotting
    plot(1:n, hawks, label = "Hawks")
    plot!(1:n, doves, label = "Doves")
    # first one is plot to create the plot, 
    # second is plot! to mutate the first plot
end

Here’s an example plot of total points in each population against number of duels:

What’s next

It’s easy to see that in the plot above that dove is the better strategy. This is completely a result of my choice of point assignments in duels. In my old C++ implementation, I had found some values that caused the ratio of the population sizes to approach an equilibrium over time. I don’t remember specifically what values I had, but I think part of it was having two doves lose points when they meet. I seem to remember justifying this to myself by saying that they would freeze at the sight of each other and thus lose a small amount of points by wasting time. I could read more literature about the game, but I’ll probably just fiddle with the numbers.

I’m also curious about how I could generalize the scenario. I’d like a setup where there’s an arbitrary number of strategies. But I’m not sure how I’d specify each interaction type without either doing it by hand, or by specifying a general function based on a fixed set of variables. They both sound boring.

I’m having a Julia problem I don’t understand completely. As it’s currently written, when I first run “BigFight”, it works fine. The second time, it complains that I’m asking it to convert an Int64 to a Bird. I don’t know why this would change like this. Any suggestions would be appreciated.

Published by Joe Moeller

Mathematician

Leave a comment