Spring Challenge 2023 - Feedbacks & Strategies

#49 Legend

My bot was a simple heuristic one with the most complex part being optimizing the beacon placement.

  1. Determine target cells based on map type (surplus, famine) and various factors such as ratio of distances to my/opponent closest base, % of eggs gathered, % of crystals gathered. Generally the goal is to go for eggs first, then contested crystals, then safe crystals. I tried to use sim/eval to determine the target cells, but didn’t manage to make it work better than the heuristics in the limited time I had.

  2. Build an optimal path connecting the selected target cells. My approach was to use a greedy nearest neighbor algo, where new paths are added based on shortest distance between any cell already in the tree and any not yet included target cell. I stopped adding new paths if my ants would be spread too thin. This algo does not guarantee to find the shortest possible path between target cells, but it seemed to work well enough in practice.

  3. Optimize beacon placement by reimplementing the ant allocator from the referee and using hill climbing to find a set of beacons that produce the desired ant movements. My implementation was buggy though.

I really liked this challenge except for the need to trick the suboptimally implemented ant allocator into making the desired ant moves. Thanks to CodinGame for organizing.

3 Likes

52nd position Legends

Initial AI: Pathfinding
My initial AI utilizes a BFS-based pathfinding algorithm to locate the best available resources on the map.
I started by path closest to the bases,
I continued to search for resources closest to the previously discovered paths, and so on.
I stopped this search when I ran out of ants to fill the paths.
I used one marker by cell (cannot find better …))

Inclusion of KPIs :
I defined several important KPIs:
-Path density factor: number of ants per marker.
-Path density factor with enemy presence: number of ants per marker when the enemy is present. This allows me to react more or less to the enemy’s presence. The path with the enemy is more costly, so it is naturally ignored if I don’t have enough ants.
-Exclusive egg collection time: I collect eggs until a certain limit of available power on the map, which is effective in resource-rich maps.
-Exclusive power time collection: I collect power since a certain limit of available power on the map

Writing the Game Simulation C :
I started the complete re-coding of the game to achieve a perfect and relatively fast simulation.
This step takes me a lot of time, particularly to accurately reproduce ant movements based on markers and perform score calculations.
It also allowed me to gain a better overall understanding of the game.

Local Testing:
To verify the proper functioning of the simulation in all situations, I wrote tests and ran the game locally , playing against myself to check conformity to CG original code in java.
These tests validated the accuracy of the simulation and helped address any potential issues.

Performance of My Game Simulation:
My algorithm is capable of executing around 60 full game simulations (60 x 100 cycles) on the most complex maps, with two AIs.
On simpler maps it can handle even more simulations.

My Final Algorithm:
I run multiple complete matches of my AI against my AI.
I test a combination of KPIs for the first AI with my current state.
The second AI has fixed KPIs and works based on the last known opponent state of ants position/scores.
After the simulated battles, I select actions corresponding to the KPIs that generated the highest score.

Conclusion:
Full game coding for efficient simulation, rigorous local testing, simulated battles using two IA (variable and fixed KPIs), KPI-based pathfinding.

Thanks CG :heart:

6 Likes

Great post and great solution. I just wonder how did you come up with the scoring function?

As with many other contests I started with heuristics to have a better understanding of the game. But this time I was not able to field anything special for quite a few days, the games seemed hectic. So inspite of my initial uncertainty in the complexity and assumed slowness of the allocation code I decided to add simulation. I had immediately jumped to top3 and remained in top10 till the end. I was surprised that first version of search worked so well, but later I realized that it was not really possible to seriously improve it. Most improvements came from new/improved heuristics, but even there only 1 out of 10 ideas worked, the game is just complex. Local testing was a must, but not enough, as variance is huge. Congratz to winners, and thanks for great challenge!

As many others I have combined target choice through heuristics and search with actual simulation for a few turns.
Heuristics worth mentioning:

  • simplified prediction of the remaining number of turns till the game is decided on score
  • two modes: “need” egg collection and cr-collection mode (partly based on previous point)
  • “keep” the trace to existing targets and choose some new targets
  • target choice depends on lots of factors, mode, distance to current paths, distance to enemy, total resource, impossible tp reach, etc.

Search features:

  • SA-like search
  • number of turns depends on length of new paths (1-4)
  • single beacon setup for multiple turns (no succeess with having more)
  • only minimal opponent predict (just a light adjust when it seems to have a clear target, everything else failed)
  • eval takes into enemy ant/score and also bonus for ant/score gained from “contested” crystal/eggs
4 Likes

59 Legend

Wood 2 to Wood 1 => Used LINE command to all crystals from my base with 1 strength

Wood 1 to Bronze => Tried prioritising cells with most crystals didn’t get good results. Then I noticed what the boss was printing… He was simply LINE command with 1 strength to all the crystals except in the first 5 turns where boss was using LINE commands to all the eggs… So I just mimic him and got promoted.

Bronze to Silver => I assign each resource to the base I own that’s closest to this resource and solve the problem for each base. I chose half of the resource cells for this base (closest from my base). I find a Minimum Spanning Tree with these cells and do a BEACON command with 1 strength to this tree.

