We're seeing some capacity limits, so parts of the site may be slow or briefly unavailable while we work on an upgrade. Please try again later, or join our Discord for updates.
Discord
Dev blog
Release notes

How a game of Dota should actually end

Dota Captain
7 min read

The story of rebuilding how my simulated matches close, from breaking high ground and taking the Tier-4 towers to reworking Roshan, map control, and the whole economy underneath it.

For too long my simulated games ended in ways that would make any Dota player wince. A team would beeline down one lane straight to the throne, a thing that basically never happens in a real game, and win off the back of it. Or worse, a match that was clearly decided by the twenty-minute mark would keep grinding on for another forty pointless minutes, trading meaningless fights while the outcome sat frozen. Every time I watched a replay end like that I felt the same small wince, because I know how a real game ends, and this was not it. So I sat down and rebuilt the ending from scratch, and in doing so I ended up rebuilding a lot more than the ending.

The way games were ending was wrong

The core problem was that my engine treated the end of a game like a race to a single number. Push a lane, chip a structure, hit the Ancient, win. That is not how Dota works, and I knew it, but the old logic let a team funnel everything into one lane and punch through to the throne without ever really earning it. Real games do not resolve that way. You do not just walk to the enemy Ancient because a health bar happened to reach zero first.

The other failure was pacing. A decisive teamfight, the kind that in a real match ends things, would land, and then nothing would happen. The game would refuse to close. It would drift on because the engine had no concept of one team having earned the right to finish. It only knew how to keep trading. Both of these bugs came from the same missing idea, and it took me far too long to name it out loud.

The idea is this: a game of Dota ends when one team earns the right to break high ground, not when a counter hits zero. Everything I rebuilt in 0.6.1 flows from that one sentence.

Rebuilding the base siege

Now a game actually finishes the way it should. The winning team breaks into the base, takes the Tier-4 towers that guard the Ancient, and only then destroys the Ancient itself. That last stretch, the throne, is protected, and it has to be pried open. A decisive teamfight can finally close a match instead of letting it limp along, and the one-lane throne rushes that used to bug me are now much rarer.

I also surfaced this in the map viewer, because I wanted you to be able to see it happen. The pair of Tier-4 towers guarding each Ancient now show up on the map, so when a base siege is underway you can watch the defenses collapse before the throne falls. It is a small thing visually, but it turns an abstract number crunch into something that reads like a real ending. You see the base opening up.

This is the transferable idea if you take nothing else from the post. High ground is a wall, not a finish line. A team that is ahead still has to commit, still has to break Tier-3s across the map, still has to fight up the ramp into the base. Games flip on high ground precisely because the defending team gets one last structural advantage. My engine ignored that for far too long.

Macro that actually resembles a real game

Once the ending was honest, the run-up to it had to be too. Dominant teams now take map control and wear down the outer towers across multiple lanes over time, rather than tunneling one lane. That is what a lead actually looks like in high-MMR play. You do not win by picking a single lane and forcing it. You win by making the whole map yours and slowly suffocating the other team's space.

I also fixed the fights that fed into all this. Roshan fights, sieges, and other objective contests used to resolve into guaranteed team wipes, which is absurd. Real objective fights are messy and rarely clean five-for-nothing. Making them survivable changed the texture of entire games, because now a team can lose a Roshan fight without instantly losing the game, which is how it should be.

Taken together, match length, structure counts, and kill totals all land much closer to real high-MMR games than they used to. I care about those three numbers a lot, because they are the easiest way to catch when a simulated game has drifted into fantasy. When they are off, something upstream is lying.

Roshan the way teams really treat him

Roshan got his own rework, because the way my engine handled him did not match how anyone actually plays around him. Teams that control the map or win a key fight now secure Roshan and the Aegis first, and use that as the setup before they commit to a high-ground push. That ordering matters. The Aegis is the insurance policy that makes a risky siege worth attempting, and my old logic did not respect that relationship at all.

I also fixed a smaller but nagging detail. An uncontested Roshan attempt is now completed in a single take, rather than being weirdly chipped down over several separate visits. If nobody is there to fight you, you kill Roshan and you leave. Watching a team make three trips to an unguarded pit was one of those little tells that quietly broke my trust in the whole simulation.

The lesson here is that Roshan is not a farm objective, he is a tempo objective. You take him to buy the right to do something aggressive next. Once I modeled him as a setup for high ground rather than a snack, the late game started making sense.

One economy instead of a pile of patches

This is the part I am quietly proud of. In an earlier post I wrote about chasing a real gold economy, and the honest truth of that chase was that I had built a stack of corrective systems, one patch on top of another, each trying to fix a symptom the last one missed. It worked, sort of, but it was fragile and I never fully trusted it.

In 0.6.1 I threw that pile out and rebuilt the match economy around a single idea: map control. Gold is now earned through towers, farm access, vision, objectives, and Roshan control, which are the things that actually generate gold in a real game. One coherent model replaced several corrective ones. The payoff is that net-worth swings, roles, last-hit counts, levels, and comeback patterns all became more believable at the same time, because they now share one honest source instead of fighting each other.

I also cleaned up a specific bit of nonsense in the timeline: lone supports no longer pull off implausible solo kills on heavily farmed cores. Isolated pickoffs now favor heroes who could realistically secure them. It is a detail, but details are where a simulation earns or loses your trust, and a position five soloing a fat carry is exactly the kind of thing that makes you stop believing the replay.

Still an approximation, still improving

I want to be clear that none of this makes the engine correct. It is an approximation of a game that real people play with far more nuance than any model of mine can capture, and I get things wrong constantly. Plenty of these fixes came from players noticing something felt off and telling me, which is genuinely how most of the good changes happen. I am one person building this in spare time, and the bug reports are half my roadmap.

What I can say is that games now end the way games should end. A team earns map control, secures Roshan, breaks high ground, takes the Tier-4 towers, and only then touches the throne. When I watch a replay finish now, I no longer wince. That is a low bar, but it is a real one, and it took rebuilding the ending, the macro, Roshan, and the entire economy to clear it. There is still plenty I have not gotten right, and I will keep chipping at it.

Release notesMatch engine