DEV Community

Cover image for The House That Remembers
Lucia W
Lucia W

Posted on Edited on

The House That Remembers

Hacktoberfest Open-Source AI Challenge: Ghost in the Machine Submission šŸ‘»šŸŒ¼

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine

What I Built

Because I love the spooky season, D&D, and reading twisty thrillers šŸ˜…, I decided to build a short D&D-style game.

Meet The House That Remembers, a text-based adventure inspired by Edgar Allan Poe’s 1839 short story The Fall of the House of Usher.

Premise

The letter arrived three nights ago, its paper creased and stained, the handwriting unmistakably your friend's - though the words were unlike anything you had ever known him to write.

"Please, you must come. There are things in this house I can no longer explain. I'm afraid it may be too late for me. You're the only person I can trust."

Now, you stand before his home. Rain lashes against your face, and thunder rolls across the darkened sky. The house looms above you, its crooked silhouette barely visible through the mist. Tall, narrow windows stare down like hollow eyes. Ivy claws at the weathered stone walls, and the iron gates behind you groan in the wind, slowly swinging shut.

You remember this house from your childhood, but something about it feels wrong. You used to play outside, spend time helping your friend with chores in the kitchen, explore the family library, and even sleep over in the guest room.

The air is unnaturally cold, carrying the scent of wet earth and rotting leaves. Though no lights burn inside, you cannot shake the feeling that someone is watching you from the upper windows.

For a moment, lightning illuminates the facade - and you could swear a shadow has just moved behind the glass.

Then the thunder cracks.

The front door stands slightly ajar.

Your friend invited you here for help. But as you stare into the darkness beyond the threshold, a troubling thought creeps into your mind:

Did he invite you into the house… or did the house invite you in?

Demo

Play The House That Remembers

The online demo runs on Render. Responses may take a little longer because the model is running on CPU.

Code

GitHub logo luc-rap / hacktoberfest-open-source-ai-week4

Hacktoberfest Challenge for week 4 - Build something with open-source AI at its core (Ghost in the Machine )

Hacktoberfest Open-Source AI - Week 4

The House That Remembers

The House That Remembers

This is my submission for the Hacktoberfest Open-Source AI Challenge: Week 4. This week's theme is "Ghost in the Machine": build something spooky, or something that honors the past in the spirit of DĆ­a de los Muertos, using open-weight models or open-source AI.

Because I love the spooky season, D&D, and reading crazy thrillers šŸ˜…, I decided to build a short D&D-style game. I call it The House That Remembers, and it's inspired by Edgar Allan Poe's 1839 story The Fall of the House of Usher. The story folder contains major spoilers, so if you want to enjoy the game, I recommend avoiding those files.

Tech stack:

  • Streamlit (app.py) - UI and chat interface
  • Python - game logic, action validation, and history, clue, and inventory tracking
  • Ollama + Gemma - narrate the permitted outcome

Trigger warning…

How I Built It

From a big prompt to a game engine

My initial idea was simple: write the story, give it to the model as one big prompt, and let the AI act as the Dungeon Master (DM) or narrator. I quickly discovered that this approach wasn't reliable. The story could get stuck, and progression and pacing were inconsistent. Giving the model more freedom didn't necessarily make the experience better.

So I started to define some conditions and states that had to be reached, and eventually my whole approach changed. I moved most of the logic to the engine, and let the model focus on narration. I divided the story into 5 stages, each stage with its own progression conditions. The engine tracks the game state - the room, inventory, clues, number of turns... game state is then used by the engine and the model.

A Memory

However, another thing emerged. Like a chatbot, the narrator needs context: what happened earlier, what the player discovered, and what the characters already know. Gemma doesn't automatically retain conversation history between independent API calls. The application has to provide the relevant context with each request.

Conceptually, each turn combines:
[system prompt] + [stored memory] + [conversation history] + [new user message]

The model "remembers" because you'd re-send everything each time, but context windows are finite. RAG-style memory is often a popular choice, but for this relatively small project, I decided to go with something simpler, and in the end built a combination of "rolling summary" - have the model summarize older turns into a compact paragraph, then keep that summary plus recent messages verbatim - and "fact-based memory" - this is the story, facts, the rules, stored in key-value style, then inject the relevant ones into the system prompt.

I built prompt.py which essentially puts together the prompt for the model each turn.
Layers (top to bottom):

  1. core: static narrator rules (How to write, what to do and what not to do)
  2. story block: premise (static per story)
  3. stage block: changes with stage, there are 5 stages/levels
  4. state block: room, exits, inventory, clues
  5. history: last N messages (older are made into summary)
  6. engine result: + reminder, attached to the player's latest message

This layered approach gave me the best consistency.

There is a trade-off, of course. The more tightly you constrain the narrator, the less freedom it has to improvise. Depending on the player's choices, the experience can sometimes feel a little restrictive. But for this game, I found that reliable story progression was more important.

The Mystery

There are 4 endings in total, each triggered when specific conditions are met. That gives players a reason to revisit the house and discover what they missed. The engine also prevents the story from getting stuck indefinitely, and the player will always reach an ending.

And yes, there's an Easter egg hidden in the story. Wink wink.

Screenshot from the game

Tips for interacting with the story

Think of this as a tabletop role-playing game in text form. You play a character, describe what they do, and the AI acts as the DM: narrating events, maintaining the atmosphere, and playing the non-player characters (NPCs).

Describe your actions, think about your goal and what you hope to achieve. There is no right or wrong way to play this game.

The narrator distinguishes spoken dialogue, physical actions, exploration, and questions about the player's memories. You can make the intent clear in natural language:

  • Speak to your friend: What can I do for you? or I say, "Let's go."
  • Combine speech and an action: I pat him on the back and say, "Let's go."
  • Explore or examine: Look around or Examine the family photos.
  • Recall an established memory: What do I remember about my friend?
  • Move directly to a destination, even when it is several rooms away: I go to the upper landing.
  • Examine a book or note to read it and add it to your inventory; no separate pickup command is needed.

The sidebar tracks the Past, Present, and Echoes fragments needed to finish the story. Two clues in each category complete a fragment; the house also holds atmospheric and optional lore discoveries that do not count toward fragment progress. Pay attention to what you discover. The details may matter later.

Tech Stack

The tech stack is intentionally simple:

  • Streamlit (app.py) - UI and chat interface
  • Python - game logic, action validation, and history, clue, and inventory tracking
  • Ollama + Gemma - narrate the permitted outcome

I tested both gemma4:e4b and gemma4:e2b. The smaller model was faster, but it struggled to narrate the story consistently, even when the game engine's conditions had been met.

It was a useful reminder that model size involves trade-offs. A smaller model may be faster and lighter, but it doesn't necessarily follow a complex narrative as reliably. More prompt engineering might help, but for this project, I decided to stick with gemma4:e4b.

Running the Game

There are two ways currently - locally and online. The local run has an advantage of having a private session, everything runs on your machine and after the initial download, it can run offline.

For online experience - that was actually a challenge! Deploying models is not as simple as it may seem. After some help and discussion with Changsu Seong (thank you!) I managed to deploy the website and the model using the Render Hackathon Participant credits. The trade-off is performance. The model runs on CPU, so responses are slower than they would be on suitable local hardware. In my tests, responses took around 24 seconds on average, with longer waits after periods of inactivity.

I'll keep the demo running until the end of Hacktoberfest or until the available credits run out, whichever comes first. šŸ˜…

Run the Streamlit app locally

By default, the app uses Ollama locally. Install Ollama and download the model:

ollama pull gemma4:e4b

Make sure the Ollama service is running. Then, from the repository root, install the project dependencies. You can optionally create and activate a virtual environment first:

py -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt

Start the app with:
python -m streamlit run .\the-house-that-remembers\app.py

Streamlit prints a local URL in the terminal; open it in your browser to view the app. After the initial download, it can run on your machine offline.

Play online

Deployed on Render: https://the-house-that-remembers.onrender.com/

Why Does Open Innovation Matter?

For me, one of the biggest benefits of open-weight models is the ability to run them locally.

With Gemma and Ollama, the game can run entirely on a player's machine. Once the model is downloaded, no remote inference service is needed, and the conversation doesn't have to leave the computer. That privacy aspect is one of the things I find most appealing about local LLMs.

Open models also make experimentation more accessible. I could build a small narrative game, test different model sizes, explore prompt engineering, and learn how much of the experience should be handled by the model versus ordinary application code.

The online deployment also taught me something practical: running an open model is possible without owning dedicated hardware, but hosting costs and inference speed still matter. For a small project, finding the right balance between accessibility, privacy, reliability, and cost is part of the challenge.

My Agent Session

I used GitHub Copilot as my coding agent, especially for validating the story flow and debugging the game logic.

One particularly useful contribution was a test engine that checked whether specific phrases triggered - or correctly didn't trigger - game-state updates. At one point, I couldn't reach a particular ending. Copilot helped me find a missing phrase condition that prevented the relevant state change from happening.

That kind of testing was valuable because a narrative game can appear to work during casual play while still hiding progression bugs in less common paths.

GitHub Copilot Session
You

