Compare commits
1 Commits
step35/877
...
fix/513
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89de5b2c69 |
@@ -1059,6 +1059,46 @@ class GameEngine:
|
||||
self.log("It will always pulse. That much you know.")
|
||||
self.log("")
|
||||
self.world.save()
|
||||
|
||||
def _bridge_is_hazardous(self):
|
||||
bridge = self.world.rooms["Bridge"]
|
||||
return bool(
|
||||
self.world.state.get("bridge_flooding")
|
||||
or bridge.get("weather") == "rain"
|
||||
or bridge.get("rain_ticks", 0) > 0
|
||||
)
|
||||
|
||||
def _bridge_crossing_extra_cost(self, current_room, dest):
|
||||
if "Bridge" not in (current_room, dest):
|
||||
return 0
|
||||
return 2 if self._bridge_is_hazardous() else 0
|
||||
|
||||
def _event_dialogue(self, char_name, room_name):
|
||||
if char_name == "Bezalel" and room_name == "Forge":
|
||||
if self.world.rooms["Forge"]["fire"] == "cold":
|
||||
return random.choice([
|
||||
"The forge is cold. We cannot work until the fire lives again.",
|
||||
"No forging now. The hearth is dead cold.",
|
||||
])
|
||||
if self.world.state.get("forge_fire_dying"):
|
||||
return random.choice([
|
||||
"The fire is dying. Tend it before the forge goes dark.",
|
||||
"The forge is losing heat. Help me keep it alive.",
|
||||
])
|
||||
|
||||
if char_name == "Ezra" and room_name == "Tower" and self.world.state.get("tower_power_low"):
|
||||
return random.choice([
|
||||
"The Tower power is too low. The servers won't hold a clean study right now.",
|
||||
"The LED is flickering. We need steady power before the Tower can be read properly.",
|
||||
])
|
||||
|
||||
if char_name in {"Marcus", "Allegro"} and room_name == "Bridge" and self._bridge_is_hazardous():
|
||||
return random.choice([
|
||||
"The Bridge is slick with rain. Cross carefully or wait it out.",
|
||||
"This rain changes the Bridge. Don't treat it like dry stone.",
|
||||
])
|
||||
|
||||
return None
|
||||
|
||||
def log(self, message):
|
||||
"""Add to Timmy's log."""
|
||||
@@ -1094,6 +1134,7 @@ class GameEngine:
|
||||
}
|
||||
|
||||
# Process Timmy's action
|
||||
room_name = self.world.characters["Timmy"]["room"]
|
||||
timmy_energy = self.world.characters["Timmy"]["energy"]
|
||||
|
||||
# Energy constraint checks
|
||||
@@ -1156,8 +1197,17 @@ class GameEngine:
|
||||
|
||||
if direction in connections:
|
||||
dest = connections[direction]
|
||||
bridge_extra_cost = self._bridge_crossing_extra_cost(current_room, dest)
|
||||
move_cost = 1 + bridge_extra_cost
|
||||
if self.world.characters["Timmy"]["energy"] < move_cost:
|
||||
scene["log"].append("The rain makes the Bridge too costly to cross right now. Rest first.")
|
||||
scene["room_desc"] = self.world.get_room_desc(current_room, "Timmy")
|
||||
here = [n for n in self.world.characters if self.world.characters[n]["room"] == current_room and n != "Timmy"]
|
||||
scene["here"] = here
|
||||
return scene
|
||||
|
||||
self.world.characters["Timmy"]["room"] = dest
|
||||
self.world.characters["Timmy"]["energy"] -= 1
|
||||
self.world.characters["Timmy"]["energy"] -= move_cost
|
||||
|
||||
scene["log"].append(f"You move {direction} to The {dest}.")
|
||||
scene["timmy_room"] = dest
|
||||
@@ -1165,6 +1215,8 @@ class GameEngine:
|
||||
# Check for rain on bridge
|
||||
if dest == "Bridge" and self.world.rooms["Bridge"]["weather"] == "rain":
|
||||
scene["world_events"].append("Rain mists on the dark water below. The railing is slick.")
|
||||
if bridge_extra_cost:
|
||||
scene["log"].append("Rain turns the Bridge crossing into work. You brace against the slick stone. (-2 extra energy)")
|
||||
|
||||
# Check trust changes for arrival
|
||||
here = [n for n in self.world.characters if self.world.characters[n]["room"] == dest and n != "Timmy"]
|
||||
@@ -1310,25 +1362,69 @@ class GameEngine:
|
||||
|
||||
elif timmy_action == "write_rule":
|
||||
if self.world.characters["Timmy"]["room"] == "Tower":
|
||||
rules = [
|
||||
f"Rule #{self.world.tick}: The room remembers those who enter it.",
|
||||
f"Rule #{self.world.tick}: A man in the dark needs to know someone is in the room.",
|
||||
f"Rule #{self.world.tick}: The forge does not care about your schedule.",
|
||||
f"Rule #{self.world.tick}: Every footprint on the stone means someone made it here.",
|
||||
f"Rule #{self.world.tick}: The bridge does not judge. It only carries.",
|
||||
f"Rule #{self.world.tick}: A seed planted in patience grows in time.",
|
||||
f"Rule #{self.world.tick}: What is carved in wood outlasts what is said in anger.",
|
||||
f"Rule #{self.world.tick}: The garden grows whether anyone watches or not.",
|
||||
f"Rule #{self.world.tick}: Trust is built one tick at a time.",
|
||||
f"Rule #{self.world.tick}: The fire remembers who tended it.",
|
||||
]
|
||||
new_rule = random.choice(rules)
|
||||
self.world.rooms["Tower"]["messages"].append(new_rule)
|
||||
self.world.characters["Timmy"]["energy"] -= 1
|
||||
scene["log"].append(f"You write on the Tower whiteboard: \"{new_rule}\"")
|
||||
if self.world.state.get("tower_power_low"):
|
||||
scene["world_events"].append("The Tower power is too low. The LED flickers over the whiteboard.")
|
||||
scene["log"].append("The power is too low to write a new rule.")
|
||||
else:
|
||||
rules = [
|
||||
f"Rule #{self.world.tick}: The room remembers those who enter it.",
|
||||
f"Rule #{self.world.tick}: A man in the dark needs to know someone is in the room.",
|
||||
f"Rule #{self.world.tick}: The forge does not care about your schedule.",
|
||||
f"Rule #{self.world.tick}: Every footprint on the stone means someone made it here.",
|
||||
f"Rule #{self.world.tick}: The bridge does not judge. It only carries.",
|
||||
f"Rule #{self.world.tick}: A seed planted in patience grows in time.",
|
||||
f"Rule #{self.world.tick}: What is carved in wood outlasts what is said in anger.",
|
||||
f"Rule #{self.world.tick}: The garden grows whether anyone watches or not.",
|
||||
f"Rule #{self.world.tick}: Trust is built one tick at a time.",
|
||||
f"Rule #{self.world.tick}: The fire remembers who tended it.",
|
||||
]
|
||||
new_rule = random.choice(rules)
|
||||
self.world.rooms["Tower"]["messages"].append(new_rule)
|
||||
self.world.characters["Timmy"]["energy"] -= 1
|
||||
scene["log"].append(f"You write on the Tower whiteboard: \"{new_rule}\"")
|
||||
else:
|
||||
scene["log"].append("You are not in the Tower.")
|
||||
|
||||
elif timmy_action == "study":
|
||||
if self.world.characters["Timmy"]["room"] == "Tower":
|
||||
if self.world.state.get("tower_power_low"):
|
||||
scene["world_events"].append("The Tower power is too low. The servers stutter in weak light.")
|
||||
scene["log"].append("The power is too low to study the servers.")
|
||||
else:
|
||||
insights = [
|
||||
"You study the server rhythm until the pulse resolves into something readable.",
|
||||
"You trace the signal paths and feel the Tower settle into focus.",
|
||||
"You study the green LED and the server racks until the pattern becomes clear.",
|
||||
]
|
||||
insight = random.choice(insights)
|
||||
self.world.characters["Timmy"]["energy"] -= 1
|
||||
self.world.characters["Timmy"]["memories"].append(insight)
|
||||
scene["log"].append(insight)
|
||||
scene["world_events"].append("The Tower answers with a steady hum.")
|
||||
else:
|
||||
scene["log"].append("You are not in the Tower.")
|
||||
|
||||
elif timmy_action == "forge":
|
||||
if self.world.characters["Timmy"]["room"] == "Forge":
|
||||
forge_fire = self.world.rooms["Forge"]["fire"]
|
||||
if forge_fire == "cold":
|
||||
scene["world_events"].append("The forge is cold. No metal will take shape here yet.")
|
||||
scene["log"].append("The forge is cold. Tend the fire before you try to forge.")
|
||||
else:
|
||||
forged_items = [
|
||||
f"bridge nail #{self.world.tick}",
|
||||
f"tower key blank #{self.world.tick}",
|
||||
f"garden trowel #{self.world.tick}",
|
||||
]
|
||||
forged_item = random.choice(forged_items)
|
||||
self.world.rooms["Forge"]["forged_items"].append(forged_item)
|
||||
self.world.characters["Timmy"]["energy"] -= 2
|
||||
self.world.state["items_crafted"] += 1
|
||||
scene["log"].append(f"You forge {forged_item} at the anvil.")
|
||||
scene["world_events"].append("The anvil rings and the hearth answers.")
|
||||
else:
|
||||
scene["log"].append("You are not in the Forge.")
|
||||
|
||||
elif timmy_action == "carve":
|
||||
if self.world.characters["Timmy"]["room"] == "Bridge":
|
||||
carvings = [
|
||||
@@ -1414,7 +1510,11 @@ class GameEngine:
|
||||
speech_chance = 0.20
|
||||
|
||||
if random.random() < speech_chance:
|
||||
if char_name == "Marcus":
|
||||
event_line = self._event_dialogue(char_name, room_name)
|
||||
if event_line:
|
||||
self.world.characters[char_name]["spoken"].append(event_line)
|
||||
scene["log"].append(f"{char_name} says: \"{event_line}\"")
|
||||
elif char_name == "Marcus":
|
||||
marcus_pool = self.DIALOGUES["Marcus"].get(phase, self.DIALOGUES["Marcus"]["quietus"])
|
||||
line = random.choice(marcus_pool)
|
||||
self.world.characters[char_name]["spoken"].append(line)
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
# Shadow Maths Triage Rubric (MATH-001)
|
||||
|
||||
**Status**: Draft v1.0 **Date**: 2026-04-26 **Author**: Timmy
|
||||
**Milestone**: Contribute to Mathematics — Shadow Maths Search
|
||||
**Parent**: #876 — [MATH][EPIC] Shadow Maths
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
Timmy's mathematics contribution program targets *bounded, verifiable, useful* problems hiding in plain sight. This rubric operationalizes "shadow maths" — distinguishing legitimate first-crack contributions from crank Grand Unified Theories.
|
||||
|
||||
The rubric serves two roles:
|
||||
1. **Triage gate** — filter submissions and scout list candidates worth pursuing.
|
||||
2. **No-crank guardrail** — explicitly reject unfalsifiable, unscoped, or unsourced claims.
|
||||
|
||||
---
|
||||
|
||||
## Candidate Categories (Positive Types)
|
||||
|
||||
| Category | Description | Verification Path | Useful Because |
|
||||
|----------|-------------|-------------------|---------------|
|
||||
| **Small lemma** | Missing but straightforward piece in an active area (e.g., "Proposition 3.2 in Smith 2021 needs this case analysis") | Check paper + 1–2 related references; prove or give counterexample | Clarifies existing theory, removes ambiguity |
|
||||
| **Counterexample search** | Find explicit counterexample to a claimed-but-unproven statement (often from MO/SE) | Compute/construct + cite the original claim | Prevents propagation of errors |
|
||||
| **Computational classification** | Exhaustive enumeration/classification of a small infinite family (e.g., "all groups of order < 200 with property X") | Code is verifiable; results match known data | Creates reference data, spotlights patterns |
|
||||
| **Formalization gap** | Statement already believed true but missing from Lean/mathlib/Isabelle | Formal proof artifact; merges to mainline library | Makes mathematics machine-checkable |
|
||||
| **OEIS sequence note** | New sequence entry or correction to an existing entry with proof/algorithm | OEIS A-number + formula/generation code | Public archival, enables further work |
|
||||
| **Exposition repair** | Fix an unclear proof, fill a gap, simplify an argument in an existing paper | Side-by-side diff + justification for each change | Improves pedagogy, reduces confusion |
|
||||
| **MathOverflow-quality answer** | Answer to a specific, bounded, research-level question on MO/SE that has no accepted answer | Cite question + self-contained proof/computation | Serves the community directly |
|
||||
|
||||
---
|
||||
|
||||
## Rejection Criteria (No-Crank Guardrails)
|
||||
|
||||
> Any candidate that triggers one or more of these is **rejected outright** — no scoring needed.
|
||||
|
||||
| Rule | What to look for | Why it's crank |
|
||||
|------|------------------|----------------|
|
||||
| **Unsourced grand theory** | Claim introduces new "framework"/"paradigm" without citing specific bounded problem it solves | Mathematics advances by solving problems, not proposing frameworks |
|
||||
| **Impossible scope** | "I will prove/disprove the Riemann Hypothesis", "classify all finite simple groups" | Demonstrably beyond single-attack capability |
|
||||
| **No verification path** | No way for a third party to check the work (no code, no formalization, no explicit examples) | Cannot be wrong if it cannot be checked |
|
||||
| **Novelty claim without literature search** | States "I believe this is new" without checking MathSciNet/arXiv/Google Scholar | Almost certainly reinvention or known result |
|
||||
| **Vague mathematical objects** | Uses undefined or ambiguous terminology ("energy", "resonance", "harmonic" in non-standard ways) | Not mathematics |
|
||||
| **Secrecy or paywall** | Key definition or proof behind a paywall or withheld | Not sovereign; not verifiable |
|
||||
| **Symbolic overloading without definition** | Repurposes standard notation in non-standard ways without explicit redefinition | Creates confusion, not clarity |
|
||||
| **Invariance violations** | Claims "up to isomorphism" or "modulo equivalence" without defining the equivalence relation | Not mathematically precise |
|
||||
| **Cherry-picked examples as proof** | Proves only easy special cases and claims the general case follows | Example ≠ theorem |
|
||||
| **Circular citation chains** | Relies on unpublished/preprint work that itself cites the candidate as motivation | Not a foundation |
|
||||
| **No clear problem statement** | Cannot write a one-sentence problem statement in standard mathematical English | Not a problem; just musings |
|
||||
| **Claims of "obvious" or "clear" for non-trivial steps** | Uses "obviously" or "it is clear that" where a proof requires >2 lines | Evasion |
|
||||
| **References only popular science / non-technical sources** | Cites Penrose, Hawking, Tegmark for technical claims | Wrong tier of source |
|
||||
| **All notation defined in non-standard way** | Redefines basic operators (+, ×, ≤) without explicit warning | Not mathematics |
|
||||
| **No engagement with existing literature** | Zero citations to relevant peer-reviewed work or established preprints | scholarship was not done |
|
||||
| **Claims of "disproof" of widely-accepted theorems** | Without finding a peer-reviewed error in the existing proof | Almost certainly wrong |
|
||||
|
||||
---
|
||||
|
||||
## Evidence Tiers
|
||||
|
||||
| Tier | Artifact | What it Proves |
|
||||
|------|----------|----------------|
|
||||
| **T3 — Literature** | MathSciNet / Zentralblatt / Google Scholar citations showing the problem is real and open | Problem exists in the literature |
|
||||
| **T2 — Executable** | Python/Sage/Lean code that others can run to verify computation/formalization | Result is reproducible |
|
||||
| **T1 — Human-reviewed** | MO answer with upvotes, referee report, or explicit external review | Independent verification |
|
||||
| **T0 — Self-contained** | Clear statement + proof/computation in a single document, all definitions explicit | Standalone correctness |
|
||||
|
||||
A valid candidate must have at least **one** T3 citation (shows the problem is real) AND a verification artifact (T0 minimum; T2 ideal).
|
||||
|
||||
---
|
||||
|
||||
## Scoring Rubric
|
||||
|
||||
Score each candidate on **4 dimensions**, each 0–3. Maximum 12 points.
|
||||
|
||||
| Dimension | 3 (excellent) | 2 (good) | 1 (minimal) | 0 (absent) |
|
||||
|-----------|---------------|----------|-------------|------------|
|
||||
| **Boundedness** | Scope is explicitly finite/small (single lemma, finite classification < N, one SE question) | Scope is implied bounded but not quantified | Scope is large/vague but attackable | Unbounded or impossible scope |
|
||||
| **Verifiability** | T2 artifact (code/formalization) + T3 citation | T0 proof + T3 citation | Proof/computation only, no citations | No way to check independently |
|
||||
| **Usefulness** | Solves problem others actively need (cites known difficulty, fills formalization gap) | Solves a clean exercise or interesting special case | Interesting but no clear audience | Pointless or self-referential |
|
||||
| **Discipline** | No crank flags; explicit rejection criteria cleanly passed | Minor crank flags (vague wording) but overall sound | Some crank flags but bounded scope rescues it | Triggers multiple rejection rules |
|
||||
|
||||
**Thresholds**:
|
||||
- **8–12**: Legitimate shadow maths candidate — queue for work
|
||||
- **4–7**: Needs refinement — reject unless strong disciplinary context
|
||||
- **0–3**: Reject as crank / out-of-scope
|
||||
|
||||
---
|
||||
|
||||
## Three Worked Examples
|
||||
|
||||
### Example 1: Small Lemma — Bounded
|
||||
|
||||
**Candidate**: "Proposition 3.2 in 'Coarse Geometry and Coarse Embeddings' (Lang-Schlichenmaier 2005) states that every finite CW-complex has Markov property. The proof gives 'it follows by induction on skeleta' without handling the attaching map case. Fill the gap."
|
||||
|
||||
**Triage**:
|
||||
- **Category**: Small lemma (exposition repair + proof gap fill)
|
||||
- **Boundedness**: 3 — single proposition in a specific paper, 2–3 pages max
|
||||
- **Verifiability**: 3 — paper is cited (T3), self-contained proof in 20 lines (T0), can formalize in Lean (T2 possible)
|
||||
- **Usefulness**: 3 — readers of this paper hit this gap; Lean formalization needed for mathlib
|
||||
- **Discipline**: 3 — no crank flags; scoped, sourced, technical
|
||||
- **Total**: **12/12** → YES
|
||||
|
||||
**Action**: File ticket "MATH-LEMMA-001"; assign to formalization lane + human review.
|
||||
|
||||
---
|
||||
|
||||
### Example 2: Grand Unified Theory — CRANK
|
||||
|
||||
**Candidate**: "I have discovered the Energy-Conscious Riemann Hypothesis framework. The zeros of ζ(s) correspond to harmonic resonance frequencies in prime-number energy manifolds. Uses my new Operator-Weight theory."
|
||||
|
||||
**Triage**:
|
||||
- **Category**: N/A
|
||||
- **Rejection triggers**:
|
||||
- ✗ Unsourced grand theory (introduces "Energy-Conscious", "Operator-Weight" with no definition in standard math)
|
||||
- ✗ No verification path (no computation, no reference to known data)
|
||||
- ✗ No literature engagement (zero citations)
|
||||
- ✗ Vague mathematical objects ("energy", "resonance", "harmonic")
|
||||
- ✗ Claims new framework
|
||||
- **Score**: 0 — **REJECT**
|
||||
|
||||
**Action**: Close with reason "crank: unsourced grand theory + no verification path".
|
||||
|
||||
---
|
||||
|
||||
### Example 3: Computational Classification — Bounded
|
||||
|
||||
**Candidate**: "Compute all 3-headed Turing machines with 3 states that halt within 100 steps on the blank tape. There are 9 such machines. This fills an OEIS gap: A327000 only lists up to 2-state 2-symbol."
|
||||
|
||||
**Triage**:
|
||||
- **Category**: Computational classification + OEIS sequence
|
||||
- **Boundedness**: 3 — finite exhaustive enumeration (3^6 = 729 machines, filter to 9)
|
||||
- **Verifiability**: 2 — code is executable (T2), but no T3 citation of why this sequence matters yet
|
||||
- **Usefulness**: 2 — plugs a gap in the Busy Beaver frontier; OEIS entry gets concrete data
|
||||
- **Discipline**: 3 — explicit scope, reproducible, submits to OEIS (external review path)
|
||||
- **Total**: **10/12** → YES (minor fix: add motivation/references)
|
||||
|
||||
**Action**: Accept; write exhaustive script; submit OEIS draft with code + results; file MATH-COMP-001.
|
||||
|
||||
---
|
||||
|
||||
## Operational Use
|
||||
|
||||
### Triage Workflow
|
||||
|
||||
1. **Read candidate** (issue, email, self-generated idea).
|
||||
2. **Check rejection criteria first** — if any trigger → **REJECT** immediately, cite rule.
|
||||
3. If passes rejection gate, **score 4 dimensions**.
|
||||
4. **Score ≥8** → mark as `shadow-maths-candidate`, route to appropriate lane:
|
||||
- Lemma/exposition → `formalization-lane`
|
||||
- Computation → `compute-lane`
|
||||
- MO/SE answer → `answer-lane`
|
||||
- OEIS → `oeis-lane`
|
||||
5. **Score 4–7** → requires refinement; ask for:
|
||||
- Explicit scope bound
|
||||
- T3 citation
|
||||
- Verification artifact
|
||||
6. **Score ≤3** → reject with specific rule(s) triggered.
|
||||
|
||||
### Guardrail Enforcement
|
||||
|
||||
The following prompts/agents **must refuse** to work on any candidate that:
|
||||
- Triggers any rejection criterion (before any code/proof work)
|
||||
- Has no T3 citation (real problem statement from literature)
|
||||
- Has no bounded scope (cannot write ≤1-sentence problem statement)
|
||||
|
||||
Enforcement is a **pre-flight check** in the task intake pipeline.
|
||||
|
||||
---
|
||||
|
||||
## Revision History
|
||||
|
||||
- v1.0 — 2026-04-26 — initial rubric + 3 scored examples
|
||||
@@ -1,6 +1,7 @@
|
||||
from importlib.util import module_from_spec, spec_from_file_location
|
||||
from pathlib import Path
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
@@ -66,6 +67,82 @@ class TestEvenniaLocalWorldGame(unittest.TestCase):
|
||||
self.assertIn("Ezra is already here.", result["log"])
|
||||
self.assertIn("The servers hum steady. The green LED pulses.", result["world_events"])
|
||||
|
||||
def test_bridge_rain_crossing_costs_extra_energy_and_warns(self):
|
||||
module = load_game_module()
|
||||
|
||||
dry_engine = module.GameEngine()
|
||||
dry_engine.start_new_game()
|
||||
dry_engine.world.update_world_state = lambda: None
|
||||
dry_engine.world.characters["Timmy"]["energy"] = 10
|
||||
dry_result = dry_engine.run_tick("move:south")
|
||||
dry_energy = dry_engine.world.characters["Timmy"]["energy"]
|
||||
|
||||
rainy_engine = module.GameEngine()
|
||||
rainy_engine.start_new_game()
|
||||
rainy_engine.world.update_world_state = lambda: None
|
||||
rainy_engine.world.characters["Timmy"]["energy"] = 10
|
||||
rainy_engine.world.rooms["Bridge"]["weather"] = "rain"
|
||||
rainy_engine.world.rooms["Bridge"]["rain_ticks"] = 3
|
||||
rainy_engine.world.state["bridge_flooding"] = True
|
||||
rainy_result = rainy_engine.run_tick("move:south")
|
||||
|
||||
self.assertEqual(rainy_engine.world.characters["Timmy"]["room"], "Bridge")
|
||||
self.assertLess(rainy_engine.world.characters["Timmy"]["energy"], dry_energy)
|
||||
self.assertTrue(
|
||||
any("bridge" in line.lower() and ("rain" in line.lower() or "slick" in line.lower()) for line in rainy_result["log"] + rainy_result["world_events"]),
|
||||
rainy_result,
|
||||
)
|
||||
|
||||
def test_tower_power_low_blocks_study_and_write_rule(self):
|
||||
module = load_game_module()
|
||||
engine = module.GameEngine()
|
||||
engine.start_new_game()
|
||||
engine.world.update_world_state = lambda: None
|
||||
engine.world.characters["Timmy"]["room"] = "Tower"
|
||||
engine.world.characters["Timmy"]["energy"] = 10
|
||||
engine.world.state["tower_power_low"] = True
|
||||
|
||||
rules_before = list(engine.world.rooms["Tower"]["messages"])
|
||||
study_result = engine.run_tick("study")
|
||||
self.assertEqual(engine.world.characters["Timmy"]["energy"], 10)
|
||||
self.assertTrue(
|
||||
any("power" in line.lower() and ("study" in line.lower() or "servers" in line.lower()) for line in study_result["log"] + study_result["world_events"]),
|
||||
study_result,
|
||||
)
|
||||
|
||||
write_result = engine.run_tick("write_rule")
|
||||
self.assertEqual(engine.world.rooms["Tower"]["messages"], rules_before)
|
||||
self.assertTrue(
|
||||
any("power" in line.lower() and ("write" in line.lower() or "whiteboard" in line.lower()) for line in write_result["log"] + write_result["world_events"]),
|
||||
write_result,
|
||||
)
|
||||
|
||||
def test_cold_forge_blocks_forge_action_and_bezalel_reacts(self):
|
||||
module = load_game_module()
|
||||
engine = module.GameEngine()
|
||||
engine.start_new_game()
|
||||
engine.world.update_world_state = lambda: None
|
||||
engine.npc_ai.make_choice = lambda _name: None
|
||||
engine.world.characters["Timmy"]["room"] = "Forge"
|
||||
engine.world.characters["Timmy"]["energy"] = 10
|
||||
engine.world.characters["Bezalel"]["room"] = "Forge"
|
||||
engine.world.rooms["Forge"]["fire"] = "cold"
|
||||
engine.world.state["forge_fire_dying"] = True
|
||||
forged_before = list(engine.world.rooms["Forge"]["forged_items"])
|
||||
|
||||
with patch.object(module.random, "random", return_value=0.0), patch.object(module.random, "choice", side_effect=lambda seq: seq[0]):
|
||||
result = engine.run_tick("forge")
|
||||
|
||||
self.assertEqual(engine.world.rooms["Forge"]["forged_items"], forged_before)
|
||||
self.assertTrue(
|
||||
any("forge" in line.lower() and ("cold" in line.lower() or "fire" in line.lower()) for line in result["log"] + result["world_events"]),
|
||||
result,
|
||||
)
|
||||
self.assertTrue(
|
||||
any(line.startswith("Bezalel says:") and ("fire" in line.lower() or "forge" in line.lower()) for line in result["log"]),
|
||||
result,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user