Silver to Gold => Lot of bugfixes by finetuning against replays that I lose by huge margin. Improved the MST slightly by prioritising to remove the farther nodes without any resources first so that I end up with a good tree with good no. of resources. I included a new constant called eggTurns that’s proportional to the number of initial crystals in map… Until eggTurns I prefer eggs over crystals. I still give 1 strength to all my BEACON targets.

Gold to Legend Strength allocation by mutating the strengths of my beacons starting from the solution with average ants as the strength at each beacon target. I had to copy the ant allocation part from the referee to compute the ants in next turn for my strength allocation. This is how I scored a game state.

long getScore() {
    long bad = 0;
    for (int i = 0; i < inPath.size(); i++) {
        long diff = cells[inPath[i]].my_ants_next - avg_per_cell;
        bad += diff * diff;
    }
    return -bad;
}

Legend bottom to mid legend: I changed the scoring function to this

long getScore() {
    long bad = 0;
    int minAnts = INF;
    for (int i = 0; i < inPath.size(); i++) {
        long diff = cells[inPath[i]].my_ants_next - avg_per_cell;
        bad += diff * diff;
        minAnts = min(minAnts, cells[inPath[i]].my_ants_next);
    }
    //minimum in this inPath should be least..
    bad -= 1000 - minAnts;
    return -bad;
}

Overall I’ve enjoyed this contest. Guess I’ve become Dr. Pym from AntMan now as I can control multiple ants at the same time :grin: Thanks to the problem setters, testers and system admins who gave a smooth contest.

6 Likes

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.

3 Likes

(Google translation below)

259ème global, 164ème/400 en gold, heuristiques.

D’abord merci à l’équipe CG pour ce challenge très intéressant. La difficulté à simuler a permis de laisser plus de chance aux personnes utilisant la logique et les heuristiques. L’épreuve s’est très bien déroulée techniquement, les délais des arènes étaient très corrects.

Pour commencer j’ai décidé de passer bronze rapidement pour éviter les embouteillages, j’ai donc soumis en 10 minutes un code qui sortait des LINE entre ma base et chaque ressource présente. Passé bronze en une demi-heure (2ème/200 après 35 minutes, puis-je avoir l’achievement correspondant ? :grin: )

Je passe les détails sur l’évolution de mon code, j’ai juste appliqué plus que d’habitude le principe du « keep it simple ». Voici le fonctionnement de mon code actuel, très facile à comprendre et à coder :

  • attribution à chaque case d’un score dépendant de la ressource présente sur cette case et celle de ses voisins directs (0 si rien, valeur plus élevée pour des œufs que pour des cristaux).

  • Calcul des distances et des chemins entre chaque case intéressante (base ou ressource) et la totalité des autres cases, avec un algo flood fill mais basé sur un dijkstra (grâce au score précédemment calculé) plutôt qu’un BFS. Cela pour accepter un léger détour si ça permet d’avoir plus de ressources.

  • Pour chaque ressources, repérer la base qui en est la plus proche.

  • Tri des ressources en fonction de leur distance avec la base qui en est la plus proche, avec un biais pour favoriser les œufs par rapport aux cristaux.

  • Et enfin calcul des BEACON à placer, pour cela une boucle sur chaque ressource (dans l’ordre précédemment trié) et tant que j’ai assez de fourmi (au moins 2 par cases) :

    • cherche quelle case contenant un BEACON (ou une base) est la plus proche de cette ressource

    • récupère le chemin entre cette case et cette ressource, place un BEACON sur chaque case du chemin.

Petite anecdote, j’ai appris que les LINE fonctionnaient mieux quand le 1er index est celui de la destination. J’ai simulé ça avec mes BEACON en les triant juste avant l’output, du plus éloigné de ma base au plus proche. Le résultat s’est effectivement fait sentir, sûrement pas autant qu’une simulation, mais faute de lire le code source du jeu ce critère s’est avéré avoir le bon rapport efficacité / simplicité.


259th overall, 164th/400 in gold, heuristics.

First thank you to the CG team for this very interesting challenge. The difficulty in simulating has given more chance to people using logic and heuristics. The event went very well technically, the time spent in arenas were very correct.

To start, I decided to go bronze quickly to avoid traffic jams, so in 10 minutes I submitted a code that printed LINEs between my base and each resource. Passed bronze in half an hour (2nd/200 after 35 minutes, can I have the corresponding achievement? :grin: )

I skip the details on the evolution of my code, I just applied more than usual the principle of “keep it simple”. Here is how my current code works, very easy to understand and to code:

  • attribution to each cell of a score depending on the resource present on this cell and that of its direct neighbors (0 if nothing, higher value for eggs than for crystals).

  • Calculation of distances and paths between each interesting cell (base or resource) and all the other cells, with a flood fill algo but based on a dijkstra (thanks to the previously calculated score) rather than a BFS. This to accept a slight detour if it allows to have more resources.

  • For each resource, locate the base that is closest to it.

  • Sorting of resources according to their distance from the nearest base, with a bias to favor eggs over crystals.

  • And finally calculation of the BEACON to place, for that a loop on each resource (in the order previously sorted) and as long as I have enough ants (at least 2 per cell):

    • finds which cell containing a BEACON (or a base) is closest to this resource

    • get the path between this cell and this resource, place a BEACON on each cell of the path.