In Untitled-1.txt you can see results from the last game.
It is a lot better, but there are still a few problems.

  1. When player wants to go from e.g. Library to Upper Landing, they must explicitly say that they go to the entrance hall, and from there to upper landing. It might feel a bit unnatural for the player I guess. At least that's what happened to me
  2. When the player was upstairs, the friend stayed in the library, but the friend still talked to the player as if they were next to him, which doesn't make sense
  3. In all of my games, I have still never encountered the sister, ever
  4. The friend is a bit passive still... It's not that bad, but he doesn't small talk much, just kinda repeats the same stuff. They could talk about childhood, or something similar
  5. I wanted to add two more inventory items - the note, and the book. But right now the player must explicitly say that they take/pick up the items, and that will add it to the inventory. Maybe examining could be enough to trigger adding the item to the inventory
  6. The model didn't want to mention what is written in the book, but clue was added that the story was about a man who buried his sister alive. I don't think it's a big problem to mention it, after all isn't Poe copyright free at this point?
  7. Why is his name suddenly E. šŸ˜…
Agent
Agent

I’ll trace the recorded gameplay issues to the game engine, prompt, and story canon, then make targeted changes and validate them with focused tests.

10 messages not shown
Agent

The gameplay state confirms the friend’s location and story clues are managed separately from the model’s narration, so I’m checking those boundaries and the existing tests before adjusting both.

8 messages not shown
Agent
Agent
Agent
3 messages not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
13 messages not shown
Agent

I found why these show up: movement rejects non-adjacent destinations, the prompt has no authoritative friend location or scheduled sister event, and takeable items are only added by the take command. I’m addressing those at the engine/prompt level, while keeping the book’s local-history reveal distinct from the friend’s family secret.

Agent
6 messages not shown
Agent
Agent
2 messages not shown
Agent
Agent
Agent
Agent
Agent

The core behavior and regression tests are now in place. I’ve also updated the player tips and added a visible friend-location indicator; I’m running final checks for the modified Python and reviewing the diff for unintended changes.

2 messages not shown
Agent
Agent
Agent
12 messages not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

Implemented the gameplay fixes:

  • Players can request a destination several rooms away; the engine routes them through the shortest open path. The friend stays behind unless the player explicitly invites him along, and his location is shown in the sidebar and enforced in narration. engine.py prompt.py app.py
  • The sister’s first brief appearance is triggered once the story reaches her stage; later appearances can recur intermittently.
  • Examining the book or note adds it to inventory. The book’s historical passage is explicitly allowed to be narrated when revealed.
  • Prompt guidance encourages varied, grounded childhood small talk and clarifies that ā€œE.ā€ is only the letter’s initial, not the friend’s established name.
  • Updated the player tips in README.md and added regression tests in test_engine.py.

The model likely hesitated because the prompt’s secrecy rules were broad; Poe’s 1839 work is public domain in the U.S., and the game’s book passage is an original summary rather than a quotation.

Validation: all 6 regression tests passed, and Python syntax checks passed. Pylance reports only two existing unused-variable warnings in engine.py.

You

Game state:
{'room': 'Entrance Hall',
'friend_room': 'Entrance Hall',
'inventory': ['The Letter'],
'clues': ['A musty smell lingers in the air',
'The clock on the wall is frozen at 3:15'],
'visited': ['Entrance Hall'],
'examined': {'Entrance Hall:smell': 1, 'Entrance Hall:clock': 1},
'stage': 0,
'turns_in_stage': 8,
'turn_count': 8,
'room_turns': 8,
'last_notice_turn': 5,
'last_nudge_turn': -99,
'nudges_here': 0,
'last_sister_turn': -99,
'denial': 6,
'pushed': 0,
'support_given': 0,
'door_open': False,
'truth_known': False,
'sister_seen': False,
'ending': None,
'last_result': {'nudge': None,
'outcome': 'no_change',
'detail': 'Nothing the engine tracks changed. Treat the '
'action as ordinary roleplay (talking, resting, '
'small gestures). Do not add items, clues, or '
'events. No room change happens: the player is '
'still in the Entrance Hall, and nobody leads them '
'elsewhere unless they choose an exit (Library, '
'Kitchen, Grand Staircase).',
'new_clue': None,
'ambient': None,
'friend': None,
'intent': 'dialogue',
'stage_advanced': False},
'history': [{'role': 'assistant',
'content': '"You came. I didn\'t think you would... Thank you."\n'
'\n'},
{'role': 'user',
'content': '"Yes, of course... Your letter worried me... What\'s '
'wrong?"'},
{'role': 'assistant',
'content': 'He shifts slightly, rubbing at his eyes with a '
'trembling hand. "It\'s... nothing much. Just tired. '
'You look well, though. What have you been doing all '
'these years?"'},
{'role': 'user', 'content': '"Tired? What do you mean?"'},
{'role': 'assistant',
'content': '"Just... heavy," he murmurs, glancing toward the '
'shadows near the grand staircase. A musty smell '
'lingers in the air, a mixture of old wood and dust. '
'"Don\'t worry about it. Do you want tea? There\'s... '
'yes. Tea."'},
{'role': 'user',
'content': '"Sure. We can sit, have some tea, and chat..."'},
{'role': 'assistant',
'content': "He manages a weak smile that doesn't reach his "
'hollow eyes. "It’s nice... just to sit down for a '
'minute." He looks toward the library door, then back '
'at you. "Do you remember how we used to explore in '
'there? The library?"'},
{'role': 'user',
'content': '"Yeah, I have always loved the library"'},
{'role': 'assistant',
'content': '"It was strange," your friend agrees quietly, '
'staring at the polished wood of the entrance hall '
'floor. "All those tall shelves... I miss that." He '
'hesitates, then looks around the vast space again. '
'"What about you? What does your life look like these '
'days?"'},
{'role': 'user',
'content': '"Well, not much, you know. Got a boring job in the '
'city and my own place"'},
{'role': 'assistant',
'content': '"A job... that sounds steady." He nods slowly, '
'looking past your shoulder toward the wall. "The '
"clock here... it's stopped, you know. Always has "
'been, maybe." He quickly glances away from the '
'timepiece and then asks, "Did you ever sleep over at '
'all? Do you remember those?"'},
{'role': 'user', 'content': '"Yeah, I do..."'},
{'role': 'assistant',
'content': 'He lets out a small, breathy sound that might have '
'been a laugh. "It was... simpler then." He pulls his '
'shoulders inward slightly, as if cold. "You should '
"sit down properly; you look like you've walked a "
'long way."'},
{'role': 'user', 'content': '"Yes, let\'s sit"'},
{'role': 'assistant',
'content': 'He nods, seeming relieved by the suggestion of rest. '
'"Better? Just... sitting here." He looks toward the '
'kitchen exit with a flicker of uncertainty. "If '
"you're hungry later, I can get something from the "
'kitchen, if that helps?"'},
{'role': 'user', 'content': '"I would like something to driink"'}],
'summary': '',
'summary_idx': 0}

1 message not shown
Agent
Agent
Agent
Agent
Agent
Agent
1 message not shown
Agent
2 messages not shown
You

the problem is that the friend is inconsistent, sort of. he invites for a tea and then starts small talking. I wanted to introduce small talk, but when it is appropriate. Do you know what I mean?

Agent

Yes—the friend should follow the thread of the conversation: if he offers tea and you accept, he should deal with the tea first, not jump to a new topic. Small talk fits in the quieter gaps, not over an unanswered request. I’ll make that priority explicit in the stage-0 prompt.

Agent
Agent
Agent
Agent
Agent
Agent

Yes—that’s what I understand: small talk belongs in natural pauses, not when it interrupts an offer or request the friend should respond to. If the player accepts tea, he should follow up on the tea first, then make room for casual conversation afterward. He can still initiate small talk when there isn’t an immediate thread to follow.

I updated the stage-0 guidance in prompt.py to make that priority explicit, while keeping the rule that he invites the player to the kitchen rather than moving them automatically. All 6 regression tests pass, and the prompt file has no syntax errors.

You

