De organisatoren van de infinibattle 2026

Infinibattle 2026: Van Lead-Berekening tot Victory

Bor Achter de schermen

De stofwolken zijn weer weg en de bustes staan weer op de vertrouwde plek. De slag is geslagen. Dit is het verhaal van hoe ik met mijn bot hivemind de infinibattle 2026 heb gewonnen.

De infinibattle

Eens in de paar jaar houden we een interne competitie waarbij we strijden om de eerste plek in een bot-vs-bot battle in een spel, bijvoorbeeld L-game of barrage-stratego. Deze keer ging het om een clone van robocode, een spel waarbij je bot een tankje bestuurt dat zich in een arena bevind en daar alle andere tankjes af moet slachten. Je kan rijden, draaien, schieten en meer. Maar, je mag maar één commando tegelijkertijd uitvoeren! Een gevecht tussen twee bots bestaat uit drie van jouw tankjes tegen drie andere tankjes. Hieronder kan je zien hoe het spel er uit ziet.

Visualisatie van gearbots

Mijn bot

Het begon allemaal op een donderdag. Ik zat tussen projecten in en had veel tijd om te besteden aan het schrijven van een goeie bot voor de infinibattle. Ik zat al even te worstelen met de vraag of dat ik überhaupt mee zou doen omdat ik ook in de organisatie zat maar meerdere mensen hebben mij toch weten te overtuigen. We hebben immers genoeg vertrouwen in elkaar dat daar niet mee gesjoemeld zou worden. Dus ben ik begonnen bij stap één; Het maken van een nieuw C#-project.

Wat is dan het eerste wat je tegenwoordig doet? Het is 2026 dus ja; we gooien de guide voor het schrijven van een bot in een goeie LLM en vragen of dat het een plan voor ons kan maken om deze gevechten te winnen. Ja, er wordt veelvuldig gebruikgemaakt van LLM’s bij het schrijven van deze bot. Deels om te leren hoe het is om dat te doen, en deels omdat het gewoon effectief is. De LLM naar keuze voor mij is op dit moment claude, al kan het af en toe credits opvreten alsof het Koekiemonster is!

Claude stelt na een poos na te denken een mooi plan voor ons op. Met een paar iteraties en eigen ideeën over het spel krijgen we dit als resultaat:

Implementation Plan (10 Steps)
Step 1 — Models & Vec2
"Create the models and Vec2 math"
•	Models/Vec2.cs — struct with operators, AngleTo(Vec2), NormalizeAngle(double), DistanceTo(Vec2)
•	GameModels.cs — all JSON records (GameEnvironment, GameState, etc.)
•	Uses Vec2 as Location everywhere so there's no conversion layer
•	✅ Done when: project compiles
---
Step 2 — Game IO Loop
"Create the GameIO stdin/stdout protocol and Program.cs game loop"
•	GameIO.cs — SendReady(), ReadEnvironment(), ReadState(), SendCommands()
•	Program.cs — handshake → env → loop → command-end
•	Bot does nothing yet, just stays alive
•	✅ Done when: bot can run against the game runner without being killed for timeout
---
Step 3 — Enemy Tracker
"Create the enemy tracker that records scan history and computes velocity"
•	IEnemyTracker.cs + EnemyTracker.cs
•	Stores last 2 sightings per enemy → derives velocity
•	UpdateFromScans(), MarkDestroyed(), Get(), GetAllAlive()
•	✅ Done when: leader can log tracked enemies with computed velocity
---
Step 4 — Communication Protocol
"Create the chat encode/decode protocol for sharing enemy intel"
•	ITeamProtocol.cs + ITeamProtocol.cs
•	Compact format: step|id:x,y:vx,vy — fits 2 enemies in 50 chars
•	Encode() + Decode() with robust parsing (ignore garbled messages)
•	Add UpdateFromIntel() to EnemyTracker
•	✅ Done when: you can round-trip encode → decode and get the same data back
---
Step 5 — Linear Predictor
"Create the movement predictor that extrapolates enemy positions"
•	IMovementPredictor.cs + LinearPredictor.cs
•	Extrapolates position using velocity, applies 2% friction per step
•	Accounts for data staleness (steps since last observation)
•	✅ Done when: predictor produces reasonable future positions from tracker data
---
Step 6 — Lead Calculator
"Create the aim point calculator for bullet travel time"
•	LeadCalculator.cs
•	Iteratively solves: where will the target be when my bullet arrives?
•	Input: shooter pos, target pos, target velocity → output: aim point + turret angle
•	✅ Done when: given a moving target, it returns an aim point ahead of the target
---
Step 7 — Role Interface & Assignment
"Create the IBotRole interface and role assignment logic"
•	IBotRole.cs — IReadOnlyList<string> Update(GameState state)
•	RoleAssigner.cs — lowest friendly Id = Leader, others = Follower
•	Bot.cs — orchestrator that owns tracker, protocol, and delegates to role
•	✅ Done when: each bot instance knows its role at startup and logs it
---
Step 8 — Leader Role
"Implement the leader: turret sweep, tracking, and broadcasting intel"
•	LeaderRole.cs
•	Turret: continuous +10°/step sweep (full 360° in 36 steps ≈ 1.4s)
•	Track: update tracker from scans each step
•	Broadcast: when chat energy is full, encode top 2 enemies with predicted positions
•	Opportunistic fire: if enemy in cone + gun charged → fire instead of sweep
•	✅ Done when: leader sweeps, tracks, and you see encoded chat messages in replay logs
---
Step 9 — Follower Role
"Implement the follower: receive intel, aim at predicted positions, fire"
•	FollowerRole.cs
•	Intel: parse teammate chat → update tracker
•	Target selection: pick closest/lowest-HP enemy from tracker
•	Aim: compute lead point → rotate turret toward it
•	Fire: when turret within ~3° of aim point + gun charged → fire-gun
•	Move: during 15-step gun cooldown, accelerate/rotate toward target
•	✅ Done when: followers aim at leader-reported positions and fire accurately
---
Step 10 — Integration & Tuning
"Wire everything into Program.cs and tune thresholds"
•	Wire Bot → GameIO → Role in Program.cs
•	Tune: firing tolerance angle, sweep speed, target priority, movement aggression
•	Add log commands throughout for replay debugging
•	✅ Done when: 3 bots coordinate — leader scans, followers focus-fire
---

 