Little anecdote, I learned that the LINE works better when the 1st index is that of the destination. I simulated this with my BEACONs by sorting them just before the output, from the farthest from my base to the closest. The result was indeed felt, certainly not as much as a simulation, but for lack of reading the source code of the game this criterion turned out to have the right efficiency/simplicity ratio.

1 Like

#3rd Legend

Overall Strategy

My bot consists of three parts:

  1. Determine target resources to harvest based on rules.
  2. Create target path shapes that will be formed by ants and include the resources.
  3. HillClimb (HC) beacon positions to achieve the target path. Repeating the process of simulating one turn and evaluating the results using the target path.

Target resources

Initially, I check whether I can win solely by harvesting crystals near my side.

If I can, I utilizes chokudai search (a time-managed beam search) with simple actions (such as setting beacons to maintain current positions or setting beacons on the minimum spanning tree of resources) to determine which resources it should go for.

If I cannot, I greedily selects all the higher-tier resources.

Tier 1: All eggs. Crystals near my base but also close to the enemy’s attack chain. Crystals at the same distance from each base, taking a few turns to obtain.
Tier 2: Crystals near my base or close to my attack chain.
Tier 3: Others.

Target path shape

I construct a minimum spanning forest by considering my bases and target resources as vertices. When choosing the path between vertices, I apply dynamic programming (DP) to score each cell based on the distance from my ants and the number of resources/ants on the cell.

HC beacon position

I set the same number of beacons as ants and evaluate the board by simulating the states after the HC process (30,000-140,000 simulations per turn).

・Neighboring states:

  1. Decrease the number of beacons at a random position by 1 and increase another position by 1.
  2. Erase all beacons at a random position and increase another position.
    The positions to increase are chosen either from the cells on the path (50%) or completely random cells (50%).

・Evaluation:

  1. Win or lose.
  2. The number of ants on the target path.
  3. The amount of acquired resources on the target path.
  4. Proximity to the target path.

Congratulations to @MSz and Domiko for the 1st and 2nd places with your impressive bots. My bot is nowhere near that level, and I’m eager to read your PMs.
I would like to express my gratitude to CodinGame for organizing this challenge, and for successfully completing it.

6 Likes

37th in Legend

My architecture is similar to @Neumann & other’s: A simulator, a ‘strategy’ optimiser which schedules which resources to begin targetting when by hill climbing, and a ‘tactics’ optimiser which mutates beacons for the next turn only.

I don’t use any scoring function for partial games, instead always sim to the end and prefer: victories, then earlier victories (or later losses), bigger victories (smaller losses) on the same turn, and only in case of same-turn crystal ties do I care about ants. I could see many had heuristics to favour early egg gathering but also that the best way to do that was very map specific and that the game should be simple enough (at resource targetting level) that some kind of optimal balance should be derivable from end game state without adding arbitrary conditionals.

The result was mixed: My bot tends to beat similar ranked players while gathering less eggs. Sometimes it will make really insightful plays and appear to trade early resources for positional advantage. But it’s undeniable that having more ants makes for robustness in the face of opponent uncertainty because you’re more likely to show up at contested resources with stronger attack, and on most maps the top bots are gathering both more eggs and more crystals than mine.

This full game sim choice also made it important to model the opponent. I run an opponent plan through the same strategy optimiser as my own each turn. Occasionally this works beautifully (eg. my bot predicting it will lose by 7 crystals in 15 turns and then doing exactly that, or spotting opportunities to steal unguarded resources in opponent’s territory, or hanging out near key resource piles but only moving on them when the opponent begins to), other times the opponent prediction and their actual play diverge too much that my bot gets very erratic or even delusional (eg. believing games which actually end at turn 40-50 can be won by attacking opponent’s expected weak lines and extending the game until turn 100).

The problems are: My plan and predicted opponent’s plan end up co-evolving in a sort of cooperative way which doesn’t reflect genuine gameplay. And I don’t have any really profound way of re-syncing the opponent’s plan to their actual play. In the last hours of the contest I finally got around to optimising my ant allocator sim (with it’s horrible n^2 log n sort, for n of the order of beaconed cells you have). After a 20 fold improvement in num sims/time I was confident my strategies would be even better and they seemed that way, but unfortunately the opponent over-fitting got much worse so I was left trying to tame that in the final 3 minutes of the contest. Overall I was a little disappointed in my final rank and eagerly wait the forever-arena to open and try to find some missing potential.

My tactics optimiser was very simple: Move a beaconed cell (or a half, quarter, eighth of the beacons) to another random cell and keep the change if it improves the attack power (as a proxy for gather power) on the next turn on any resource that has less or equal opponent ants on it the current turn. I’d wanted to make it a bit more focused at improving partially-built paths (something like @cegprakash’s method sounds good), and perhaps look 2-3 turns ahead but this was already powerful so I didn’t expend much work on it.

Feedback wise: I concur with others that indirect ant movement was a great choice over simple MOVE commands some requested. However I also agree the ant allocator logic was a bit more arbitrary than ideal, because of the ordering by cell indices. It might have been more intuitive and lead to more creative beaconing solutions if the ordering was by beacon strength or some other priority we could control. But there was plenty of depth to the game elsewhere and the default movement logic wasn’t that bad most of the time even without mindless mutation mashing.