Game state:
{'room': 'Kitchen',
'friend_room': 'Entrance Hall',
'inventory': ['The Letter'],
'clues': ['A musty smell lingers in the air',
'The clock on the wall is frozen at 3:15',
'A third cup sits set out at the table, as if someone else were '
'expected'],
'visited': ['Entrance Hall', 'Kitchen'],
'examined': {'Entrance Hall:smell': 1,
'Entrance Hall:clock': 1,
'Kitchen:cups': 1},
'stage': 1,
'turns_in_stage': 3,
'turn_count': 12,
'room_turns': 3,
'last_notice_turn': 11,
'last_nudge_turn': -99,
'nudges_here': 0,
'last_sister_turn': -99,
'denial': 5,
'pushed': 0,
'support_given': 0,
'door_open': False,
'truth_known': False,
'sister_seen': False,
'ending': None,
'last_result': {'nudge': None,
'outcome': 'no_change',
'detail': 'Nothing the engine tracks changed. Treat the '
'action as ordinary roleplay (talking, resting, '
'small gestures). Do not add items, clues, or '
'events. No room change happens: the player is '
'still in the Kitchen, and nobody leads them '
'elsewhere unless they choose an exit (Entrance '
'Hall).',
'new_clue': None,
'ambient': None,
'friend': None,
'intent': 'dialogue',
'stage_advanced': False},
'history': [{'role': 'assistant',
'content': '"You came. I didn\'t think you would... Thank you."\n'
'\n'},
{'role': 'user',
'content': '"Yes, of course... Your letter worried me"'},
{'role': 'assistant',
'content': 'He manages a faint nod, looking down at his hands. '
'"It\'s... it\'s just strange here now. I haven\'t '
'been sleeping much." He glances toward the staircase '
'before quickly averting his gaze again.'},
{'role': 'user', 'content': '"Did something happen?"'},
{'role': 'assistant',
'content': 'He shifts uncomfortably, running a pale hand over '
'the fabric of his shirt. "Nothing... nothing major. '
'Just things are different." The musty smell of old '
'wood and dust seemed to settle deeper into the '
'silence between you.'},
{'role': 'user', 'content': '"What do you mean by that?"'},
{'role': 'assistant',
'content': 'He hesitates, biting his lower lip until it turns '
'white. "It\'s just... the house feels different. '
'Like it remembers things I don\'t." He offers a weak '
"smile that doesn't reach his eyes."},
{'role': 'user', 'content': '"In what ways?"'},
{'role': 'assistant',
'content': 'He shrugs vaguely, looking around the cavernous hall '
'as if expecting something to materialize there. "I '
"don't know how to explain it. It just... sits "
'differently than before." He trails off, staring at '
'the dark wood of the front door.'},
{'role': 'user', 'content': 'examine entrance hall'},
{'role': 'assistant',
'content': 'You notice a tall clock against the wall, its hands '
'frozen precisely at three fifteen. The pendulum '
'hangs motionless in the quiet air. Your friend '
'glances over when you look toward it. "It probably '
'just needs winding," he murmurs before looking away '
'again.'},
{'role': 'user', 'content': '"Do you want to talk about it?"'},
{'role': 'assistant',
'content': 'He looks at you, the exhaustion etched deep around '
'his eyes. "Maybe later," he whispers, his voice '
'barely audible above the silence. He seems to '
'deflate slightly, as if the effort of conversation '
'is too much for him right now.'},
{'role': 'user',
'content': '"Sure... So what do you want to do?"'},
{'role': 'assistant',
'content': '"Well," your friend begins slowly, rubbing his '
'temples with both hands. "I... I could use some tea. '
"There's some in the kitchen. Would you like to "
'come?" He looks hopeful for a moment before his '
'anxiety pulls him back.'},
{'role': 'user', 'content': '"Yes, that sounds good"'},
{'role': 'assistant',
'content': 'He lets out a small, shaky breath of relief at your '
'agreement. "The kitchen... it\'s just down this '
'way." He gestures vaguely toward the archway leading '
'deeper into the house, but does not move to lead you '
'there himself.'},
{'role': 'user', 'content': 'I go to the kitchen'},
{'role': 'assistant',
'content': 'The familiar clutter of the kitchen greets you as '
'you enter. The rain continues its steady tick '
'against the dark window above the sink. Your friend '
'remains visible in the Entrance Hall, a still '
'silhouette near the front door.'},
{'role': 'user', 'content': '"Can I sit?"'},
{'role': 'assistant',
'content': 'Your friend does not follow you into the kitchen '
'immediately; he remains lingering near the threshold '
"of the Entrance Hall. He doesn't hear your words "
'from this distance. You are alone in the narrow '
'space now.'},
{'role': 'user', 'content': 'Is he coming?'},
{'role': 'assistant',
'content': 'From the Entrance Hall, you hear his hesitant '
'footsteps approaching slowly. He stops just inside '
'the doorway to the kitchen, watching you with wide '
'eyes. "I... I didn\'t know if you wanted me to '
'come," he murmurs softly. Two cups wait by the '
'teapot, and then your gaze drifts to a third cup set '
'out at the far end of the table, untouched.'},
{'role': 'user',
'content': '"Well, like I said. your letter sounded worrisome"'}],
'summary': 'The player went into the entrance hall and found a tall clock '
'whose hands were frozen at three fifteen. The narrator stated '
'that things felt strange in the house, saying it seemed like it '
'remembered things he did not. When asked for more detail, the '
'narrator explained the house "sits differently than before." '
'Finally, the player asked if the narrator wanted to talk about '
'it.',
'summary_idx': 12}

It says the friend is still in the entrance hall, but he should be in the kitchen

Agent

I see the mismatch: the movement rule only brings him along when the player explicitly says ā€œwith me,ā€ but in this exchange the friend invited the player to the kitchen and they accepted. I’ll check how the engine receives conversation history, then make an accepted invitation carry through to the move without having the friend follow every time.

Agent
Agent
Agent
Agent
Agent
Agent

The engine sees the prior assistant turn, so it can distinguish ā€œI accepted your invitationā€ from an ordinary move. I’ll track an accepted, room-specific invitation briefly and have the friend accompany the player only when they head to that destination.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

Fixed the friend’s location continuity. When the player accepts an invitation to a specific room, the engine remembers that destination briefly; when the player then goes there, the friend accompanies them. Ordinary movement still leaves the friend where he is unless invited.

All 7 regression tests pass. The test file remains under tests/, as requested, but is ignored by .gitignore; I left that file unchanged. The engine change is in engine.py.

You

I see that the small talk is only mentioned in Stage 0, but it can occur any time. Can you makethe change more generic?

Agent

I’ll move the conversational-continuity and small-talk guidance into the shared narrator rules so it applies in every stage, and remove the stage-0-only version.

Agent
Agent
Agent
Agent
Agent
Agent
Agent

Done. I moved the friend’s conversational-continuity and small-talk guidance into the shared narrator rules in prompt.py, so it applies in every stage. Stage 0 now only describes his early demeanor; small talk should fit his current state and the moment throughout the story.

Validation passed: all 7 regression tests pass, and the prompt has no syntax errors.

You

Game state:
{'room': 'Kitchen',
'friend_room': 'Entrance Hall',
'friend_follow_target': None,
'friend_follow_expires': -1,
'inventory': ['The Letter'],
'clues': ['A musty smell lingers in the air',
'The clock on the wall is frozen at 3:15',
'A third cup sits set out at the table, as if someone else were '
'expected',
'A kitchen chair is pulled out, as though someone had just stood '
'up'],
'visited': ['Entrance Hall', 'Kitchen'],
'examined': {'Entrance Hall:smell': 1,
'Entrance Hall:clock': 1,
'Kitchen:cups': 1,
'Kitchen:chair': 1},
'stage': 1,
'turns_in_stage': 4,
'turn_count': 11,
'room_turns': 4,
'last_notice_turn': 11,
'last_nudge_turn': -99,
'nudges_here': 0,
'last_sister_turn': -99,
'denial': 4,
'pushed': 0,
'support_given': 0,
'door_open': False,
'truth_known': False,
'sister_seen': False,
'ending': None,
'last_result': {'nudge': None,
'outcome': 'no_change',
'detail': 'Nothing the engine tracks changed. Treat the '
'action as ordinary roleplay (talking, resting, '
'small gestures). Do not add items, clues, or '
'events. No room change happens: the player is '
'still in the Kitchen, and nobody leads them '
'elsewhere unless they choose an exit (Entrance '
'Hall).',
'new_clue': 'A kitchen chair is pulled out, as though someone '
'had just stood up',
'ambient': 'One chair at the table is pulled out slightly, as '
'though someone had only just stood up from it.',
'friend': None,
'intent': 'dialogue',
'stage_advanced': False},
'history': [{'role': 'assistant',
'content': '"You came. I didn\'t think you would... Thank you."\n'
'\n'},
{'role': 'user',
'content': '"Of course... Your letter worried me. Are you '
'okay?"'},
{'role': 'assistant',
'content': 'He shifts slightly, rubbing at his temples with a '
'pale hand. "I\'m... I’m fine. It\'s just... things '
'here." He manages a weak smile before looking away '
'again. "What have you been up to? Still around the '
'city?"'},
{'role': 'user',
'content': '"Yeah... You know, got a boring job and moved to the '
'city. What about you?"'},
{'role': 'assistant',
'content': 'He stares at the floor for a moment, then shrugs '
'faintly. "Same old things, I suppose. Just... '
'staying put." The musty smell of old wood and dust '
'hangs heavy in the vast hall around you both. He '
'seems to deflate slightly under your gaze.'},
{'role': 'user', 'content': 'examine entrance hall'},
{'role': 'assistant',
'content': 'You notice the tall clock against the wall; its '
'hands are stuck at a quarter past three. The '
'pendulum hangs motionless, and nothing in this hall '
'seems to tick. He glances over at it briefly. "It '
'just needs winding," he murmurs, then looks back '
'toward you. "Did you want tea? There\'s... yes. '
'Tea."'},
{'role': 'user', 'content': '"Sure, tea sounds good"'},
{'role': 'assistant',
'content': 'He nods slowly, seeming relieved by the simple '
'agreement. "Good. I... I could use it." He hesitates '
'for a beat before gesturing vaguely toward an unseen '
'area. "I suppose we could go to the kitchen?"'},
{'role': 'user', 'content': '"Yes"'},
{'role': 'assistant',
'content': 'He nods again, the fragile hope flickering across '
'his face. "The kitchen... it\'s just down this way." '
"He doesn't move to lead you, simply waiting for your "
'next step toward that direction. "It\'s quiet in '
'there, usually."'},
{'role': 'user', 'content': '"Go to the kitchen"'},
{'role': 'assistant',
'content': 'He watches you move toward the Kitchen exit, his '
'expression unreadable for a moment. "Just... let me '
'know if you need anything," he says softly, his '
'voice barely above a whisper. He seems to gather '
'himself, then adds, "It\'s strange being back in '
'this place."'},
{'role': 'user', 'content': 'Go to the kitchen'},
{'role': 'assistant',
'content': 'The narrow kitchen greets you with its familiar '
'clutter and the steady tick of rain against the dark '
'window above the sink. The air here is heavy with a '
'musty scent. Your friend remains back in the '
'Entrance Hall. You can hear the quiet drumming of '
'the rain from that distant hall.'},
{'role': 'user', 'content': 'examine kitchen'},
{'role': 'assistant',
'content': 'Two cups rest neatly beside the teapot on the '
'counter. Further across the small table, a third cup '
'sits waiting, untouched and stark against the wood. '
'The rain continues its steady rhythm against the '
'windowpane.'},
{'role': 'user', 'content': 'Wait for my friend'},
{'role': 'assistant',
'content': 'You stand by the counter, listening only to the rain '
'and the silence of the kitchen. After a moment, you '
'hear his hesitant footsteps approaching from the '
'hall. "Sorry," he murmurs as he enters, looking '
'exhausted. "I just... I needed a minute."'},
{'role': 'user', 'content': '"No worries"'},
{'role': 'assistant',
'content': 'He looks at you, his eyes wide for a second before '
'they settle back into their weary focus. "It\'s '
'just... always something here," he says quietly, '
'gesturing vaguely around the cluttered space. He '
'shifts his weight, looking like he might collapse. '
'"Do you want me to pour the tea?"'},
{'role': 'user',
'content': '"Yes, that would be nice. It is so cold outside"'}],
'summary': '',
'summary_idx': 0}

