Pucker Up For A Punch-up

Ben Steenhuisen
datdota
Published in
5 min readNov 15, 2020

Back in 2014, Beyond The Summit created (along with some 3rd parties they hired) the ‘teamfight recap’. It’s goal was to answer a simple question — “what just happened in that fight?”. It did this in a succinct manner, and over time they iterated on it — improving the quality of the data it showed and the presentation. The project was shuttered after Valve, without notice, compensation or recognition, implemented an inferior version as part of the game client.

One of the earliest versions of the BeyondTheSummit teamfight recap.

Whilst the ethics of Valve’s action was obviously questionable, it did provide succinct summary data of potentially complex teamfights (due to legacy reasons, even a 1-on-1 kill is still considered a ‘teamfight’) for all spectators to see — in all tournaments.

Valve’s Source 1 implementation

The presentation of the data in Valve’s version became more and more complex (showing all individual player shifts instead of overall team shifts) but also less accurate (frequently wrong values, or misleading values). People not involved in the fight are included, even if they were farming on the other side of the map their gold shift would increase as if they were in the fight. There was also no way to differentiate completely distinct teamfights occurring on different parts of the map — if people were fighting anywhere it was rolled into one big teamfight.

BTS’s product could’ve grown to become more accurate and provide more context, but simply because Valve produced an (inferior) product in-client, the marginal value they would gain in providing an improved product was reduced. Valve has never really spent any more time in improving teamfight recaps beyond this.

At datDota, data collection using the Valve-like logic was implemented in early 2015, but was not really in a useful format — and didn’t end up getting used too much for anything (analytics, in-game statistics). Getting the data collection accurate and represented in a meaningful way was always a long-term goal of mine — and over time (with loads of iterations) the data is now good enough to be turned into some visualization for analysis. Dota 2 gameplay can really be reduced (on a per-player basis) in the following flow-chart (shown with some associated statistics we care about tracking in each stage).

Simple game flow representation, and associated stats (or, lack thereof)

In this way teamfights act as a natural way to split up the game into segments, since at a point in time every player is either involved or not involved in an active teamfight. At no other time in the game is there such consistent synchronization across multiple players in the game. In this way objectives are either the precursor to a teamfight, the reward for winning a teamfight, or the reward for dodging a teamfight.

As of now, we’ve parsed a little over 1 million teamfights, of which 640k are in professional games (remember that ticketed semi-professional games are tracked in order to help recognize outliers in low-sample situations). The teamfights are categorized by the number of people involved (who have dealt damage, or been dealt damage) from each side — with distance and damage thresholds to reduce edge cases:

  • SOLO fights are 1 vs 1
  • GANKs are 1 vs X where X >1
  • BATTLE’s are when it’s X vs Y where X, Y ≥ 4
  • SKIRMISH’s are all other cases
Teamfight distribution averages PER PLAYER per game, ordered by patch (note that 7.09 — 7.18 had relatively few games so might appear more spiky)

It’s almost amazing that despite so many changes in the underlying game of Dota 2 in various patches, the structural outcome remains so similar.

From this we can dig further into the solo kills, and evaluate which players average the most per game & die solo the most per game. Noticing some names appear on both, this also naturally inspires asking the question which players have the extremes of (avg solo kills - avg solo deaths) as an aggregate method for the solo kill ‘shift’ averages in a game.

Solo statistics for highest avg kills | highest avg deaths | highest avg kills-deaths | lowest avg kills-deaths

We can also consider Battles and Skirmishes that occur after the 10 minute mark (this is fairer because cores are mostly ‘stuck’ to their lane for the laning stage, and are not free to roam around and fight so early — there’s no decision by them in these cases). Obviously we expect to see supports right at the top of fight involvement (like, Victoria, SVG) and hard carries towards the bottom (like K1, Ritsu, Ame) — but it’s still interesting to quantify their involvement in a manner more accurate than how Valve’s Fantasy system reports teamfight participation: (player kills + player assists) / total_kills_by_team.

Top and bottom end of the post-10 minute teamfight involvement for Battles & Skirmishes

We can then also dig deeper into these teamfights to see what players get, on average, for attending one. No[o]ne for example gets 1.1 kills+assists (= 0.5 kills + 0.6 assists) for every battle or skirmish he goes into, only dying on average 0.21 per teamfight. On the other extreme, March and ixmike88 only get 0.68 kills+assists per teamfight they join. This decomposition can also be helpful later in calculating adjusted teamfight win shifts.

Average kills + assists | kills | assists | deaths in a game, sorted by kills + assists descending and ascending

And lastly we can do some interesting filtering — for example finding all Ganks (1vX) Gone Bad, in this case the worst Gank Gone Bad.

This is kind of just the tip of the iceberg for teamfight data, normally sourcing and verifying the data is a pretty straightforward task and there’s already a clear vision for how the data will be used. Here there are some ideas already (like adjusted win%) but the possibilities go way beyond that. I’ll definitely be adding some interface for teamfight analysis to the frontend — whenever that pesky Frontend 2.0 comes out …

Cheers, Noxville

--

--

Ben Steenhuisen
datdota

Dota 2 statsman and occasional caster | runs @datdota