This was my first CG contest I dedicated proper time to and really enjoyed it! - thanks to everyone involved in running it, and everyone else who competed (especially those taking time to share write-ups) :smiley: :ant:

3 Likes

#1st Legend

Overall

I enjoyed the contest and the game. It was very non-trivial, requiring several different algorithms to work together. And still, it is not clear to me what could be an optimal strategy.
Thanks to the organizers!
Also, special thanks to @aCat for support and discussions and to the rest of the University of Wrocław group. And of course, thanks to the great opponents!

The overall idea of my bot relies on two phases and is similar to the others. In the first phase, it makes a general project for current path connections (tree), and in the second phase, it runs micromanagement modifying beacon weights. For calculating beacon placement, I did not see an alternative way to enforce the best move than trying many configurations by simulation, so the branching factor is huge. As in the Cultist Wars contest, I tried to make efficiency to be my strength; this time, instead of exploring deeper, the effort was to explore the game tree wider, which just means trying more own moves.

I spent the first days encoding the algorithm from the referee and optimizing it. Additionally, I had to adapt to constantly-changing rules. At that time, without any reasonable AI algorithm, I found it hard to reach even Bronze. But Bronze was required to test the full rules. The situation changed immediately after adding any path-making algorithm with equal beacons. However, as I saw improving the micromanagement phase affects the results much more and actually compensates for the weakness of the high-level design, I neglected the development of the first phase and meta-decisions for too long. Yet, they were also very important when encountering strong mature agents.

There were also some glitches worth noting: The rules are quite difficult (because the movement algorithm is a part of the rules, although undescribed) and they were changing through the first half of the contest. Therefore, implementing the engine early was risky, and indeed I had to redesign it a bit after some changes. While I understand and accept the concept that ants cannot be controlled directly, the algorithm behind it looks artificial, because it affects the ants globally. This globality also makes it difficult to design moves by hand, in favour of simulation. In my option, a more natural way to model ants would be, e.g., when an ant goes to the nearest beacon that it sees, follows other ants if possible.

Technical details

The tree phase

The tree is computed in a greedy/Hill Climbing way. The resources (crystals or eggs) are added iteratively, each time evaluating the resulting constructions. The estimation is based on the value of the extracted resources divided by the build cost. The resource is connected to the existing tree with the cheapest path, assuming, e.g., that going through existing ants is cheaper and promoting going through other resources.
This is computed with the standard Dijkstra algorithm. It is not a minimum spanning tree (MST) on resources, because we can connect a new resource directly to a part of an existing path; if we knew what exactly resources we want to connect (but we do not), it would be a variant of the Steiner tree problem.
Instead of adding a new resource, it is also possible to increase the width of an already existing path.
The proposed width (the number of wanted ants) at each cell is also a part of the project, and it takes the opponent’s attack chains into account, increasing the width wherever needed along the way up to the base.

The construction is complete when its size exceeds the number of ants, with a slight surplus allowed.

The micromanagement phase

This is absolutely crucial for the strength of my agent. The variant with the tree only has only about 0.82% win ratio against the full agent. Improving this part was a game-changer several times. The goal of micromanagement is not just moving ants as closest as possible to the given high-level project, but it can extract nearby resources apart from the plan, adjust the width of chains, block the opponent, and maximize the extraction. Actually, it may lead to a trap of ignoring these factors in the first phase and I fell in that for a few days.

Here, the algorithm takes the initial tree project and modifies it in a Hill Climbing manner. The initial project is applied and the algorithm identifies places that might be good to change: where there are fewer or more ants than proposed, not forgetting about nearby resources. There are several iterations, modifying either a pair of beacons or a single beacon (increase or decrease). If time permits, more candidates to try are added. What may be distinguishing from other solutions, I do not try random changes but rather search through all possible single changes exhaustively. And in this phase, I do not keep the constraint relating the number of beacons to the number of ants, allowing exploring the full spectrum of possible moves.

The evaluation uses a simulation of two turns, applying the same move and adding the value after the second turn with a strong decay. The main components are, of course, the score and the number of ants, and also the agreement level with the initial project, as well as the formed attack chains against the opponent. Initially, I was using three turns. However, after the evaluation became more complex, the third turn stopped being profitable. And surprisingly, this is not just because it is more expensive, but the third turn prediction seems already too unreliable and useless. The bot using three turns achieves a win ratio of about 48% against the same bot using two turns when they are both limited to evaluate at most 10 000 moves instead of a time limit. Of course, its situation is worse if we take the time overhead into account (then it achieves only 45%).

Additionally, before the entire phase, a simplified and short micromanagement for predicting the opponent is run, which is slightly better than assuming that it does not move. The predicted move consists of beacons at the places occupied by the opponent’s ants and adjacent to them.

Optimization

I was seeing a big difference in results depending on the time budget, so I paid much attention to optimization, which is also the part that I like the most. The original movement algorithm is implemented in a very inefficient way. I got rid of generating and sorting pairs by computing them on the fly. For this, it was enough to precompute for each cell and each distance, the list of cells at this distance. Also, the list of ant cells is dynamically modified. This still has worst-case quadratic time complexity but works much faster when the beacons are close to ants (usual case) and there are enough beacons to assign the ants shortly.

