darkjaslo.dev

AI Coliseum 2025 ended

First time participating but wow was it fun! I got to win the Sprint Tournament and finished in 2nd place in the Final Tournament, which was very close too! Yes, it was decided by a literal coinflip, but the whole tournament was still very enjoyable.

My approach

I like having fun:

I only needed to copy this fast set that uses string operations, which are unfairly broken in terms of bytecode. I also got the idea to use a binary kernel for fast accessibility checks from here as I was looking for ways to hack the bytecode part. They really should give some pre-made custom instrumented structures.

I did everything else myself (and it's on GitHub too). Here's an adapted version of the repository's README if you find it interesting:

About the code

Some considerations:

Structure

The structure is really simple: Each crafter has a current list of objectives and a strategy switch condition. The main loop runs objectives from the list in order until one has been followed (returns true) and yields.

Here's an example:

Condition a = new HasExplored(8);
Condition b = new HasCraftable(Craftable.PICKAXE, 9);
condition = new OrCondition(b, a);

// Has a shovel (had to fight)
objectives = new Objective[8];
objectives[0] = new Heal();
objectives[1] = new BreakPeople();
objectives[2] = new BreakAgendas();
objectives[3] = new TycoonMentality();
objectives[4] = new DoubleGather(Material.STONE, 1, Material.WOOD , 2);
objectives[5] = new Craft(Craftable.BAKED_POTATO, 10);
objectives[6] = new GetThatGrass();
objectives[7] = new Explore();

This is the objective list and switch condition for when crafters have crafted just a shovel soon after spawning. When running the loop, it will:

  1. Try to heal. Done optimally and never yields
  2. Look for enemies and run micro if needed (if it does then it yields. Applies to all following objectives too)
  3. If no enemies, look for enemy structures and destroy any visible one.
  4. If no structures, take a quick look to see if a) a pickaxe can be crafted and b) there are a few mine resources in sight. If both conditions are true then it crafts a pickaxe.
  5. Get stone or wood if visible, but at most 1 stone and 2 wood.
  6. Get potatoes and/or craft baked potatoes.
  7. Explore the map. Never fails to run so if no other objectives can be accomplished this will always be run.

Yes, the objective names are extremely descriptive.

Now take a look at the condition:

Each condition also specifies a number that points to the next list of objectives+condition to switch to if true.

The condition here is the OR operation of those two, meaning that if HasExplored() is true then we go to whatever 8 means and if it gets a pickaxe instead we go to 9 (basically, in strategy 8 we explore but also get grass, and in strategy 9 we use the pickaxe).

The crafter works towards both these objectives either by getting materials for a potential pickaxe and looking at mine locations, or by just exploring. In some cases, the switch condition is just "Has this crafter been alive for more than 200 rounds?", for example.

This idea allows:

  1. Priorities
  2. Limiting allowed actions
  3. Easily rewiring the whole strategy depending on specific conditions while reusing a lot of code

Downsides?

If I had to say, in some cases it's better to run a few different checks first and decide the best option later. This model is designed for specific checks and actions in the same objective. However, it's actually compatible with other approaches if you separate certain objectives in two or three phases (it's just less comfortable).

It's also a bit redundant in some cases, but copying ~5 lines of objectives that are always there is OK to me.

Strategy

In the end, no communications were used. They definitely had potential but I didn't want to consider the cooldown issues.

There is a big reason why strategies for this game won't be very detailed.

Maps can be too different. For a given set of maps one knows what to expect, but the tournaments are always run on new maps. In this game specifically, the spawn points of units are random in practice and can be whatever.

There can be new maps where there's no contact ever between teams, and maps where you spawn next to an enemy and the game is over in 30 rounds. And preparing for them is not worth it because the other ~60 maps never meet these conditions. This is common here, and that's why a generalist adaptive strategy works better.

Now, a summary list of important stuff to go for to win.

Stuff you can do to improve your chances:

Stuff you can do to make the opponent's life miserable, thus improving your chances:

To have a chance at winning first I prioritize crafting beds, which means you need an axe early. All "Geneneration 1" crafters will be wired to do this. Important because if you don't you're not only playing with less crafters, you're also giving the opponent your side of the map's worth in axe materials.

Any crafter that spawns later than the first rounds gets a more adaptable strategy that collects basic materials for any of the three tools and only crafts one of them depending on what they find first. Then they are rewired to specialize in that tool. This ensures variety.

Usually, units that have crafted an axe or a shovel also try to make a pickaxe later for late game, but units with only a pickaxe don't craft any other tools.

It looks like there are some cases when crafting a shovel is suboptimal if you can craft something else. I've seen it's usually better because units with a shovel also refresh materials on the ground and I need some of them too. Of course I could test more specific conditions for optimal results (crafting a more expensive tool can remove a weight tie for instance).

A few specific things it does other than that, in no particular order

In this kind of competition, and I think that in this game in particular it applies even more, you are at the mercy of the maps. The devs did a pretty good job with this overall.

Testing

The idea is to run as many games as possible in all maps matching an older version and the newer one to see if the new version is better. Two limitations:

  1. Games take some time to run.
  2. They also write to disk no matter what (easily 10MB per game...). Terrible for SSD lifespan, I've already lost one not to this, but to something similar in the past.

So I used to run about 10 games per map for all maps (running some more when we only had like 19 maps) and check winrates. Overall it's difficult to make changes that improve winrate in almost all maps, usually it goes down in a few and goes up in like half of them.

What's next?

It's a really fun concept. I don't know if I'll be able to participate in either Battlecode 2026 or the next AI Coliseum, but I definitely want to. Battlecode is really competitive, let's see how I match up there...

Tags: ai_coliseum