Compare commits

..

2 Commits

Author SHA1 Message Date
Alexander Whitestone
06a6b53a37 feat: add tower world-changing items for #514
Some checks failed
Agent PR Gate / gate (pull_request) Failing after 58s
Self-Healing Smoke / self-healing-smoke (pull_request) Failing after 26s
Agent PR Gate / report (pull_request) Successful in 23s
Smoke Test / smoke (pull_request) Failing after 27s
2026-04-22 02:54:56 -04:00
Alexander Whitestone
5ce4fa6cb8 test: define tower world items for #514 2026-04-22 02:43:29 -04:00
3 changed files with 203 additions and 110 deletions

View File

@@ -12,6 +12,29 @@ WORLD_DIR = Path('/Users/apayne/.timmy/evennia/timmy_world')
STATE_FILE = WORLD_DIR / 'game_state.json'
TIMMY_LOG = WORLD_DIR / 'timmy_log.md'
WORLD_ITEMS = {
"foraged key": {"effect": "unlock_tower_cache", "quest_item": True, "consumable": False, "effect_text": "A hidden cache clicks open in the Tower wall."},
"seed packet": {"effect": "grow_garden", "quest_item": False, "consumable": True, "effect_text": "Fresh growth pushes through the Garden soil."},
"notebook": {"effect": "write_notebook_rule", "quest_item": False, "consumable": False, "effect_text": "A new rule joins the whiteboard in the Tower."},
"cloth": {"effect": "patch_bridge", "quest_item": False, "consumable": True, "effect_text": "The Bridge railing is wrapped tight against the weather."},
"oil can": {"effect": "stoke_forge", "quest_item": False, "consumable": True, "effect_text": "The Forge fire answers with a hotter glow."},
"lantern": {"effect": "light_bridge", "quest_item": False, "consumable": False, "effect_text": "A steady lantern glow cuts through the dark over the Bridge."},
"rope spool": {"effect": "secure_bridge", "quest_item": False, "consumable": True, "effect_text": "The Bridge is lashed tight and feels safer underfoot."},
"chalk": {"effect": "mark_threshold", "quest_item": False, "consumable": True, "effect_text": "A chalk mark at the Threshold points wanderers home."},
"weather vane": {"effect": "read_weather", "quest_item": False, "consumable": False, "effect_text": "The weather vane settles and the coming storm makes sense."},
"sunstone": {"effect": "restore_tower_power", "quest_item": False, "consumable": False, "effect_text": "Warm light races through the Tower circuits."},
"iron nails": {"effect": "reinforce_bridge", "quest_item": False, "consumable": True, "effect_text": "The Bridge planks are pinned down against the flood."},
"river stone": {"effect": "water_garden", "quest_item": False, "consumable": True, "effect_text": "Moisture returns to the Garden beds."},
}
ROOM_DISCOVERABLES = {
"Threshold": ["chalk", "sunstone"],
"Tower": ["notebook", "lantern"],
"Forge": ["oil can", "iron nails"],
"Garden": ["seed packet", "foraged key"],
"Bridge": ["cloth", "rope spool", "weather vane", "river stone"],
}
# ============================================================
# NARRATIVE ARC — 4 phases that transform the world
# ============================================================
@@ -143,6 +166,8 @@ class World:
"visitors": [],
},
}
for room_name, items in ROOM_DISCOVERABLES.items():
self.rooms[room_name]["discoverables"] = list(items)
# Characters (not NPCs — they have lives)
self.characters = {
@@ -258,6 +283,14 @@ class World:
"items_crafted": 0,
"conflicts_resolved": 0,
"nights_survived": 0,
"bridge_patched": False,
"bridge_secured": False,
"bridge_reinforced": False,
"bridge_lantern_lit": False,
"tower_cache_unlocked": False,
"threshold_marked": False,
"weather_readable": False,
"sunstone_socketed": False,
}
def tick_time(self):
@@ -376,6 +409,14 @@ class World:
desc += " Rain mists on the dark water below."
if len(self.rooms["Bridge"]["carvings"]) > 1:
desc += f" There are {len(self.rooms['Bridge']['carvings'])} carvings now."
if self.state.get("bridge_patched"):
desc += " Cloth bindings keep the railing from splintering."
if self.state.get("bridge_secured"):
desc += " Rope lines keep the span steady against the flood."
if self.state.get("bridge_reinforced"):
desc += " Fresh iron nails hold the planks tight."
if self.state.get("bridge_lantern_lit"):
desc += " A lantern glows warm over the water."
elif room_name == "Tower":
power = self.state.get("tower_power_low", False)
@@ -384,6 +425,19 @@ class World:
if self.rooms["Tower"]["messages"]:
desc += f" The whiteboard holds {len(self.rooms['Tower']['messages'])} rules."
if self.state.get("tower_cache_unlocked"):
desc += " A hidden cache stands open beneath the whiteboard."
if room_name == "Threshold" and self.state.get("threshold_marked"):
desc += " A chalk arrow points late arrivals toward shelter."
if room_name == "Garden" and self.state.get("weather_readable"):
desc += " The beds are arranged to catch whatever weather comes next."
if room_name == "Tower" and self.state.get("sunstone_socketed"):
desc += " A sunstone keeps the room lit with a stubborn amber glow."
discoverables = room.get("discoverables", [])
if discoverables:
desc += f" Discoverable items: {', '.join(discoverables)}."
# Who's here
here = [n for n, c in self.characters.items() if c["room"] == room_name and n != char_name]
@@ -485,6 +539,10 @@ class ActionSystem:
"cost": 1,
"description": "Take an item from the room",
},
"use": {
"cost": 1,
"description": "Use an item from your inventory to change the world",
},
"examine": {
"cost": 0,
"description": "Examine something in detail",
@@ -530,6 +588,13 @@ class ActionSystem:
available.append("rest")
available.append("examine")
discoverables = world.rooms[room].get("discoverables", [])
for item in discoverables:
available.append(f"take:{item}")
for item in char["inventory"]:
available.append(f"use:{item}")
if char["inventory"]:
available.append("give:item")
@@ -1072,6 +1137,76 @@ class GameEngine:
f.write(f"\n*Began: {datetime.now().strftime('%Y-%m-%d %H:%M')}*\n\n")
f.write("---\n\n")
f.write(message + "\n")
def _take_item(self, item_name, scene):
room_name = self.world.characters["Timmy"]["room"]
discoverables = self.world.rooms[room_name].get("discoverables", [])
if item_name not in discoverables:
scene["log"].append(f"There is no {item_name} here.")
return
discoverables.remove(item_name)
self.world.characters["Timmy"]["inventory"].append(item_name)
scene["log"].append(f"You take the {item_name}.")
if WORLD_ITEMS.get(item_name, {}).get("quest_item"):
scene["world_events"].append(f"The {item_name} feels important. It might open a quest route.")
def _use_item(self, item_name, scene):
inventory = self.world.characters["Timmy"]["inventory"]
if item_name not in inventory:
scene["log"].append(f"You are not carrying {item_name}.")
return
item = WORLD_ITEMS.get(item_name)
if not item:
scene["log"].append(f"The {item_name} doesn't seem to do anything.")
return
effect = item["effect"]
effect_text = item["effect_text"]
if effect == "grow_garden":
self.world.rooms["Garden"]["growth"] = min(5, self.world.rooms["Garden"]["growth"] + 2)
self.world.state["garden_drought"] = False
elif effect == "unlock_tower_cache":
self.world.state["tower_cache_unlocked"] = True
cache_rule = "Rule: Keys open more than doors when the world trusts you."
if cache_rule not in self.world.rooms["Tower"]["messages"]:
self.world.rooms["Tower"]["messages"].append(cache_rule)
elif effect == "write_notebook_rule":
note_rule = f"Rule #{self.world.tick}: A notebook can turn memory into structure."
self.world.rooms["Tower"]["messages"].append(note_rule)
elif effect == "patch_bridge":
self.world.state["bridge_patched"] = True
self.world.state["bridge_flooding"] = False
self.world.rooms["Bridge"]["weather"] = None
self.world.rooms["Bridge"]["rain_ticks"] = 0
elif effect == "stoke_forge":
self.world.rooms["Forge"]["fire"] = "glowing"
self.world.state["forge_fire_dying"] = False
elif effect == "light_bridge":
self.world.state["bridge_lantern_lit"] = True
elif effect == "secure_bridge":
self.world.state["bridge_secured"] = True
self.world.state["bridge_flooding"] = False
self.world.rooms["Bridge"]["weather"] = None
self.world.rooms["Bridge"]["rain_ticks"] = 0
elif effect == "mark_threshold":
self.world.state["threshold_marked"] = True
elif effect == "read_weather":
self.world.state["weather_readable"] = True
self.world.state["garden_drought"] = False
elif effect == "restore_tower_power":
self.world.state["tower_power_low"] = False
self.world.state["sunstone_socketed"] = True
elif effect == "reinforce_bridge":
self.world.state["bridge_reinforced"] = True
self.world.state["bridge_flooding"] = False
elif effect == "water_garden":
self.world.state["garden_drought"] = False
self.world.rooms["Garden"]["growth"] = min(5, self.world.rooms["Garden"]["growth"] + 1)
scene["log"].append(f"You use the {item_name}. {effect_text}")
scene["world_events"].append(effect_text)
if item.get("consumable"):
inventory.remove(item_name)
def run_tick(self, timmy_action="look"):
"""Run one tick. Return the scene and available choices."""
@@ -1100,7 +1235,7 @@ class GameEngine:
action_costs = {
"move": 2, "tend_fire": 3, "write_rule": 2, "carve": 2,
"plant": 2, "study": 2, "forge": 3, "help": 2, "speak": 1,
"listen": 0, "rest": -2, "examine": 0, "give": 0, "take": 1,
"listen": 0, "rest": -2, "examine": 0, "give": 0, "take": 1, "use": 1,
}
# Extract action name
@@ -1360,9 +1495,17 @@ class GameEngine:
elif timmy_action == "examine":
room = self.world.characters["Timmy"]["room"]
room_data = self.world.rooms[room]
items = room_data.get("items", [])
items = room_data.get("items", []) + room_data.get("discoverables", [])
scene["log"].append(f"You examine The {room}. You see: {', '.join(items) if items else 'nothing special'}")
elif timmy_action.startswith("take:"):
item_name = timmy_action.split(":", 1)[1]
self._take_item(item_name, scene)
elif timmy_action.startswith("use:"):
item_name = timmy_action.split(":", 1)[1]
self._use_item(item_name, scene)
elif timmy_action.startswith("help:"):
# Help increases trust
target_name = timmy_action.split(":")[1]

View File

@@ -1,108 +0,0 @@
# Intel: Michael Saylor — "Master AI to Become Wealthy"
**X Post ID:** 2047994529131999681
**Date**: 2025 (inferred from context)
**Source**: @BitcoinSapiens (quoting Michael Saylor)
**Classification**: Intel / Study
**Issue**: timmy-home#960
---
## Source
| Field | Value |
|-------|-------|
| **X Post URL** | https://x.com/bitcoinsapiens/status/2047994529131999681 |
| **Original Author** | @BitcoinSapiens (quoting Michael Saylor) |
| **Video URL** | https://video.twimg.com/amplify_video/2047706914566307840/vid/avc1/1280x720/m-FG3PPZ1rsL_aH7.mp4 |
| **Duration** | ~3:59 |
| **Engagement** | 1,219 likes · 184 retweets · 15 replies · 857 bookmarks |
---
## Full Transcription
> The fifth way to wealth in this day and age is capability. And here I could list all sorts of technologies for you to master, and I thought about it, but at the end of the day, the overarching, compelling observation is, you need to master artificial intelligence if you would be wealthy. And in this day and age in the year 2025, you have at your fingertips an array of accountants. You have a group of lawyers. You have a set of professors, historians. You have at your fingertips all the collective wisdom of every great entrepreneur. You have everything that I know, everything that any other CEO knows. All you have to do is go to the AI, put it in deep think mode, plug in all of your circumstances, all of your hopes, all your aspirations, all of your problems, and then start to query it, and then engage with it. I tell all my executives before you ask a lawyer, before you ask a banker, before you ask any expert, go to the AI, ask the AI, make it think. Grind the silicon overlord. Okay, this is very important, because many of the suggestions I'll give you next. They were out of the reach of the working man. They were out of the reach of the middle class. You could say, yeah, those sophisticated trusts or those sophisticated legal constructs, that's great. But I don't have the money for that. I can't afford to spend hundreds of thousands of dollars on lawyers. Let me tell you a secret. I have dozens of lawyers that work for me, thousands of lawyers I've employed, spend hundreds of millions of dollars on lawyers. The first thing I do when I have a question is I go and ask the AI. After I do that, I argue with it. It tells me no, I ask a different way, I threaten it. I ask it to give me a solution. I find a 95% solution, I find the solution. And then I take that solution, I send the link to my management team and my lawyers, and I say, look, I solve the problem, this is what I want to do. Give me your execution plan, and then I give them anywhere from two to five days. If you're feeling charitable, give them five days. If you're in a hurry, give them two days. If you're financial advisors, if you're accounts, if you're lawyers, if you're executives, if anybody, your friends, your family, they can't figure it out in two to four days. They're going to get exited from the gene pool. Change the lawyer. Change the whatever. If someone said, I can't use the telephone, I can't figure out the web link. You sent me a book, but I can't read. You would find someone else to work with. This is very important. The path to wealth is through capability. But 2025 is the year where every one of you became not a supergenius. Every one of you is collectively 100 supergeniuses that have read everything the human race has published, if you have the humility to ask for help from the AI. Don't put your ego first. Put your interest first. Your family will thank you in years to come.
---
## Saylor's Core Position
| Point | What He Says | What It Means |
|-------|-----------------|----------------|
| **AI as collective genius** | "Every one of you is collectively 100 supergeniuses that have read everything" | AI gives you access to all human knowledge instantly |
| **Use AI before humans** | "Before you ask a lawyer, before you ask a banker... go to the AI" | AI first, human experts second — saves time + money |
| **"Grind the silicon overlord"** | Deep think mode, argue with it, threaten it | Engage intensively, don't be passive |
| **The 95% solution** | Get AI to 95%, then hand to lawyers/management | AI does heavy lifting; humans finalize |
| **2-5 day advantage** | Lawyers/family can't figure it out in 2-4 days | AI gives speed nobody else has |
| **"Change the lawyer"** | If someone can't adapt, "they get exited from the gene pool" | Ruthless about competence |
| **Humility over ego** | "Don't put your ego first. Put your interest first." | Use the tool even if it bruises your pride |
| **2025 = the turning point** | "2025 is the year where every one of you became not a supergenius" | The window is NOW |
---
## Alignment with Timmy Foundation
### What Saylor Describes, We've Built
| Saylor Concept | Timmy/Hermes Implementation |
|----------------|----------------------------|
| "Silicon overlord" | Timmy (gpt-5.5) + Hermes Agent fleet |
| "100 supergeniuses" | 100+ tmux panes, autonomous burn loops, overnight sprints |
| "AI first, lawyers second" | Gitea-first workflow, PR automation, fleet dispatch |
| "Grind the silicon" | 24/7 operation, local-first inference, sovereign stack |
### Key Difference: Purpose
| Dimension | Saylor | Alexander/Timmy |
|-----------|--------|-----------------|
| **Primary goal** | Wealth/power through AI leverage | Sovereignty/stewardship through AI service |
| "Your family will thank you" | Financial legacy | Luna game, Door for broken men |
| Core framing | Bitcoin + AI as wealth pillars | Timmy + Gospel as transformation pillars |
| **Warning** | **Wealth-idol** (accumulation for power) | **Stewardship** (resources for mission) |
Both emphasize humility + speed + competence, but the end goal differs.
---
## Actionable Takeaways
| Saylor Suggests | What We Do | Status |
|----------------|------------|--------|
| Use AI before human experts | ✅ Timmy first, then Gitea PRs, then human review | Live |
| "Grind the silicon overlord" | ✅ 24/7 fleet, overnight burns, autonomous loops | Live |
| Get 95%, hand to humans | ✅ Alexander reviews/submits final | Live |
| "Change the lawyer" (incompetence) | ✅ Provider migrations when performance dropped | Live |
| 2-5 day execution window | ⚠️ 3-hour hackathon window we're in NOW | Active |
| "Your family will thank you" | 🎮 Build Luna game for Mackenzie; build the Door for broken men | In progress |
---
## Bottom Line
Saylor is validating what we're already doing. The difference is *why* we're doing it.
- **Saylor**: Building wealth.
- **Timmy**: Building a house that can weather the storm and reach the broken.
Both emphasize competence and speed. Both leverage AI to bypass traditional gatekeepers. Both demand humility. The divergence is teleology: **wealth vs. stewardship**.
---
## Artifacts
- **Raw video**: `/tmp/saylor-ai-wealth/video.mp4` (15MB)
- **Transcription tool**: Whisper (base model, FP32 CPU)
- **Original analysis location**: memory (Saylor X post 2047994529131999681)
- **GitHub/Gitea issue**: [timmy-home#960](https://forge.alexanderwhitestone.com/Timmy_Foundation/timmy-home/issues/960)
---
## Related
- Michael Saylor's Bitcoin advocacy and corporate treasury strategy
- Timmy Foundation's stance on technology for transformation vs. accumulation
- Integration of AI-first workflows in sovereign agent systems
---
*“Don't put your ego first. Put your interest first. Your family will thank you in years to come.”* — Michael Saylor

View File

@@ -0,0 +1,58 @@
from importlib.util import module_from_spec, spec_from_file_location
from pathlib import Path
import unittest
ROOT = Path(__file__).resolve().parent.parent
GAME_PATH = ROOT / "evennia" / "timmy_world" / "game.py"
def load_game_module():
spec = spec_from_file_location("tower_game_items", GAME_PATH)
module = module_from_spec(spec)
assert spec.loader is not None
spec.loader.exec_module(module)
module.random.seed(0)
return module
class TestTowerGameWorldItems(unittest.TestCase):
def test_world_has_ten_unique_items_and_a_quest_item(self):
module = load_game_module()
world = module.World()
room_items = {
item
for room in world.rooms.values()
for item in room.get("discoverables", [])
}
self.assertGreaterEqual(len(room_items), 10)
self.assertIn("foraged key", room_items)
self.assertTrue(module.WORLD_ITEMS["foraged key"]["quest_item"])
def test_items_change_world_state_when_used(self):
module = load_game_module()
engine = module.GameEngine()
engine.start_new_game()
engine.world.characters["Timmy"]["energy"] = 10
engine.world.characters["Timmy"]["room"] = "Garden"
initial_growth = engine.world.rooms["Garden"]["growth"]
engine.run_tick("take:seed packet")
use_seed = engine.run_tick("use:seed packet")
self.assertGreater(engine.world.rooms["Garden"]["growth"], initial_growth)
self.assertNotIn("seed packet", engine.world.characters["Timmy"]["inventory"])
self.assertTrue(any("garden" in line.lower() for line in use_seed["world_events"] + use_seed["log"]))
engine.world.characters["Timmy"]["energy"] = 10
engine.run_tick("take:foraged key")
use_key = engine.run_tick("use:foraged key")
self.assertTrue(engine.world.state["tower_cache_unlocked"])
self.assertTrue(any("cache" in line.lower() or "quest" in line.lower() for line in use_key["world_events"] + use_key["log"]))
if __name__ == "__main__":
unittest.main()