As to the ant movement, it depends on the cell neighbors ordering, which is affected by the assignment (target cell), other ants (game state), and beacons (move). I sort the neighbors using dedicated sorting networks for 2–6 elements. Under a sensitive measurement, it appeared slightly faster to sort in this way than selecting from unsorted neighbors, which is most likely because we may need to do this several times for the same ant cell. Here, I regret the change in the rules that beacons also affect ant’s path selection. Before that change, the neighbors order depended only on the state, so could be precomputed for faster performance.

For computing the attack and harvest chains, I implemented Dial’s algorithm (Dijkstra with buckets as the priority queue). The maximum number of ants on one cell is usually small, so it works faster than Dijkstra with the traditional heap. Also note that updating the chains is cheaper, and there are some cases when updating is not needed.

Finally, everything that was possible was precomputed, like resource cells, quantities in different areas, and distances of various types.

How important was the efficiency? See the following comparison of my bot with different time limits (in milliseconds) with itself:

   MSz(5 ms) vs MSz(100 ms): 15%
  MSz(10 ms) vs MSz(100 ms): 23%
  MSz(30 ms) vs MSz(100 ms): 36%
  MSz(50 ms) vs MSz(100 ms): 42%
 MSz(200 ms) vs MSz(100 ms): 55%
MSz(1000 ms) vs MSz(100 ms): 60%

Conclusion: If it was 3 times slower, it would be of similar strength to @DomiKo bot (based on its the estimated win ratio of 34% after the final recalculation).

Meta

Designing meta strategies was also quite important. The most important rule is: harvest on the border first (the cells at an equal distance from the players’ bases and around), and when there is nothing left, go back to own area. The resources threatened by the opponent but not in its area are also considered a priority. Additionally, when detecting that it will lose because there are not enough crystals, it tries to steal something from the opponent’s area; however, when such a situation occurs then it is usually hopeless anyway.

Finally, there were many parameters to tune (e.g., egg values, localization bonuses, path cost, eval parameters, and the values controlling micromanagement). I find that there are more of them compared to other of my agents for other games.

Not working ideas

It is natural that some improvement attempts do not work, and I also had a lot of them. From those ideas I remember, e.g., hashing the moves to avoid repeated eval computation, trying several paths from one resource, estimating the local density of resources to prefer rich locations, and predicting the game ending after 100 turns to affect meta decisions.

14 Likes

Legend 4th

My strategy is consists of two phases.

  1. Choosing which resources (= eggs, crystals) on the map to be target destination using rule based strategy. And then decide the path from my bases to the target destinations using BFS.
  2. Deciding how to place beacons by hill climbing algorithm. It maximize the number of crystals and eggs and minimize ants distances from the paths decided in phase 1.

[Phase 1]

Choosing target destinations

  • Eggs are more important than crystals for the first 5 turns.
  • After that, priority is given to crystals near the middle cells.
    (“middle cells” is which have same distance from my bases and opponent bases.)
  • Put off the crystals that are close to my own bases (however, if the opponent’s ant chain is closer than mine, go to defense it).
  • Excluding places where the opponent’s attack chain is effective and I cannot create more powerful chain.
  • In games where players are competing for a small amount of crystals in a single center cell, always mark it’s cell as the important target destination.

Deciding paths

  • Sort the target destinations in order to distance from my bases(closer is better), and connect one by one by the shortest path to my bases or already connected cells.
  • Repeating this process until the total path length reaches the number of ants I have, or all the target destination path is decided.

[Phase 2]

Hill climbing to optimize the beacons placement.

  • The initial solution is to repeatedly place beacons one by one on the paths decide in phase 1 until the number of beacons is equals to ants I have.
  • Solution changing is to place and remove beacons on the paths decided in phase 1 or its 6 neighbours. The number of beacons and the number of ants always be same.
  • Two players alternatively improve their moves. This is same algorithm as I used in Fall Challenge 2022.

Evaluation

  • In the early game, giving high evaluation value to the number of ants.
  • In the middle or end of the game, giving high evaluation value to crystals that targeted by rule based strategy in phase 1.
  • As a tiebreaker, using sum of distances between the ants positions and the beacons(closer is better). For this, I used a simple algorithm that sequentially run BFS from each beacon and assign nearest unassigned ant.
  • As a further tiebreaker, I call the Phase 1 rule-based routine in the evaluation function and evaluate the sum of distances to its paths. This is useful when there are cells that will be empty of resources in this turn, to move surplus ants to the next target destination in advance.
  • In games where players are competing for crystals in a single center cell, I give high evaluation to getting the majority of them.

Thank you to the organizers for giving us this valuable opportunity to have fun and compete.

9 Likes

#32 Legend

Thanks CodinGame for this great contest, and congratulations to all my teammates at 1337 school for securing the 2nd place in the school’s leaderboard.

