context
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseContext
上下文(World对象)
"The context IS the world as seen from inside the closure." — Dave Ungar, on lexical scope
"上下文就是闭包内部所看到的整个世界。" — 戴夫·昂加尔,关于词法作用域
What Is It?
什么是World上下文?
The world object is passed to every compiled closure. It provides:
- Standard keys — Always present (adventure, player, room, turn)
- Extended keys — Contextual (object, target, npc)
- Skill namespaces — Skills register state under
world.skills.skill_name - Utility functions — API for interacting with the world
world 对象会传递给每个已编译的闭包。它提供以下内容:
- 标准键 —— 始终存在(adventure、player、room、turn)
- 扩展键 —— 上下文相关(object、target、npc)
- 技能命名空间 —— 技能会在下注册状态
world.skills.skill_name - 工具函数 —— 用于与游戏世界交互的API
Why "world" not "ctx"?
为什么用“world”而不是“ctx”?
- More evocative — closures see the WORLD
- Self-documenting — ,
world.playerworld.room - Matches the mental model
- 更具表现力 —— 闭包看到的是整个“世界”
- 自文档化 —— 、
world.player一目了然world.room - 符合心智模型
Standard Keys
标准键
Always present in every world:
javascript
world.turn // Current simulation turn
world.timestamp // ISO timestamp
world.adventure // Root adventure state
.name
.flags // Global boolean flags
.world_state // Global key/value state
world.player // Current player
.id
.name
.location // Path to current room
.inventory // Array of item ids
.buffs // Active buffs
world.room // Current room
.id
.name
.path
.exits
.objects
.is_dark
.is_dangerous
world.party // Party state
.members
.leader每个World上下文中始终存在以下键:
javascript
world.turn // Current simulation turn
world.timestamp // ISO timestamp
world.adventure // Root adventure state
.name
.flags // Global boolean flags
.world_state // Global key/value state
world.player // Current player
.id
.name
.location // Path to current room
.inventory // Array of item ids
.buffs // Active buffs
world.room // Current room
.id
.name
.path
.exits
.objects
.is_dark
.is_dangerous
world.party // Party state
.members
.leaderExtended Keys
扩展键
Present when relevant:
javascript
// When running object simulate/methods:
world.object // The object being simulated
.id
.state // Object's mutable state
// Methods are bound: world.consume_fuel(1)
// When action targets something:
world.target // The target
.id
.type // "object", "character", "room"
// When NPC is simulating:
world.npc // The NPC
.id
.goals
.state仅在相关场景下存在:
javascript
// 运行对象模拟/方法时:
world.object // 正在被模拟的对象
.id
.state // 对象的可变状态
// 方法已绑定:world.consume_fuel(1)
// 操作有目标对象时:
world.target // 目标对象
.id
.type // "object", "character", "room"
// NPC进行模拟时:
world.npc // NPC对象
.id
.goals
.stateSkill State Namespaces
技能状态命名空间
Skills register state under using underscores:
world.skills.<skill_name>javascript
// Skill "economy" → world.skills.economy
world.skills.economy.gold // 100
// Skill "pie-menu" → world.skills.pie_menu (underscore!)
world.skills.pie_menu.last_selection // "north"
// Skill "time" → world.skills.time
world.skills.time.hour // 14
world.skills.time.phase // "afternoon"Why underscores? Dashes aren't valid JS/Python identifiers. skill → namespace.
foo-barfoo_barThis keeps skill state organized and avoids collisions.
技能会使用下划线在下注册状态:
world.skills.<skill_name>javascript
// 技能 "economy" → world.skills.economy
world.skills.economy.gold // 100
// 技能 "pie-menu" → world.skills.pie_menu(使用下划线!)
world.skills.pie_menu.last_selection // "north"
// 技能 "time" → world.skills.time
world.skills.time.hour // 14
world.skills.time.phase // "afternoon"为什么使用下划线? 短横线不是有效的JS/Python标识符。例如技能会对应命名空间。
foo-barfoo_bar这样可以保持技能状态的组织性,避免命名冲突。
Utility Functions
工具函数
Methods bound to world for interaction:
绑定到World对象的交互方法:
Narrative
叙事相关
javascript
world.emit("The lamp dies!") // Show message
world.narrate("Darkness falls.", "dramatic")javascript
world.emit("The lamp dies!") // Show message
world.narrate("Darkness falls.", "dramatic")Events
事件触发
javascript
world.trigger_event("GRUE_APPROACHES", { room: world.room.path })javascript
world.trigger_event("GRUE_APPROACHES", { room: world.room.path })Inventory
物品栏操作
javascript
world.has("brass-key") // true/false
world.give("gold-coins") // Add to inventory
world.take("used-potion") // Remove from inventoryjavascript
world.has("brass-key") // true/false
world.give("gold-coins") // Add to inventory
world.take("used-potion") // Remove from inventoryFlags
标志位操作
javascript
world.flag("dragon_slain") // Get flag
world.set_flag("treasure_found", true) // Set flagjavascript
world.flag("dragon_slain") // Get flag
world.set_flag("treasure_found", true) // Set flagState
状态操作
javascript
world.get("object.state.fuel") // Get by path
world.set("object.state.lit", true) // Set by pathjavascript
world.get("object.state.fuel") // Get by path
world.set("object.state.lit", true) // Set by pathNavigation
导航操作
javascript
world.go("../maze/room-a/") // Move player
world.can_go("north") // Check exitjavascript
world.go("../maze/room-a/") // Move player
world.can_go("north") // Check exitBuffs
增益效果操作
javascript
world.add_buff({ name: "Caffeinated", effect: { energy: +2 }, duration: 5 })
world.remove_buff("caffeinated")
world.has_buff("grue_immunity")javascript
world.add_buff({ name: "Caffeinated", effect: { energy: +2 }, duration: 5 })
world.remove_buff("caffeinated")
world.has_buff("grue_immunity")Logging
日志输出
javascript
world.log("Debug: fuel = " + world.object.state.fuel)javascript
world.log("Debug: fuel = " + world.object.state.fuel)Example: Lamp Simulate
示例:油灯模拟
javascript
simulate_js: (world) => {
if (world.object.state.lit) {
world.consume_fuel(1); // Call object method
if (world.object.state.fuel <= 0) {
world.extinguish(); // Call object method
world.emit("The lamp sputters and dies!");
if (world.room.is_dark && world.room.is_dangerous) {
world.trigger_event("GRUE_APPROACHES");
}
}
}
}javascript
simulate_js: (world) => {
if (world.object.state.lit) {
world.consume_fuel(1); // Call object method
if (world.object.state.fuel <= 0) {
world.extinguish(); // Call object method
world.emit("The lamp sputters and dies!");
if (world.room.is_dark && world.room.is_dangerous) {
world.trigger_event("GRUE_APPROACHES");
}
}
}
}Example: Guard Expression
示例:守卫表达式
yaml
guard: "player has the key AND room is not dark"
guard_js: (world) => world.has("brass-key") && !world.room.is_darkyaml
guard: "player has the key AND room is not dark"
guard_js: (world) => world.has("brass-key") && !world.room.is_darkExample: Score Calculation
示例:分数计算
yaml
score_if: "player is tired OR room is dark"
score_if_js: (world) => world.has_buff("tired") || world.room.is_darkyaml
score_if: "player is tired OR room is dark"
score_if_js: (world) => world.has_buff("tired") || world.room.is_darkExample: Skill State
示例:技能状态
yaml
undefinedyaml
undefinedSkill "economy" needs to check gold
Skill "economy" needs to check gold
guard: "player has at least 10 gold"
guard_js: (world) => world.skills.economy.gold >= 10
guard: "player has at least 10 gold"
guard_js: (world) => world.skills.economy.gold >= 10
Skill "pie-menu" checks last selection
Skill "pie-menu" checks last selection
score_if: "last pie menu selection was north"
score_if_js: (world) => world.skills.pie_menu.last_selection === "north"
---score_if: "last pie menu selection was north"
score_if_js: (world) => world.skills.pie_menu.last_selection === "north"
---Design Principles
设计原则
Structured, Not Arbitrary
结构化而非随意性
world is NOT just a bag of key/values. It has defined structure:
- Standard keys are always present
- Extended keys appear in context
- Skills namespace their state (with underscores!)
- Functions are bound methods
World上下文并非简单的键值对集合,它有明确的结构:
- 标准键始终存在
- 扩展键仅在相关场景出现
- 技能使用命名空间管理状态(必须用下划线!)
- 函数均为已绑定的方法
Skill Namespaces (Underscores!)
技能命名空间(必须用下划线!)
Skills don't pollute root world. They register under :
world.skills.skill_namejavascript
// Skill "economy" → world.skills.economy
world.skills.economy.gold
world.skills.economy.currency
// Skill "pie-menu" → world.skills.pie_menu (underscore!)
world.skills.pie_menu.last_selection
world.skills.pie_menu.hover_direction
// Skill "foo-bar" → world.skills.foo_bar
world.skills.foo_bar.some_stateRule: with dashes → with underscores in namespace.
skill-nameskill_name技能不会污染根World上下文,而是注册在下:
world.skills.skill_namejavascript
// 技能 "economy" → world.skills.economy
world.skills.economy.gold
world.skills.economy.currency
// 技能 "pie-menu" → world.skills.pie_menu(使用下划线!)
world.skills.pie_menu.last_selection
world.skills.pie_menu.hover_direction
// 技能 "foo-bar" → world.skills.foo_bar
world.skills.foo_bar.some_state规则: 带短横线的会转换为带下划线的命名空间。
skill-nameskill_nameMethods Are Bound
方法已绑定
Object methods appear as functions on world:
javascript
// Object defines:
methods:
consume_fuel: "reduce fuel by amount"
// At runtime, method is bound:
world.consume_fuel(1) // Works!对象的方法会作为函数绑定到World上下文:
javascript
// 对象定义:
methods:
consume_fuel: "reduce fuel by amount"
// 运行时,方法已绑定:
world.consume_fuel(1) // 可直接调用!Related Skills
相关技能
- object — Provides ctx.object
- room — Provides ctx.room
- adventure — Provides ctx.adventure
- buff — Used by ctx.add_buff/has_buff
- object —— 提供ctx.object
- room —— 提供ctx.room
- adventure —— 提供ctx.adventure
- buff —— 被ctx.add_buff/has_buff使用
Dual Runtime: Python + JavaScript
双运行时:Python + JavaScript
CRITICAL: We always generate BOTH AND versions of compiled expressions.
_js_pyyaml
undefined关键注意事项: 我们始终为编译后的表达式生成和两种版本。
_js_pyyaml
undefinedNatural language
自然语言表达式
guard: "player has the key AND room is not dark"
guard: "player has the key AND room is not dark"
BOTH generated:
两种版本都会生成:
guard_js: (world) => world.has("brass-key") && !world.room.is_dark
guard_py: lambda world: world.has("brass-key") and not world.room.is_dark
undefinedguard_js: (world) => world.has("brass-key") && !world.room.is_dark
guard_py: lambda world: world.has("brass-key") and not world.room.is_dark
undefinedWhy Dual Runtimes?
为什么需要双运行时?
| Runtime | Purpose |
|---|---|
| Python | Server-side simulation, testing, LLM tethering |
| JavaScript | Browser runtime, standalone play |
| 运行时 | 用途 |
|---|---|
| Python | 服务器端模拟、测试、LLM集成 |
| JavaScript | 浏览器端运行、独立游玩 |
Keeping Them In Sync
保持双运行时同步
- Same semantics — Both should produce identical results
- Same world structure — ,
world.player, etc.world.room - Same utility functions — ,
world.has(), etc.world.emit() - Generated together — LLM produces both in one pass
- 语义一致 —— 两种版本必须产生完全相同的结果
- World结构一致 —— 、
world.player等结构完全相同world.room - 工具函数一致 —— 、
world.has()等函数行为一致world.emit() - 同步生成 —— LLM会一次性生成两种版本
The Compilation Event
编译事件定义
yaml
- event: COMPILE_EXPRESSION
field: guard
source: "player has the key"
targets:
- field: guard_js
language: javascript
- field: guard_py
language: python
expected_type: booleanyaml
- event: COMPILE_EXPRESSION
field: guard
source: "player has the key"
targets:
- field: guard_js
language: javascript
- field: guard_py
language: python
expected_type: booleanPython Runtime Class
Python运行时类
python
class World:
"""Python runtime context — mirrors JavaScript World class."""
def __init__(self, adventure_data):
self.turn = 0
self.adventure = adventure_data
self.player = adventure_data['player']
self.room = None # Set on navigation
self.party = adventure_data['party']
self.object = None # Set during object simulation
self.skills = {} # Skill state namespaces
def has(self, item_id: str) -> bool:
return item_id in self.player.get('inventory', [])
def flag(self, name: str) -> bool:
return self.adventure.get('flags', {}).get(name, False)
def emit(self, message: str):
print(message) # Or queue for output
def trigger_event(self, name: str, data=None):
# Event system handles this
passpython
class World:
"""Python runtime context — mirrors JavaScript World class."""
def __init__(self, adventure_data):
self.turn = 0
self.adventure = adventure_data
self.player = adventure_data['player']
self.room = None # Set on navigation
self.party = adventure_data['party']
self.object = None # Set during object simulation
self.skills = {} # Skill state namespaces
def has(self, item_id: str) -> bool:
return item_id in self.player.get('inventory', [])
def flag(self, name: str) -> bool:
return self.adventure.get('flags', {}).get(name, False)
def emit(self, message: str):
print(message) # Or queue for output
def trigger_event(self, name: str, data=None):
# Event system handles this
passJavaScript Runtime Class
JavaScript运行时类
javascript
class World {
/** JavaScript runtime context — mirrors Python World class. */
constructor(adventureData) {
this.turn = 0;
this.adventure = adventureData;
this.player = adventureData.player;
this.room = null; // Set on navigation
this.party = adventureData.party;
this.object = null; // Set during object simulation
this.skills = {}; // Skill state namespaces
}
has(itemId) {
return (this.player.inventory || []).includes(itemId);
}
flag(name) {
return (this.adventure.flags || {})[name] || false;
}
emit(message) {
console.log(message); // Or queue for UI
}
triggerEvent(name, data) {
// Event system handles this
}
}javascript
class World {
/** JavaScript runtime context — mirrors Python World class. */
constructor(adventureData) {
this.turn = 0;
this.adventure = adventureData;
this.player = adventureData.player;
this.room = null; // Set on navigation
this.party = adventureData.party;
this.object = null; // Set during object simulation
this.skills = {}; // Skill state namespaces
}
has(itemId) {
return (this.player.inventory || []).includes(itemId);
}
flag(name) {
return (this.adventure.flags || {})[name] || false;
}
emit(message) {
console.log(message); // Or queue for UI
}
triggerEvent(name, data) {
// Event system handles this
}
}Protocol Symbol
协议标识
RUNTIME-CONTEXT — The world passed to closures (Python + JavaScript)RUNTIME-CONTEXT — The world passed to closures (Python + JavaScript)