Skip to main content

dnd5e-engine

Pure-Python D&D 5e SRD rules engine — host-agnostic, zero I/O. Combat, checks, effects, on a 2-D grid.

Status

Working engine. It resolves combat against the typed 2024-SRD corpus shipped by dnd5e-srd-data (the BundledAssetLoader reads the bundled canonical/ data — no network, no DB). The engine is edition-agnostic: it consumes whatever typed content it is handed.

Combat resolves on a 2-D grid: pass grid_scene=GridScene(width, height) to start_combat. Positions are "col,row" cell ids (cell_id(col, row)), and distance is Chebyshev (8-direction, one cell = cell_size_ft).

Install

Dev (editable, from this directory):

cd packages/dnd5e-engine
uv venv && uv pip install -e '.[dev]'
uv run --extra dev pytest -q
uv run --extra dev ruff check src/ tests/ scripts/
uv run --extra dev mypy src/

Standalone wheel:

uv build          # builds dist/dnd5e_engine-*.whl (+ sdist)

A clean-room install smoke builds both wheels, installs them into a throwaway venv with no editable path deps, and runs a real grid combat through the public API:

bash scripts/smoke_clean_install.sh   # ends with "==> SMOKE PASSED"

Quickstart

A minimal grid combat: open it, move a PC one cell, then close it.

import asyncio

from dnd5e_engine import (
    EncounterMemberSpec,
    GridScene,
    PartyMemberSpec,
    PlayerIntent,
    cell_id,
    end_combat,
    start_combat,
    submit_player_intent,
)


async def main() -> None:
    start = await start_combat(
        session_id="demo",
        party=[
            PartyMemberSpec(
                entity_id="char:hero",
                name="Hero",
                initiative=20,
                hp_current=12,
                hp_max=12,
                ac=12,
                zone_id=cell_id(0, 0),
            )
        ],
        encounter=[
            EncounterMemberSpec(
                entity_id="mon:foe",
                entity_type="Monster",
                name="Foe",
                initiative=1,
                hp_current=7,
                hp_max=7,
                zone_id=cell_id(5, 0),
            )
        ],
        grid_scene=GridScene(width=10, height=10),
        rng_seed=1,
    )

    # The hero won initiative; move one cell diagonally.
    await submit_player_intent(
        start.handle,
        actor_id="char:hero",
        intent=PlayerIntent(intent_type="move", target_zone_id=cell_id(1, 1)),
    )

    result = await end_combat(start.handle)
    print("ended reason:", result.outcome.ended_reason)


asyncio.run(main())

An attack instead of a move: submit PlayerIntent(intent_type="attack", target_id="mon:foe", weapon_id="longsword") — the engine fetches the typed weapon from the bundled corpus and walks its activities.

Public API

All exported names live in __all__ in src/dnd5e_engine/__init__.py. The key entry points:

  • start_combat(*, session_id, party, encounter, grid_scene, rng_seed, ...) — open a combat, materialize runtime state, return a StartCombatResult (.handle, opening .events).
  • submit_player_intent(handle, actor_id, intent) — validate and resolve a PC's PlayerIntent for the current turn.
  • advance_monster_turn(handle) — resolve the active monster's turn via its typed action repertoire + behavior gambits.
  • end_combat(handle) — close a combat and return an EndCombatResult (.outcome, .events, .final_active_effects).
  • narration_events(handle) — async iterator streaming the CombatEvent union for the narrator.
  • get_actor_active_effects(handle, entity_id) — read-only snapshot of one combatant's active effects.
  • resolve_check(spec) — resolve an out-of-combat ability check / saving throw (CheckSpec → CheckResult).
  • build_party_member(spec, ...) — project a CharacterBuildSpec into a combat-ready PartyMemberSpec.
  • make_build_spec(...) — assemble a CharacterBuildSpec (ability scores, class, species).

Spatial helpers and spec types:

  • GridScene / cell_id(col, row) / parse_cell(cell_id) — 2-D grid scene + cell-id codec.
  • PartyMemberSpec / EncounterMemberSpec — combatant inputs to start_combat.
  • PlayerIntent — a PC's submitted intent (move / attack / cast_spell / use_item / ...).
  • CombatHandle, StartCombatResult, EndCombatResult, CombatOutcome, CombatEvent, ActiveEffect, CheckSpec/CheckResult/CheckKind, CharacterBuildSpec, AbilityScores, and the IntentType / ActionType literals.

Layout

packages/dnd5e-engine/
├── pyproject.toml          hatchling build; pydantic + d20 + dnd5e-srd-data deps
├── LICENSE                 MIT (engine code)
├── scripts/
│   ├── _smoke_grid_combat.py     clean-room smoke program (runs in a fresh venv)
│   └── smoke_clean_install.sh    builds wheels + installs them + runs the smoke
├── src/dnd5e_engine/
│   ├── __init__.py         public API (__all__)
│   ├── orchestrator.py     start/submit/advance/end combat seam + live state
│   ├── spatial.py          grid topology, cell_id / parse_cell
│   ├── specs.py            GridScene, party/encounter specs
│   ├── check.py            out-of-combat ability check / saving throw resolver
│   ├── build_party.py / build_spec.py   character build → combat spec projection
│   ├── lib_loader.py       BundledAssetLoader singleton (typed SRD corpus)
│   ├── events.py / outcome.py / results.py   event union, outcome, result envelopes
│   ├── activities/         typed-Activity resolvers (attack / save / monster actions)
│   ├── rules/              dice, conditions, gambits, ...
│   └── types/              combat / effects / conditions / intent value types
└── tests/                  pytest suite

License

  • Engine code: MIT — see LICENSE. The engine ships no SRD data.
  • SRD content (the typed corpus consumed via dnd5e-srd-data): CC-BY-4.0 — see the dnd5e-srd-data package's LICENSE and NOTICE for the dataset license and attribution chain.

Metadata

Release files for dnd5e-engine 0.7.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 dnd5e-engine 0.7.0
File Size Uploaded
dnd5e_engine-0.7.0.tar.gz 847.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for dnd5e-engine 0.7.0
File Interpreter ABI Platform
dnd5e_engine-0.7.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.3 MB

Release files / dnd5e_engine-0.7.0.tar.gz

Download URL dnd5e_engine-0.7.0.tar.gz
Size 847.4 kB
Tags Source
SHA-256 checksum
How to use checksums
7cf8459a0cd663478682373b2783473977f1f5b181a8168f856000c03f6ba237
BLAKE2b-256 checksum
How to use checksums
89d3c8ac0567870f1403a5e2c9be91a7d3c944fff4e0371eae0292f2684fcbfc
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 10, 2026.

Transparency log

Release files / dnd5e_engine-0.7.0-py3-none-any.whl

Download URL dnd5e_engine-0.7.0-py3-none-any.whl
Size 411.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bd95949c1f1b4c84dea4da7ba4d0518b74f7229c0e421d13143e9db1ecd6de8a
BLAKE2b-256 checksum
How to use checksums
bdc5453ca54dbdd7d4993695a515ba3ab682c7c2e67ff042a848cf3d44190ffa
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 10, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.7.0 This release

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

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