It still says that the friend is in the entrance hall, but he is in the kitchen a pouring tea. Can we fix this?

Agent

The state shows two causes: the friend didn’t accompany the player on the kitchen move, and ā€œWait for my friendā€ was treated as ordinary roleplay, so the engine never recorded his arrival. I’ll make waiting for the friend an explicit engine-resolved action, so the narrator and authoritative location stay aligned.

Agent
Agent
Agent
Agent
Agent
Agent
2 messages not shown
You

I dont think it's a good idea to overly rely on specific keywords to be said. The model or the engine should be able to distinguish this. The problem before was that the friend stayed in the library, the player went upstairs, and the model thought the player was talking to the friend, and the friend was responding. So instead of those keywords, let's just say: keep the continuity in mind, or always make sure the friend is in the same room as the player if they want to speak with each other

Agent

Understood: no brittle phrase matching. I’ll remove the invitation/wait keyword tracking and make the rule intent-based: when the player starts a conversation while separated from the friend, the engine brings him into the player’s room before the narrator replies.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

Changed this to be intent-based rather than phrase-based. When the engine classifies an action as dialogue or action-plus-dialogue and the friend is in another room, it updates his location to the player’s room and tells the narrator to describe him joining before responding. Ordinary movement still leaves his location unchanged.

Removed the invitation and ā€œwait for friendā€ keyword tracking. Updated the dialogue guidance and regression test in prompt.py and test_engine.py. All 6 tests pass, and git diff --check is clean.

2 messages not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
2 messages not shown
Agent
2 messages not shown
Agent
1 message not shown
You

add things like:

We can do it together, it is going to be okay, we can face... i want to help / i can help / take my hand, give me your hand /

Agent

I’ll add those supportive phrases to the engine’s existing support matcher, including common wording variants, then run a focused check that they trigger the kindness effect.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

Added those supportive phrases to the matcher in engine.py. It now recognizes phrases such as ā€œWe can do it together,ā€ ā€œIt’s going to be okay,ā€ ā€œI want to help,ā€ and ā€œTake my hand,ā€ including the variants covered by the tests.

All 7 regression tests pass.

You

{'role': 'user',
'content': 'I stand up, go closer and and look into his face. '
'"What is in that room?" I show him the upper landing '
'note I took'},
{'role': 'assistant',
'content': 'The heavy silence of the library gives way as you '
'move through the Entrance Hall and up the Grand '
'Staircase toward the Upper Landing. As you reach the '
'narrow landing, a fleeting shadow drifts past—a '
'whispery form that seems to repeat its warning about '
'leaving before dissolving entirely. Your friend '
'remains in the Library below.'},

this is not correct

Agent

The action says ā€œgo closerā€ and mentions the upper-landing note as an object, but the narrator treated ā€œgoā€ plus ā€œupper landingā€ as a room destination. I’ll reproduce that against the current resolver and add a regression test so an item reference won’t send the player upstairs.

Agent
Agent
Agent
Agent
Agent
Agent
Agent

The resolver confirms the false positive: it sees ā€œgo closerā€ as a movement verb and matches ā€œUpper Landingā€ inside ā€œUpper Landing Noteā€ as the destination. I’ll exclude names of inventory items when extracting room destinations, so a note mention won’t be mistaken for travel.

Agent
Agent
Agent
Agent
Agent
Agent

The new test exposed a second false positive: ā€œlook into his faceā€ was classified as room exploration because ā€œlandingā€ appeared later in the note reference. I’ll apply the same inventory-item filtering to intent classification, then rerun the regression.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

Fixed the false movement trigger in engine.py. The resolver was interpreting ā€œgo closerā€ as a movement command and the words ā€œupper landingā€ in the carried note’s name as the destination. It now excludes carried item names when parsing room destinations and exploration intent, so showing the note shouldn’t send the player upstairs.

All 8 regression tests pass, including the reported action, and the engine file has no syntax errors.

2 messages not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
1 message not shown
Agent
2 messages not shown
Agent
3 messages not shown
You

I am in stage 5, but the room is still upper landing, I said "enter bedroom" and it didn't change

Agent

I’ll check the door gate and movement parser. If ā€œenter bedroomā€ should work in the current state, I’ll pin down which condition is blocking it.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

The door opening and entering the bedroom are two separate actions:

  1. On the Upper Landing, opening the door sets door_open and advances the engine to stage 5. The player remains on the landing.
  2. Then enter bedroom should move the player into the Parents’ Bedroom.

I reproduced enter bedroom with stage = 5 and door_open = True; the engine moved into the bedroom. If it didn’t in your game, check door_open and the outcome/detail in last_result. Stage 5 with door_open = False would be inconsistent with the normal progression, and the engine would block entry. The door check is in _try_door; stage progression is in _advance_stage.

You

