The Ace of Coders programming tournament has ended after a month of ferocious coding chaos, with the leaderboards fluctuating by the hour. We’ve exhaustively simulated all 584,086 pairwise matches between all players and come up with the final ranking. Behold, the top ten Ace of Coders champions!
Full rankings are here for all 1345 players. Coming in first is our returning Greed and Zero Sum champion Wizard Dude with only three losses–one to NoJuice4u and two to me.
Second place goes to our other returning Zero Sum champion, NoJuice4u, with 1527 wins and only six losses. Staying in first place almost the entire competition, NoJuice4u was the main target of up-and-coming players, but he still managed to fend them all off with a nearly unstoppable job-queue-based strategy mixing all four unit types.
Because red and blue sides were mirrored for this level, you can see just how close a fight it was in the replays for these two matchups between Wizard Dude and NoJuice4u:
Wizard Dude (blue) vs. NoJuice4u (red) (or on YouTube)
Wizard Dude (red) vs. NoJuice4u (blue) (or on YouTube)
Third place went to MountainRunner, who I just couldn’t manage to beat myself–using many more soldiers than the other top players, he only accrued 11 losses in total. It’s not just all about amassing a huge force of kiting, missile-dodging archers!
I asked Wizard Dude and NoJuice4u about their strategies, and they shared their code and general AI structure, which I’ve linked to and excerpted below. I also spent a lot of time playing this one, trying to win it (but planning to exclude myself from the final rankings). I used all my tricks at the last minute and managed to win 1531 games with 4 losses, but even with all that, I still couldn’t surpass Wizard Dude. I’ll share my code and strategy as well.
If you read these strategies and want to play with ideas to defeat them, go ahead and play–the Ace of Coders ladder will remain open!
Wizard Dude (Michael Heasell)
The game is largely about gaining control of the centre point and trying to outplay the opponent’s army to gain a unit advantage. My army strategy focuses mainly on archers, using kiting tactics to keep them alive for as long as possible while dealing damage to priority targets.
To gain control of the centre, I simply move my goliath directly to the point and stand on top of it. Of course, the enemy goliath will usually try to do the same thing, which results in a wrestling match between the two goliaths. I use my goliath’s abilities to try and keep them away while I accumulate a gold advantage.
The farA and farB points are usually only defended by one unit and it can be difficult for an opponent to recapture them once the battle for centre has started. I build an artillery at the start of the game and, if it survives the initial conflict, I use it to pressure these points.
Once the battle for centre has been decided, I clean up the remaining units on the map, then send units to attack the enemy goliath until he goes down.
He lists a few clever tricks he uses:
I immediately build three archers and send them towards centre to secure it as quickly as possible. I use buildXY to summon them in front of me to save a bit more walking distance. I then summon two more archers, one for each of the nearA and nearB points. Again, I use buildXY to summon them closer to their respective points. Finally, I summon an artillery.
In my opening, I use buildXY to build the artillery behind and a bit off to the side of my goliath. I found that, from this unexpected position, it is sometimes possible to survive the first shot from an enemy artillery while also taking theirs out.
Another important topic is when to build units. The goliath is usually right next to enemy forces, which is not where you want archers to be, so summoning at the wrong time can lead to their immediate death. To cope with this, my strategy prevents spawning units if any of the following conditions are met:
- We are on the opponent’s side of the map and the enemy goliath is behind us.
- We are in splash range of an incoming artillery shell or boulder.
His artillery strategy was particularly effective:
Artillery has the longest range in the game, so as long as you stay far back, the only real threat is another artillery piece. My logic goes like this, in priority order:
- If an enemy artillery is in range, fire at it.
- If the artillery isn’t in range, but is within range + 10, fire at max range in their direction to try and hit them with splash damage.
- If we are less than 4 seconds in and the enemy doesn’t have enough gold to buy an artillery, attack the ground in front of their goliath.
- If an enemy tower is in range, attack it.
- If an enemy unit comes too close, move away from them.
- If we are near where an enemy shell will land, move away from that location.
- If there’s an enemy artillery on the field, but it’s not in range, use a forces algorithm to steer towards it while trying to avoid any other enemy units on the field.
- If farA or farB belong to the enemy team, attack their position, choosing whichever one is nearest if they have both been captured.
- Attack the nearest enemy tower.
- Attack the enemy goliath.
He summarizes his tournament experience:
Winning was pretty tough, and I should credit NoJuice4u for playing the game of ping-pong with me as we both repeatedly adjusted our strategies to one-up each other in fights. Nick Winter was also a fierce competitor, though I’m sure that at least some of his advantage was due to having built a large portion of the game. :)
Let’s check out some of his threat avoidance strategy:
Wizard Dude has posted a full, detailed strategy writeup here and shared his full solution on GitHub.
NoJuice4u (Wayne Chen)
This time around, I had to structure my code more carefully otherwise it would become very difficult to analyze and debug. To accomplish this, I created a "Job Queue" object to effectively assign jobs to various units. That way, an archer assigned to "Capture" will focus on capturing, and not assault. This also means that while my opponent can identify the type of unit I have, it is largely unknown what purpose that unit actually serves.
Alongside the job queue structure, he came up with an advanced danger zone detection algorithm:
My findDangerZones function is essentially the backbone of my strategy. While many of the tournament players have a strategy to move their units out of AoE effects, the difference with mine is that I also calculate whether my unit will walk into an AoE effect, and move along the perimeter to get closer to the target, while staying out of danger. That's why they rarely "bounce in and out". However, there's a few edge-case issues that cause the bounces to occur when multiple AoE's overlap. Each "Danger Zone" has a type category and a defined radius, so that I can keep a short distance from soldiers, medium distance from the boulder, and great distance from the artillery shell.
I take these into consideration as "AoE Effects":
- All artillery shells (obviously).
- Goliath throw boulders.
- Enemy Goliath.
- Arrow Towers that are targeting the unit.
- The farthest point from the enemy goliath that is within 8 units of my goliath, if I am close enough, and hurl is not on cooldown. (Where I will likely hurl him)
- Enemy Soldiers.
Wizard Dude commented on NoJuice4u’s job queue and danger zone approach:
I liked NoJuice's job queue approach to assigning units -- I actually also invented something similar, but later threw it away because it was too complicated. My idea was to have a "bucket" for each point, and calculate the "size" of each bucket based on distance and enemy presence in the area. Then I would sort the buckets from smallest to largest and assign units until the first was full, then repeat on the next bucket until out of units. This would allow me to pressure side points if centre became too hard to take, or deal with base swap scenarios. NoJuice's take on it seems to be superior though, as his definition of a job is more flexible and he considers the roles each unit can perform (and, of course, because his idea actually made it to his final version).
His danger zone logic to prevent units dithering in and out of dangerous areas is also pretty interesting. I tried for some time to come up with a forces based approach (since it worked so well for gold collection in greed and zero sum) to solve this problem for archers, which would allow them to path towards an attack target or objective via the least dangerous route. However, I found it too difficult to properly tune, so I gave up on it. In practice I didn't notice too many issues with my archers dithering, but I did have this problem with artillery. This is why my artillery *does* use forces to navigate when trying to approach an enemy artillery. It's pretty poorly tuned but it worked for the scenario I was having trouble with.
NoJuice4u had most advanced danger zone avoidance pathfinding routine I saw:
NoJuice4u has posted a full, detailed strategy writeup here and shared his full solution on GitHub.
I only build archers, trying to build up an archer superiority. If the enemy builds an artillery or tower that my archers or hero aren't close enough to quickly kill, I'll build a reactive artillery to help destroy it.
I used a simpler method of having my archers flee from soldiers, the enemy hero, towers, and incoming artillery shells and enemy boulders. They just run straight away from the center of the danger until they're far enough away, and if they run into a wall, they stand their ground and die! I don't handle overlapping danger zones very well, instead trying to prevent the enemy from making multiple types of danger zones at once. MountainRunner was able to defeat me by making more soldiers than I was expecting to deal with, and hiding archers behind them so that I couldn't kite them.
My big advantage on the dodging of enemy artillery shells came from the way I determined whether a shell needed dodging. Where Wizard Dude dodged any shell that was within 20m of its intended target, and NoJuice4u dodged any shell regardless of when it was fired, I only dodged shells that had a negative z-velocity--they were already halfway through their flight trajectory. Since shells take about four seconds to land, this let my archers keep firing much longer before fleeing, even if the aggressive artillery was shooting from nearby. Another key optimization I added at the last minute was to not build anything if a shell or boulder was about to land on me until it had safely exploded on my sturdy goliath.
I also really aggressively went for the center point, even going so far as to have my own goliath hurl himself toward the center at the beginning of the match to get there faster, never mind the self-inflicted damage. My goliath would often avoid even attacking so that he wouldn't pause for a split second in issuing his commands to his archers, so that the archers could dodge and target more responsively. He mostly tried to stand exactly on the center point so that the other goliath couldn't take it from him, thus building up a resource advantage at the cost of his own health until the late game.
To try to build up my archer horde, I built the archers away from the enemy hero, unless he was closer to the center, in which case I rotated my build offset by 90˚ in hopes that I could hurl the enemy goliath to the other side soon.
I dedicated only one unit to controlling each point unless I didn't yet own it, in which case I would send a larger squad. If I managed to build up an advantage, I was often able to take all the points except the far corner while the enemy did heavy damage to my hero, but then all the gold was mine and I'd get a giant ring of archers to end the match in a matter of seconds.
I’ve posted my full solution here: http://pastebin.com/8WLcE2wj
If you enjoyed this, subscribe to the blog or the newsletters that come with your CodeCombat account to learn when the next tournament is available, and play some Ace of Coders to get ready.