Skip to main content

aoe2-openings

Read an Age of Empires II: Definitive Edition replay and get, for every player, the opening they played, their age-up times, the army, buildings and technologies of each age, and their economy: market trades, tributes, APM.

Every opening comes with the rule that named it and the counts behind it, so a call is never a black box:

$ aoe2-openings game.aoe2record
game.aoe2record
  Arabia · 22:33 · rated
  1. Player 1 (Chinese)  won
     Scout Rush  ·  Feudal 6:14  Castle 15:24  Imperial -  ·  43 villagers  ·  99.3 APM
     why: 3+ Scouts in the Feudal Age, with a Stable
  2. Player 2 (Britons)  lost
     Archer Rush  ·  Feudal 6:38  Castle 17:50  Imperial -  ·  48 villagers  ·  74.6 APM
     why: 4+ Archers in the Feudal Age

Age times are when the player clicked to advance, in game time.

These are the rules AoE2Meta used to classify more than half a million ranked 1v1 games and show which play styles win, by civilization, map and ELO. OPENINGS.md lists all 25 of them in plain English.

Install

pip install aoe2-openings

Python 3.10 or newer. It reads replays with mgz-fast, AoE2Insights' fork of mgz that keeps up with the latest game patches, and names units, maps and civilizations with aocref.

Use it

From the command line, for one replay or many:

aoe2-openings game.aoe2record                      # a short summary
aoe2-openings game.aoe2record --json               # everything, as JSON
aoe2-openings replays/*.aoe2record --json --timeline > games.json

From Python:

from aoe2_openings import analyze

game = analyze("game.aoe2record")      # a path, bytes, or an open binary file
print(game.map, game.duration_ms)

for p in game.players:
    print(p.name, p.civ, p.opening.name)
    print("  why:", p.opening.reason)           # 3+ Militia in the Dark Age, ...
    print("  feudal at", p.feudal_ms, "ms")      # None if they never got there
    print("  feudal army:", dict(p.units["feudal"]))   # {unit id: count}

game_dict(game) gives the same thing as plain data, with names next to every ID and times as m:ss, the way --json prints it.

To classify from counts you already have, without a replay, build the signals yourself:

from aoe2_openings import classify, signals

opening = classify(signals(
    civ="Franks", feudal_ms=600_000, castle_ms=1_080_000,
    dark_units={74: 3},             # 3 Militia
    feudal_units={4: 5},            # 5 Archers
    feudal_buildings={10: 1},       # an Archery Range
))
opening.name    # "Drush Archers"
opening.rule    # "drush_archers"

What comes out of a replay

Per player
Match Map, duration, rated or not, game and save version
Player Name, profile ID, civilization, team, colour, result
Ages When they clicked Feudal, Castle and Imperial Age
Villagers Queued in each age, and in total
Army Every unit queued, by age
Buildings Every building and wall segment placed, by age
Technologies Every technology researched, by age
Economy Market buys and sells, tributes sent
Activity Commands issued, APM, deletions
Timeline Running totals of army and villagers every 30 seconds (--timeline)
Opening Its name, the rule that matched, the reason and the signals

Limits worth knowing

  • Counts are commands. A replay is a log of what players told the game to do, not what happened. A unit queued and then cancelled still counts, and nothing about resources at a given moment can be read without replaying the match in the game.
  • The result is inferred. A replay doesn't say who won. A side whose players all resigned lost; if exactly one side is left, it won. Otherwise won is None.
  • APM is raw. Every command counts, so it runs higher than an effective (eAPM) figure.
  • Names follow aocref. After a patch adds a civilization or map, update aocref to get its name.
  • Tuned on 1v1. The opening rules were built on ranked 1v1 Random Map; OPENINGS.md has the known gaps.

Tests

pip install -e ".[test]"
pytest

Every rule has a test built from hand-made counts. To also run the tests on real games, put .aoe2record files in tests/replays/ or point AOE2_REPLAYS at a folder of them.

How it was made

I built this, like AoE2Meta, by vibe coding with Claude: I knew the game and what I wanted out of it, and Claude wrote the code. The full story is in the write-up.

Thanks to happyleavesaoc for mgz, which does the hard part of reading replays, to AoEInsights for keeping its fast parser current, and to the Siege Engineers for the reference data.

License

MIT. Age of Empires II: Definitive Edition is a trademark of Microsoft Corporation. This is an independent fan project, not affiliated with or endorsed by Microsoft, made under Microsoft's Game Content Usage Rules.

Metadata

Release files for aoe2-openings 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aoe2-openings 0.1.0
File Size Uploaded
aoe2_openings-0.1.0.tar.gz 16.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for aoe2-openings 0.1.0
File Interpreter ABI Platform
aoe2_openings-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 32.3 kB

Release files / aoe2_openings-0.1.0.tar.gz

Download URL aoe2_openings-0.1.0.tar.gz
Size 16.6 kB
Tags Source
SHA-256 checksum
How to use checksums
1de2b375f9d90c43991dc3855ac0245d53cd4cc418d39ce91423754e02d901da
BLAKE2b-256 checksum
How to use checksums
76baaeba111afaeb05595fa59b941c96ce2de53e3469f871943f11dd9aac83a4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.

Transparency log

Release files / aoe2_openings-0.1.0-py3-none-any.whl

Download URL aoe2_openings-0.1.0-py3-none-any.whl
Size 15.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
960ad539e23e1277b3c4aaf02a6573de968ce4c1a2f1fd35446941980ca4f6b1
BLAKE2b-256 checksum
How to use checksums
1d3616428479369ef888df5c352c289034a53805286980e8f933ae120165358d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page