Sharpee Lantern

Sharpee Lantern

As a part of the development of Sharpee, I had always intended to build on top of it some kind of interactive writing tool. To be clear, not an IDE or development environment like VC Code and not like Inform 7. Something "else".

When Sharpee was still thought to be a C# platform, the direction was going to be a fluent API on top of the standard library. Something like:

world.room("Kitchen").toEast("Living Room").isDark(false).Build();

Boy in hindsight that's kind of awful. Even the current verbose Sharpee Typescript is better than that.

Once Sharpee reached a stable point, I started thinking about developing the old Textfyre specification pattern with LLM API calls to be a sort of project manager and code generator. I honestly hadn't gotten far with that when the intfiction thread on always starting with maps popped up.

That was interesting, but what really kicked my brain was Jeff Nyman's comment:

"Many years ago, I had some working sessions (experiments, really) with various writers – some established (including multiple published novels), others new on the scene (a few self-published works) – and I had them working with Inform 6, Inform 7, and TADS 3. Primarily, however, it was Inform 7. And what you’re saying here is exactly what the vast majority told me as well. They could never really get behind the current crop of IF systems.
I should note this included screenwriters and others of that ilk (we did a lot of moral premise work), including one well-known director who was potentially curious about the idea of a new “Zork” game in the context of his “Mystery Box” philosophy and who tried to buy the Infocom brand.
At the time, the general consensus was (and I’m putting most of this in my own words, but I believe accurately conveying the sentiment) that the difficulty lies in the fact that most interactive fiction systems demand that a writer also be a cartographer and a physicist before they are allowed to be a storyteller. When an author sits down at a blank page, they are usually chasing a sequence of emotional beats, like the way a character’s realization mirrors the dimming light in a room. This is as opposed to the specific containment logic of whether a brass lantern is a member of the class of portable objects.
For the writers I worked with, the friction was almost entirely structural. Most of these systems require the world to be “compiled” into existence before the narrative can inhabit it. They felt it’s a process of building the house, plumbing and all, before you’re permitted to describe the conversation happening in the kitchen. But a novelist or a screenwriter, as was pointed out to me numerous times, typically works by following the heat of a scene. They want to capture the dialogue and tension first, and only later, once the narrative trajectory is clear, do they care whether the door to the north is locked or whether the player needs a silver key to proceed.
What I found is that this created a fundamental cognitive dissonance. So, I agree with you, at least based on these experiences: the natural flow of storytelling is often a straight line that only sprouts branches once the trunk is strong enough to support them. When you force a writer to define the branching conditions at the same time they are trying to find the character’s voice, you effectively split their brain between the creative and the systemic.
The director I mentioned found this particularly frustrating. His “Mystery Box” approach relies on the power of the unknown and the evocative nature of the unseen, yet most development environments require every “box” to be explicitly defined, coded, and bounded from the very first line of text. It was described to me as like trying to paint a masterpiece while simultaneously being asked to explain the chemical composition of the pigment and the exact tension of the canvas. For those used to the fluidity of the prose or the screen, the rigid, object-oriented (and even rule–oriented) nature of traditional interactive tools felt less like a medium and more like a cage.
Anyway, my post is a distraction from the thread, and I apologize, but all of this is bringing back memories! And I’m very intrigued by the solution you are working on and trying to determine if this would have worked better in the context I was in."

The part that really set my brain on fire was, "including one well-known director who was potentially curious about the idea of a new “Zork” game in the context of his “Mystery Box” philosophy and who tried to buy the Infocom brand."

I think it's common knowledge this was J.J. Abrams, but that's beside the point. The idea of a tool that respected the concept of "mystery box" and still was able to produce a working game was what put me to work. A whole massive pivot from all previous writing tool concepts.

I did a quick POC, using Claude API's to build an inference engine. The author would simply write their story (scene by scene) in a center pane and when they paused, the inference engine would run. The results would appear in the right panel as cards like this:

We iterated through a number of writing scenes to build and improve the inference engine and it works. Lantern can extract game objects and patterns, especially since I had already defined a set of core IF patterns here: https://sharpee.net/design-patterns/

Once the direction was mostly validated (this is crazy, but doable), we archived that original Typescript prototype and reset to design a proper architecture with empirical design patterns for an inference engine discerning IF things that can be translated to blocks of Sharpee code.

The new design is built on Rust and currently only running as a command line implementation and focuses on LLM API calls that have preset prompts when an inference is identified:

[[patterns]]
id = "PUZ-005"
category = "puzzle"
name = "Sequence Puzzle"
description = """
Actions must be performed in a specific order. The player must discover \
and execute a sequence of steps — pulling levers, pressing buttons, \
performing rituals — in the correct order to trigger a result."""

detection_signals = [
    "pull the levers in the right order",
    "the sequence must be exact",
    "first the red, then the blue",
    "the mechanism resets if you err",
    "each step must follow the last",
    "the ritual requires precise ordering",
]

[[patterns.required_entities]]
role = "sequenced_elements"
entity_type = "item"
required_traits = ["mechanical"]

[[patterns.required_entities]]
role = "result"
entity_type = "item"
required_traits = []

[[patterns.required_relationships]]
from_role = "sequenced_elements"
to_role = "result"
relationship = "triggers_when_ordered"

[patterns.codegen_recipe]
template_id = "sequence-puzzle"
parameters = ["sequenced_elements", "result"]

[[patterns.readiness_checklist]]
what = "sequence elements have descriptions"
entity_role = "sequenced_elements"
field = "description"

[[patterns.readiness_checklist]]
what = "correct sequence is defined"
entity_role = "sequenced_elements"
field = "sequence_order"

[[patterns.readiness_checklist]]
what = "result of correct sequence is described"
entity_role = "result"
field = "description"

Which when run as a small test results in:

     ╔══════════════════════════════════════════════════════════╗
     ║  STAGE 0 — Author Prose                                  ║
     ╚══════════════════════════════════════════════════════════╝

The vault antechamber is a circular stone chamber, cold and dry. Three iron levers protrude from the far wall, each crowned with a coloured gem: red on the left, blue in the centre, green on the right. They are stiff but movable.

Beneath the levers, a massive vault door is set into the rock — a slab of dark iron banded with bronze. There is no handle or keyhole. Whatever opens it is not obvious.

You are standing in the middle of the chamber. A low archway to the south leads back to the corridor.

