From a09d2ce582aa7105661385e12bdcf64f314415a8 Mon Sep 17 00:00:00 2001 From: Anthony Sligar Date: Sat, 6 Jun 2026 07:21:02 -0400 Subject: [PATCH] docs: add compose & theme-scaling graphics, surface both in README (0.23.2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two new diagrams for the GitHub/PyPI page, matching the existing dark asset style (all values verified against the engine): - assets/compose.svg — lego-style components snapping onto the ruleset via typed set/add/union contributions; one snap-in cascades downstream. - assets/theme-scaling.svg — one computation graph re-skinned per setting via ThemeScalingLayer (input_overrides, lookup_overrides, display-only flavor_renames); same plate node -> AC 18/18/19/19 across themes. README gains a "Composable" section and a "Re-skin for any setting — theme scaling" section (theme scaling was previously undocumented in the README), each with a verified runnable snippet, plus a feature-table row. Docs-only patch release so the graphics reach the (per-version-frozen) PyPI page. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 16 ++++- README.md | 38 ++++++++++++ assets/compose.svg | 121 ++++++++++++++++++++++++++++++++++++++ assets/theme-scaling.svg | 107 +++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- src/dndwright/__init__.py | 2 +- 6 files changed, 283 insertions(+), 3 deletions(-) create mode 100644 assets/compose.svg create mode 100644 assets/theme-scaling.svg diff --git a/CHANGELOG.md b/CHANGELOG.md index 76ade01..a8b7395 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,19 @@ breaking changes; these will always be noted here. ## [Unreleased] +## [0.23.2] — 2026-06-06 + +### Documentation +- **Two new README/PyPI diagrams.** `assets/compose.svg` shows lego-style components snapping + onto the ruleset — typed `set`/`add`/`union` contributions that keep the target node's id, so + one snap-in recomputes the whole downstream subtree. `assets/theme-scaling.svg` shows one + computation graph re-skinned per setting via `ThemeScalingLayer`: the same `plate` node emerges + as AC 18 (traditional / modern, re-flavored) or AC 19 (sci-fi / steampunk) through + `input_overrides`, `lookup_overrides`, and display-only `flavor_renames`. +- **New README sections** — "Composable — snap mini-graphs onto the ruleset" and "Re-skin for + any setting — theme scaling" (theme scaling was previously undocumented in the README), plus a + `apply_theme_scaling` / `ThemeScalingLayer` row in the feature table. Docs-only; no code change. + ## [0.23.1] — 2026-06-06 ### Fixed @@ -441,7 +454,8 @@ from a working application. Pure (pydantic + stdlib); no application/framework coupling. Rules content derives from the SRD 5.2 (CC-BY-4.0); see NOTICE. -[Unreleased]: https://github.com/sligara7/dndwright/compare/v0.23.1...HEAD +[Unreleased]: https://github.com/sligara7/dndwright/compare/v0.23.2...HEAD +[0.23.2]: https://github.com/sligara7/dndwright/compare/v0.23.1...v0.23.2 [0.23.1]: https://github.com/sligara7/dndwright/compare/v0.23.0...v0.23.1 [0.23.0]: https://github.com/sligara7/dndwright/compare/v0.22.0...v0.23.0 [0.22.0]: https://github.com/sligara7/dndwright/compare/v0.21.0...v0.22.0 diff --git a/README.md b/README.md index 35b1d96..30dda90 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,43 @@ and serialisable — not buried in imperative code. `DND_5E_2024_RULESET` is a 1 The dndwright computation graph: ability scores, level, class and equipment flow through ability modifiers and proficiency bonus to saves, skills, spell DC/attack, spell slots, HP, AC and initiative

+### Composable — snap mini-graphs onto the ruleset + +Items, feats and species traits are themselves tiny graphs. `compose()` merges a +`Component`'s nodes and contributions onto a base ruleset and returns a new, larger +`Ruleset` — the base is never mutated. Because each contribution keeps its target node's +**id**, every existing edge downstream re-derives for free: a `set`/`add`/`union` on one +node ripples out to every modifier, save, skill and attack that depends on it. + +

+ Components are lego-style mini-graphs that snap onto the dndwright ruleset: a Belt of Giant Strength sets the Strength score, a Ring of Protection adds to Armor Class, and Dwarven Resilience unions in poison resistance — compose() merges them and one snap-in recomputes the whole downstream subtree (strength modifier to athletics, saving throws and melee attack) +

+ +```python +from dndwright import DND_5E_2024_RULESET, compose, modifier +ring = modifier("ring_of_protection", target="armor_class", amount=1) +rs = compose(DND_5E_2024_RULESET, ring) # base untouched; AC now aggregates the +1 +``` + +### Re-skin for any setting — theme scaling + +The same engine runs sci-fi, modern-warfare, steampunk or cosmic-horror. A `ThemeScalingLayer` +folds three kinds of override onto a ruleset via `apply_theme_scaling()` (pure, like `compose`): +`input_overrides` re-baseline a node's default value, `lookup_overrides` deep-merge into the +lookup tables (so `plate` armour can read AC 19 instead of 18), and `flavor_renames` relabel +terms for display **without ever changing a computed value**. The graph's shape never changes — +only its numbers and names. + +

+ dndwright theme scaling: one computation graph re-skinned per setting. A ThemeScalingLayer applies input_overrides (re-baseline node defaults), lookup_overrides (merge tables like armor AC and weapon ranges) and flavor_renames (display labels only). The same plate armor node emerges as 'plate AC 18' in traditional D&D, 'tactical body armor AC 18' in modern warfare, 'power armor AC 19' in sci-fi, and 'clockwork full-plate AC 19' in steampunk +

+ +```python +from dndwright import DND_5E_2024_RULESET, apply_theme_scaling, get_theme_scaling +rs = apply_theme_scaling(DND_5E_2024_RULESET, get_theme_scaling("sci_fi")) +rs.lookup_tables["armor_base_ac"]["plate"] # 19 (base is still 18, untouched) +``` + ## What's inside | Component | What it does | @@ -139,6 +176,7 @@ and serialisable — not buried in imperative code. `DND_5E_2024_RULESET` is a 1 | `validate_ruleset` / `assert_valid_ruleset` | Static integrity check for a ruleset (unknown ops, cycles, dangling refs) — catch authoring errors before evaluation. | | `compose` / `modifier` / `Component` | Snap mini-graphs (items/feats/traits) onto a ruleset; downstream values cascade. | | `component_from_content` | Build a `Component` from a bundled item/feat's `component` field — magic items & feats as data that snap onto a character (constant, dynamic, player-chosen, or *conditional* effects). | +| `apply_theme_scaling` / `ThemeScalingLayer` / `get_theme_scaling` | Re-skin the ruleset for any setting (sci-fi, modern, steampunk, …): override node defaults & lookup tables and re-flavor names, same graph shape. `PREDEFINED_THEME_SCALING` ships ready-made themes. | | `to_mermaid` / `to_dot` | Render the computation DAG as Mermaid or Graphviz DOT — *see* the dependency graph. | | `dndwright.dice` | Typed dice engine: parse/roll 5e expressions, attacks, saves, damage, stat arrays. | | `dndwright.combat` | Pure combat rules over a frozen `CombatantState`: damage, temp HP, healing, death saves. | diff --git a/assets/compose.svg b/assets/compose.svg new file mode 100644 index 0000000..9734371 --- /dev/null +++ b/assets/compose.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + dndwright + Components are mini-graphs that snap onto the ruleset — compose() merges them and downstream values recompute. + + + Components + Base graph · targets + Cascades to + + + + + + + + + + + + + + + + set + + add + + union + + + + + + + + + + + Belt of Giant Strength + Strength score = 25 + + + + + + + + + Ring of Protection + +1 Armor Class + + + + + + + + + Dwarven Resilience + + poison resistance + + + + + + + Strength score + now an aggregate node + + + Armor Class + + + Resistances + + + + + + Strength modifier + + + Athletics + + STR saving throw + + Melee attack + + + One snap-in recomputes the + whole downstream subtree. + + compose(base, *components) -> a larger Ruleset · pure (the base graph is never mutated) · the target id is kept, so existing edges cascade + diff --git a/assets/theme-scaling.svg b/assets/theme-scaling.svg new file mode 100644 index 0000000..c2bdd77 --- /dev/null +++ b/assets/theme-scaling.svg @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + dndwright + One graph, any setting — a ThemeScalingLayer re-baselines values & lookup tables, and re-flavors names. + + + Base ruleset + ThemeScalingLayer + Same graph, per theme + + + + + + + + + + + + + + armor_base_ac (lookup) + plate = 18 + + + + + Armor Class + + 138-node DAG · formulas as data + + + + + + + + input_overrides + re-baseline a node's default value + + + lookup_overrides + merge tables: armor AC, weapon ranges + + + flavor_renames + display label only — never changes math + + + apply_theme_scaling(rs, layer) + pure — the base graph is untouched + + + + + + + + Traditional D&D + plate · AC 18 + + + + + Modern warfare + tactical body armor · AC 18 + + + + + Sci-fi + power armor · AC 19 + + + + + Steampunk + clockwork full-plate · AC 19 + + + precedence: explicit input > themed default > original · flavor_renames never change a value · composes with compose() + diff --git a/pyproject.toml b/pyproject.toml index 9e2e23e..dff9fa3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "dndwright" -version = "0.23.1" +version = "0.23.2" 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)." readme = "README.md" requires-python = ">=3.10" diff --git a/src/dndwright/__init__.py b/src/dndwright/__init__.py index 2277d7e..20b8cef 100644 --- a/src/dndwright/__init__.py +++ b/src/dndwright/__init__.py @@ -100,7 +100,7 @@ validate_ruleset, ) -__version__ = "0.23.1" +__version__ = "0.23.2" __all__ = [ # high-level (dict in -> computed sheet out)