Hello CodinGamers, @logiqub here, finished 60th legend, using Python only.
Intro
In this contest, I was top 100 from start to end, hovering near 50, but around
15 in the first days. Unluckily, I never tried to read the referee code,
because I kept coming up with ideas, watching the games, coding, evaluating and
deciding whether to keep new features or not. The last two days, after legend
opened, I was stuck against the wall of players who knew how to adjust weights
to raise their population quickly. Couldn’t stay in the top 50 because of that.
Snowballing
What must be understood about this game is that it snowballs extremely fast.
Missing one ant in the first 5 turns can mean defeat with little hope of
recovery. Because I am still bad at coding, I used obsveration and tricks to
keep up with everyone else. And I am glad that the game’s design allowed for
intuitive types like myself, to get that far with heuristics alone.
Overall strategy
Here is how this game works (I think):
- PHASE 0: Collect eggs only, about 50%. PHASE 1: Rush the center for
- contested crystals, but keep collecting eggs. PHASE 2: Once there are no
- more contested crystals, fall back and take
all resources closer to your bases.
- PHASE 3: There should not be a phase 3, but as a safety measure, collect
anything that is still on the map.
For this to work you need to triage resource nodes by type and sort them by
distance to your bases.
- Near resources (eggs and crystals): distance to your base <= 1.5 times
distance to the opponent base. If you target every resource, you will
overextend, form weak chains, have low income and get blocked. Plus, it is
not necessary to win (read below).
- Constested resources (eggs and crystals): difference of the distance of that
cell to your base and the opponent base <= 1. The key is that to win the game
you only need half the crystals or more. Therefore, rushing the center is
paramount to rob the opponent of any hope of victory (Sun Tzu). If you take
more than half the center crystals, even if you have less ants, it will be
impossible for the opponent to win because, as you fall back, your chains get
stronger and theirs get weaker.
Pathing
And to get the distances, you can compute everything at the start, since you
have 1000 milliseconds of initialization for the first turn. There has to be a
smarter way of doing this, but I just wrote a flood fill algorithm and repeated
it on every square, putting the results in a map.
To build chains, I put beaons starting from the resource node trying to join
with another beacon already set or a base. This way, I guarantee minimal
routes, but I also take into account the existence of other resources on the
way back. That way, I maximize income. And finally after every beacon is set,
I remove the redundant ones (remove and check if everything is still connected)
to maximize the strength of chains. And by the way, unless it is the first
chain (some maps have the closest egg 5 cells away), all chains must have
strength >= 2, unless there is good reason to do otherwise (like contesting the
center when there is a single crystal).
Going on the offense ?!
That is it. What should be done to improve this strategy is to include chain
strength calculation to seek and cross opponent chains that are weaker than
yours. Should be possible, even if you have a lower ant count, and you could
paralyze the opponent by gonig through their base, checkmating them.
There are also times where one or two crystals are on the map, but those
situations occur only in 1-3% of games. I spent hours optimizing for that case
but ended up removing that code, because this percentage is too low.
Benchmarking
And this is the crucial ingredient that makes everything work. Fine tuning
parameters and features. I keep versioned source files, which I set to readonly
with a version number. It’s far easier than dealing with git or mercurial.
I wrote my own tester in the previous contest where I finished 176th. And
pretty much used the same code without modifications. The only new thing I did
was using the game log to filter which phases where activated (thanks to the
message you can write). That is how I knew maps with one or two crystals
represent only 1 to 3% of games, and I should never have spent time optimizing
for it.
Accuracy begins with samples of 150 games, and are usually good enough above
500. Because my code runs in less than a millisecond, I could run a thousand
games in about 3 minutes, using 3 cores. After each run, against all previous
versions, if the improvement is above 5%, I can consider keeping the change.