I used a simple heuristic from wood to gold, to achieve the legendary league. I needed a search and simulation algorithm. That’s why I implemented the game engine from Java to C++. After that, I utilized a Genetic Algorithm (GA) for the search because it’s the one I’m most comfortable with. In the first turn, I computed the distances between all cells and stored them. After each turn, I ran the GA for 100 milliseconds, combining random beacons in random cells. Then I called the simulation to get the score after the ants’ movement. I repeated this process for each player, swapping for another player in each GA iteration. The scoring mechanism was simple: I scored for eggs before reaching half of the eggs on the map, and I considered the crystals and Voronoi diagram to guide the bot towards resources near the enemy. Since I only simulated three turns, I could only see the resources within three cells’ distance. This is where I used Voronoi in the scoring. Thanks again for organizing this great contest.

7 Likes

#31 Legend

thanks CodinGame and my teammates at 1337 school.

My bot is a combination of heuristics and basic simulations.

  1. SWITCH FROM EGGS TO CRYSTALS :
    need to know how many eggs I need, to collect all the crystals in the minimum turns possible, using the 1000ms of the first turn I simulate all the combinations of decisions, it’s a collection of clamming (crystals * N + eggs * M) N and M is the number of turns of clamming each one of them. then I save the best collection which has a minimum number of turns to collect the (INIT_CRYSTALS_SUM / 2) needed to win. it lets me know how many eggs need to end the game earlier to switch from collecting eggs to crystals.

  2. PATHS COLLECTION :
    build a tree of paths that contain the minimum empty cells. using a map of targets, first, make my bases as a target and run BFSs multiple times from all the resources (that I didn’t make them as a target yet) to the nearest target, save the shortest one, then make all the cells of that path as a target, and repeat that until (I make all the resources as a target or number of target + path.size() <= MY_CURRENT_ANTS_SUM), to make sure that I can reach all the cells of the tree.
    Perform a breadth-first search (BFS) using priority_queue (the minimum empty cells at the top of the queue) to add the path to the tree.

  3. SIMULATION
    I did this part to get a good expansion in the tree,
    for this, I needed to know how ants move when I placed beacons in the cells within a strength,
    that’s why I reimplemented the movement of ants from the source code of the game in cpp to predict where ants will move,
    then I randomized the beacons strength of the tree cells for the 100ms and each time I predict the next turn if I use it
    using a scoring func I save the best.

  4. SCORING FUNC
    I calculated the flow of ants in each cell that contains ants from the bases for me and the opponent to know how much we can collect from the eggs, and do it again after adding the clamming eggs to each base to know how much we can collect from the crystals,
    then use this for scoring

    if (I_STILL_NEED_EGGS)
        return (my_collected_eggs / opp_collected_eggs) * 2 + (my_collected_crystals / opp_collected_crystals);
    else
        return (my_collected_eggs / opp_collected_eggs) + (my_collected_crystals / opp_collected_crystals) * 2;
14 Likes

#50 Legend

Thanks CodingGame for the contests. Once again, it has been a lot of fun.

About the Bot itself:

Simulation:

Even with the help of ChatGPT, it took me a while to get it fully working (with optimizations).
Around 2 full days, I guess…

Actions:

During the first turn I compute a MST by pruning the tree using Kruskal’s algorithm since I saw it in a discussion on Discord.
(ChatGPT has gently written most of the code for me…)
But I also added direct path from any of my base to any of the resources.

For each path, there are 3 possible actions:

  • Put beacons of strength 1 on all cells of the path
  • Put beacons of strength 2 on all cells of the path
  • Put beacons of strength 3 on all cells of the path

Search algorithm:

My bot mostly uses a kind of MCTS search.

I was using a genetic search first.
But the randomness was not helping me understanding where were my bugs.

In this kind of MCTS search, each node represent a path to put beacon with strength 1, 2 or 3.

When expanding, the algo can choose only actions using paths not already chosen by any of the parents or a wait action.
When choosing a wait action it will not expand anymore.

During parent update, I don’t compute the mean, but I only keep the best score of the children.

I already used that approach during “Legends of Code & Magic”.

Score:

At each turn, I compute a match temperature score between 0 and 1.
1 means that we are the beginning of the game and that we should get some ants.
0 means that the game is ending and crystals are the priority.

For each rollouts, I do a simulation during 6 turns.
For each turn I compute a turn_score = my score - opponent score
With player_score = crystals + 10 * match_temperature * eggs

Then the final_score is using a discount factor of 0.4:
final_score = current turn_score + 0.4 * turn_score in one turn + 0.4 * 0.4 * turn_score in 2 turns + …

What I will remember the most from this contest:

1. Using chatGPT to translate the Java referee code into C++ code.

Only the functions I was interested in. Not the whole thing.
That has been helpful since the logic of ant movements was quite hard to understand.
However, I have had to rewrite most of it for performance reasons, but that has been helpful anyway.

2. An epic fight against the Gold Boss 30 min before the end of the contest.

I have had a hard time to pass the Gold Boss.
On sunday night, I decided that I could not do it.
On Monday morning, I woke up with a simple idea of a bug fix on my match temperature.
(It is still not working correctly, BTW…)

I have pushed the change in the arena at 07:17. Just in case…
The server were completely overloaded at that time.
It was slow as hell…
So when I finally got to 100% around 9:30, I had already drunk 10 coffees and was in a state of excitement hard to describe :wink:

