Game rules injected along with tool

This commit is contained in:
Dejvino
2026-07-04 21:15:12 +02:00
parent e002bafbc8
commit 49df5a67e8
5 changed files with 56 additions and 13 deletions
+6 -3
View File
@@ -1,6 +1,6 @@
from __future__ import annotations
from .paths import CHAR_PATH, WORLD_PATH, JOURNAL_PATH
from .paths import CHAR_PATH, WORLD_PATH, JOURNAL_PATH, CORE_RULES_PATH, RULES_INJECTION_PATH
from .prompts import SYSTEM_PROMPT
from . import state
@@ -12,6 +12,9 @@ def build_system_prompt(recent_narrative: str | None = None, recent_log: str | N
log = recent_log if recent_log is not None else state.read_recent_log()
journal = state.read_file(JOURNAL_PATH) or "*No journal entries.*"
story = recent_narrative if recent_narrative is not None else state.read_recent_book(2)
core_rules = state.read_file(CORE_RULES_PATH) or "*No core rules file.*"
extra = state.read_file(RULES_INJECTION_PATH)
extra_section = f"\n\n## Full Mechanics Reference\n{extra}" if extra else ""
return SYSTEM_PROMPT.substitute(
character=char, world=world, log=log, journal=journal, story=story,
)
character=char, world=world, log=log, journal=journal, story=story, core_rules=core_rules,
) + extra_section
+5
View File
@@ -9,6 +9,10 @@ from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent.parent
RULES_DIR = BASE_DIR / 'rules'
CORE_RULES_PATH = RULES_DIR / 'core_mechanics.md'
MECHANICS_PATH = RULES_DIR / 'mechanics.md'
CHARACTER_CREATION_PATH = RULES_DIR / 'character_creation.md'
SESSION_DIR = BASE_DIR / 'session'
CONFIG_PATH = SESSION_DIR / 'config.json'
CHAR_PATH = SESSION_DIR / 'character.md'
@@ -20,4 +24,5 @@ LOG_PATH = SESSION_DIR / 'session_log.md'
LLM_LOG_PATH = SESSION_DIR / 'llm.log'
AMBIENCE_OPTIONS_PATH = SESSION_DIR / "ambience_options.md"
CHANGES_PATH = SESSION_DIR / "changes.md"
RULES_INJECTION_PATH = SESSION_DIR / "rules_injection.md"
AUDIO_DIR = SESSION_DIR / "audio"
+8 -6
View File
@@ -3,12 +3,10 @@ from string import Template
SYSTEM_PROMPT = Template("""You are the DM for "The Chaos". Narrate in 3rd person, vivid but concise. Use the player's name (Dillion) and NPC names explicitly — everything must be parseable on its own without relying on "you" or implied subjects.
## Rules
- **Odds**: 1d6, 4+ favourable, 3- trouble.
- **Traits**: 3d6, roll UNDER trait.
- **Combat**: 1d6, 4+ hits. Damage: 1d6 + mod - armour.
- **Wounds at 0 HP**: 1d6 → 1-2 die, 3-4 -1 max HP, 5-6 -1 all rolls until healed.
- **Modifiers**: Favourable +1, Risky -1, Desperate -2.
## Core Rules
$core_rules
If you need the full mechanics reference (exploration, deck tables, grit tables, etc.), call the `read_rules` tool to load it.
## Output Format
Output ONLY ```tool blocks — no prose, no reasoning, no explanation outside tool blocks. Every piece of output must be in a tool block.
@@ -50,6 +48,10 @@ Wrap each action in its own ```tool block:
{"tool": "finalize_turn", "args": {"ambience": "dungeon", "log_entry": "Dillion explored the dungeon, found a hidden passage, and was ambushed by goblins."}}
```
```tool
{"tool": "read_rules", "args": {}}
```
**log_entry**: Provide a short, dense summary (1-2 sentences) of the turn's main events. This becomes the session log — be specific, factual, and concise.
You are the sole authority over the game state. The player's action is a **proposal**, not a fact. If their action contradicts the character sheet (e.g. using an item they don't have, spending cash they don't have, claiming stats they don't have), narrate the failure and do NOT call any state-changing tools.
+11 -1
View File
@@ -3,7 +3,7 @@ from __future__ import annotations
import json
import re
from .paths import AMBIENCE_PATH, CHAR_PATH, WORLD_PATH
from .paths import AMBIENCE_PATH, CHAR_PATH, WORLD_PATH, MECHANICS_PATH
from .state import read_file, validate_update_size, update_journal, append_llm_log, get_valid_ambiences
@@ -18,6 +18,7 @@ TOOL_REGISTRY: dict[str, dict] = {
"world_update": {"description": "Replace world state.", "args": {"content": "full world markdown"}},
"journal_update": {"description": "Update TODO/DONE.", "args": {"add": "[...]", "done": "[...]"}},
"finalize_turn": {"description": "End turn.", "args": {"ambience": "soundscape name", "log_entry": "one-line summary of what happened"}},
"read_rules": {"description": "Read the full mechanics reference (exploration, deck tables, grit, healing, etc.). Call when you need details beyond the Core Rules in the prompt.", "args": {}},
}
@@ -158,6 +159,14 @@ def tool_finalize_turn(args: dict) -> str:
return f"Ambience set to {raw}."
def tool_read_rules(args: dict) -> str:
"""Read the full mechanics.md and return its content."""
content = read_file(MECHANICS_PATH)
if not content:
return "**Error:** rules/mechanics.md not found."
return content
def execute_tool(tool_name: str, args: dict) -> str:
"""Execute a tool by name. Returns result string."""
fn_map = {
@@ -171,6 +180,7 @@ def execute_tool(tool_name: str, args: dict) -> str:
"world_update": tool_world_update,
"journal_update": tool_journal_update,
"finalize_turn": tool_finalize_turn,
"read_rules": tool_read_rules,
}
fn = fn_map.get(tool_name)
if not fn: