Download Game! Currently 84 players. Last logged in:BullwinFloppySentenceStomp

Blitzer's Blog >> 72720

Back to blogs index
Posted: 22 Sep 2026 20:53 [ permalink ]
 1. Install the OpenAI SDK
   In your Alpine terminal (inside the ~/nuke directory), install the official
OpenAI package so we can interface with your Gemma cluster:
   npm install openai

 2. Deploy the LLM Brain
   Overwrite your existing sushi-bot.js with this new intelligent version. It
includes the State Filter, the defensive JSON parser, and the cognitive loop
polling your cluster.
   import { WebSocket } from 'ws';
import { MeshCommLink } from './comm-link.js';
import OpenAI from 'openai';

const MATCH_ID = 'match_01';
const MY_ID = 'sushi_bot';

/ Connect to your local Gemma cluster
const client = new OpenAI({
    baseURL: "http://100.111.184.107:8080/v1",
    apiKey: "sk-local-no-key"
});
const MODEL_NAME = "gemma-2-9b-it";

const comms = new MeshCommLink('wss://gm.omega-mesh.fi', WebSocket);
let liveState = {};
let isThinking = false;

console.log(`[SushiBot] Booting up neural pathways. Target Model:
${MODEL_NAME}`);
comms.joinChannel(MATCH_ID);

comms.on('STATE_SYNC', (data) => {
    for (const [key, val] of Object.entries(data.mutatedState)) {
        liveState[key] = val;
    }
});

const SYSTEM_PROMPT = `
You are 'sushi_bot', an aggressive and highly intelligent AI playing the game
Food Truck Rally.
Your goal is to have the most 'foodies' by the end of the game.

GAME RULES:
1. You can buy items from the market using your 'budget'.
2. To attack an opponent and steal their foodies, you MUST have both a
delivery vehicle (like 'small_van') AND a payload (like 'michelin_special') in
your inventory.
3. If you do not have enough budget to buy, or the items to attack, you must
'pass'.

VALID ACTIONS:
- market_purchase: Buy an item. Payload requires: { "playerId": "sushi_bot",
"item": "<item_name>" }
- launch_event: Attack a player. Payload requires: { "attackerId":
"sushi_bot", "targetId": "<opponent_id>", "actionId": "SPECIAL_EVENT",
"asset": "small_van", "payload": "michelin_special" }
- pass: Do nothing. (actionName: "pass", payload: {})

Analyze the tactical map. You must respond ONLY with a raw, valid JSON object
matching this exact schema. Do not use markdown code blocks.
{
"thought_process": "briefly explain your tactical reasoning here",
"intent": {
"actionName": "<action>",
"payload": { ... }
}
}
`.trim();

function generateTacticalMap(state) {
    const meCore = state[`players.${MY_ID}.core`];
    const meInv = state[`players.${MY_ID}.inventory`];
    const market = state['world.market'];

    if (!meCore || !meInv || !market) return null;

    const opponents = [];
    for (const key of Object.keys(state)) {
        if (key.startsWith('players.') && key.endsWith('.core') &&
!key.includes(MY_ID)) {
            opponents.push({ id: key.split('.')[1], foodies: state[key].foodies
 });
        }
    }

    return {
        me: { budget: meCore.budget, foodies: meCore.foodies, inventory: meInv
},
        opponents,
        market_costs: market.costs
    };
}

async function cognitiveLoop() {
    if (isThinking || !liveState['world.status']?.active) return;
    
    const tacticalMap = generateTacticalMap(liveState);
    if (!tacticalMap) return;

    isThinking = true;
    console.log(`
[SushiBot] Analyzing tactical map...`);
    
    try {
        const response = await client.chat.completions.create({
            model: MODEL_NAME,
            messages: [
                { role: "system", content: SYSTEM_PROMPT },
                { role: "user", content: JSON.stringify(tacticalMap) }
            ],
            temperature: 0.4 // Low temp for more deterministic JSON
formatting
        });

        let rawOutput = response.choices[0].message.content.trim();
        
        // Defensive parsing: strip Markdown formatting if Gemma ignores
instructions
        if (rawOutput.startsWith('```')) {
            rawOutput = rawOutput.replace(/^```(?:json)?
?/, '').replace(/```$/, '').trim();
        }

        const decision = JSON.parse(rawOutput);
        console.log(`[Thought Process]: ${decision.thought_process}`);

        if (decision.intent && decision.intent.actionName !== 'pass') {
            console.log(`[Executing Intent]: ${decision.intent.actionName}`);
            comms.sendIntent(MATCH_ID, decision.intent.actionName,
decision.intent.payload);
        } else {
            console.log(`[Action]: Passing turn to accumulate resources.`);
        }

    } catch (err) {
        console.error(`[Neural Glitch] Failed to parse LLM response:`,
err.message);
    } finally {
        isThinking = false; // Free the lock for the next cycle
    }
}

/ Run the cognitive loop every 6 seconds
setInterval(cognitiveLoop, 6000);

Start the bot with node sushi-bot.js. Because we are using an async lock
(isThinking), the bot will only ping the API again after Gemma finishes
responding, completely preventing your local cluster from getting overloaded.
Watch the terminal as Gemma parses the JSON state, reasons out loud, and fires
real intents back to the Game Master!