Game state:
{'room': 'Upper Landing',
'friend_room': 'Upper Landing',
'inventory': ['The Letter', 'Upper Landing Note', 'Library Book'],
'clues': ['A musty smell lingers in the air',
'A third cup sits set out at the table, as if someone else were '
'expected',
'A kitchen chair is pulled out, as though someone had just stood up',
'A plate of food sits on the counter, untouched',
'The calendar on the wall is turned to March, but it is currently '
'October. Year is faded and unreadable',
'The clock on the wall is frozen at 3:15',
"A note on the parents' bedroom door reads: do not open, do not "
'change anything',
'Slow knocking comes from inside the locked bedroom',
'A book was recently removed from the shelf',
"The house's first owner buried his sister alive and died soon "
'after; the house stood empty for years',
'The same few footsteps repeat overhead',
'Family photos on the wall are blurred and unrecognizable'],
'visited': ['Entrance Hall',
'Kitchen',
'Grand Staircase',
'Upper Landing',
'Library'],
'examined': {'Entrance Hall:smell': 1,
'Kitchen:cups': 2,
'Kitchen:chair': 1,
'Kitchen:food': 2,
'Kitchen:calendar': 2,
'Entrance Hall:clock': 1,
'Upper Landing:door': 2,
'Library:shelf': 1,
'Library:history': 2,
'Library:footsteps': 1,
'Library:photos': 1},
'stage': 5,
'turns_in_stage': 3,
'turn_count': 76,
'room_turns': 4,
'last_notice_turn': 54,
'last_nudge_turn': 76,
'nudges_here': 1,
'last_sister_turn': 76,
'denial': 0,
'pushed': 0,
'support_given': 0,
'door_open': True,
'truth_known': False,
'sister_seen': True,
'ending': None,

Agent
Agent

This state shows that the door is open and the game is in stage 5, but the player is still on the Upper Landing. The bedroom is reached in a separate movement action; truth_known becomes True only when that move succeeds. The room alias includes ā€œbedroomā€ at engine.py, and the truth flag is set during movement at engine.py.

Try ā€œgo to the parents’ bedroomā€. If it still doesn’t move, check last_result for its outcome and detail; that will show whether the engine blocked the move or resolved it some other way.

3 messages not shown
Agent
Agent
Agent
Agent
Agent
1 message not shown
You

truth_known is True, denial is 0, but the friend is still refusing to talk about it.

Agent

The screenshot shows the game is in the bedroom, but the stage-5 guidance still encourages ā€œfragmentsā€ and the shared prompt says direct questions should be met with evasion. I’ll check the current prompt/state block, then make the truth-stage instruction clearly override that earlier deflection rule.

Agent
Agent
Agent
Agent
Agent
Agent

I found a prompt conflict: the global rule tells the friend to evade questions about the past, while stage 5 only asks for ā€œfragments.ā€ I’ll make that evasion explicitly pre-revelation, and only authorize a direct account after truth_known becomes true on entering the bedroom.

Agent
Agent
Agent
Agent
Agent

The stage-5 prompt was pulling in opposite directions: it told the friend to speak in fragments, while a global rule allowed him to dodge direct questions. I updated prompt.py so that evasion applies only before the bedroom truth is faced. After the player enters and truth_known is true, the prompt now directs the friend to answer questions honestly and plainly, while keeping his delivery gentle and halting.

The prompt also explicitly prevents revealing the family’s fate while the player is still outside the bedroom. I verified both prompt states and confirmed the file has no syntax errors.

You

I suppose, if denial is too high, he will still refuse, and eventually it might lead to the bad ending. If denial is low, truth is know, they open the door, the friend will eventually be able to face the truth

Agent

I’ll align the prompt with that progression: door opening alone shouldn’t unlock a clear admission; the friend’s ability to face the truth should depend on denial being low enough, consistent with the engine’s ending logic. I’ll check the actual denial thresholds and prompt state before adjusting it.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

I’ll keep the door’s existing denial gate, but stop the engine from automatically lowering denial when the door opens or the player enters. That lets stage 5 retain a ā€œnot ready yetā€ state; supportive dialogue can lower it, and the existing time limit can still lead to the denial ending if he remains stuck.

Agent
Agent
Agent
Agent
Agent
Agent
Agent

That’s the distinction I’ve implemented: the player can learn the truth before the friend is ready to accept it.

The door still won’t open while denial is above 3. I removed the automatic denial reductions that used to happen when it opened and when the player entered, so denial at the threshold can persist. Supportive dialogue can lower it; if it remains unresolved at the turn limit, the denial ending can still occur. The stage-5 prompt now makes the friend’s willingness to answer depend on his denial level.

The engine and prompt regression tests pass: 10 tests passed. See engine.py and prompt.py.

You

attachment:Pasted text #1

Agent

I’ll read the pasted gameplay log to see how the updated denial/truth progression behaved, then compare the relevant state transitions with the implementation.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

This run looks like the intended truth progression worked: the final state has truth_known=True, denial=0, and ends with leave_together. The friend starts out evasive, then admits that his father locked the family in and told him not to open the door. support_given=0 doesn’t mean denial stayed high—discovering clues also lowers denial.

One detail to note: the game ended on the player saying ā€œLet’s leave together,ā€ while the friend had just said, ā€œI don’t know if I could.ā€ The engine currently treats that phrase as enough to trigger the ending once the truth is known; it doesn’t require the friend to explicitly agree or the player to reach the front door. That behavior is in engine.py. The turn count being 92 doesn’t contradict the limit: the denial ending applies at the limit only if the truth is still unknown or denial remains unresolved.

I haven’t changed anything based on the transcript.

You

Hmm, I am not sure. I am thinking about explicitly adding the memory fragments into the UI/sidebar. Start with 0/3 memory fragments. But still haven't decided. Right now we get the "good ending" but we dont learn anything, only that the father locked them in the room. We dont learn about the sister, or the mother, or the conflict, why the friend is in denial, etc. How to approach this scenario?

Agent

I’ll ground the design in the current fragment rules, sidebar, and story canon so the recommendation separates what the game already tracks from the revelations it still needs to deliver.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

I’d separate solving the house’s mystery from understanding the family’s story. Right now, the three memory fragments—routine, rules, and night—are clue categories that unlock the bedroom door. They don’t represent the mother, sister, or the friend’s conflict, so opening the door can technically satisfy the game’s puzzle without delivering its emotional resolution.

I’d add the 0/3 fragment tracker to the sidebar. Show progress by category, for example ā€œRoutine 1/2,ā€ ā€œRules 0/2,ā€ and ā€œNight 1/2,ā€ and mark each complete. This makes the door’s requirements understandable without spelling out the answer. The current clue list can continue to show what the player has found.

Then, treat the bedroom as the start of the final revelations, not as the moment when all of them are automatically explained. Your canon already establishes three strong story beats:

  1. The mother was affected first. She became exhausted and unwell, and recognized that something was wrong with the house.
  2. The father tried to preserve the family. After failing to help her, he grew fearful and locked himself, the mother, and the sister in the room. His attempt to protect them caused their deaths.
  3. The friend and sister each responded differently. The friend denied the danger, fell out with his father, and obeyed the last instruction not to open the door out of anger and fear. The sister tried to keep the family together; after death, she clung to the preserved illusion.

Those details are already in canon.json, but the stage-5 prompt doesn’t currently ensure the player hears them.

A useful distinction in the engine would be ā€œthe player has seen the truthā€ versus ā€œthe friend is ready to face it.ā€ The current truth_known flag marks entering the bedroom, but the friend’s admission and the together ending aren’t gated on learning the family story or his agreement. That’s why a run can reach a ā€œgoodā€ ending after only a partial confession.

My recommendation: keep the three existing fragments as the door puzzle, then have the final scene reveal the family history in a few authored, paced beats. Let the friend resist or reveal only part of it while denial is high; as he becomes ready, make the mother’s decline, the sister’s role, and his conflict with his father available through dialogue. The ending can remain the player’s choice, but the together ending should feel like a genuine resolution rather than arriving before the friend has faced what happened.

So: yes to a clear sidebar 0/3 tracker; separately, add a small, engine-tracked final-story progression so the ending has room to earn its emotional payoff.

You

Okay, agreed

Agent

I’ll implement the two agreed changes: a clear 0/3 memory-fragment tracker in the sidebar, and an engine-owned final-story progression so the mother’s decline, the sister’s role, and the friend’s conflict are surfaced before a together ending can resolve. I’ll first check the current ending conditions and UI patterns so this fits the existing game flow.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

The core progression is in place: the sidebar will show 0/3 fragment completion, and the engine authorizes four final family memories one at a time during conversation when denial is below 3. I’ve also gated the together ending on those memories being shared; I’m validating that sequence and the updated prompt/canon now.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

Implemented the agreed approach.

  • The sidebar now starts at 0/3 memory fragments and shows clue progress for Routine, Rules, and Night. After the bedroom truth is faced, it also tracks the friend’s four family memories.
  • In stage 5, face-to-face conversation can reveal the family story one memory at a time: the mother’s decline and the family’s deaths, the father’s attempt to preserve them, the sister’s hope, and the friend’s conflict and silence. The narrator is directed to include each engine-authorized memory clearly.
  • The together ending now requires denial below 3 and all four memories shared. The bedroom-door puzzle and the other endings remain unchanged.

The UI and engine are in app.py and engine.py; prompt and canon guidance are updated in prompt.py and canon.json. I also updated the player guidance in README.md.

Validation passed: 14 regression tests, Python compilation, canon JSON parsing, and git diff --check.

1 message not shown
Agent
1 message not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
1 message not shown
Agent
Agent
Agent
3 messages not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
5 messages not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
2 messages not shown
Agent
1 message not shown
Agent
Agent
Agent
Agent
Agent
Agent
3 messages not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
1 message not shown
You

I would add something, an optional ending, sort of.
I would add that it is possible to talk to the sister ghost (but only if certain conditions are met)
The sister would explain her POV if player asks: that she is protecting and preserving the family. she wants things to stay as they are because it is keeping their family alive. (based on the canon). In this scenario it is possible to reach an optional ending - that the player accepts the sister's bargain and leaves alone (the difference is, that he learns partial truth)

Agent

That fits the canon: the sister’s ā€œbargainā€ would offer preservation instead of confronting the family’s deaths. The main design choice is when the player can reach her and how much is revealed; those determine whether this feels like a real alternate path or a quick shortcut.

Agent
Agent

I’ll implement that as a third encounter: after two warning appearances, the next cooldown appearance can become a conversation opportunity only when the friend is elsewhere. I’ll ensure addressing her doesn’t summon the friend, and that her perspective and ending require explicit player choices.

Agent
Agent
Agent
Agent
Agent
Agent
Agent

Current story flow

The engine has six stages, but the first five are mostly narrative permissions. The door puzzle—not simply reaching a stage—is the main progression gate.

  1. Arrival: The friend is glad you came, but exhausted and struggling to explain himself. After five turns and visiting at least two rooms, the stage advances.
  2. Contradictions: He talks as though his family is around. After five turns and finding at least one clue, the stage advances.
  3. Frozen house: Clocks, dates, food, and routines suggest that time and daily life have gone wrong. After five turns and finding at least four clues, the stage advances.
  4. Sister’s warnings: Her warning appearances become available. After five turns and finding at least seven clues, the stage advances.
  5. Parents’ fragments: Knocking, footsteps, humming, and the bedroom-door note point toward the locked room. This stage lasts until the door opens.
  6. Reconstructed truth: The player opens and enters the bedroom. Entering sets truth_known; if denial is low enough, the friend can then disclose four authored family memories, one per conversation.

The stage thresholds are in STAGE_RULES. Finding two clues in each of Routine, Rules, and Night completes the three fragments shown in the sidebar. The bedroom door requires all three fragments and denial at 3 or lower (fragments_complete, _try_door).

Denial and endings

  • Denial begins at 8. Each new clue lowers it by one; up to three supportive actions can also lower it by one each.
  • Before all fragments are complete, bluntly confronting the friend about the family’s deaths raises denial. The third such confrontation triggers the denial ending. After all fragments are complete, that confrontation instead lowers denial.
  • At turn 90, the denial ending triggers if the player hasn’t entered the bedroom or denial is still 3 or higher.
  • The together ending requires truth_known, denial below 3, and all four family memories shared. A recognized ā€œleave togetherā€ phrase then ends the game.
  • The alone ending is triggered by leaving from the Entrance Hall.

The together ending is currently a decision trigger, not a literal exit journey: the player doesn’t have to return to the Entrance Hall, and the engine doesn’t require the friend to speak an explicit ā€œyes.ā€ The ending prompt narrates them leaving.

The sister’s optional path

Your idea fits the story’s conflict: the friend tries to face what happened, while his sister believes keeping the family preserved is a way of keeping them alive. An optional ending where the player accepts her offer and leaves alone would give that belief a meaningful alternative, while revealing only her partial understanding.

That path isn’t implemented yet. Right now, the engine tracks only whether she has appeared, and she repeats her warning after a cooldown; it doesn’t count two warnings, make her available for conversation, or track her perspective being revealed. Also, dialogue normally brings the friend into the player’s room if he’s elsewhere, so the sister conversation would need a specific exception (sister_seen and appearance handling, dialogue arrival logic). The canon currently labels the sister’s bargain ending optional and unimplemented (ending directions).

The unlock you described is specific enough to implement: after two warning appearances, when the cooldown passes and the friend isn’t with the player, she appears again as a conversation opportunity instead of giving another warning. The player can ask why she wants them to leave; she explains that preserving the family is, to her, keeping them alive. Accepting her bargain must be explicit, and the ending can state that the player leaves alone with this partial truth.

One thing to decide for the story’s clarity: should her conversation and bargain be possible before the player enters the bedroom, or only after? Before entry makes it an alternate path that avoids the full truth; after entry makes it a competing response to seeing the family. Either is consistent with the canon, but the first makes ā€œpartial truthā€ especially clear.

You

actually both might be okay? they both give the story a compelling different perspective. If the conditions are met before the door is open its okay, and if the conditions are met after the bedroom is open it is also okay, however, the other condition should still be met, that the player should be alone and friend elsewhere

Agent
Agent

The engine and prompt changes applied, but the canon update didn’t match the current wording, so I’m checking the exact result before continuing. I’ll also verify the new encounter state and acceptance path with focused tests.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

The first regression run caught an important ambiguity: asking ā€œWhy do you want to keep them as they are?ā€ was accidentally being read as accepting the bargain. I tightened acceptance to explicit agreement phrases and am rerunning the progression tests, including both before- and after-bedroom paths.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

Implemented the optional sister’s-bargain path. The sister can offer a private conversation after two warnings and the cooldown, but only when the friend is in another room. This can happen before or after the player enters the bedroom.

If the player asks about her perspective, she explains that she believes preserving the family keeps them alive. The player must explicitly accept after hearing her explanation to trigger the ending; a question like ā€œWhy do you want to keep them as they are?ā€ won’t count as acceptance. The ending reflects her partial perspective without contradicting anything the player has already seen.

Updated engine.py, prompt.py, canon.json, and README.md. All 18 regression tests pass; Python syntax and canon JSON checks are clean.

You

if after two warnings + cooldown the player is not alone, what will happen? can it be retriggered currently? e.g. when he is alone later

Agent
Agent

Yes. If the cooldown expires while the friend is with the player, the sister gives another warning and resets her appearance timer. Since the warning count stays capped at two, she can offer the private conversation after a later cooldown when the friend is elsewhere.

One detail: once that private conversation becomes available, the current code keeps it available until the player moves. So the player can talk to her on a later turn while the friend remains elsewhere; it doesn’t require catching her on the exact turn she appears. This logic is in engine.py.

1 message not shown
Agent
Agent
Agent
3 messages not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
1 message not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
2 messages not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
2 messages not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
1 message not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
1 message not shown
You

Okat, that's good. Now I would think about how to call those categories.
What about: Past fragments - calendar, clock, book history, family photos
Present fragments - third cup, food plate, chair, ... but dunno about the third one. Let's discuss, dont change it right now

Agent

Past / Present / Echoes feels like a promising set. It gives each category a clear idea, and it fits the clues better than Routine / Rules / Night:

  • Past — the stopped clock and outdated calendar suggest time that has already gone wrong. Family photos could fit as traces of the family’s history, though their blurred faces make them more about lost memory than specific events.
  • Present — the third cup, chair, and food are ordinary signs of a household that seems to be carrying on. This feels like your strongest category.
  • Echoes — repeating footsteps, humming, and knocking are things the house seems to replay. I prefer this to Night, since those clues aren’t necessarily about nighttime.

One wrinkle: the book history is about the house’s previous owner, not this family’s past. It could stay an optional discovery, rather than counting toward Past. The family photos could be optional too—or become a Past clue if you want that thread to include them.

Mechanically, I’d preserve the current balance of 4 / 3 / 3 clues across the categories. The note on the bedroom door is currently a Rules clue; if we move toward Past / Present / Echoes, we’d need to decide where it belongs. It might fit Past as a trace of the father’s decision, though its meaning points toward preservation and ā€œkeeping things as they are.ā€

So my tentative recommendation is Past / Present / Echoes, with the book history remaining optional. That gives the three threads a shared framing without making every discovery count toward them.

You

I agree. Note can be in the past, but balance out the number of clues, or change how many are required

Past: clock, calendar, family photos, note
Present: third cup, chair, food
Echoes: Footsteps, humming, knocking, maybe voices, crying etc

Optional: book, smell, the rest of them

Agent

That gives us a clear structure: Past / Present / Echoes, with optional discoveries outside those threads.

One small count detail: the engine currently treats the food as two separate discoveries—the untouched plate, then later the fact it hasn’t spoiled. So the counts would be:

  • Past — 4: clock, calendar, family photos, note
  • Present — 4: third cup, chair, untouched food, food that hasn’t spoiled
  • Echoes — 3: footsteps, humming, knocking
  • Optional — 4: book history, musty smell, missing-book gap, changing portrait

The family photos are currently optional, so they’d need to become a Past clue to match your proposed grouping. The book-history clue would remain optional, as you suggested.

I’d keep the requirement at 2 clues per category. That gives the player some choice in what to investigate without making the Echoes thread require every single clue. It also keeps the existing progression difficulty: six categorized discoveries to complete the three fragments. If we later add voices or crying, they can expand Echoes without forcing us to raise the requirement.

The photo clue may need thought in the framing: blurred faces read as lost memory more than straightforward history, but that still fits Past thematically. No changes made.

You

Okay, I agree. Implement changes

Agent

I’ll update the category names and assignments consistently across engine, sidebar, canon, docs, and tests, then verify the category counts and regression suite.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

Implemented the new Past / Present / Echoes categories and kept the requirement at two clues per category.

  • Past (4): clock, calendar, family photos, note
  • Present (4): third cup, chair, untouched food, food that hasn’t spoiled
  • Echoes (3): footsteps, humming, knocking
  • Optional (4): musty smell, missing-book gap, local-history book, changing portrait

Updated the sidebar categories, engine assignments, canon, README, and category regression test in engine.py, canon.json, README.md, and test_engine.py.

All 21 tests pass; syntax, JSON, clue counts, and diff checks are clean.

1 message not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
1 message not shown
You

I am in stage 4, missing one Echo fragment, I read the note and went upstairs. Maybe when the player tries opening the door, it can also trigger the knocking

Agent

I’ll check how locked-door attempts are currently handled, then wire the knocking clue to that interaction and add a regression test so the player can discover it while trying to open the door.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

Implemented. When the player tries the still-locked bedroom door, three slow knocks answer from within and the knocking clue is recorded—so this works even if they already read the note. It’s a one-time discovery; repeat attempts won’t add it again.

All 22 regression tests pass, and the changed Python files compile successfully. See engine.py and test_engine.py.

4 messages not shown
You

Your friend looks up at you then, a flicker of relief crossing his exhausted features. "It's alright," he says softly, though his voice remains thin. He seems to deflate slightly, the tension draining from his shoulders for a moment. "You came."

šŸ§‘
"I think you need to let go"

šŸ•Æļø
He watches you for a long moment, his expression unreadable against the pallor of exhaustion. "I don't know how," he replies simply, looking back towards the still figures on the bed. "It feels like if I let go, they'll just... fade away completely." He pauses, then adds quietly, "What have you been doing all these years? I mean... if you want to tell me."

I think we need to remove small talk from stage 5/or after door open and and truth known

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

Updated the narration guidance so after the player enters the bedroom and learns the truth, the friend won’t initiate small talk, ask about the player’s life, or pivot to ordinary topics. His conversation stays focused on his family, grief, and the player’s immediate choices. The existing small-talk guidance and example lines are now explicitly marked for the story before the truth is faced.

Added regression tests confirming the guidance switches at that point. All 24 tests pass, and syntax and diff checks are clean. Changes are in prompt.py, canon.json, and test_engine.py.

1 message not shown
Agent
Agent
Agent
Agent
Agent
2 messages not shown
Agent
Agent
Agent
Agent
3 messages not shown
Agent
Agent
Agent
3 messages not shown
Agent
Agent
Agent
Agent
3 messages not shown
Agent
Agent
Agent
Agent
2 messages not shown
Agent
Agent
Agent
Agent
3 messages not shown
Agent
Agent
Agent
Agent
Agent
2 messages not shown
Agent
1 message not shown
Agent
Agent
Agent
2 messages not shown
Agent
Agent
5 messages not shown
Agent
Agent
Agent
1 message not shown
Agent
Agent
Agent
Agent
2 messages not shown
Agent
Agent
Agent
Agent
Agent
3 messages not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
2 messages not shown
Agent
Agent
Agent
Agent
Agent
2 messages not shown
Agent
3 messages not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
4 messages not shown
Agent
Agent
Agent
Agent
Agent
Agent
2 messages not shown
Agent
Agent
Agent
2 messages not shown
Agent
Agent
Agent
Agent
1 message not shown
Agent
Agent
Agent
2 messages not shown
Agent
Agent
Agent
Agent
Agent
2 messages not shown
Agent
Agent
Agent
Agent
Agent
Agent
4 messages not shown
Agent
Agent
Agent
Agent
1 message not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
2 messages not shown
Agent
Agent
Agent
3 messages not shown
Agent
Agent
Agent
Agent
Agent
3 messages not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
2 messages not shown
Agent
1 message not shown
Agent
Agent
Agent
2 messages not shown
Agent
1 message not shown
Agent
Agent
Agent
2 messages not shown
Agent
Agent
Agent
Agent
2 messages not shown
Agent
Agent
1 message not shown
Agent
Agent
Agent
3 messages not shown
Agent
Agent
Agent
2 messages not shown
Agent
Agent
Agent
Agent
Agent
Agent
2 messages not shown
Agent
Agent
Agent
Agent
Agent
3 messages not shown
Agent
Agent
Agent
Agent
3 messages not shown
Agent
Agent
Agent
Agent
Agent
Agent
5 messages not shown
You

She hesitates, glancing toward the upper landing before turning back to you. "It's just... what we keep here," she murmurs, her voice barely above a whisper. Then, looking past you towards the library archway, she suggests gently, "Perhaps we should go back inside? To the library."

It is weird that the sister invited us to go to the library. Is that written somewhere?

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

That exact invitation isn’t scripted in the game rules. It was generated by the narrator.

The prompt does give the model the shared memory of ā€œexploring the family libraryā€ (prompt.py), and it generally allows characters to suggest places (prompt.py). The sister-specific guidance says she may explain her belief about preserving the family, but doesn’t address suggesting destinations (prompt.py.

So the library suggestion is an improvised, somewhat misplaced attempt at conversational steering—not an authored sister behavior or an engine-triggered event. The prompt could be tightened to keep her focused on her warning or preservation belief, and stop her from casually inviting the player elsewhere.

You

yeah, it is a bit out of place. she should keep the focus on the warning

Agent

I’ll tighten the sister-specific narration rules: her appearances should stay on the warning, and private conversation can explore her preservation belief without turning into casual talk or invitations to other rooms.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

Updated the narration guidance so the sister stays focused on warning the player to leave. She won’t suggest going to another room or drift into casual conversation. During her private encounter, she can still explain her preservation belief and discuss her bargain—but the exchange should remain centered on that.

Added a regression test for the prompt guidance. All 25 tests pass, and syntax and diff checks are clean. See prompt.py and test_engine.py.

You

I changed the model to gemma4:e2b because it was too slow. Now It's acting a little weird. It isn't all that bad, but this is in the output:
Game state:
{'room': 'Entrance Hall',
'friend_room': 'Entrance Hall',
'inventory': ['The Letter'],
'clues': [],
'visited': ['Entrance Hall'],
'examined': {},
'stage': 0,
'turns_in_stage': 1,
'turn_count': 1,
'room_turns': 1,
'last_notice_turn': -99,
'last_nudge_turn': -99,
'nudges_here': 0,
'last_sister_turn': -99,
'sister_warning_count': 0,
'sister_bargain_available': False,
'sister_bargain_heard': False,
'sister_present': False,
'sister_conversation_turns': 0,
'denial': 8,
'pushed': 0,
'support_given': 0,
'door_open': False,
'truth_known': False,
'final_revelations': [],
'sister_seen': False,
'ending': None,
'last_result': {'nudge': None,
'outcome': 'no_change',
'detail': 'Nothing the engine tracks changed. Treat the '
'action as ordinary roleplay (talking, resting, '
'small gestures). Do not add items, clues, or '
'events. No room change happens: the player is '
'still in the Entrance Hall, and nobody leads them '
'elsewhere unless they choose an exit (Library, '
'Kitchen, Grand Staircase).',
'new_clue': None,
'ambient': None,
'friend': None,
'intent': 'dialogue',
'stage_advanced': False},
'history': [{'role': 'assistant',
'content': '"You came. I didn\'t think you would... Thank you."\n'
'\n'},
{'role': 'user',
'content': '"I was worried. I read your letter... What\'s '
'wrong?"'}],
'summary': '',
'summary_idx': 0}
Game state:
{'room': 'Entrance Hall',
'friend_room': 'Entrance Hall',
'inventory': ['The Letter'],
'clues': [],
'visited': ['Entrance Hall'],
'examined': {},
'stage': 0,
'turns_in_stage': 2,
'turn_count': 2,
'room_turns': 2,
'last_notice_turn': -99,
'last_nudge_turn': -99,
'nudges_here': 0,
'last_sister_turn': -99,
'sister_warning_count': 0,
'sister_bargain_available': False,
'sister_bargain_heard': False,
'sister_present': False,
'sister_conversation_turns': 0,
'denial': 8,
'pushed': 0,
'support_given': 0,
'door_open': False,
'truth_known': False,
'final_revelations': [],
'sister_seen': False,
'ending': None,
'last_result': {'nudge': None,
'outcome': 'no_change',
'detail': 'Nothing the engine tracks changed. Treat the '
'action as ordinary roleplay (talking, resting, '
'small gestures). Do not add items, clues, or '
'events. No room change happens: the player is '
'still in the Entrance Hall, and nobody leads them '
'elsewhere unless they choose an exit (Library, '
'Kitchen, Grand Staircase).',
'new_clue': None,
'ambient': None,
'friend': None,
'intent': 'dialogue',
'stage_advanced': False},
'history': [{'role': 'assistant',
'content': '"You came. I didn\'t think you would... Thank you."\n'
'\n'},
{'role': 'user',
'content': '"I was worried. I read your letter... What\'s '
'wrong?"'},
{'role': 'assistant',
'content': 'He shifts his weight, his gaze flickering toward the '
'shadowed corners of the hall. "Nothing is wrong," he '
'murmurs, his voice thin. "It\'s just... things are '
'different now. I don\'t know how to explain it."'},
{'role': 'user', 'content': '"Different in... what way?"'}],
'summary': '',
'summary_idx': 0}
Game state:
{'room': 'Entrance Hall',
'friend_room': 'Entrance Hall',
'inventory': ['The Letter'],
'clues': ['A musty smell lingers in the air'],
'visited': ['Entrance Hall'],
'examined': {'Entrance Hall:smell': 1},
'stage': 0,
'turns_in_stage': 3,
'turn_count': 3,
'room_turns': 3,
'last_notice_turn': 3,
'last_nudge_turn': -99,
'nudges_here': 0,
'last_sister_turn': -99,
'sister_warning_count': 0,
'sister_bargain_available': False,
'sister_bargain_heard': False,
'sister_present': False,
'sister_conversation_turns': 0,
'denial': 7,
'pushed': 0,
'support_given': 0,
'door_open': False,
'truth_known': False,
'final_revelations': [],
'sister_seen': False,
'ending': None,
'last_result': {'nudge': None,
'outcome': 'no_change',
'detail': 'Nothing the engine tracks changed. Treat the '
'action as ordinary roleplay (talking, resting, '
'small gestures). Do not add items, clues, or '
'events. No room change happens: the player is '
'still in the Entrance Hall, and nobody leads them '
'elsewhere unless they choose an exit (Library, '
'Kitchen, Grand Staircase).',
'new_clue': 'A musty smell lingers in the air',
'ambient': 'A musty smell lingers in the air, a mixture of '
'old wood and dust.',
'friend': None,
'intent': 'dialogue',
'stage_advanced': False},
'history': [{'role': 'assistant',
'content': '"You came. I didn\'t think you would... Thank you."\n'
'\n'},
{'role': 'user',
'content': '"I was worried. I read your letter... What\'s '
'wrong?"'},
{'role': 'assistant',
'content': 'He shifts his weight, his gaze flickering toward the '
'shadowed corners of the hall. "Nothing is wrong," he '
'murmurs, his voice thin. "It\'s just... things are '
'different now. I don\'t know how to explain it."'},
{'role': 'user', 'content': '"Different in... what way?"'},
{'role': 'assistant',
'content': 'He looks down at his hands, rubbing them together '
'slowly. "It\'s just... heavy," he says quietly. "The '
"house feels like it's holding something. I don't "
'know what to say about it."'},
{'role': 'user', 'content': '"What can I do to help you?"'}],
'summary': '',
'summary_idx': 0}

The model says:

He sighs, a sound that seems too large for the quiet hall. "Just... sit down," he suggests, gesturing vaguely toward a nearby, dark chair. "We can just... be here for a while."

It didn't mention the clue, but the game state updated anyway. what's the problem?

Agent
Agent
Agent
Agent
Agent
Agent

The game state is behaving correctly: on turn 3, the engine surfaced the musty smell and recorded ā€œA musty smell lingers in the airā€ as a clue. That clue and its description are passed to the model in the engine note.

The likely issue is narration, not clue detection. The prompt asks the model to weave ambient details in quietly, but also to answer the player directly. The smaller gemma4:e2b model followed the conversation and dropped the secondary detail. The unsupported ā€œnearby, dark chairā€ is another sign it’s improvising beyond the room facts.

The Game state: dump is separate: engine.py prints it to the terminal for debugging. It isn’t part of the player-facing response.

So the clue is saved and should count for progress even though it wasn’t narrated. To make narration more reliable with e2b, we could strengthen the instruction to explicitly include each newly discovered clue—but the model may still occasionally miss one.

You

Hmm... would it help to make the context bigger?

Agent

Probably not for this case. The game already sets the context window to 8,192 tokens in generation_options, and this happened within the first few turns, so there’s no sign the conversation exceeded it.

A larger context helps when the model is losing earlier story details in a long conversation. It generally doesn’t make a smaller model more reliable about following a specific instruction—and a bigger context can use more memory and potentially add latency. Here, gemma4:e2b seems to have prioritized answering your question over including the newly surfaced clue.

A more targeted fix would be to make the prompt say plainly: when the engine reports a new clue or ambient detail, include it in the reply even if the player asked something else. We could also show discoveries separately in the UI so the player doesn’t depend on the narrator mentioning every one.

You

We can try, but I don't want to change too many things, if it doesn't work, I will revert it back to gemma4:e4b

Agent

I’ll make one narrow prompt change: require the narrator to mention any new clue or ambient detail the engine supplies, while still answering the player’s message. I’ll add a small prompt regression test and leave the model and context settings untouched.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

Made one targeted prompt change: when the engine supplies a new clue or ambient detail, the narrator is now explicitly required to mention it in that turn—even while responding to dialogue. The model and context settings are unchanged.

All 26 regression tests pass, including a test that checks this instruction is present for a turn with a surfaced clue. Syntax and diff checks are clean. See prompt.py and test_engine.py.

You

šŸ•Æļø
"You came. I didn't think you would... Thank you."

šŸ§‘
"I was worried about you"

šŸ•Æļø
He shifts his weight, his gaze fixed on you. "It's strange being back in the library. We used to spend whole afternoons in here."

Yeah no

Agent

That’s exactly the kind of non sequitur the smaller model can pick up from the example lines. I’ll tighten the prompt so childhood memories only come up when they fit the current room and moment, and add a regression check for the entrance-hall case.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
You

no

Agent

Got it—no changes. I jumped to an implementation before checking what you meant.

That library line is explicitly in the prompt’s example dialogue, so Gemma likely repeated it without respecting the current room. I’ll leave it as is unless you ask me to change something.

You

I will revert back to gemma4:e4b

Agent

That makes sense. Since the weird dialogue appeared after switching to gemma4:e2b, going back to gemma4:e4b is a reasonable way to restore the previous behavior. No code changes made.

2 messages not shown
Agent
Agent
Agent
1 message not shown
Agent
Agent
Agent
3 messages not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
3 messages not shown
Agent
Agent
Agent
Agent
Agent
1 message not shown
Agent
Agent
Agent
Agent
Agent
1 message not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
1 message not shown
Agent
Agent
Agent
Agent
9 messages not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
4 messages not shown
Agent
Agent
Agent
Agent
Agent
1 message not shown
You

File "[REDACTED]\AppData\Roaming\Python\Python314\site-packages\streamlit\runtime\scriptrunner\script_runner.py", line 924, in code_to_exec
exec(code, module.dict) # noqa: S102
~~^^^^^^^^^^^^^^^^^^^^^^^
File "[REDACTED]\Documents\coding stuff\hacktober\the-house-that-remembers\app.py", line 557, in <module>
maybe_summarize(game, MODEL_NAME)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
TypeError: maybe_summarize() takes 1 positional argument but 2 were given

Agent

I’ll check the current app and prompt function signatures, then align the call with the function definition and run the focused tests.

Agent
Agent
Agent
Agent
Agent
Agent
Agent

The error is from a mismatch between the app.py and prompt.py versions loaded by Streamlit. On disk, the call passes game and MODEL_NAME, and maybe_summarize accepts both. I verified the loaded function signature is (game, model), and all 26 tests pass.

Stop the running Streamlit process completely, then start it again so it reloads the current files:

python -m streamlit run .\the-house-that-remembers\app.py

No code change was needed.

2 messages not shown
Agent
Agent
Agent
Agent
Agent
3 messages not shown
Agent
Agent
Agent
Agent
1 message not shown
Agent
Agent
Agent
Agent
2 messages not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
1 message not shown
You

denial ending triggers

Agent
Agent

The denial ending triggers in either of these cases:

  • Three blunt confrontations before you’ve completed all three memory fragments. The third makes your friend shut down. Once all three fragments are complete, confrontations lower his denial instead of adding to the push count. Confrontation rules
  • You reach 90 turns without facing the truth, or with your friend’s denial still at 3 or higher. Turn limit
1 message not shown
Agent
Agent
3 messages not shown
You

"Is your family dead?"

šŸ•Æļø
He appears at the base of the stairs shortly after your question, stopping near where you stand on the landing. His face tightens with a visible hurt beneath his exhaustion. "No," he says quickly, shaking his head slightly. "They're... they're fine. Everything is fine."

šŸ§‘
"Your sister is dead"

šŸ•Æļø
He flinches visibly at your words, a flicker of genuine panic crossing his features. "No," he repeats, voice low and strained. "That's not... that's not true. What are you talking about? How was your trip here?"

šŸ§‘
I know you let your family die

šŸ•Æļø
He avoids your eyes, his gaze darting past you toward the landing railing. The portrait hanging above seems even more askew now; the faces within are difficult to make out in the dim light. "You shouldn't say things like that," he murmurs, shifting his weight uneasily. "We should just... talk about something else."

šŸ§‘
they bodies are in the bedroom

šŸ•Æļø
He closes his eyes briefly, drawing a ragged breath against his pale chest. "Don't say that," he whispers, sounding utterly defeated. He looks down at the worn pattern of the carpet beneath your feet, unable to meet your gaze again.

Agent

The transcript shows why the ending didn’t trigger: the regex misses ā€œdieā€ in ā€œlet your family die,ā€ and the last accusation says ā€œbodiesā€ without a family term. I’ll check the existing confrontation tests and make those natural phrasings count.

Agent
Agent
Agent
Agent
Agent
Agent

I’ve updated the confrontation matching so ā€œdieā€ is recognized and a direct ā€œtheir/they bodiesā€ accusation counts without requiring an explicit ā€œfamilyā€ word. I’m verifying those exact phrases now.

Agent
Agent
Agent
Agent
Agent
Agent

Your transcript revealed why the denial ending didn’t trigger: ā€œI know you let your family dieā€ wasn’t matched because die was missing from the confrontation regex. The last line also wasn’t matched because ā€œbodiesā€ appeared without a family term.

I fixed both cases in engine.py and added regression tests for the phrases in your example. All 28 tests pass.

You

add also: ghost (if the player points our his sister is a ghost)
kill, murder - if the plsyer accuses him of killing his family
also if the player is dissmissive, or something similar

Agent

I’ll extend the confrontation rules to recognize those accusations and dismissive language, while avoiding false triggers for ordinary mentions. I’ll check the current patterns and tests first.

Agent
Agent
Agent
Agent
Agent
Agent
Agent

The new rules treat calling the sister a ghost, accusing the friend of killing/murdering family, and several clear dismissals as premature confrontations. I’ve also added negative checks so unrelated ghost/killing mentions and a non-dismissive use of ā€œdismissā€ don’t count.

Agent
Agent
Agent
Agent
Agent
Agent

Added those as premature confrontations: pointing out that the sister is a ghost, accusing the friend of killing or murdering his family, and dismissive lines such as ā€œStop being so dramaticā€ or ā€œYou’re imagining things.ā€ Each increases denial and the confrontation count; after three premature confrontations, the denial ending triggers.

I also added checks so unrelated mentions—like ā€œI saw a ghostā€ or ā€œI will kill the monsterā€ā€”don’t count. All 30 focused tests pass. The matching rules are in engine.py.

The Spirit of It

I wanted to create something that celebrates the spooky season and brings together two things I love: immersive storytelling and the feeling of getting lost in a good book. I’ve always enjoyed stories that leave you wondering what’s really going on beneath the surface, and I wanted to turn that feeling into an experience where you don’t just read the story - you become part of it.

Beneath the haunted-house atmosphere, the story also explores guilt, acceptance, and the choice between living in denial and facing the truth. The house reflects the minds of those who inhabit it - or perhaps their minds shape the house? šŸ‘»

Prize Categories

  • Best Use of Render: The online demo is deployed on Render, including the model.
  • Best Use of Gemma: The narrator is powered by Gemma 4 through Ollama.
  • Best Use of GitHub Copilot: Copilot helped develop and test the story engine, validate progression conditions, and debug the endings.

Thanks for reading! And if you enter the house, pay attention to what it remembers.

Top comments (1)

Collapse
 
supersaiyan101 profile image
Anavay Sharma •

Checked out your project after your kind comment on mine , Adapting The House of Usher with Gemma and local Ollama inference was a brilliant choice. I really enjoy light novel type branching-choice games like "Until then" where your decisions alter the outcome
Best of luck with the challenge! šŸ‘