Op het eerste gezicht ziet het er goed uit. Dat is al goed genoeg voor mij om ermee door te gaan. Het zit dicht tegen vibecoden aan, maar voor een project als dit maakt dat niet zo veel uit. Ik begin met het uitvoeren van de eerste stappen door de LLM, dat werkt erg goed, totdat ik bij stap 6 moet ingrijpen. Bij het berekenen van de richthoek wordt de draaitijd van de turret niet meegenomen. De LLM prijst me de hemel in om mijn verbetering en voegt het vervolgens toe.

Zo ga ik verder tot stap 10. Dat is een interessante stap. Hoe kan de LLM mijn programma tunen zonder dat hij het spel zelf kan spelen? Dit is wat er uit komt:

Fix 1 — Stdout auto-flush (prevent timeout kill)
Program.cs — Console.SetOut(new StreamWriter(...) { AutoFlush = true }). Without this, piped stdout could buffer and the game runner would timeout-kill the bot.
Fix 2 — Invariant culture (prevent malformed commands)
Program.cs — Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture. On an EU-locale server, $"rotate {5.7}" would produce "rotate 5,7" which the game parser would reject.
Fix 3 — Friendly fire safety
FollowerRole.cs — new IsFriendlyInLineOfFire() check. Since TankScans only contains tanks in the 10° scan cone (centered on turret heading = firing direction), any non-enemy visible and closer than the target is directly in the bullet path. Hold fire.
Fix 4 — Wall avoidance every step
MovementHelper.cs — split into GetWallAvoidance() (urgent, always) + GetGeneralMovement() (periodic). LeaderRole.cs + FollowerRole.cs — wall check runs EVERY step, movement commands gated by cadence.
Fix 5 — Focus-fire convergence
FollowerRole.cs — target selection changed from OrderBy(distance) to OrderByDescending(LastSeenStep). The leader broadcasts its focus target every 25 steps, making it the freshest entry in the tracker. All followers now naturally converge on the same enemy.
Fix 6 — Map-bounded predictions
LinearPredictor.cs — accepts MapSize, clamps position to [0, width] × [0, height], breaks the prediction loop on wall contact (since game zeroes velocity at walls).

Oke, fix 1 en 2 heb ik niet veel commentaar op maar ik vind het toch knap dat hij ineens een heel systeem heeft toegevoegd om niet op eigen tankjes te schieten. En dat met alleen maar onze guide en de codebase als handleiding. De overige fixes klinken ook erg goed en nuttig. Top! Implementeer ze allemaal maar!

Dan is het tijd om mijn tanks los te laten in de wijde wereld. De output van de LLM’s en de zekerheid waarmee het alles brengt geeft mij een goed gevoel over hoe de tankjes het gaan doen. De code ziet er mooi uit, het is redelijk geordend en de functies en files hebben goede namen. Wat kan er misgaan?

Ik laat ze spelen tegen een van de bots die de organisatie heeft meegeleverd met het spel: Hunterbot. Mijn tanks lopen meteen een muur in en worden afgeslacht…

Ik denk dat dit een goed moment is om te zeggen dat LLM’s heel goed zijn in vertrouwen geven over je code. Misplaatst vertrouwen. Dit is ook ongeveer het moment dat ik écht door begin te krijgen dat het developer vak niet echt in gevaar is want bij het instrueren van de LLM om de tanks niet tegen de muur aan te laten rijden maar om te draaien en de andere kant op te gaan, gaan er maar liefst 7 iteraties en een uur tijd voorbij aan “sorry”s en “Oh ja, ik los het voor je op” zonder dat de tanks ook maar ooit een centimeter van die muur af komen.

Ik word wanhopig.

"The tanks are driving into the wall, and are also very slow. Can we fix the problem once and for all? I want the tanks to reverse when they find a wall and drive away in the reversed direction until they find another wall again. Reverting direction to go forward again. At a reasonable pace. above speed 8"