[The levers must be pulled in the order red, green, blue. Pulling them out of order resets all three to the up position. When the correct sequence is completed, the vault door swings open.]

     ╔══════════════════════════════════════════════════════════╗
     ║  STAGE 1 — Scene Inference (live API call)               ║
     ╚══════════════════════════════════════════════════════════╝

     {
       "scene_id": "vault-antechamber",
       "rooms": [
         {
           "id": "vault-antechamber",
           "name": "vault antechamber",
           "description": "The vault antechamber is a circular stone chamber, cold and dry. Three iron levers
     protrude from the far wall, each crowned with a coloured gem: red on the left, blue in the centre, green on
     the right. They are stiff but movable. Beneath the levers, a massive vault door is set into the rock — a slab
     of dark iron banded with bronze. There is no handle or keyhole. Whatever opens it is not obvious.",
           "aliases": [
             "antechamber",
             "chamber",
             "vault chamber"
           ],
           "interactive": true,
           "is_dark": false,
           "source": "The vault antechamber is a circular stone chamber, cold and dry."
         }
       ],
       "objects": [
         {
           "id": "red-lever",
           "name": "red lever",
           "examine": "",
           "traits": [
             "fixed",
             "pushable"
           ],
           "aliases": [
             "lever",
             "left lever",
             "red-crowned lever"
           ],
           "location": "vault-antechamber",
           "initial_state": "up",
           "source": "Three iron levers protrude from the far wall, each crowned with a coloured gem: red on the
     left"
         },
         {
           "id": "blue-lever",
           "name": "blue lever",
           "examine": "",
           "traits": [
             "fixed",
             "pushable"
           ],
           "aliases": [
             "lever",
             "centre lever",
             "center lever",
             "blue-crowned lever"
           ],
           "location": "vault-antechamber",
           "initial_state": "up",
           "source": "blue in the centre"
         },
         {
           "id": "green-lever",
           "name": "green lever",
           "examine": "",
           "traits": [
             "fixed",
             "pushable"
           ],
           "aliases": [
             "lever",
             "right lever",
             "green-crowned lever"
           ],
           "location": "vault-antechamber",
           "initial_state": "up",
           "source": "green on the right"
         },
         {
           "id": "vault-door",
           "name": "vault door",
           "examine": "a massive vault door is set into the rock — a slab of dark iron banded with bronze. There is
      no handle or keyhole. Whatever opens it is not obvious.",
           "traits": [
             "fixed",
             "door"
           ],
           "aliases": [
             "door",
             "massive door",
             "iron door",
             "slab"
           ],
           "location": "vault-antechamber",
           "initial_state": "closed",
           "source": "Beneath the levers, a massive vault door is set into the rock — a slab of dark iron banded
     with bronze."
         },
         {
           "id": "red-gem",
           "name": "red gem",
           "examine": "",
           "traits": [
             "fixed",
             "scenery"
           ],
           "aliases": [
             "gem",
             "coloured gem"
           ],
           "location": "vault-antechamber",
           "initial_state": "",
           "source": "each crowned with a coloured gem: red on the left"
         },
         {
           "id": "blue-gem",
           "name": "blue gem",
           "examine": "",
           "traits": [
             "fixed",
             "scenery"
           ],
           "aliases": [
             "gem",
             "coloured gem"
           ],
           "location": "vault-antechamber",
           "initial_state": "",
           "source": "blue in the centre"
         },
         {
           "id": "green-gem",
           "name": "green gem",
           "examine": "",
           "traits": [
             "fixed",
             "scenery"
           ],
           "aliases": [
             "gem",
             "coloured gem"
           ],
           "location": "vault-antechamber",
           "initial_state": "",
           "source": "green on the right"
         },
         {
           "id": "archway",
           "name": "archway",
           "examine": "",
           "traits": [
             "fixed",
             "scenery"
           ],
           "aliases": [
             "low archway"
           ],
           "location": "vault-antechamber",
           "initial_state": "",
           "source": "A low archway to the south leads back to the corridor."
         }
       ],
       "characters": [
         {
           "id": "player",
           "name": "player",
           "role": "pc",
           "description": "",
           "aliases": [
             "you"
           ],
           "location": "vault-antechamber",
           "source": "You are standing in the middle of the chamber."
         }
       ],
       "transitions": [
         {
           "from": "vault-antechamber",
           "to": "corridor",
           "direction": "south",
           "blocked": false,
           "key": "",
           "one_way": false,
           "hidden": false,
           "source": "A low archway to the south leads back to the corridor."
         }
       ],
       "dialogue": [],
       "stage_directions": [
         {
           "instruction": "The levers must be pulled in the order red, green, blue. Pulling them out of order
     resets all three to the up position. When the correct sequence is completed, the vault door swings open.",
           "applies_to": "vault-door",
           "category": "mechanic"
         }
       ],
       "detected_patterns": [
         {
           "pattern_id": "PUZ-005",
           "confidence": 1.0,
           "entities": {
             "result": "vault-door",
             "sequence_elements": "red-lever,green-lever,blue-lever"
           },
           "source": "The levers must be pulled in the order red, green, blue",
           "is_adhoc": false,
           "description": ""
         },
         {
           "pattern_id": "PUZ-006",
           "confidence": 0.9,
           "entities": {
             "object": "red-lever,blue-lever,green-lever",
             "states": "up,down"
           },
           "source": "They are stiff but movable... resets all three to the up position",
           "is_adhoc": false,
           "description": ""
         }
       ],
       "unwritten": [
         {
           "what": "examine text for red lever",
           "why": "player will want to examine the levers",
           "entity_id": "red-lever",
           "source": ""
         },
         {
           "what": "examine text for blue lever",
           "why": "player will want to examine the levers",
           "entity_id": "blue-lever",
           "source": ""
         },
         {
           "what": "examine text for green lever",
           "why": "player will want to examine the levers",
           "entity_id": "green-lever",
           "source": ""
         },
         {
           "what": "examine text for red gem",
           "why": "player may examine the gems on the levers",
           "entity_id": "red-gem",
           "source": ""
         },
         {
           "what": "examine text for blue gem",
           "why": "player may examine the gems on the levers",
           "entity_id": "blue-gem",
           "source": ""
         },
         {
           "what": "examine text for green gem",
           "why": "player may examine the gems on the levers",
           "entity_id": "green-gem",
           "source": ""
         },
         {
           "what": "examine text for archway",
           "why": "player may examine the exit",
           "entity_id": "archway",
           "source": ""
         },
         {
           "what": "room description for corridor",
           "why": "transition leads to corridor but it's not described",
           "entity_id": "",
           "source": "A low archway to the south leads back to the corridor."
         }
       ],
       "ambiguities": [
         {
           "question": "What lies beyond the vault door when opened?",
           "context": "The stage direction says the door swings open when the sequence is completed, but doesn't
     specify what's behind it",
           "options": [
             "another room",
             "the actual vault",
             "a treasure chamber",
             "a dead end"
           ],
           "category": "missing_content"
         }
       ]
     }

     ╔══════════════════════════════════════════════════════════╗
     ║  STAGE 2 — Reconciled World Model                        ║
     ╚══════════════════════════════════════════════════════════╝

     {
       "rooms": [
         {
           "canonical_id": "vault-antechamber",
           "name": "vault antechamber",
           "description": "The vault antechamber is a circular stone chamber, cold and dry. Three iron levers
     protrude from the far wall, each crowned with a coloured gem: red on the left, blue in the centre, green on
     the right. They are stiff but movable. Beneath the levers, a massive vault door is set into the rock — a slab
     of dark iron banded with bronze. There is no handle or keyhole. Whatever opens it is not obvious.",
           "aliases": [
             "antechamber",
             "chamber",
             "vault chamber"
           ],
           "interactive": true,
           "is_dark": false,
           "source_scenes": [
             "scene-1"
           ]
         },
         {
           "canonical_id": "corridor",
           "name": "corridor",
           "description": null,
           "aliases": [],
           "interactive": false,
           "is_dark": false,
           "source_scenes": []
         }
       ],
       "objects": [
         {
           "canonical_id": "green-lever",
           "name": "green lever",
           "examine": "",
           "traits": [
             "fixed",
             "pushable"
           ],
           "aliases": [
             "red lever",
             "lever",
             "left lever",
             "red-crowned lever",
             "blue lever",
             "centre lever",
             "center lever",
             "blue-crowned lever",
             "right lever",
             "green-crowned lever"
           ],
           "location_history": [
             {
               "scene": "scene-1",
               "location": "vault-antechamber"
             },
             {
               "scene": "scene-1",
               "location": "vault-antechamber"
             },
             {
               "scene": "scene-1",
               "location": "vault-antechamber"
             }
           ],
           "initial_state": "up",
           "source_scenes": [
             "scene-1"
           ]
         },
         {
           "canonical_id": "vault-door",
           "name": "vault door",
           "examine": "a massive vault door is set into the rock — a slab of dark iron banded with bronze. There is
      no handle or keyhole. Whatever opens it is not obvious.",
           "traits": [
             "fixed",
             "door"
           ],
           "aliases": [
             "door",
             "massive door",
             "iron door",
             "slab"
           ],
           "location_history": [
             {
               "scene": "scene-1",
               "location": "vault-antechamber"
             }
           ],
           "initial_state": "closed",
           "source_scenes": [
             "scene-1"
           ]
         },
         {
           "canonical_id": "green-gem",
           "name": "green gem",
           "examine": "",
           "traits": [
             "fixed",
             "scenery"
           ],
           "aliases": [
             "red gem",
             "gem",
             "coloured gem",
             "blue gem"
           ],
           "location_history": [
             {
               "scene": "scene-1",
               "location": "vault-antechamber"
             },
             {
               "scene": "scene-1",
               "location": "vault-antechamber"
             },
             {
               "scene": "scene-1",
               "location": "vault-antechamber"
             }
           ],
           "initial_state": "",
           "source_scenes": [
             "scene-1"
           ]
         },
         {
           "canonical_id": "archway",
           "name": "archway",
           "examine": "",
           "traits": [
             "fixed",
             "scenery"
           ],
           "aliases": [
             "low archway"
           ],
           "location_history": [
             {
               "scene": "scene-1",
               "location": "vault-antechamber"
             }
           ],
           "initial_state": "",
           "source_scenes": [
             "scene-1"
           ]
         }
       ],
       "characters": [
         {
           "canonical_id": "player",
           "name": "player",
           "role_by_scene": {
             "scene-1": "pc"
           },
           "description": "",
           "aliases": [
             "you"
           ],
           "location_history": [
             {
               "scene": "scene-1",
               "location": "vault-antechamber"
             }
           ],
           "dialogue": [],
           "source_scenes": [
             "scene-1"
           ]
         }
       ],
       "transitions": [
         {
           "from": "vault-antechamber",
           "to": "corridor",
           "to_raw": null,
           "direction": "south",
           "blocked": false,
           "key": "",
           "one_way": false,
           "hidden": false,
           "source_scene": "scene-1"
         }
       ],
       "detected_patterns": [
         {
           "pattern_id": "PUZ-005",
           "confidence": 1.0,
           "entities": {
             "result": "vault-door",
             "sequence_elements": "red-lever,green-lever,blue-lever"
           },
           "source": "The levers must be pulled in the order red, green, blue",
           "is_adhoc": false,
           "description": ""
         },
         {
           "pattern_id": "PUZ-006",
           "confidence": 0.9,
           "entities": {
             "object": "red-lever,blue-lever,green-lever",
             "states": "up,down"
           },
           "source": "They are stiff but movable... resets all three to the up position",
           "is_adhoc": false,
           "description": ""
         }
       ],
       "adhoc_patterns": [],
       "contradictions": [
         {
           "entity_id": "green-lever",
           "field": "location",
           "values": [
             [
               "scene-1",
               "vault-antechamber"
             ],
             [
               "scene-1",
               "vault-antechamber"
             ],
             [
               "scene-1",
               "vault-antechamber"
             ]
           ],
           "resolved": false,
           "resolution": null
         },
         {
           "entity_id": "green-gem",
           "field": "location",
           "values": [
             [
               "scene-1",
               "vault-antechamber"
             ],
             [
               "scene-1",
               "vault-antechamber"
             ],
             [
               "scene-1",
               "vault-antechamber"
             ]
           ],
           "resolved": false,
           "resolution": null
         }
       ],
       "unwritten": [
         {
           "what": "examine text for red lever",
           "why": "player will want to examine the levers",
           "entity_id": "red-lever",
           "source": ""
         },
         {
           "what": "examine text for blue lever",
           "why": "player will want to examine the levers",
           "entity_id": "blue-lever",
           "source": ""
         },
         {
           "what": "examine text for green lever",
           "why": "player will want to examine the levers",
           "entity_id": "green-lever",
           "source": ""
         },
         {
           "what": "examine text for red gem",
           "why": "player may examine the gems on the levers",
           "entity_id": "red-gem",
           "source": ""
         },
         {
           "what": "examine text for blue gem",
           "why": "player may examine the gems on the levers",
           "entity_id": "blue-gem",
           "source": ""
         },
         {
           "what": "examine text for green gem",
           "why": "player may examine the gems on the levers",
           "entity_id": "green-gem",
           "source": ""
         },
         {
           "what": "examine text for archway",
           "why": "player may examine the exit",
           "entity_id": "archway",
           "source": ""
         },
         {
           "what": "room description for corridor",
           "why": "transition leads to corridor but it's not described",
           "entity_id": "",
           "source": "A low archway to the south leads back to the corridor."
         }
       ],
       "readiness": {
         "rooms_total": 2,
         "rooms_described": 1,
         "objects_total": 4,
         "objects_with_examine": 4,
         "characters_total": 1,
         "transitions_resolved": 1,
         "transitions_unresolved": 0,
         "patterns_detected": 2,
         "patterns_complete": 0,
         "patterns_incomplete": 2,
         "unwritten_items": [
           {
             "what": "examine text for red lever",
             "why": "player will want to examine the levers",
             "entity_id": "red-lever",
             "source": ""
           },
           {
             "what": "examine text for blue lever",
             "why": "player will want to examine the levers",
             "entity_id": "blue-lever",
             "source": ""
           },
           {
             "what": "examine text for green lever",
             "why": "player will want to examine the levers",
             "entity_id": "green-lever",
             "source": ""
           },
           {
             "what": "examine text for red gem",
             "why": "player may examine the gems on the levers",
             "entity_id": "red-gem",
             "source": ""
           },
           {
             "what": "examine text for blue gem",
             "why": "player may examine the gems on the levers",
             "entity_id": "blue-gem",
             "source": ""
           },
           {
             "what": "examine text for green gem",
             "why": "player may examine the gems on the levers",
             "entity_id": "green-gem",
             "source": ""
           },
           {
             "what": "examine text for archway",
             "why": "player may examine the exit",
             "entity_id": "archway",
             "source": ""
           },
           {
             "what": "room description for corridor",
             "why": "transition leads to corridor but it's not described",
             "entity_id": "",
             "source": "A low archway to the south leads back to the corridor."
           }
         ],
         "playable": false,
         "blockers": [
           "2 incomplete patterns"
         ]
       }
     }

     ╔══════════════════════════════════════════════════════════╗
     ║  STAGE 2a — Readiness Metrics                            ║
     ╚══════════════════════════════════════════════════════════╝

     {
       "rooms_total": 2,
       "rooms_described": 1,
       "objects_total": 4,
       "objects_with_examine": 4,
       "characters_total": 1,
       "transitions_resolved": 1,
       "transitions_unresolved": 0,
       "patterns_detected": 2,
       "patterns_complete": 0,
       "patterns_incomplete": 2,
       "unwritten_items": [
         {
           "what": "examine text for red lever",
           "why": "player will want to examine the levers",
           "entity_id": "red-lever",
           "source": ""
         },
         {
           "what": "examine text for blue lever",
           "why": "player will want to examine the levers",
           "entity_id": "blue-lever",
           "source": ""
         },
         {
           "what": "examine text for green lever",
           "why": "player will want to examine the levers",
           "entity_id": "green-lever",
           "source": ""
         },
         {
           "what": "examine text for red gem",
           "why": "player may examine the gems on the levers",
           "entity_id": "red-gem",
           "source": ""
         },
         {
           "what": "examine text for blue gem",
           "why": "player may examine the gems on the levers",
           "entity_id": "blue-gem",
           "source": ""
         },
         {
           "what": "examine text for green gem",
           "why": "player may examine the gems on the levers",
           "entity_id": "green-gem",
           "source": ""
         },
         {
           "what": "examine text for archway",
           "why": "player may examine the exit",
           "entity_id": "archway",
           "source": ""
         },
         {
           "what": "room description for corridor",
           "why": "transition leads to corridor but it's not described",
           "entity_id": "",
           "source": "A low archway to the south leads back to the corridor."
         }
       ],
       "playable": false,
       "blockers": [
         "2 incomplete patterns"
       ]
     }

     ╔══════════════════════════════════════════════════════════╗
     ║  STAGE 3 — Generated TypeScript (story.ts)               ║
     ╚══════════════════════════════════════════════════════════╝

     // Generated by Lantern — edit only Lantern work is completed
     // Story: The Vault
     // Author: DC Jarbigruen

     import { Story, StoryConfig } from '@sharpee/engine';
     import { ActorTrait, Direction, DoorTrait, EntityType, IFEntity, IdentityTrait, PushableTrait, RoomTrait,
     SceneryTrait, WorldModel } from '@sharpee/world-model';

     class GeneratedStory implements Story {
       config: StoryConfig = {
         id: 'the-vault',
         version: '0.1.0',
         title: 'The Vault',
         author: 'DC Jarbigruen',
         description: '',
         ifid: 'GENERATED-IFID-PLACEHOLDER',
       };

       initializeWorld(world: WorldModel): void {
         // ── PC alias (created in createPlayer) ──
       const player = world.getEntity('player')!;

         // ── Rooms ──
       const vault_antechamber = world.createEntity('vault-antechamber', EntityType.ROOM);
       vault_antechamber.add(new IdentityTrait({
         name: 'vault antechamber',
         description: 'The vault antechamber is a circular stone chamber, cold and dry. Three iron levers protrude
     from the far wall, each crowned with a coloured gem: red on the left, blue in the centre, green on the right.
     They are stiff but movable. Beneath the levers, a massive vault door is set into the rock — a slab of dark
     iron banded with bronze. There is no handle or keyhole. Whatever opens it is not obvious.',
           aliases: ['antechamber', 'chamber', 'vault chamber'],
       }));
       vault_antechamber.add(new RoomTrait({
         exits: {},
       }));
       const corridor = world.createEntity('corridor', EntityType.ROOM);
       // TODO: author must write description for room 'corridor'
       corridor.add(new IdentityTrait({
         name: 'corridor',
       }));
       corridor.add(new RoomTrait({
         exits: {},
       }));

         // ── Objects ──
       const green_lever = world.createEntity('green-lever', EntityType.ITEM);
       green_lever.add(new IdentityTrait({
           name: 'green lever',
           description: '',
           aliases: ['red lever', 'lever', 'left lever', 'red-crowned lever', 'blue lever', 'centre lever', 'center
      lever', 'blue-crowned lever', 'right lever', 'green-crowned lever'],
         }));
       green_lever.add(new SceneryTrait());
       green_lever.add(new PushableTrait());
       // Initial state: up
       world.moveEntity(green_lever.id, vault_antechamber.id);
       const vault_door = world.createEntity('vault-door', EntityType.DOOR);
       vault_door.add(new IdentityTrait({
           name: 'vault door',
           description: 'a massive vault door is set into the rock — a slab of dark iron banded with bronze. There
     is no handle or keyhole. Whatever opens it is not obvious.',
           aliases: ['door', 'massive door', 'iron door', 'slab'],
         }));
       vault_door.add(new SceneryTrait());
       vault_door.add(new DoorTrait());
       // Initial state: closed
       world.moveEntity(vault_door.id, vault_antechamber.id);
       const green_gem = world.createEntity('green-gem', EntityType.SCENERY);
       green_gem.add(new IdentityTrait({
           name: 'green gem',
           description: '',
           aliases: ['red gem', 'gem', 'coloured gem', 'blue gem'],
         }));
       green_gem.add(new SceneryTrait());
       // Initial state:
       world.moveEntity(green_gem.id, vault_antechamber.id);
       const archway = world.createEntity('archway', EntityType.SCENERY);
       archway.add(new IdentityTrait({
           name: 'archway',
           description: '',
           aliases: ['low archway'],
         }));
       archway.add(new SceneryTrait());
       // Initial state:
       world.moveEntity(archway.id, vault_antechamber.id);

       // ── Exit wiring ──
       const vault_antechamberRoomTrait = vault_antechamber.get(RoomTrait);
       vault_antechamberRoomTrait.exits[Direction.SOUTH] = { destination: corridor.id };
       const corridorRoomTrait = corridor.get(RoomTrait);
       corridorRoomTrait.exits[Direction.NORTH] = { destination: vault_antechamber.id };

         // ── Pattern recipes ──
       // ── Pattern: PUZ-005 (unresolved entities) ──
       // TODO: recipe entity 'sequence_elements' → 'red_levergreen_leverblue_lever' not found in world model
       // ── Pattern: PUZ-006 (unresolved entities) ──
       // TODO: recipe entity 'object' → 'red_leverblue_levergreen_lever' not found in world model
       // TODO: recipe entity 'states' → 'updown' not found in world model

       }

       createPlayer(world: WorldModel): IFEntity {
       const player = world.createEntity('player', EntityType.ACTOR);
       player.add(new IdentityTrait({
           name: 'player',
           description: '',
           aliases: ['you'],
         }));
       player.add(new ActorTrait());
         return player;
       }
     }

     export const story = new GeneratedStory();
     export default story;


     ╔══════════════════════════════════════════════════════════╗
     ║  STAGE 3a — Codegen Metadata                             ║
     ╚══════════════════════════════════════════════════════════╝

       Rooms:      2
       Objects:    4
       Characters: 1
       Imports:    ["ActorTrait", "Direction", "DoorTrait", "EntityType", "IdentityTrait", "PushableTrait",
     "RoomTrait", "SceneryTrait", "WorldModel"]
       Playable:   false (blockers: ["2 incomplete patterns"])
     test puz005_full_live_pipeline ... ok

Note this is the current state of the inference engine. It's early stages.

What the LLM did well:
  - Split the three levers into individual entities (red-lever, blue-lever, green-lever) with correct initial states  ("up")
- Detected PUZ-005 (Sequence Puzzle) at 1.0 confidence and PUZ-006 (State Machine) at 0.9 — recognizing the levers  have discrete states
  - Extracted the vault door with door trait, inferred the corridor as an unwritten room from the transition
  - Flagged 8 pieces of unwritten content (examine text for each lever/gem, corridor description)
  - Asked a smart ambiguity question: "What lies beyond the vault door when opened?"

  What exposed pipeline issues:
  - Reconciler merged the three levers into one ("green-lever" ate red and blue) — they share the alias "lever", so the  synonym grouping collapsed them. That's a real bug.
  - Same thing happened to the gems — all three merged into "green-gem"
  - Recipe entity refs use comma-separated IDs (red-lever,green-lever,blue-lever) which codegen treats as a single var  name, producing the TODO: not found in world model fallback. The recipe system doesn't handle multi-entity roles yet.


So this is direction I'm going with an IF writing -> code generation tool. The jury is still out on whether it becomes production ready, but it's worth making the attempt.

I'm acutely aware that GenAI has a long way to go before creative people find space for it (if ever). The creative guardrails are built into the DNA of Lantern and that includes a user never seeing generated prose or puzzles. The author must supply those things through writing or describing things. Even then this will likely limit the kinds of games you could create with Lantern. But, the author can always take the generated code and work on it outside of Lantern. It would be a sort of proof of concept tool that maybe gets you 80% there (a pure guess) and then you have to slug it out for the last 20%. Or if the author never designs an overly complex technical story (or system), maybe it gets all the way there.

Subscribe to My So Called Interactive Fiction Life

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe