{"slug": "9-months-building-an-advanced-starcraft-reporting-tool-with-go-and-claude", "title": "9 months building an advanced StarCraft reporting tool with Go and Claude", "summary": "A developer spent nine months building screpdb, an advanced StarCraft: Brood War reporting tool using Go and Claude, after finding existing tools like BWChart, SCRChart, RepMastered, and vespene.gg insufficient for extracting higher-level semantics from replay files. The tool parses .rep files via the screp library, stores structured data in SQLite, and exposes an MCP server for natural-language queries, though the creator pivoted from a chatbot to a reporting tool after real usage felt off.", "body_md": "# 9 months in: building an advanced StarCraft reporting tool with Go & Claude\n\n## How it started\n\nWhen I was 15, I met my best mate at a LAN party in downtown Buenos Aires, right as the StarCraft: Brood War craze was catching fire. We fell in love with the game on the spot.\n\nIt’s 2026 now, we have jobs and live on opposite ends of the world, but we still Zoom almost every weekend to play and mess around. And through that decades-long passion came an idea.\n\n## The idea, on a weekend call\n\n“What if we could just *ask* an AI in natural language about the game we’d just played?”\n\ne.g.:\n\n- Does this guy always go Carriers?\n- Did all 3 of them ally against us last game?\n- What’s the average time I should have turrets against this particular Zerg?\n\nThe natural shape for that is an [MCP server](https://en.wikipedia.org/wiki/Model_Context_Protocol)\n, but what do we feed to it?\n\nBrood War writes a replay file (a `.rep`\n\n) after every game, which lets the game client play the game back to you. But MCP doesn’t read StarCraft replay files.\n\nSo I went looking for parsers, and András Belicza’s [screp](https://github.com/icza/screp)\nwas the natural choice (which is already written in Go).\n\nThus, `screpdb`\n\nwas born:\n\n- Parse replays using\n`screp`\n\n- Structure the output and ingest it onto a relational database (SQLite)\n- Put an MCP server in front\n\nThe [first commit](https://github.com/marianogappa/screpdb/commit/70a437bc65b24d3307683de7fe9a706fed9c014f)\nlanded on **September 30th, 2025**.\n\n## From chatbot to reporting tool\n\nThen we actually used it for a couple of sessions, and something felt off. Typing questions to a chatbot was an impedance mismatch.\n\n*The first version: Claude querying screpdb over MCP and drawing its own chart. Powerful, but asking a chatbot to rebuild every view, every time, got old fast.*\n\nWhat we wanted wasn’t a conversation. It was a *reporting tool*.\n\nHowever, that’s a much bigger build. Before I’d get into that, I’d want to see what options already existed.\n\n## The tools that already existed\n\n** BWChart\n**is the pioneer tool we all used since 2003. Its last update was in 2017, after which SCRChart (below) superseded it. Its\n\n[Liquipedia page](https://liquipedia.net/starcraft/BWChart)has the full history.\n\n** SCRChart\n**is the 2019 heir to BWChart, though it seems to have been abandoned later that same year. Being a Java application, it’s cross-platform.\n\n** RepMastered\n**is\n\n[András Belicza](https://github.com/icza)’s excellent web database and in-browser analyzer, built on his\n\n`screp`\n\nparser, which this whole project stands on ([intro thread](https://tl.net/forum/brood-war/558819-repmastered-replay-sharing-and-analyzer-site)). It holds\n\n[over 100 million replays](https://tl.net/forum/brood-war/558819-repmastered-replay-sharing-and-analyzer-site?page=13#246)and is fully featured:\n\n- Bulk-upload and share replays, even whole folders at once.\n- A huge, filterable database: search by player, map, matchup, or tag.\n- In-browser analysis: APM/EAPM, hotkeys, build-order and strategy charts, and an animated map replay.\n\n** vespene.gg\n**is the newest arrival (2026) and the boldest: according to its creator, dethsc, responding to my question in the\n\n[intro thread on TL.net](https://tl.net/forum/brood-war/645147-vespenegg-bw-replays-in-browser), it’s a fresh reverse-engineering of SC:R’s binaries, which lets you upload a replay and watch the game in-browser, with all kinds of real-time metrics, an AI coach, a replay database, tournaments, Twitch streams and more.\n\nThese are great tools. But what we needed wasn’t quite covered. We wanted to see **higher-level semantics** during our play sessions.\n\nBut before building the reporting tool, I had to learn how tricky it was to get semantics from a replay file.\n\n## Why StarCraft replays are secretly, wonderfully hard\n\nA StarCraft replay does not record what happened. It records **only the commands**: the stream of orders each player issued.\n\nThe engineering of the game engine and replays is fascinating, but it’s not the focus of this article; if you’re interested, I vibe-coded this didactic site that goes deeper into it while staying approachable: [ Inside Brood War](https://marianogappa.github.io/inside-brood-war/)\n.\n\nIn short: in order to know what really happened in the game, you’d need to run the commands against the game engine, but as of SC:Remastered, there’s no headless API to do this (BWAPI & OpenBW exist for pre-remastered but come with tradeoffs; dethsc’s reverse-engineering of Remastered is closed source).\n\nThis means that certain things just cannot be answered from replays alone:\n\n- How many resources does each player have over time? Is a player supply-blocked right now?\n- A unit was created, but was it killed later? Was a building destroyed?\n- A unit was ordered to go somewhere, but was it able to arrive there? When?\n\nAnd the commands themselves are noisy:\n\n**Spam.** Plenty of orders were recorded but rejected by the engine (not enough resources, cancelled builds, a worker killed on the way), and we often can’t tell which ones actually happened.**Missing data.** Commands that ought to carry coordinates, or a source unit or target, frequently just don’t.**Ambiguous units.** Units are identified by a recyclable 16-bit “unit tag”, not a type, so it’s often impossible to say which unit did what.\n\nBut then AI started getting so good, so fast, that I could prototype things that would have been impossible back when BWChart and SCRChart were built.\n\n## So, how do you get semantics anyway?\n\nHere are the main techniques screpdb uses to turn that messy command stream into meaning.\n\n### 💎 Understanding the map with base polygons\n\nThis is a big one.\n\nscrepdb leans on a companion library I wrote, [scmapanalyzer](https://github.com/marianogappa/scmapanalyzer)\n, which carves each map into *base polygons*, one per start location and expansion, each with:\n\n- the polygon outlining its ground area\n- its centroid, and the path to its natural (for start locations)\n- its clock position (“at 5”)\n- flags like “mineral-only” or “natural expansion”\n\n*Big Game Hunters, as screpdb sees it: every start, natural, and the center expansion as a labelled polygon.*\n\nThis allows things like narrating game events in terms of “expanded to their natural (mineral only)”.\n\nAnd because it knows who owns which base and where buildings sit, it can flag all these semantics:\n\na Photon Cannon built early, right in the opponent’s base.[Cannon rush](https://liquipedia.net/starcraft/Cannon_Rush):a Pylon dropped inside the enemy’s mineral line to block mining (the impolite inverse of a proxy).[Manner pylon](https://liquipedia.net/starcraft/Manner_Pylon):a production building planted forward in the enemy’s half of the map, but not inside a base.[Proxy gate](https://liquipedia.net/starcraft/Proxy):**Offensive** a Zerg Nydus Canal exit surfacing inside enemy territory as a real army insertion.[nydus](https://liquipedia.net/starcraft/Nydus_Canal):the Big Game Hunters classic of dropping a Siege Tank onto the cliff behind certain start locations, then shelling the mineral line from safety.[Cliff drop](https://liquipedia.net/starcraft/Big_Game_Hunters/Cliffing):\n\n💩 It doesn’t always get them right, but it’s promising so far. More people poking at them is exactly how they get sharper.\n\n### 💎 Which buildings were *actually* built\n\nThe raw replay logs every misclick, re-placement, and cancelled order, so a single re-placed Gateway can look like three. screpdb cuts through that:\n\n- From the unit-selection stream, it works out which building produced each unit.\n- Then it drops any production building (Gateway, Barracks, Factory, and friends) that never actually produced anything.\n\nWhat’s left is the buildings that really stood and did something, which is what makes “2 Gate” reliably mean 2 Gate.\n\n💩 But a Gateway built just to wall a choke, one that never pumps a unit, wrongly disappears. Pick your battles 🤷\n\n### 💎 Build-order detection\n\nTo read an opener precisely (building on the step above), screpdb runs a tiny [per-player economy simulation](https://github.com/marianogappa/screpdb/tree/main/internal/earlyfilter)\nover the first few minutes:\n\n- It seeds the correct starting resources and supply, then walks the commands in order.\n- It drops any command it couldn’t have afforded: not enough minerals, not enough supply.\n- A backtrack pass fixes over-filtering, using Brood War’s own rule that it refuses orders without prerequisites.\n\nThe result is realistic worker and building counts, so a “9 Pool” reads as a 9 Pool and not as inflated spam.\n\n💩 Build Orders are not just hard to infer (and imperfect to measure), they’re also hard to name. This is a work in progress. You can help improve this feature by [filing an issue](https://github.com/marianogappa/screpdb/issues/new/choose)\n.\n\n### 💎 Skill proxies\n\nBeyond APM, here are some worthy measurements available with replay data:\n\n**Viewport multitasking:** how often your screen jumps to a distant part of the map, a proxy for managing several fronts at once.**Unit-production cadence:** how*steadily*you pump army, rewarding an even stream over feast-and-famine bursts.**First-unit efficiency:** how quickly you actually use a new production building once it finishes, instead of letting it sit idle.\n\n💩 They’re proxies, not verdicts, and might need some tweaking. Happy to hear other ideas, but note that replays come with strict limitations.\n\n## A few more favourites\n\nFighting replay uncertainty was only half of it. The rest was building things that are genuinely useful, or just delightful, to look at.\n\n**✨ Alliance and team-stacking detection.** On melee games, alliances are manual, so who’s *really* on whose side is a live question. screpdb reconstructs the alliance timeline and flags when teams are lopsided, like a “3v2 stacked” the lobby never advertised.\n\n**✨ Stage-and-watch.** Find a game via filtering on the game list, click once, and screpdb stages that replay so you can watch it in the actual game client. Report to replay in one click.\n\n**✨ Player fingerprints.** It flags telling habits, like a player who never bound a hotkey, or never took a single upgrade or research the whole game.\n\n**✨ Staying fast.** All of this is only useful if it stays responsive over thousands of replays, so ingestion throughput is benchmarked automatically on every merge.\n\n## Has AI made all of this?\n\nNo, it enabled it. Throughout the 9 months I spent developing screpdb, AI kept getting better and better:\n\nAnd it allowed a backend engineer with no UI chops whatsoever to go from this:\n\n*The old dashboard: functional, but a flat wall of numbers.*\n\nTo this:\n\n*The current per-game summary: one .rep, fully unpacked into build orders, unit composition across early/mid/late game, spellcasts, and per-player stats.*\n\nBut even with this colossal swiss army knife, I had to fight the ways AI quietly gets things wrong. The code is correct, reads well, passes the tests, and is often quietly wasteful. It never shows up on three replays, only once you point it at thousands:\n\n- It built the dashboard’s queries with\n**no composite indexes**, so every panel full-scanned the commands table ([fixed](https://github.com/marianogappa/screpdb/commit/8e038734cf0878ad709aa09f01acbbffccc6f66f)). - It kept\n**re-doing work it had already done**: re-computing expert timings on every page load, re-scanning the command table for a signal it had already stored ([#101](https://github.com/marianogappa/screpdb/pull/101)). - And some things it simply\n*couldn’t*do. No model just knows a game was a cheeky proxy 2-gate, so I watched and labelled[171 replays](https://github.com/marianogappa/screpdb/tree/main/internal/patterns/markers/testdata/replays)by hand to teach the detectors what’s real.\n\n## Can you build on top of it?\n\nThe dashboard is only the friendly face. Underneath, screpdb is a plain database with an API, so you can wire it into your own tooling:\n\n- Ingest replays straight from the CLI (\n`screpdb ingest`\n\n). - Run the server headless (\n`screpdb dashboard --headless`\n\n) and hit it programmatically. - Every dashboard feature is exposed over a documented\n[OpenAPI](https://github.com/marianogappa/screpdb/blob/main/api/openapi/dashboard.v1.yaml)surface.\n\nThere’s more in the [developer docs](https://github.com/marianogappa/screpdb#developer-features)\n.\n\n## I’d love it if you’d check it out!\n\nIt’s fully free 🍺 and open source. No donations needed, don’t buy me a coffee.\n\nIt ships with a **sample replay pack**, so you don’t even need your own replays to see what it does:\n\nOn **Windows**, install with [Scoop](https://scoop.sh)\n:\n\n```\nscoop bucket add screpdb https://github.com/marianogappa/screpdb\nscoop install screpdb\nscrepdb-gui\n```\n\nOn **macOS**, install with [Homebrew](https://brew.sh)\n:\n\n```\nbrew install marianogappa/screpdb/screpdb\nscrepdb\n```\n\nOn **Linux**, use the one-line installer:\n\n```\ncurl -fsSL https://raw.githubusercontent.com/marianogappa/screpdb/main/install.sh | sh\nscrepdb\n```\n\nFull instructions are on the [project page](https://github.com/marianogappa/screpdb#installation)\n.\n\nFeel free to let me know if you enjoyed it, and please [file an issue](https://github.com/marianogappa/screpdb/issues/new/choose)\nif there’s a problem.\n\nBut honestly, the tool is the least important part of this. What’s worth holding onto is people you’ll still be playing and laughing with decades from now. I hope screpdb helps a little. Keep playing together!\n\n*screpdb is open source (MIT) and lives here\n. Built on icza/screp\n, without which none of this would exist.*\n\n*screpdb isn’t a competitor to RepMastered or vespene.gg, it’s complementary! Each do slightly different things. András helped me build screpdb through knowledge transfer, others helped betatesting too like 2Pacalypse- from TL.net. And I hope screpdb’s most useful features are rightfully stolen and improved upon 👍*", "url": "https://wpnews.pro/news/9-months-building-an-advanced-starcraft-reporting-tool-with-go-and-claude", "canonical_source": "https://marianogappa.github.io/thoughts/building-an-advanced-starcraft-reporting-tool/", "published_at": "2026-07-21 05:03:32+00:00", "updated_at": "2026-07-21 05:23:02.711049+00:00", "lang": "en", "topics": ["developer-tools", "artificial-intelligence", "large-language-models"], "entities": ["screpdb", "Claude", "Go", "screp", "András Belicza", "BWChart", "SCRChart", "RepMastered"], "alternates": {"html": "https://wpnews.pro/news/9-months-building-an-advanced-starcraft-reporting-tool-with-go-and-claude", "markdown": "https://wpnews.pro/news/9-months-building-an-advanced-starcraft-reporting-tool-with-go-and-claude.md", "text": "https://wpnews.pro/news/9-months-building-an-advanced-starcraft-reporting-tool-with-go-and-claude.txt", "jsonld": "https://wpnews.pro/news/9-months-building-an-advanced-starcraft-reporting-tool-with-go-and-claude.jsonld"}}