Ik ben ondertussen ook van strategie gewisseld naar achteruit rijden i.p.v. omdraaien omdat het in mijn ogen makkelijker te implementeren is, en ook nog eens efficiënter dan een rondje draaien en de andere kant op gaan. Uiteindelijk implementeer ik dit lekker zelf, want aan die LLM heb ik uiteindelijk weinig. het heeft misschien niet genoeg ervaring met iets niche als dit om mij écht te helpen.

Nu rijden de tanks goed rond en na het fixen van wat andere kleinere dingen met behulp van mijn trusty LLM komt er echt iets degelijks uit. Hunterbot wordt met gemak verslagen doordat er geschoten wordt op hun voorspelde plek en zij missen bijna al hun schoten. Om de infinibattle te winnen is dit natuurlijk niet genoeg, dus ik begin kleine aanpassingen te maken die in mijn ogen een betere bot maken, zoals het verkiezen van een nieuwe leider als de oude stukgeschoten wordt en een beter chatprotocol om meer informatie over tanks aan elkaar door te spelen.

Ik probeer zelf replay files te sanitizen zodat ik ze aan de LLM kan geven om betere strategieën te ontdekken. Naar mijn verbazing komen er nuttige dingen uit. Het ontdekt dat mijn leader tank soms met volle “gun energy” zit zonder te schieten. en koppelt dit aan een stuk code wat nét wat meer geoptimaliseerd kan worden. Uiteindelijk doe ik er niks mee want het is al 18:00 uur geweest en ik heb ook eten nodig. Tijd besteed aan het schrijven van deze bot: 1 uur. Tijd besteed aan wachten op antwoorden van de LLM: 7 uur.

De sourcecode van de bot is te vinden op github: https://github.com/superbandit/Hivemind

Het moment supreme

Het is maandagmiddag 15:00 uur en het format wordt bekend gemaakt. Er doen 16 bots mee en we spelen een double elimination bracket. Dat betekent dat je er uit ligt na twee keer te hebben verloren. Uit ervaring weet ik toevallig dat je als eerste eindigt als je niet verliest dus daar doelen we dan ook op.

Na een korte introductie van alle bots spelen we onze eerste wedstrijd tegen Samsung Smart Fridge van Joris L. Een hels gevecht breekt los. Dit is geen malse tegenstander, de smart fridges weten mijn kogels aardig te ontwijken omdat ze best veel in het rond bewegen. Dat betekent echter ook dat ze veel minder tijd besteden aan het schieten en tracken van mijn tanks. Uiteindelijk wint de hivemind met 2 nog levende tanks. Door naar de volgende!

Al snel komen we erachter dat hivemind best sterk is. In de tweede ronde verslaan we RainbowTank van Wilco met alle tanks nog in leven (maar zwaar beschadigd). Deze keer winnen we doordat al onze tanks op dezelfde vijand schieten. Hoe minder vijanden hoe minder kogels! RainbowTank eindigt uiteindelijk als 3e!

In de derde ronde vechten we tegen Gladiolator van Suzan. Dit is een bot gebaseerd op het paint project van Jeroen Heijmans. Hij heeft iets gemaakt waardoor je een bot kan maken door gewoon in paint te tekenen hoe je wilt dat ze fungeren. Ik weet niet wat ze dat algoritme voeren, maar Hivemind ontsnapt nipt aan de dood. Gladiolator lijkt keihard op mijn tanks af te rijden waardoor het onmogelijk lijkt om de kogels te ontwijken. Daarbij ontwijken ze ook mijn kogels omdat ze een onbekend patroon rijden, niet recht, maar ook geen rondje. Een beetje geluk aan onze zijde zorgt er toch voor dat we er goed uit komen.

In de upper-bracket finale spelen we tegen Backtracker van Koen. Deze bot rijdt alleen maar achteruit en fopt daarom allerlei andere bots die er vanuit gaan dat iedereen vooruit rijd. Oh hee dat doen wij ook! Dat achteruitrijden bij een muur had blijkbaar een neveneffect dat we moeilijker te raken waren. Gelukkig Baseren wij onze voorspellingen op behaalde resultaten en schieten we wél de goede kant op. Het simpele rondje wat Backtracker rijd is geen match voor onze tanks. We winnen soepel, ondanks het feit dat de laatst overgebleven Backtracker nog twee health powerups weet te vinden. Ohja die bestaan ook! En yes, we staan in de finale!

Dan is het wachten op wie er uit de lower bracket komt. We spelen uiteindelijk tegen de winnaar daarvan in een best-of-three om het goud. En dat is… Backtracker! Dat achteruitrijden was echt een geniale tactiek. Helaas voor backtracker komt er nu wel een einde aan. We verslaan ze met 2-0 om het goud voor onszelf te claimen. Binnenkort komt er een buste van mijn hoofd in het pand te staan. GG!

Andere Infi-evenementen

Een afspraak maken bij ons op kantoor of wil je even iemand spreken? Stuur ons een mail of geef een belletje.