However, I got lucky and finished in Legend league.
(But without these server lags, I guess that @egaetan and @BlitzProg would have passed the boss, too)

9 Likes

#84 Legend

my bot used a heuristic to find shortest path from resource to previously placed beacons, then approximating the beacon’s strength using simulated annealing, scoring 5 turns ahead in each SA iteration.

evaluation function was simple, scoring the resources gathered.
neighbors generation, randomly choosing one beacon and assigning randomly from 0 to 3.

performance, on large maps I did 6k iterations in the first rounds (without to 1000 ms round), when beacons and ants grow I do ~500 iterations.

Enjoyed the contest, good job everyone!
Special thanks to @Sheeesh @AlgoAce and all 1337 team for lending their help when I was stuck.

7 Likes

Yeah, it was insane. Possibly the closest one can be from a league and not getting into it on my side

  • Went in front of the boss because of a strength/weakness triangle (@bourgeof >> Gold boss > Me > @bourgeof)
  • @bourgeof got into legend before I could finish my games
  • My AI failed to sustain its rank and completed its games behind the boss by mere 0.01 point
  • got pushed in front of the boss briefly (for only like 10 seconds), but the server was too laggy to notice
  • boss got back in front with very varying degrees of distance, shortened down all the way back to 0.01 point at the very end of contest
  • @egaetan pushes right before the end, he only has to win once to the boss or lose once to me - but that doesn’t happen
  • further matches winden the gap by 1.5 points because his AI is very strong against me but too weak against boss

Since this contest, I checked my code and could find a few glitches, but the removal of their side effect caused my AI to perform slightly worse for some reason. So to this day I don’t know if there’s any simple improvement that could have made it to the ultimate promotion.

3 Likes

Feedback for the CG team on the game. Just a heads up this is mostly negative.

Pros:

  • It was different from most contests of the past two years.
  • State representation (once parsed properly) was nice to work with.
  • Game rules made it interesting to explore very different approaches. Nice learning opportunity.
  • Great to see contests continue on this platform! This is what I’m here for.
  • Game felt uniquely well suited for advanced solutions, and also 100ms response time gives lots of breathing room for computation to support this.

Cons:

  • Overall this was a horrible contest for newbies. Everything about it seemed to make engaging with the game more difficult rather than more intuitive and fun. Examples: black-box ant movement, unintuitive state representation (cells + neighbours), awkward cell ids, etc. I tried onboarding some colleagues to CG starting with this contest and it was probably the worst contest/game I could have picked to do it. Nobody liked it. Not a good introduction to what CG contests can be…
  • Visually it was so cluttered and chaotic, that it was very difficult / nigh-on impossible to parse/debug visually in the browser IDE.
  • The rules of the game (specifically ant movement) presented a much higher barrier to simulation (compared to previous contests) but also made it more difficult to use heuristics, as it wasn’t clear or explained anywhere what the rules for ant movement were. The only way to figure out ant movement was from the game source code, and that really shouldn’t be necessary for regular players to understand the rules.
  • Discord was dead. Not just dead, but dead dead. Community is the greatest strength of this platform. Discord clearly does not facilitate that sense of community and discussions during contests (and in clash of code). Bring back the in-browser chat. Somehow. Please.

Nitpicking:

  • Contest cover art was obviously AI generated and lacked inspiration (why are some antennae floating midair?). Big thumbs down. Please bring back human cover art.
  • Visually the game is the most boring I’ve seen, it is dull and grey and horrifically complex to look at. Where are the pretty visuals like in Spring 2021? This combined with the above made this contest ‘feel’ relatively low budget.
9 Likes

#16 Legend, C#

My best individual result so far, and also the best result of our University Team (team’s top5 in global top30). Many thanks to all core team members and other students that were here to help. I hope you had at least as good time as I.

Congratulations to our top 5: @MSz @DomiKo @marwar22 @Sko0owi as well as the rest of you in Legend: @surgutti @alechin28 @Zylo - great work. Special thanks and praise for @MSz and @DomiKo - extraordinary performance - it is a real privilege to be your friend and be able to work with you.

My Solution

Actually, nothing extremally fancy nor different from multiple other top-legend approaches. My todo list was still quite large, and there were some obvious improvements I didn’t have time to implement.

The first part is a greedy algorithm that creates a beacon tree by joining resources. Nodes of the tree are bases and previously joined resources. I check all pairs of tree nodes and unconnected resources and evaluate them, taking into account their base weights, amount, placement, etc. adding (with some small weight) scores of the resources on the way. The cost of the path depends on how competitive it is (opponent’s attack chain value) and how many ants I already have placed on the way. I choose the best path maximizing score/distance that fits within a budget (unassigned ants). Then an edge is created going from the new resource to the tree node (I don’t have a connection to any closest node with an already placed beacon). Edge is also chosen greedily in each step prioritizing: shortest route, cells with already placed beacons, cells with most ants, cells with resources.

Then, there is a random hill-climber. I randomize a beacon cell and weight to be added or subtracted (max delta is an average ants/beacon value). If the state evaluation after 2 turns is better than the current best - it stays. The state is evaluated mostly based on the number of my and opponent’s crystals and ants (somehow taking into account their placement - near me, near opponent, border), plus a medium reward for winning and smaller for drawing. There are some special modes that influence particular weights depending on the number of bases and when only one or two crystal cells are left.

