Download Game! Currently 76 players and visitors. Last logged in:PotionOmnipotentCelbornKandahar

Blitzer's Blog >> 72443

Back to blogs index
Posted: 15 Sep 2026 14:31 [ permalink ]
This is the crowning feature of the engine. By treating `room_labels`,
`inventories`, and `bags` as mathematically identical string-array endpoints,
you have created a universal memory addressing system that requires almost
zero instruction tokens for the LLM to understand.

To make `PULL`, `PUSH`, and the `HAS` condition work magically without forcing
Gemma to specify the source or destination every time, we can use a classic
Virtual Machine trick: **Program Counter Inference.**

Because your VM executes sequentially, we can always figure out the "current
environment" simply by looking at where the Program Counter (`ctx.pc`) is
right now and finding the label that immediately preceded it!

Here are the precise patches to implement the entire Universal Container
Abstraction.

### 1. The Single-Quote Lexer Patch (`lexer.js`)

To allow arrays to be saved and loaded cleanly into the `fso-state` block, we
just need to tell the lexer to respect single quotes (so we can save an array
like this: `VAR INVENTORY ARR '["GOLD_KEY"]'`).

Update the token matching line inside `lexer.js`:

```javascript
    // In lexer.js -> tokenize()
    // Split by space, preserve double OR single quoted strings, remove quotes
    let tokens = line.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)
                     .map(t => t.replace(/(^["']|["']$)/g, ''));

```

### 2. The Item Opcodes (`opcodes.js`)

We will add a helper function to infer the current room, register
`PUSH`/`PULL`, update `VAR` to handle arrays, and teach `JMP_IF` the `HAS`
operator.

```javascript
    // -- MEMORY -- (Update the VAR opcode)
    vm.register('VAR', (ctx, args) => {
        const [name, type, val] = args;
        if (type === 'NUM') ctx.memory[name] = parseFloat(val);
        else if (type === 'BOOL') ctx.memory[name] = (val === 'true');
        else if (type === 'ARR') {
            try { ctx.memory[name] = JSON.parse(val); } catch(e) {
ctx.memory[name] = []; }
        }
        else ctx.memory[name] = val; 
    });

    // -- CONTAINER HELPERS & OPCODES --
    
    // VM Trick: Find the label that immediately precedes the current Program
Counter
    function getCurrentEnvironment(ctx) {
        let currentEnv = "UNKNOWN";
        let maxPc = -1;
        for (const [label, pc] of Object.entries(ctx.labels)) {
            if (pc <= ctx.pc && pc > maxPc) {
                maxPc = pc;
                currentEnv = label;
            }
        }
        return currentEnv;
    }

    vm.register('PULL', (ctx, rawArgs) => {
        // Strip out LLM filler words
        const args = rawArgs.filter(w => !['THE', 'A', 'AN'].includes(w.toUpper
Case()));
        const item = String(args[0]).toUpperCase();

        // Defaults based on Context
        let source = getCurrentEnvironment(ctx);
        let dest = 'INVENTORY';

        // Override if LLM explicitly used FROM / INTO
        const fromIdx = args.findIndex(a => a.toUpperCase() === 'FROM');
        if (fromIdx !== -1 && args[fromIdx + 1]) source = args[fromIdx + 1];

        const intoIdx = args.findIndex(a => a.toUpperCase() === 'INTO');
        if (intoIdx !== -1 && args[intoIdx + 1]) dest = args[intoIdx + 1];

        // Ensure both endpoints exist as arrays
        if (!Array.isArray(ctx.memory[source])) ctx.memory[source] = [];
        if (!Array.isArray(ctx.memory[dest])) ctx.memory[dest] = [];

        // Move the item
        const itemIndex = ctx.memory[source].indexOf(item);
        if (itemIndex > -1) {
            ctx.memory[source].splice(itemIndex, 1);
            ctx.memory[dest].push(item);
        }
    });

    vm.register('PUSH', (ctx, rawArgs) => {
        const args = rawArgs.filter(w => !['THE', 'A', 'AN'].includes(w.toUpper
Case()));
        const item = String(args[0]).toUpperCase();

        // Inverse defaults for PUSH (Drop)
        let source = 'INVENTORY';
        let dest = getCurrentEnvironment(ctx);

        const fromIdx = args.findIndex(a => a.toUpperCase() === 'FROM');
        if (fromIdx !== -1 && args[fromIdx + 1]) source = args[fromIdx + 1];

        const intoIdx = args.findIndex(a => a.toUpperCase() === 'INTO');
        if (intoIdx !== -1 && args[intoIdx + 1]) dest = args[intoIdx + 1];

        if (!Array.isArray(ctx.memory[source])) ctx.memory[source] = [];
        if (!Array.isArray(ctx.memory[dest])) ctx.memory[dest] = [];

        const itemIndex = ctx.memory[source].indexOf(item);
        if (itemIndex > -1) {
            ctx.memory[source].splice(itemIndex, 1);
            ctx.memory[dest].push(item);
        }
    });

```

*Don't forget to add this line to your `JMP_IF` operator logic inside
`opcodes.js`:*

```javascript
    else if (condUpper === 'HAS') {
        const arr = Array.isArray(ctx.memory[varName]) ? ctx.memory[varName] :
[];
        isTrue = arr.includes(expectedVal);
    }

```