diff --git a/src/osrlib/__init__.py b/src/osrlib/__init__.py index ba27d94..7e422e6 100644 --- a/src/osrlib/__init__.py +++ b/src/osrlib/__init__.py @@ -7,61 +7,82 @@ the same seed and the same commands replay the same game. Every name has one import home and the package root re-exports nothing, so you import from -the module that defines the symbol. The modules fall into three layers. +the module that defines the symbol. The modules fall into three layers, and each table below +lists one layer's modules in the order you meet them. + +## The core kernel The kernel, under `osrlib.core`, is the rules on their own: no session, no dungeon, no -adventure. Use it directly to roll a character, resolve an attack, or price a sword with no -game running. - -- [`osrlib.core.rng`][osrlib.core.rng]: a master seed in, one named stream per subsystem out. -- [`osrlib.core.ruleset`][osrlib.core.ruleset]: the optional rules you switch on, in one model the kernel reads. -- [`osrlib.core.dice`][osrlib.core.dice]: a dice expression in, a parsed expression or a roll and its own dice out. -- [`osrlib.core.alignment`][osrlib.core.alignment]: the three alignments, shared by characters and monsters. -- [`osrlib.core.abilities`][osrlib.core.abilities]: an ability score in, the modifier or chance the tables grant it out. -- [`osrlib.core.classes`][osrlib.core.classes]: a class definition and a character in, titles, XP, advancement out. -- [`osrlib.core.character`][osrlib.core.character]: creation choices and a stream in, a character or refusals out. -- [`osrlib.core.items`][osrlib.core.items]: templates and an inventory in, purchases, equipment, and encumbrance out. -- [`osrlib.core.spells`][osrlib.core.spells]: a caster and a spell in, memorization, casting, and turning undead out. -- [`osrlib.core.monsters`][osrlib.core.monsters]: a monster template in, a spawned instance with its own hit points out. -- [`osrlib.core.combat`][osrlib.core.combat]: combatants and a stream in, initiative, attacks, damage, and saves out. -- [`osrlib.core.effects`][osrlib.core.effects]: a condition or effect in, a ledger that ticks and expires it out. -- [`osrlib.core.treasure`][osrlib.core.treasure]: a treasure type and a stream in, coins, valuables, magic items out. -- [`osrlib.core.tables`][osrlib.core.tables]: hit dice or an armour class in, the printed row for it out. -- [`osrlib.core.npc`][osrlib.core.npc]: a party level and a stream in, a generated NPC adventuring party out. -- [`osrlib.core.clock`][osrlib.core.clock]: rounds in, turns and days out, with the boundaries each crossing reports. -- [`osrlib.core.events`][osrlib.core.events]: the base class every event inherits, and the contract its code follows. -- [`osrlib.core.validation`][osrlib.core.validation]: the refusal value a rules check hands back instead of raising. +adventure. You hand a function the inputs a rule needs, plus a seeded stream and a ruleset, +and it returns the outcome and the events that describe it. Call it directly to roll a +character, resolve an attack, or price a sword with no game running. Kernel modules never +import from `osrlib.crawl`, so what you build on the kernel keeps working whatever the crawl +framework does above it. + +| Module | What it's for | +| --- | --- | +| [`osrlib.core.rng`][] | A master seed in, one named stream per subsystem out. | +| [`osrlib.core.ruleset`][] | The optional rules you switch on, in one model the kernel reads. | +| [`osrlib.core.dice`][] | A dice expression in, a parsed expression or a roll and its own dice out. | +| [`osrlib.core.alignment`][] | The three alignments, shared by characters and monsters. | +| [`osrlib.core.abilities`][] | An ability score in, the modifier or chance the tables grant it out. | +| [`osrlib.core.classes`][] | A class definition and a character in, titles, XP, and advancement out. | +| [`osrlib.core.character`][] | Creation choices and a stream in, a character or refusals out. | +| [`osrlib.core.items`][] | Templates and an inventory in, purchases, equipment, and encumbrance out. | +| [`osrlib.core.spells`][] | A caster and a spell in, memorization, casting, and turning undead out. | +| [`osrlib.core.monsters`][] | A monster template in, a spawned instance with its own hit points out. | +| [`osrlib.core.combat`][] | Combatants and a stream in, initiative, attacks, damage, and saves out. | +| [`osrlib.core.effects`][] | A condition or effect in, a ledger that ticks and expires it out. | +| [`osrlib.core.treasure`][] | A treasure type and a stream in, coins, valuables, and magic items out. | +| [`osrlib.core.tables`][] | Hit dice or an armour class in, the printed row for it out. | +| [`osrlib.core.npc`][] | A party level and a stream in, a generated NPC adventuring party out. | +| [`osrlib.core.clock`][] | Rounds in, turns and days out, with the boundaries each crossing reports. | +| [`osrlib.core.events`][] | The base class every event inherits, and the contract its code follows. | +| [`osrlib.core.validation`][] | The refusal value a rules check returns instead of raising. | + +## The crawl framework The crawl framework, under `osrlib.crawl`, is the game around those rules: a party in a -mapped dungeon, driven by commands. Start at the session and work outwards. - -- [`osrlib.crawl.dungeon`][osrlib.crawl.dungeon]: cells, edges, doors, areas, and traps in, a mapped dungeon out. -- [`osrlib.crawl.adventure`][osrlib.crawl.adventure]: dungeons and a town in, one adventure a session can play out. -- [`osrlib.crawl.party`][osrlib.crawl.party]: characters in, marching order, group movement, and combat ranks out. -- [`osrlib.crawl.session`][osrlib.crawl.session]: a party, an adventure, and a seed in, a game taking commands out. -- [`osrlib.crawl.commands`][osrlib.crawl.commands]: every command you can execute, each with the modes it is legal in. -- [`osrlib.crawl.events`][osrlib.crawl.events]: every event a command can emit, and the parser that reads one back. -- [`osrlib.crawl.views`][osrlib.crawl.views]: a session in, what a player may see or what a referee may see out. -- [`osrlib.crawl.exploration`][osrlib.crawl.exploration]: movement, doors, searching, light, rest, and wandering checks. -- [`osrlib.crawl.encounter`][osrlib.crawl.encounter]: a meeting in, surprise, distance, reaction, evasion, pursuit out. -- [`osrlib.crawl.battle`][osrlib.crawl.battle]: an encounter that came to blows in, a round-by-round battle out. -- [`osrlib.crawl.stocking`][osrlib.crawl.stocking]: an empty area and a stream in, its monsters and treasure out. -- [`osrlib.crawl.gates`][osrlib.crawl.gates]: a condition and a session in, whether the way opens out. -- [`osrlib.crawl.triggers`][osrlib.crawl.triggers]: an event pattern in, a match against what just happened out. -- [`osrlib.crawl.quests`][osrlib.crawl.quests]: objectives and the clauses that complete them, as authored content. -- [`osrlib.crawl.narrative`][osrlib.crawl.narrative]: the authored text on a mechanical object, one block per audience. -- [`osrlib.crawl.interpreter`][osrlib.crawl.interpreter]: a listener you register in, an adventure playing itself out. -- [`osrlib.crawl.content_pack`][osrlib.crawl.content_pack]: keyed room content out of one adventure and into another. - -The shared services sit at the top level and serve both layers. - -- [`osrlib.data`][osrlib.data]: a content id in, the frozen rules entry behind it out. -- [`osrlib.errors`][osrlib.errors]: the exceptions the library raises, and which failure each one stands for. -- [`osrlib.messages`][osrlib.messages]: an event in, a line of default English out. -- [`osrlib.persistence`][osrlib.persistence]: a session in, a save document out, and back again by loading or replaying. -- [`osrlib.versioning`][osrlib.versioning]: the two version stamps on every document, and the envelope for them. - -The quickstart below runs the whole loop: characters, party, adventure, session, commands, +mapped dungeon, driven by commands. You author the content, start a session, and hand it one +command per player action. The session runs the exploration, encounter, and battle procedures +for you and returns the events. Start at the session and work outwards. + +| Module | What it's for | +| --- | --- | +| [`osrlib.crawl.dungeon`][] | Cells, edges, doors, areas, and traps in, a mapped dungeon out. | +| [`osrlib.crawl.adventure`][] | Dungeons and a town in, one adventure a session can play out. | +| [`osrlib.crawl.party`][] | Characters in, marching order, group movement, and combat ranks out. | +| [`osrlib.crawl.session`][] | A party, an adventure, and a seed in, a game taking commands out. | +| [`osrlib.crawl.commands`][] | Every command you can execute, each with the modes it's legal in. | +| [`osrlib.crawl.events`][] | Every event a command can emit, and the parser that reads one back. | +| [`osrlib.crawl.views`][] | A session in, what a player may see or what a referee may see out. | +| [`osrlib.crawl.exploration`][] | Movement, doors, searching, light, rest, and wandering checks. | +| [`osrlib.crawl.encounter`][] | A meeting in, surprise, distance, reaction, evasion, and pursuit out. | +| [`osrlib.crawl.battle`][] | An encounter that came to blows in, a round-by-round battle out. | +| [`osrlib.crawl.stocking`][] | An empty area and a stream in, its monsters and treasure out. | +| [`osrlib.crawl.gates`][] | A gate's condition and a session in, whether the gate opens out. | +| [`osrlib.crawl.triggers`][] | An event pattern in, a match against what just happened out. | +| [`osrlib.crawl.quests`][] | Objectives and the clauses that complete them, as authored content. | +| [`osrlib.crawl.narrative`][] | The authored text on a mechanical object, one block per audience. | +| [`osrlib.crawl.interpreter`][] | The listener you register on a session to play the adventure's triggers and quests. | +| [`osrlib.crawl.content_pack`][] | Keyed room content out of one adventure and into another. | + +## Shared services + +The shared services sit at the top level and serve both layers: the compiled rules content, +the exceptions, the message formatter, and the save and version documents. + +| Module | What it's for | +| --- | --- | +| [`osrlib.data`][] | A content id in, the frozen rules entry behind it out. | +| [`osrlib.errors`][] | The exceptions the library raises, and which failure each one stands for. | +| [`osrlib.messages`][] | An event in, a line of default English out. | +| [`osrlib.persistence`][] | A session in, a save document out, and back again by loading or replaying. | +| [`osrlib.versioning`][] | The two version stamps on every document, and the envelope for them. | + +## Quickstart + +The quickstart runs the whole loop: characters, party, adventure, session, commands, events, save, and load. For the documentation, including a stepwise walk through this example, see https://mmacy.github.io/osrlib-python/ diff --git a/tests/test_docs_gates.py b/tests/test_docs_gates.py index be7ed9a..78f5650 100644 --- a/tests/test_docs_gates.py +++ b/tests/test_docs_gates.py @@ -212,3 +212,21 @@ def test_the_stream_page_names_every_stream_constant(self): if name.endswith("_STREAM") and f"`{name}`" not in page: missing.append(f"{info.name}.{name}") assert not missing, f"stream constants absent from docs/reference/rng-streams.md: {missing}" + + def test_the_api_overview_tables_every_exporting_module_once(self): + import importlib + import pkgutil + + import osrlib + + overview = osrlib.__doc__ or "" + rows = re.findall(r"^\| \[`(osrlib\.[\w.]+)`\]\[\] \|", overview, re.M) + exporting = sorted( + info.name + for info in pkgutil.walk_packages(osrlib.__path__, "osrlib.") + if getattr(importlib.import_module(info.name), "__all__", None) + ) + assert sorted(rows) == exporting, ( + f"missing from the package docstring tables: {sorted(set(exporting) - set(rows))}; " + f"tabled but not exporting or duplicated: {sorted(set(rows) - set(exporting))}" + ) diff --git a/tools/docs/gen_api_reference.py b/tools/docs/gen_api_reference.py index 3b67242..42106cd 100644 --- a/tools/docs/gen_api_reference.py +++ b/tools/docs/gen_api_reference.py @@ -52,17 +52,15 @@ def _layer(name: str) -> str: for symbol in exported: page.write(f" - {symbol}\n") +# The overview page is the package docstring: it introduces each layer and tables its +# modules in the order a reader meets them, with a link to every module page. with mkdocs_gen_files.open("reference/api/index.md", "w") as index: index.write("# API reference\n\n") - index.write("::: osrlib\n options:\n members: false\n\n") - index.write("One page per module, each rendering that module's public (importable) surface:\n\n") - for layer_title, _ in _LAYERS: - index.write(f"\n## {layer_title}\n\n") - for name, exported in modules: - if _layer(name) == layer_title: - path = name.replace(".", "/") + ".md" - summary = importlib.import_module(name).__doc__.strip().splitlines()[0].rstrip(".") - index.write(f"- [`{name}`]({path}) — {summary} ({len(exported)} symbols)\n") + index.write("::: osrlib\n") + index.write(" options:\n") + index.write(" members: false\n") + index.write(" heading_level: 1\n") + index.write(" show_root_toc_entry: false\n") with mkdocs_gen_files.open("reference/api/SUMMARY.md", "w") as summary: summary.write("\n".join(summary_lines) + "\n")