The Game

Didn’t like it. Turned out to be more interesting than I thought, but with forced, very artificial behaviors, middle-challenge bugfixes and changes of rules, and uninformative hard-to-read visualization. It is also really a shame and pity that all the hard graphics/programmers work on fancy ant visualization goes to nothing as everyone uses only debug mode.

Takeaways

What went well:

  • Efficiency matters. Especially if you’re not using c++. Counting time for each section of the code and keeping a close eye on performance bottlenecks pays off. For nearly the same state, I went from 54(!) state evaluations per 80ms to ~1600.
  • Weights of the parameters are essential. There is no time wasted on tuning them.

What did not:

  • Complaining about rules and allowing destructive thoughts that I don’t like the game. Focus on coding, not complaining!
  • Leaving the code-writing (of important parts) to the last day. I made ‘some’ progress luckily but it was too much work under stress and I couldn’t properly focus on algorithms. The last day should be only for param tuning and adding small enhancements to quick test.
9 Likes

it seems that a lot of codingamer used the hill-climbing algorithm. If like me you don’t know what it is and you don’t speak/read technical English but French, you can watch this lesson.

or read this
http://gdac.uqam.ca/inf4230/diapos/05-recherche-locale.pdf

[FR] translate
il semble que beaucoup de codingameur ont utilisé l’algorithme d’escalade (Hill-climbing). Si comme moi vous ne savez pas ce que c’est et que vous ne parlez/lisez pas l’anglais technique mais le français, vous pouvez regarder cette leçon.

thanks to codingame for this challenge which always pushes you to learn new techniques, learning by playing is the best

5 Likes

#2 in Legend. (First Gold Boos :wink: )

Thanks CodinGame for the contest. It was a bit of a bumpy adventure, but overall I really like it.

First Challange

First I have to tell what was the biggest challenge at the beginning: MOTIVATION
When the contest started people started hating the game.
The main concern were of course the beacons.
So I told myself just to have fun and don’t bother with understanding how they worked.
That single thing helped me sooo much. I was able to test many ideas without losing any drive for reaching good rank.

Overall

As everybody I divided my algo into two phases.
First: build the tree.
Second: Hill Climbing to optimize ants movement.

Phase 1

I randomly create many trees for 35 ms, and then I take the best one, evaluated with sim for 6 turns.
I start every tree with my bases only.
Then I add random path from a node from tree to any cell with resources.
It isn’t uniform. Each edge has it’s own ppb: (1 / distance) * resource_value.
Then I add that edge to the tree with a certain number of ants for each cell (random with some constraints).
I keep adding edges to the tree until I don’t have enough ants.
After each added edge I evaluate the tree.

Eval

Sim for 6 turns.

I assign value for each resource.

First I was looking for a good value for eggs. It wasn’t easy to decide if eggs are still important or not.
I finally come up with such a value: egg_value = (total_important_crystals / number_of_my_ants)
Where total_important_crystals is the sum of crystals in the middle part of the board.

Then I learned that crystals in the middle of the board are soo much more important than the ones closer to my bases.
For each cell I calculated diff[pos] = abs(distance_from_my_base[pos] - distance_from_opp_base[pos]).

Then resource_value[pos] = 1 * pow(0.3, (diff - min_diff)).
That was resources with minimal diff has value equal to 1, and those far from the middle part are equal almost to 0.

Phase 2

Hill Climbing with the same eval as Phase 1, but with only 3 turns.

It can only change existing beacons.
With ppb=15 + bonuses change strength of the because by ±2.
I give a little bonus for ends of the paths.

That was pretty simple to write and immediately crashed the version without HC.
But wait there’s more!

It’s using the same beacons for 3 turns, does it make sense?
Kinda, but not really. Imagine simple example where you have a resource and it will be harvested in one turn. You definitely don’t wanna have ants in there for the next two turns.

So instead of having one beacons for 3 turns I changed it to:

  • beacons for first turn,
  • beacons for second and third turn.

I start with the same beacons for 1 and 2/3 turn.

BOOM 64% win rate against version without it!

Sim

Referee was super slow, so you have to optimize it a lot.
Remove sorts, sets, that bfs inside ant pathfinding etc…

For me enemy was always waiting, but still I was harvesting with him, and spawning new ants, etc…

Another big change, was implementing the end game tiebreakers.
It gave me 58% winrate.
I changed my eval result from single float to tuple <float, crystals diff, ants diff>.
So when I reached terminal state in my sim eval was just 1e9 - depth for the win, -1e9 for the lost.
It was crucial for me, because I had that custom evaluation of crystals and eggs.

Local Arena

It was crucial to test things localy. I made 30 versions. Each of them gave me some improvement.
Sometimes it was 51%, sometimes 95%, but mostly in the beginning it was something about 60%, but with time it was soo much more challenging to gain even 53%.

In the end my bots played few millions of games.

Final thoughts

As you can see I didn’t do anything crazy, but still my bot was really powerful. There was just many simple little things that gave it that power.

Congrats to @Msz for dominated top1.

3 Likes