context

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Context

上下文(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:
  1. Standard keys — Always present (adventure, player, room, turn)
  2. Extended keys — Contextual (object, target, npc)
  3. Skill namespaces — Skills register state under
    world.skills.skill_name
  4. Utility functions — API for interacting with the world
world 对象会传递给每个已编译的闭包。它提供以下内容:
  1. 标准键 —— 始终存在(adventure、player、room、turn)
  2. 扩展键 —— 上下文相关(object、target、npc)
  3. 技能命名空间 —— 技能会在
    world.skills.skill_name
    下注册状态
  4. 工具函数 —— 用于与游戏世界交互的API

Why "world" not "ctx"?

为什么用“world”而不是“ctx”?

  • More evocative — closures see the WORLD
  • Self-documenting —
    world.player
    ,
    world.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
  .leader

Extended 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
  .state

Skill State Namespaces

技能状态命名空间

Skills register state under
world.skills.<skill_name>
using underscores:
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.
foo-bar
skill →
foo_bar
namespace.
This 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-bar
技能会对应
foo_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 inventory
javascript
world.has("brass-key")                    // true/false
world.give("gold-coins")                  // Add to inventory
world.take("used-potion")                 // Remove from inventory

Flags

标志位操作

javascript
world.flag("dragon_slain")                // Get flag
world.set_flag("treasure_found", true)    // Set flag
javascript
world.flag("dragon_slain")                // Get flag
world.set_flag("treasure_found", true)    // Set flag

State

状态操作

javascript
world.get("object.state.fuel")            // Get by path
world.set("object.state.lit", true)       // Set by path
javascript
world.get("object.state.fuel")            // Get by path
world.set("object.state.lit", true)       // Set by path

Navigation

导航操作

javascript
world.go("../maze/room-a/")               // Move player
world.can_go("north")                     // Check exit
javascript
world.go("../maze/room-a/")               // Move player
world.can_go("north")                     // Check exit

Buffs

增益效果操作

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_dark

yaml
guard: "player has the key AND room is not dark"
guard_js: (world) => world.has("brass-key") && !world.room.is_dark

Example: Score Calculation

示例:分数计算

yaml
score_if: "player is tired OR room is dark"
score_if_js: (world) => world.has_buff("tired") || world.room.is_dark

yaml
score_if: "player is tired OR room is dark"
score_if_js: (world) => world.has_buff("tired") || world.room.is_dark

Example: Skill State

示例:技能状态

yaml
undefined
yaml
undefined

Skill "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_name
:
javascript
// 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_state
Rule:
skill-name
with dashes →
skill_name
with underscores in namespace.
技能不会污染根World上下文,而是注册在
world.skills.skill_name
下:
javascript
// 技能 "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-name
会转换为带下划线的
skill_name
命名空间。

Methods 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
_js
AND
_py
versions of compiled expressions.
yaml
undefined
关键注意事项: 我们始终为编译后的表达式生成
_js
_py
两种版本。
yaml
undefined

Natural 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
undefined
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
undefined

Why Dual Runtimes?

为什么需要双运行时?

RuntimePurpose
PythonServer-side simulation, testing, LLM tethering
JavaScriptBrowser runtime, standalone play
运行时用途
Python服务器端模拟、测试、LLM集成
JavaScript浏览器端运行、独立游玩

Keeping Them In Sync

保持双运行时同步

  1. Same semantics — Both should produce identical results
  2. Same world structure
    world.player
    ,
    world.room
    , etc.
  3. Same utility functions
    world.has()
    ,
    world.emit()
    , etc.
  4. Generated together — LLM produces both in one pass
  1. 语义一致 —— 两种版本必须产生完全相同的结果
  2. World结构一致 ——
    world.player
    world.room
    等结构完全相同
  3. 工具函数一致 ——
    world.has()
    world.emit()
    等函数行为一致
  4. 同步生成 —— 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: boolean
yaml
- event: COMPILE_EXPRESSION
  field: guard
  source: "player has the key"
  targets:
    - field: guard_js
      language: javascript
    - field: guard_py
      language: python
  expected_type: boolean

Python 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
        pass
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
        pass

JavaScript 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)