From 3649407a1a96e07a9018fc1b21a794304a005a42 Mon Sep 17 00:00:00 2001 From: Marsh Macy Date: Wed, 14 Oct 2026 17:54:04 -0700 Subject: [PATCH 1/2] Rewrite the quickstart in a plain, instructional voice Cut the meta-opening, the design justifications, and the over-explained prose. Every sentence now does instruction. The code and the complete program are unchanged. Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe --- docs/getting-started/quickstart.md | 34 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md index ac22bac..7f7a497 100644 --- a/docs/getting-started/quickstart.md +++ b/docs/getting-started/quickstart.md @@ -1,8 +1,6 @@ # Quickstart -Roll characters, form a party, build the smallest possible adventure, start a session, execute commands, read the events, and round-trip the game through a save. That's the whole loop, and you run it once here. [The complete program](#the-complete-program) is at the end of the page, and every snippet along the way comes from it. - -Install [osrlib from PyPI](https://pypi.org/project/osrlib/) with [uv](https://docs.astral.sh/uv/) or pip. You'll need Python ≥ 3.14. +Install [osrlib from PyPI](https://pypi.org/project/osrlib/). Python ≥ 3.14 required. ```sh uv add osrlib @@ -16,7 +14,7 @@ pip install osrlib ## Roll the party -Character creation follows the SRD's procedure: roll ability scores, choose a class, roll hit points and starting gold. [`create_character`][osrlib.core.character.create_character] runs the whole procedure in one call. Every random draw in osrlib comes from a named stream forked from a master seed, so the same seed always produces the same characters: +[`create_character`][osrlib.core.character.create_character] runs the B/X procedure: ability scores, class, hit points, starting gold. Randomness flows through named streams forked from a master seed — same seed, same characters. ```{.python .no-run} # Roll two 1st-level characters; every random draw comes from a named, seeded stream. @@ -27,11 +25,11 @@ cleric = create_character(name="Osric", class_id="cleric", alignment=Alignment.L party = Party(members=[fighter.character, cleric.character]) ``` -`class_id` takes any id from [`load_classes`][osrlib.data.load_classes], listed in [the class id index][classes-index]. The result bundles the created [`Character`][osrlib.core.character.Character] with the raw rolls, which is why you build the party from `fighter.character`. +`class_id` takes any id from [`load_classes`][osrlib.data.load_classes]. The result wraps the character with its rolls; the party takes `fighter.character`. -## Build the smallest adventure +## Build the adventure -An [`Adventure`][osrlib.crawl.adventure.Adventure] is a town plus one or more dungeons. A dungeon level is a grid of 10-foot cells, and every edge between two cells is a wall unless you declare it open or a door. The dungeon you build here is a single corridor, two cells joined west-east: +An [`Adventure`][osrlib.crawl.adventure.Adventure] is a town plus dungeons. This one is a two-cell corridor. ```{.python .no-run} # The smallest adventure: a town and a one-corridor dungeon, two cells joined west-east. @@ -44,11 +42,11 @@ town = TownSpec(name="Threshold", travel_turns={"crypt": 1}) adventure = Adventure(name="A First Delve", town=town, dungeons=(crypt,)) ``` -The edge key `"1,0:west"` names the west side of cell `(1, 0)`, the boundary between the two cells. For the geometry and the content models in full, see [Building an adventure](building-an-adventure.md). +`"1,0:west"` keys the west side of cell `(1, 0)`. See [Building an adventure](building-an-adventure.md) for the full geometry. ## Start the session and move -A [`GameSession`][osrlib.crawl.session.GameSession] starts in town. [`EnterDungeon`][osrlib.crawl.commands.EnterDungeon] places the party at the entrance and switches the session to `exploring`, the only mode where movement and door commands are legal: +A [`GameSession`][osrlib.crawl.session.GameSession] starts in town. [`EnterDungeon`][osrlib.crawl.commands.EnterDungeon] places the party at the entrance and switches the session to `exploring`. ```{.python .no-run} # A session starts in town; entering the dungeon switches it to exploring. @@ -57,7 +55,7 @@ session.execute(EnterDungeon(dungeon_id="crypt")) assert session.mode is SessionMode.EXPLORING ``` -Game state changes only through commands, and every rules resolution comes back as typed events. A rejected command changes nothing. Rejection is a normal in-fiction outcome, not an exception (see [the rejection code reference](../reference/rejection-codes.md)): +Commands change the game state. Each returns typed events: ```{.python .no-run} # Commands in, events out: every rules resolution is a typed event with a message code. @@ -67,11 +65,11 @@ lines = [format_message(event) for event in result.events] assert lines # every event formats to a default English line ``` -Events have structured fields and a message code, never baked prose. [`format_message`][osrlib.messages.format_message] is the default English formatter, and your front end can supply its own (see [the message code reference](../reference/message-codes.md)). +A rejected command changes nothing. Events carry structured fields and a message code, never baked prose. [`format_message`][osrlib.messages.format_message] renders the default English line; your front end can supply its own. ## Save and load -The whole session serializes to a JSON-compatible dict. Loading restores the session from that state alone and re-executes nothing. Replay is the separate [`replay_game`][osrlib.persistence.replay_game] path, which rebuilds the same session by re-executing the command log from the same seed. Load and replay always land in the identical state, and that's the determinism guarantee (see [Determinism, saves, and replay](../guides/determinism-saves-replay.md)): +The session serializes to a JSON-compatible dict. Loading restores it from that state alone: ```{.python .no-run} # The whole session round-trips through JSON: same seed, same commands, same game. @@ -80,6 +78,8 @@ restored = load_game(document) assert save_game(restored) == document ``` +For replay — rebuilding the same session by re-executing the command log — see [Determinism, saves, and replay](../guides/determinism-saves-replay.md). + ## The complete program ```python @@ -130,8 +130,8 @@ assert save_game(restored) == document ## Where next -- [Building an adventure](building-an-adventure.md) - the dungeon itself: the grid and its edges, keyed areas, and the content that binds to those areas. -- [Gates, triggers, and quests](../guides/gates-triggers-quests.md) - the authored layer: a door that opens only with the right key, a lever that opens a portcullis, an errand that ends the adventure. -- [Sessions, commands, and events](../guides/sessions-commands-events.md) - the command loop in depth: modes, rejections, the event log. -- [Determinism, saves, and replay](../guides/determinism-saves-replay.md) - what the seed guarantees and how saves and replay reach the same state. -- [The TUI crawler](../front-ends/tui-crawler.md) - a complete example game built on everything above. +- [Building an adventure](building-an-adventure.md) - the dungeon grid, edges, keyed areas, and content. +- [Gates, triggers, and quests](../guides/gates-triggers-quests.md) - keyed doors, levers, quests. +- [Sessions, commands, and events](../guides/sessions-commands-events.md) - the command loop: modes, rejections, the event log. +- [Determinism, saves, and replay](../guides/determinism-saves-replay.md) - the seed guarantee and how saves and replay agree. +- [The TUI crawler](../front-ends/tui-crawler.md) - a complete game built on this. From 11df4124894249199b3e5011232b9e23c0fc7664 Mon Sep 17 00:00:00 2001 From: Marsh Macy Date: Wed, 14 Oct 2026 17:59:50 -0700 Subject: [PATCH 2/2] =?UTF-8?q?Say=20'same=20rolls,'=20not=20'same=20chara?= =?UTF-8?q?cters'=20=E2=80=94=20the=20name=20is=20caller-supplied?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe --- docs/getting-started/quickstart.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md index 7f7a497..0757204 100644 --- a/docs/getting-started/quickstart.md +++ b/docs/getting-started/quickstart.md @@ -14,7 +14,7 @@ pip install osrlib ## Roll the party -[`create_character`][osrlib.core.character.create_character] runs the B/X procedure: ability scores, class, hit points, starting gold. Randomness flows through named streams forked from a master seed — same seed, same characters. +[`create_character`][osrlib.core.character.create_character] runs the B/X procedure: ability scores, class, hit points, starting gold. Randomness flows through named streams forked from a master seed — same seed, same rolls. ```{.python .no-run} # Roll two 1st-level characters; every random draw comes from a named, seeded stream.