dndwright 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dndwright-0.2.0/.github/workflows/ci.yml +27 -0
- dndwright-0.2.0/.github/workflows/publish.yml +41 -0
- dndwright-0.2.0/.gitignore +23 -0
- dndwright-0.2.0/CHANGELOG.md +53 -0
- dndwright-0.2.0/LICENSE +21 -0
- dndwright-0.2.0/NOTICE +24 -0
- dndwright-0.2.0/PKG-INFO +101 -0
- dndwright-0.2.0/README.md +78 -0
- dndwright-0.2.0/pyproject.toml +37 -0
- dndwright-0.2.0/src/dndwright/__init__.py +69 -0
- dndwright-0.2.0/src/dndwright/content/__init__.py +56 -0
- dndwright-0.2.0/src/dndwright/content/classes.json +147 -0
- dndwright-0.2.0/src/dndwright/content/creatures.json +334 -0
- dndwright-0.2.0/src/dndwright/content/generate.py +87 -0
- dndwright-0.2.0/src/dndwright/content/magic_items.json +1915 -0
- dndwright-0.2.0/src/dndwright/content/species.json +111 -0
- dndwright-0.2.0/src/dndwright/ontology/__init__.py +25 -0
- dndwright-0.2.0/src/dndwright/ontology/dnd.yaml +141 -0
- dndwright-0.2.0/src/dndwright/ontology/loader.py +104 -0
- dndwright-0.2.0/src/dndwright/rules/__init__.py +35 -0
- dndwright-0.2.0/src/dndwright/rules/adapters.py +691 -0
- dndwright-0.2.0/src/dndwright/rules/assembler.py +377 -0
- dndwright-0.2.0/src/dndwright/rules/character_evaluator.py +248 -0
- dndwright-0.2.0/src/dndwright/rules/components.py +654 -0
- dndwright-0.2.0/src/dndwright/rules/dnd_5e_2024.py +606 -0
- dndwright-0.2.0/src/dndwright/rules/evaluator.py +217 -0
- dndwright-0.2.0/src/dndwright/rules/lookup_tables.py +501 -0
- dndwright-0.2.0/src/dndwright/rules/operations.py +412 -0
- dndwright-0.2.0/src/dndwright/rules/schema.py +69 -0
- dndwright-0.2.0/src/dndwright/rules/theme_scaling.py +207 -0
- dndwright-0.2.0/tests/test_api_contract.py +53 -0
- dndwright-0.2.0/tests/test_content.py +72 -0
- dndwright-0.2.0/tests/test_engine.py +113 -0
- dndwright-0.2.0/tests/test_ontology.py +81 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: ${{ matrix.python-version }}
|
|
20
|
+
- name: Install
|
|
21
|
+
run: |
|
|
22
|
+
python -m pip install --upgrade pip
|
|
23
|
+
pip install -e ".[dev]"
|
|
24
|
+
- name: Lint (ruff)
|
|
25
|
+
run: ruff check src tests
|
|
26
|
+
- name: Test (pytest)
|
|
27
|
+
run: pytest -q
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
# Publishes to PyPI via Trusted Publishing (OIDC) when a GitHub Release is
|
|
4
|
+
# published. No API token needed — PyPI verifies this repo + workflow + env.
|
|
5
|
+
# To release: tag (vX.Y.Z) and create a GitHub Release for that tag, e.g.
|
|
6
|
+
# gh release create v0.2.0 --generate-notes
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
release:
|
|
10
|
+
types: [published]
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.12"
|
|
20
|
+
- name: Build sdist + wheel
|
|
21
|
+
run: |
|
|
22
|
+
python -m pip install --upgrade build
|
|
23
|
+
python -m build
|
|
24
|
+
- uses: actions/upload-artifact@v4
|
|
25
|
+
with:
|
|
26
|
+
name: dist
|
|
27
|
+
path: dist/
|
|
28
|
+
|
|
29
|
+
publish:
|
|
30
|
+
needs: build
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
environment: pypi # must match the "Environment name" in PyPI's publisher config
|
|
33
|
+
permissions:
|
|
34
|
+
id-token: write # REQUIRED for Trusted Publishing (OIDC)
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/download-artifact@v4
|
|
37
|
+
with:
|
|
38
|
+
name: dist
|
|
39
|
+
path: dist/
|
|
40
|
+
- name: Publish to PyPI
|
|
41
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
|
|
9
|
+
# Virtualenvs
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
env/
|
|
13
|
+
|
|
14
|
+
# Test / tooling caches
|
|
15
|
+
.pytest_cache/
|
|
16
|
+
.ruff_cache/
|
|
17
|
+
.coverage
|
|
18
|
+
htmlcov/
|
|
19
|
+
|
|
20
|
+
# Editors / OS
|
|
21
|
+
.vscode/
|
|
22
|
+
.idea/
|
|
23
|
+
.DS_Store
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to dndwright are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/), and the project aims to follow
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
**Public API** = the names exported in `dndwright.__all__` (pinned by
|
|
8
|
+
`tests/test_api_contract.py`). While the version is `0.x`, minor versions may make
|
|
9
|
+
breaking changes; these will always be noted here.
|
|
10
|
+
|
|
11
|
+
## [Unreleased]
|
|
12
|
+
|
|
13
|
+
## [0.3.0] — 2026-06-01
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- **Bundled starter content** — `load_content(category)` + `categories()`: original
|
|
17
|
+
homebrew classes/species/creatures, plus 236 SRD 5.2 (CC-BY) magic items.
|
|
18
|
+
- **LLM-agnostic content generator** — `generate_library(llm, ...)` (and
|
|
19
|
+
`generate_classes`/`species`/`creatures`): you pass a `complete_json(prompt, system)
|
|
20
|
+
-> dict` callable wrapping your own LLM; prompts produce *original homebrew* (no
|
|
21
|
+
official content), matching the bundled schema and component ontology.
|
|
22
|
+
|
|
23
|
+
## [0.2.0] — 2026-06-01
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
- **Component ontology** — `load_ontology()` → `Ontology`: a graph schema for D&D
|
|
27
|
+
building blocks (Class, Species, Spell, Equipment, MagicItem, Background, Feat,
|
|
28
|
+
Subclass, Creature) and how a Character connects to them (`HAS_*`, `INSTANCE_OF`,
|
|
29
|
+
`HAS_STAT_BLOCK`). Typed models (`NodeTypeDef`, `EdgeTypeDef`, `PropertyDef`) with
|
|
30
|
+
`edges_from`/`edges_to` helpers, parsed from the bundled `dnd.yaml`.
|
|
31
|
+
- Dependency: `pyyaml` (for the ontology loader).
|
|
32
|
+
|
|
33
|
+
## [0.1.0] — 2026-06-01
|
|
34
|
+
|
|
35
|
+
Initial release. The D&D 5e (2024) rules & character-computation engine, extracted
|
|
36
|
+
from a working application.
|
|
37
|
+
|
|
38
|
+
### Added
|
|
39
|
+
- `evaluate_character(data) -> dict` — one-call evaluation: character data in,
|
|
40
|
+
computed sheet out (ability modifiers, proficiency, saves, spell DC/attack, HP,
|
|
41
|
+
AC, initiative, …).
|
|
42
|
+
- The computation engine: `DND_5E_2024_RULESET` (a 135-node DAG), `evaluate`,
|
|
43
|
+
`assemble_character_inputs`, `apply_modifiers`, and the `Ruleset` / `ComputationNode`
|
|
44
|
+
/ `FormulaSpec` / `NodeType` schema — formulas as data, not code.
|
|
45
|
+
- Neutral adapters: `character_data_to_inputs`, `computed_values_to_sheet`.
|
|
46
|
+
- Typed component models under `dndwright.rules.components`
|
|
47
|
+
(`ClassMechanics`, `SpeciesMechanics`, …) and SRD-derived rules tables.
|
|
48
|
+
|
|
49
|
+
Pure (pydantic + stdlib); no application/framework coupling. Rules content derives
|
|
50
|
+
from the SRD 5.2 (CC-BY-4.0); see NOTICE.
|
|
51
|
+
|
|
52
|
+
[Unreleased]: https://github.com/sligara7/dndwright/compare/v0.1.0...HEAD
|
|
53
|
+
[0.1.0]: https://github.com/sligara7/dndwright/releases/tag/v0.1.0
|
dndwright-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Anthony Sligar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
dndwright-0.2.0/NOTICE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
dndwright
|
|
2
|
+
Copyright (c) 2026 Anthony Sligar
|
|
3
|
+
Licensed under the MIT License (see LICENSE).
|
|
4
|
+
|
|
5
|
+
------------------------------------------------------------------------------
|
|
6
|
+
Game-rules content
|
|
7
|
+
------------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
dndwright's rules tables (hit dice, spellcasting progression / spell slots,
|
|
10
|
+
armor base AC, save proficiencies, class spellcasting abilities, etc.) encode
|
|
11
|
+
game *mechanics* — facts about how D&D 5e (2024) characters are computed. Where
|
|
12
|
+
those facts come from published game content, they derive from the:
|
|
13
|
+
|
|
14
|
+
Dungeons & Dragons System Reference Document 5.2 (SRD 5.2)
|
|
15
|
+
© Wizards of the Coast LLC, licensed under
|
|
16
|
+
Creative Commons Attribution 4.0 International (CC-BY-4.0).
|
|
17
|
+
https://creativecommons.org/licenses/by/4.0/
|
|
18
|
+
|
|
19
|
+
This project is not affiliated with, endorsed, or sponsored by Wizards of the
|
|
20
|
+
Coast. It contains no content from the Player's Handbook, Dungeon Master's
|
|
21
|
+
Guide, Monster Manual, or any other non-SRD source.
|
|
22
|
+
|
|
23
|
+
"Dungeons & Dragons" and "D&D" are trademarks of Wizards of the Coast; they are
|
|
24
|
+
used here only nominatively to describe the rules system this engine computes.
|
dndwright-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dndwright
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Domain-neutral D&D 5e (2024) rules & character-sheet computation engine: a data-driven DAG of formulas (ability mods, proficiency, spell DC/slots, HP, AC).
|
|
5
|
+
Project-URL: Homepage, https://github.com/sligara7/dndwright
|
|
6
|
+
Project-URL: Repository, https://github.com/sligara7/dndwright
|
|
7
|
+
Author: Anthony Sligar
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
License-File: NOTICE
|
|
11
|
+
Keywords: character-sheet,computation-graph,dnd,dnd5e,rules-engine,srd,ttrpg
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Games/Entertainment :: Role-Playing
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Requires-Dist: pydantic>=2.5
|
|
18
|
+
Requires-Dist: pyyaml>=6.0
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Requires-Dist: pytest>=7.4; extra == 'dev'
|
|
21
|
+
Requires-Dist: ruff>=0.1; extra == 'dev'
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# dndwright
|
|
25
|
+
|
|
26
|
+
> ⚠️ **Early development (v0.1, alpha).** The API is still moving and may change without
|
|
27
|
+
> notice between minor versions. Extracted from a working application; usable today, but
|
|
28
|
+
> pin a tag/commit if you depend on it.
|
|
29
|
+
|
|
30
|
+
**A domain-neutral D&D 5e (2024) rules & character-sheet computation engine.** A character
|
|
31
|
+
sheet is modelled as a **directed acyclic computation graph** — nodes are values, edges are
|
|
32
|
+
dependencies, and formulas are *data* (a JSON-serialisable DSL), not code. Pure Python
|
|
33
|
+
(`pydantic` + stdlib), no application or framework coupling: map your own character data in,
|
|
34
|
+
read computed stats out.
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install git+https://github.com/sligara7/dndwright.git
|
|
40
|
+
# or, for local development:
|
|
41
|
+
pip install -e ".[dev]"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Quickstart
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from dndwright import evaluate_character
|
|
48
|
+
|
|
49
|
+
sheet = evaluate_character({
|
|
50
|
+
"ability_scores": {"strength": 8, "dexterity": 14, "constitution": 14,
|
|
51
|
+
"intelligence": 18, "wisdom": 12, "charisma": 10},
|
|
52
|
+
"class_data": {"class_name": "wizard"},
|
|
53
|
+
"species_data": {"name": "Human", "speed": 30},
|
|
54
|
+
"level": 5,
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
sheet["proficiency_bonus"] # 3
|
|
58
|
+
sheet["ability_modifiers"] # {"intelligence": 4, "dexterity": 2, ...}
|
|
59
|
+
sheet["spellcasting_type"] # "full_caster"
|
|
60
|
+
# ...plus armor_class, hit_points, hit_dice, initiative, saves, features, ...
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Lower level — assemble typed inputs and evaluate against the ruleset:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from dndwright import DND_5E_2024_RULESET, assemble_character_inputs, evaluate, apply_modifiers
|
|
67
|
+
from dndwright.rules.components import ClassMechanics
|
|
68
|
+
|
|
69
|
+
inputs = assemble_character_inputs(class_mechanics=..., ability_scores={...}, level=5)
|
|
70
|
+
computed = apply_modifiers(evaluate(DND_5E_2024_RULESET, inputs), inputs)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Why a computation graph?
|
|
74
|
+
|
|
75
|
+
Derived character values form a dependency DAG: ability scores → modifiers → proficiency →
|
|
76
|
+
save DCs / spell slots / AC / HP. dndwright represents that DAG explicitly and stores the
|
|
77
|
+
formulas as **data** (`FormulaSpec`: an op + args), so the rules are inspectable, testable,
|
|
78
|
+
and serialisable — not buried in imperative code. `DND_5E_2024_RULESET` is a 135-node graph.
|
|
79
|
+
|
|
80
|
+
## What's inside
|
|
81
|
+
|
|
82
|
+
| Component | What it does |
|
|
83
|
+
|-----------|--------------|
|
|
84
|
+
| `evaluate_character` | One call: character data dict → fully computed sheet. |
|
|
85
|
+
| `DND_5E_2024_RULESET` | The 135-node 5e-2024 computation DAG (formulas as data). |
|
|
86
|
+
| `evaluate` / `assemble_character_inputs` / `apply_modifiers` | The lower-level engine. |
|
|
87
|
+
| `Ruleset` / `ComputationNode` / `FormulaSpec` / `NodeType` | The DAG schema. |
|
|
88
|
+
| `dndwright.rules.components` | Typed inputs (`ClassMechanics`, `SpeciesMechanics`, …). |
|
|
89
|
+
| `dndwright.rules.lookup_tables` | SRD-derived rules tables (hit dice, spell slots, AC, saves). |
|
|
90
|
+
|
|
91
|
+
## API stability
|
|
92
|
+
|
|
93
|
+
The public API is exactly `dndwright.__all__`, pinned by `tests/test_api_contract.py`.
|
|
94
|
+
Versioning follows [SemVer](https://semver.org/); at `0.x` minor versions may break, with
|
|
95
|
+
every change recorded in `CHANGELOG.md`.
|
|
96
|
+
|
|
97
|
+
## Credits & license
|
|
98
|
+
|
|
99
|
+
MIT licensed (see `LICENSE`). The rules tables encode game *mechanics* derived from the
|
|
100
|
+
**D&D System Reference Document 5.2** (© Wizards of the Coast, **CC-BY-4.0**); see `NOTICE`.
|
|
101
|
+
Not affiliated with or endorsed by Wizards of the Coast. Contains no PHB/DMG/MM content.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# dndwright
|
|
2
|
+
|
|
3
|
+
> ⚠️ **Early development (v0.1, alpha).** The API is still moving and may change without
|
|
4
|
+
> notice between minor versions. Extracted from a working application; usable today, but
|
|
5
|
+
> pin a tag/commit if you depend on it.
|
|
6
|
+
|
|
7
|
+
**A domain-neutral D&D 5e (2024) rules & character-sheet computation engine.** A character
|
|
8
|
+
sheet is modelled as a **directed acyclic computation graph** — nodes are values, edges are
|
|
9
|
+
dependencies, and formulas are *data* (a JSON-serialisable DSL), not code. Pure Python
|
|
10
|
+
(`pydantic` + stdlib), no application or framework coupling: map your own character data in,
|
|
11
|
+
read computed stats out.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install git+https://github.com/sligara7/dndwright.git
|
|
17
|
+
# or, for local development:
|
|
18
|
+
pip install -e ".[dev]"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quickstart
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from dndwright import evaluate_character
|
|
25
|
+
|
|
26
|
+
sheet = evaluate_character({
|
|
27
|
+
"ability_scores": {"strength": 8, "dexterity": 14, "constitution": 14,
|
|
28
|
+
"intelligence": 18, "wisdom": 12, "charisma": 10},
|
|
29
|
+
"class_data": {"class_name": "wizard"},
|
|
30
|
+
"species_data": {"name": "Human", "speed": 30},
|
|
31
|
+
"level": 5,
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
sheet["proficiency_bonus"] # 3
|
|
35
|
+
sheet["ability_modifiers"] # {"intelligence": 4, "dexterity": 2, ...}
|
|
36
|
+
sheet["spellcasting_type"] # "full_caster"
|
|
37
|
+
# ...plus armor_class, hit_points, hit_dice, initiative, saves, features, ...
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Lower level — assemble typed inputs and evaluate against the ruleset:
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from dndwright import DND_5E_2024_RULESET, assemble_character_inputs, evaluate, apply_modifiers
|
|
44
|
+
from dndwright.rules.components import ClassMechanics
|
|
45
|
+
|
|
46
|
+
inputs = assemble_character_inputs(class_mechanics=..., ability_scores={...}, level=5)
|
|
47
|
+
computed = apply_modifiers(evaluate(DND_5E_2024_RULESET, inputs), inputs)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Why a computation graph?
|
|
51
|
+
|
|
52
|
+
Derived character values form a dependency DAG: ability scores → modifiers → proficiency →
|
|
53
|
+
save DCs / spell slots / AC / HP. dndwright represents that DAG explicitly and stores the
|
|
54
|
+
formulas as **data** (`FormulaSpec`: an op + args), so the rules are inspectable, testable,
|
|
55
|
+
and serialisable — not buried in imperative code. `DND_5E_2024_RULESET` is a 135-node graph.
|
|
56
|
+
|
|
57
|
+
## What's inside
|
|
58
|
+
|
|
59
|
+
| Component | What it does |
|
|
60
|
+
|-----------|--------------|
|
|
61
|
+
| `evaluate_character` | One call: character data dict → fully computed sheet. |
|
|
62
|
+
| `DND_5E_2024_RULESET` | The 135-node 5e-2024 computation DAG (formulas as data). |
|
|
63
|
+
| `evaluate` / `assemble_character_inputs` / `apply_modifiers` | The lower-level engine. |
|
|
64
|
+
| `Ruleset` / `ComputationNode` / `FormulaSpec` / `NodeType` | The DAG schema. |
|
|
65
|
+
| `dndwright.rules.components` | Typed inputs (`ClassMechanics`, `SpeciesMechanics`, …). |
|
|
66
|
+
| `dndwright.rules.lookup_tables` | SRD-derived rules tables (hit dice, spell slots, AC, saves). |
|
|
67
|
+
|
|
68
|
+
## API stability
|
|
69
|
+
|
|
70
|
+
The public API is exactly `dndwright.__all__`, pinned by `tests/test_api_contract.py`.
|
|
71
|
+
Versioning follows [SemVer](https://semver.org/); at `0.x` minor versions may break, with
|
|
72
|
+
every change recorded in `CHANGELOG.md`.
|
|
73
|
+
|
|
74
|
+
## Credits & license
|
|
75
|
+
|
|
76
|
+
MIT licensed (see `LICENSE`). The rules tables encode game *mechanics* derived from the
|
|
77
|
+
**D&D System Reference Document 5.2** (© Wizards of the Coast, **CC-BY-4.0**); see `NOTICE`.
|
|
78
|
+
Not affiliated with or endorsed by Wizards of the Coast. Contains no PHB/DMG/MM content.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "dndwright"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Domain-neutral D&D 5e (2024) rules & character-sheet computation engine: a data-driven DAG of formulas (ability mods, proficiency, spell DC/slots, HP, AC)."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Anthony Sligar" }]
|
|
13
|
+
keywords = ["dnd", "dnd5e", "ttrpg", "rules-engine", "character-sheet", "srd", "computation-graph"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Topic :: Games/Entertainment :: Role-Playing",
|
|
19
|
+
]
|
|
20
|
+
dependencies = ["pydantic>=2.5", "pyyaml>=6.0"]
|
|
21
|
+
|
|
22
|
+
[project.optional-dependencies]
|
|
23
|
+
dev = ["pytest>=7.4", "ruff>=0.1"]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://github.com/sligara7/dndwright"
|
|
27
|
+
Repository = "https://github.com/sligara7/dndwright"
|
|
28
|
+
|
|
29
|
+
[tool.hatch.build.targets.wheel]
|
|
30
|
+
packages = ["src/dndwright"]
|
|
31
|
+
|
|
32
|
+
[tool.pytest.ini_options]
|
|
33
|
+
testpaths = ["tests"]
|
|
34
|
+
|
|
35
|
+
[tool.ruff]
|
|
36
|
+
line-length = 100
|
|
37
|
+
target-version = "py310"
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""dndwright — a domain-neutral D&D 5e (2024) rules & character-computation engine.
|
|
2
|
+
|
|
3
|
+
A character sheet is modelled as a **directed acyclic computation graph**: nodes
|
|
4
|
+
are values, edges are dependencies, and formulas are *data* (a JSON-serialisable
|
|
5
|
+
DSL), not code. The engine is pure (pydantic + stdlib) — no application or
|
|
6
|
+
framework coupling — so any tool can map its own character data in and read
|
|
7
|
+
computed stats out.
|
|
8
|
+
|
|
9
|
+
Quickstart (one call — dict in, computed sheet out):
|
|
10
|
+
|
|
11
|
+
from dndwright import evaluate_character
|
|
12
|
+
sheet = evaluate_character({
|
|
13
|
+
"ability_scores": {"strength": 8, "dexterity": 14, "constitution": 14,
|
|
14
|
+
"intelligence": 18, "wisdom": 12, "charisma": 10},
|
|
15
|
+
"class_data": {"class_name": "wizard"},
|
|
16
|
+
"species_data": {"name": "Human", "speed": 30},
|
|
17
|
+
"level": 5,
|
|
18
|
+
})
|
|
19
|
+
sheet["proficiency_bonus"] # 3
|
|
20
|
+
sheet["ability_modifiers"] # {"intelligence": 4, ...}
|
|
21
|
+
|
|
22
|
+
Lower level (assemble typed inputs, evaluate against the ruleset):
|
|
23
|
+
|
|
24
|
+
from dndwright import (DND_5E_2024_RULESET, assemble_character_inputs,
|
|
25
|
+
evaluate, apply_modifiers)
|
|
26
|
+
from dndwright.rules.components import ClassMechanics
|
|
27
|
+
inputs = assemble_character_inputs(class_mechanics=..., ability_scores={...}, level=5)
|
|
28
|
+
computed = apply_modifiers(evaluate(DND_5E_2024_RULESET, inputs), inputs)
|
|
29
|
+
|
|
30
|
+
The rules tables (hit dice, spell slots, armour AC, save proficiencies) encode
|
|
31
|
+
game mechanics derived from the **D&D SRD 5.2 (CC-BY-4.0)** — see NOTICE.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from .content import categories, generate_library, load_content
|
|
35
|
+
from .ontology import Ontology, load_ontology
|
|
36
|
+
from .rules.adapters import character_data_to_inputs, computed_values_to_sheet
|
|
37
|
+
from .rules.assembler import apply_modifiers, assemble_character_inputs
|
|
38
|
+
from .rules.character_evaluator import compute_key_stats, evaluate_character
|
|
39
|
+
from .rules.dnd_5e_2024 import DND_5E_2024_RULESET
|
|
40
|
+
from .rules.evaluator import evaluate
|
|
41
|
+
from .rules.schema import ComputationNode, FormulaSpec, NodeType, Ruleset
|
|
42
|
+
|
|
43
|
+
__version__ = "0.3.0"
|
|
44
|
+
|
|
45
|
+
__all__ = [
|
|
46
|
+
# high-level (dict in -> computed sheet out)
|
|
47
|
+
"evaluate_character",
|
|
48
|
+
"compute_key_stats",
|
|
49
|
+
# ruleset + low-level evaluation
|
|
50
|
+
"DND_5E_2024_RULESET",
|
|
51
|
+
"evaluate",
|
|
52
|
+
"assemble_character_inputs",
|
|
53
|
+
"apply_modifiers",
|
|
54
|
+
# neutral adapters
|
|
55
|
+
"character_data_to_inputs",
|
|
56
|
+
"computed_values_to_sheet",
|
|
57
|
+
# schema types
|
|
58
|
+
"Ruleset",
|
|
59
|
+
"ComputationNode",
|
|
60
|
+
"FormulaSpec",
|
|
61
|
+
"NodeType",
|
|
62
|
+
# component ontology (graph schema)
|
|
63
|
+
"load_ontology",
|
|
64
|
+
"Ontology",
|
|
65
|
+
# content (bundled starter + generator)
|
|
66
|
+
"load_content",
|
|
67
|
+
"categories",
|
|
68
|
+
"generate_library",
|
|
69
|
+
]
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Bundled starter content + a generator for original homebrew.
|
|
2
|
+
|
|
3
|
+
from dndwright.content import load_content, categories
|
|
4
|
+
load_content("creatures") # list of creature dicts (bundled samples)
|
|
5
|
+
categories() # ["classes", "creatures", "magic_items", "species"]
|
|
6
|
+
|
|
7
|
+
The bundled ``classes`` / ``species`` / ``creatures`` are original homebrew (no
|
|
8
|
+
official content); ``magic_items`` are SRD 5.2 (CC-BY) — see NOTICE. Grow the
|
|
9
|
+
library with :func:`generate_library` (you supply the LLM).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import importlib.resources
|
|
15
|
+
import json
|
|
16
|
+
|
|
17
|
+
from .generate import (
|
|
18
|
+
JsonLLM,
|
|
19
|
+
generate_classes,
|
|
20
|
+
generate_creatures,
|
|
21
|
+
generate_library,
|
|
22
|
+
generate_species,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# category -> (filename, top-level array key)
|
|
26
|
+
_CONTENT = {
|
|
27
|
+
"classes": ("classes.json", "classes"),
|
|
28
|
+
"species": ("species.json", "species"),
|
|
29
|
+
"creatures": ("creatures.json", "creatures"),
|
|
30
|
+
"magic_items": ("magic_items.json", "magic_items"),
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def categories() -> list[str]:
|
|
35
|
+
"""The bundled content categories."""
|
|
36
|
+
return sorted(_CONTENT)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def load_content(category: str) -> list[dict]:
|
|
40
|
+
"""Load the bundled starter content for ``category`` as a list of dicts."""
|
|
41
|
+
if category not in _CONTENT:
|
|
42
|
+
raise ValueError(f"unknown category {category!r}; choose from {categories()}")
|
|
43
|
+
filename, key = _CONTENT[category]
|
|
44
|
+
text = (importlib.resources.files("dndwright.content") / filename).read_text(encoding="utf-8")
|
|
45
|
+
return json.loads(text).get(key, [])
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
__all__ = [
|
|
49
|
+
"load_content",
|
|
50
|
+
"categories",
|
|
51
|
+
"generate_library",
|
|
52
|
+
"generate_classes",
|
|
53
|
+
"generate_species",
|
|
54
|
+
"generate_creatures",
|
|
55
|
+
"JsonLLM",
|
|
56
|
+
]
|