<p><div><div><div><div data-width="100" data-show-faces="false" data-send="true" data-layout="button_count" data-height="21" data-href="https://www.r-bloggers.com/who-survives-riddler-nation/"></div></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="https://ntguardian.wordpress.com/2017/06/05/who-survives-riddler-nation/"> R – Curtis Miller's Personal Website</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<h2>Introduction</h2>
<p>Last week, I <a target="_blank" rel="nofollow" href="https://ntguardian.wordpress.com/2017/05/29/winning-the-battle-for-riddler-nation-an-agent-based-modelling-approach/">published an article</a> on learning to fight in the Battle for Riddler Nation. Here’s a refresher of the rules:</p>
<blockquote><p>
In a distant, war-torn land, there are 10 castles. There are two warlords: you and your archenemy. Each castle has its own strategic value for a would-be conqueror. Specifically, the castles are worth 1, 2, 3, …, 9, and 10 victory points. You and your enemy each have 100 soldiers to distribute, any way you like, to fight at any of the 10 castles. Whoever sends more soldiers to a given castle conquers that castle and wins its victory points. If you each send the same number of troops, you split the points. You don’t know what distribution of forces your enemy has chosen until the battles begin. Whoever wins the most points wins the war.
</p>
</blockquote>
<p><span></span></p>
<blockquote><p>
Submit a plan distributing your 100 soldiers among the 10 castles. Once I receive all your battle plans, I’ll adjudicate all the possible one-on-one match-ups. Whoever wins the most wars wins the battle royale and is crowned king or queen of Riddler Nation!
</p>
</blockquote>
<p>This battle took place in <a target="_blank" rel="nofollow" href="https://fivethirtyeight.com/features/can-you-rule-riddler-nation/">two</a> <a target="_blank" rel="nofollow" href="http://fivethirtyeight.com/features/the-battle-for-riddler-nation-round-2/">rounds</a>. In the first round, I submitted the solution <code>(1, 1, 1, 1, 1, 1, 23, 23, 24, 24)</code> using intuition, and did not do particularly well. In the second round, I used <a target="_blank" rel="nofollow" href="https://en.wikipedia.org/wiki/Agent-based_model">agent-based modelling</a> and an evolutionary model to get a solution. The strategy <code>(3, 3, 13, 2, 4, 1, 27, 30, 10, 7)</code>, performed best in the last round of the simulated 100-years war of Riddler Nation, so I submitted it to the contest.</p>
<p><a target="_blank" rel="nofollow" href="https://fivethirtyeight.com/features/how-much-should-you-bid-for-that-painting/">The results of the second round are now out</a>, and although Mr. Roeder enjoyed my article enough to give it a shout-out (sweet, my name is on <a target="_blank" rel="nofollow" href="https://fivethirtyeight.com/">FiveThirtyEight</a>, which is among my favorite news sites!), I did not win. Now, this is not shocking; with 931 participants, and game theory essentially reducing every game to either domination by one player or an optimal gamble (the latter being true here), the chances of my coming out on top are slim. With that in mind, I cannot be disappointed. In fact, I have some reasons to feel good; my strategy beats the top performers, who seemed to expect others would play the optimal strategy from the first round (Mr. Roeder’s statistics revealed a downward shift in troop allocations to lower-valued castles) and thus chose a strategy to beat it. Perhaps my strategy was ahead of its time? Who knows.</p>
<p>Mr. Roeder has <a target="_blank" rel="nofollow" href="https://github.com/fivethirtyeight/data/tree/master/riddler-castles">published the strategies his readers submitted for the second round</a>, and I will be rebuilding a strategy from that data using <strong>Mesa</strong> and agent-based modeling again, but we will be doing more.</p>
<p>First, let’s see how well my strategy did in the last round, because I want to know.</p>
<p>Second, I’m going to explore the idea that my method generates a mixture of strategies that reflects the game-theoretic optimal strategy. (See the conclusion of my previous post to see the definition of this strategy and its properties.) In some sense, this allows me to determine if I lost the last round because of bad luck or because other players were choosing superior strategies.</p>
<p>Third, I want to cluster the strategies so I can see what “species” of strategies people chose. I use the excellent R package <strong>mclust</strong> for this clustering.</p>
<p>Fourth and finally, I visualize the evolution of strategies as my simulation plays out, using <strong>ggplot2</strong>, dimensionality reduction, and <a target="_blank" rel="nofollow" href="https://github.com/dgrtwo/gganimate"><strong>gganimate</strong></a>.</p>
<h2>Results of the <a target="_blank" rel="nofollow" href="http://fivethirtyeight.com/features/the-battle-for-riddler-nation-round-2/">Last Battle for Riddler Nation</a></h2>
<p>The winning strategy of the last round, submitted by Vince Vatter, was <code>(0, 1, 2, 16, 21, 3, 2, 1, 32, 22)</code>, with an official record<sup><a target="_blank" rel="nofollow" href="https://ntguardian.wordpress.com/2017/06/05/who-survives-riddler-nation/#fn-2971-1">1</a></sup> of 751 wins, 175 losses, and 5 ties. Naturally, the top-performing strategies look similar. This should not be surprising; winning strategies exploit common vulnerabilities among submissions.</p>
<p>I’ve downloaded the <a target="_blank" rel="nofollow" href="https://github.com/fivethirtyeight/data/blob/master/riddler-castles/castle-solutions-2.csv">submitted strategies</a> for the second round (I already have the first round’s strategies). Lets load them in and start analyzing them.</p>
<pre>
import pandas as pd
from pandas import Series, DataFrame
import numpy as np
from numpy.random import poisson, uniform
import mesa, mesa.time, mesa.datacollection
import pymysql, sqlalchemy
import random
</pre>
<pre>
# Code from the last post
def gen_strat(obj):
    &quot;&quot;&quot;Generating a Series consisting of castle troop assignments

    args:
        obj: An iterable (NumPy array, Series, list, tuple, etc.) containing troop assignment

    return:
        Series; The generated strategy
    &quot;&quot;&quot;

    srs = Series(obj).apply(int)
    if (len(srs) != 10) or (srs.sum() != 100) or ((srs &lt; 0).values.any()):
        raise ValueError(&quot;A valid strategy consists of ten integers between 0 and 100 that sum to 100.&quot;)
    srs.index = pd.Index(np.arange(10) + 1)

    return srs

def check_strat(strat):
    &quot;&quot;&quot;Checks that strat is a valid strategy, like that created by gen_strat

    args:
        strat: Series; An object to check

    return:
        bool; True if valid
    &quot;&quot;&quot;

    if str(type(strat)) == &quot;</pre>