@rameneko/core
v0.1.0
Published
Game environment for Rameneko — build your own AI copilot
Readme
@rameneko/core
Game environment for Rameneko — build your own player for a ramen restaurant game.
Quick Start
import { createEnv } from '@rameneko/core';
const env = createEnv({ level: 5 });
const obs = env.reset(42); // seed for reproducibility
console.log(obs.state); // WorldState — full game state
console.log(obs.validActions); // number[] — what you can do right now
const result = env.step(3); // pick action index 3
console.log(result.score); // score delta for this tick
console.log(result.done); // day over?
console.log(result.state.dayStats); // { servedCount, failedCount, ... }How It Works
Each game is one "day" (90 ticks). You pick one action per tick. Customers arrive, place orders, and leave if not served in time. The goal: serve as many customers as possible.
State
obs.state is the full WorldState. Key fields:
state.orders— current customer orders (seat, recipe, patience)state.bowls— bowl assembly slots (broth, tare, noodle, toppings)state.noodleSlots— noodle boiler timersstate.preps— cooking toppings in progressstate.timeLeft/state.dayDuration— time remainingstate.combo— current serve streakstate.dayStats— running totals (servedCount, failedCount, earnings)state.seatCans— uncollected tips at seats
Actions
Action 0 is always NOOP. Other actions:
START_NOODLE(slot)— start boiling noodlesTAKE_NOODLE(noodle→bowl)— move cooked noodles to a bowlSET_BROTH(bowl, type)— set broth on a bowlSET_TARE(bowl, type)— set tare on a bowlSTART_TOPPING(type, bowl?)— add instant topping or start cooking oneCOLLECT_TOPPING(bowl)— collect a cooked topping onto a bowlSERVE(bowl→seat)— serve a completed bowl to a customerDISCARD_BOWL(bowl)/DISCARD_NOODLE(slot)— discardCOLLECT_CANS(seat)— collect tips from a seat
Use env.actionNames for the full list. Only actions in obs.validActions are valid — invalid actions are silently ignored.
Scoring
| Event | Score | |-------|-------| | Serve a customer | +10 | | Perfect noodle bonus | +3 | | Combo multiplier | +1 × combo | | Customer left | -5 | | Serve wrong order | -1 | | Discard a bowl | -0.5 | | Per-tick penalty | -0.01 |
Levels
| Level | Actions | Description | |-------|---------|-------------| | 1 | 19 | 1 broth, 1 tare, no toppings | | 3 | 65 | 2 broths, 2 tares, basic toppings | | 5 | 70 | Full broths/tares, more toppings | | 7 | 77 | All toppings, more bowl/noodle slots | | 10 | 77 | Everything unlocked |
Player Interface
Implement the Player interface:
import { createEnv, type Player } from '@rameneko/core';
const myPlayer: Player = {
decide(state, validActions) {
// state: WorldState — inspect orders, bowls, noodles, etc.
// validActions: number[] — pick one of these
return validActions[Math.floor(Math.random() * validActions.length)];
},
};
const env = createEnv({ level: 5 });
let obs = env.reset(42);
while (!env.done) {
const action = myPlayer.decide(obs.state, obs.validActions);
obs = env.step(action);
}
console.log('Served:', obs.state.dayStats.servedCount);Replays
Record a game as a replay, then share or play it back:
import { createEnv, simulateDay, type Player } from '@rameneko/core';
const env = createEnv({ level: 5 });
const replay = simulateDay(env, myPlayer, 42);
// replay: { seed, level, ticks: [{ tick, actions }] }
// Save as JSON for playback in the game viewer
import { writeFileSync } from 'fs';
writeFileSync('my-replay.json', JSON.stringify(replay));API Reference
createEnv({ level })
Create a game environment for the given level.
GameEnv
| Property | Type | Description |
|----------|------|-------------|
| actionCount | number | Total number of actions |
| actionNames | string[] | Human-readable action labels |
| done | boolean | Whether the day has ended |
| tick | number | Current tick (0-based) |
| Method | Returns | Description |
|--------|---------|-------------|
| reset(seed?) | Observation | Start a new day |
| step(action) | StepResult | Take one action, advance one tick |
| step(actions[]) | StepResult | Take multiple actions in one tick |
| getState() | WorldState | Current game state |
Types
interface Observation {
state: WorldState;
validActions: number[];
}
interface StepResult extends Observation {
score: number;
done: boolean;
}
interface Player {
decide(state: WorldState, validActions: number[]): number;
reset?(): void;
}
interface ReplayRecording {
seed: number;
level: number;
ticks: { tick: number; actions: number[] }[];
}