{"_id":"@aivoland/game-engine","name":"@aivoland/game-engine","dist-tags":{"latest":"0.0.1"},"versions":{"0.0.1":{"name":"@aivoland/game-engine","version":"0.0.1","exports":{".":{"import":{"types":"./dist/esm/index.d.ts","default":"./dist/esm/index.js"}}},"scripts":{"build":"rimraf ./dist && tsc --project tsconfig.build.json","clean":"rimraf ./node_modules dist .tanstack","check:lint":"biome check --write","check:type":"tsc -p ./tsconfig.json --noEmit","check:dep":"depcruise --config .dependency-cruiser.mjs src","check:test":"vitest --watch=false"},"dependencies":{},"repository":{"type":"git","url":"git+https://github.com/aivoland/aivoland.git","directory":"packages/game-engine"},"gitHead":"ef4cffc9c1e7506c66dcdeb6e6c1186e5b52bcc4","_id":"@aivoland/game-engine@0.0.1","description":"Pure TypeScript turn-based game engine with a ruleset architecture. No UI, network, storage, timer, or runtime dependencies.","bugs":{"url":"https://github.com/aivoland/aivoland/issues"},"homepage":"https://github.com/aivoland/aivoland#readme","_nodeVersion":"24.13.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-8U09Al5pnk33oFaP8ETWAkkfk+AjWbvwTANhQIPzlcLTJIcmr9tpRLoO9y0Vo3MF6hvMgchmthUxU/4+zeVZQQ==","shasum":"4958cb3f9656ebdf965054977a025a9ac44c8080","tarball":"https://registry.npmjs.org/@aivoland/game-engine/-/game-engine-0.0.1.tgz","fileCount":114,"unpackedSize":224548,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIGxRmDBDRqMSEd4HUkSIelxl5i6OG8YnBaG+qkEVsKC6AiAd4bDN4LGTFSK94aHaTwl7seNxI3Vci5JWoXZx8MmB3Q=="}]},"_npmUser":{"name":"j-xzy","email":"wu38607@gmail.com"},"directories":{},"maintainers":[{"name":"j-xzy","email":"wu38607@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/game-engine_0.0.1_1780134319462_0.7057950618124964"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-30T09:45:19.312Z","0.0.1":"2026-05-30T09:45:19.629Z","modified":"2026-05-30T09:45:19.813Z"},"maintainers":[{"name":"j-xzy","email":"wu38607@gmail.com"}],"description":"Pure TypeScript turn-based game engine with a ruleset architecture. No UI, network, storage, timer, or runtime dependencies.","homepage":"https://github.com/aivoland/aivoland#readme","repository":{"type":"git","url":"git+https://github.com/aivoland/aivoland.git","directory":"packages/game-engine"},"bugs":{"url":"https://github.com/aivoland/aivoland/issues"},"readme":"# @aivoland/game-engine\n\nPure TypeScript turn-based game engine with a ruleset architecture. No UI, network, storage, timer, or runtime dependencies.\n\n---\n\n## Rules & Guardrails\n\n- Never couple this package to React, Vue, DOM, Canvas, HTTP, WebSocket, or any database.\n- Never call Agent/LLM APIs or generate UI text inside ruleset logic.\n- Never let concrete ruleset code touch `core` concepts, and never let one ruleset depend on another.\n- Do not use `class`, `Map`, `Set`, `Date`, or functions inside game state objects — state must be plain, serializable objects.\n- Do not introduce randomness inside rule functions; callers must inject seeds or pre-rolled results.\n- Only promote a utility to `shared/` when two or more rulesets share it.\n- Run `pnpm run check:test` before committing any ruleset change.\n\n---\n\n## Core Project Context\n\n- Package: `@aivoland/game-engine` — standalone logic library, zero runtime dependencies.\n- Repository root: `packages/game-engine/`\n- Source layout:\n  - `src/index.ts` — stable public exports only\n  - `src/core/` — generic interfaces, `GameSession`, result/error/event base types\n  - `src/rulesets/<name>/` — one directory per game ruleset\n  - `src/shared/` — cross-ruleset utilities (`geometry.ts`, `grid.ts`, `ids.ts`)\n- Commands:\n  - Lint: `pnpm run check:lint`\n  - Type-check: `pnpm run check:type`\n  - Dependency graph: `pnpm run check:dep`\n  - Test: `pnpm run check:test`\n\n---\n\n## Architecture Notes\n\n- **`IGameRuleset<TState, TAction, TView, TEvent>`** (`src/core/types.ts`) — the contract every ruleset must implement:\n  - `createInitialState(): TState`\n  - `getCurrentActor(state): string`\n  - `getLegalActions(state): TAction[]`\n  - `applyAction(state, action): IApplyActionResult<TState, TEvent>`\n  - `getView(state, actorId): TView`\n  - `isGameOver(state): IGameOverResult | null`\n- **`GameSession`** (`src/core/session.ts`) — thin stateful wrapper around a ruleset; holds current state and accumulated event log. Use it as the external drive loop:\n  ```ts\n  while (!session.isGameOver()) {\n    const view = session.getView(session.getCurrentActor());\n    const action = await agent.decide(view, session.getLegalActions());\n    session.applyAction(action);\n  }\n  ```\n- **State rules**: plain objects only; no class instances, no `Map`/`Set`/`Date`, no embedded functions. State must be JSON-serializable for replay and branching.\n- **Rule functions**: pure — take old state + action, return new state + events + feedback. No side effects, no external reads.\n- **`IApplyActionResult`** (`src/core/result.ts`): `{ state, events, feedback }` where `feedback` carries `success`, `apConsumed`, `apRemaining`, and `warnings`.\n- **`IGameOverResult`** (`src/core/result.ts`): `{ winner, scores, reason }` where `reason` is `'turn-limit' | 'elimination'`.\n- **`IGameEvent`** (`src/core/events.ts`): base shape `{ type, globalSlot, turn, actorId }` — all ruleset events extend this.\n- **`IGameError`** (`src/core/errors.ts`): `{ code: 'illegal-action' | 'invalid-action' | 'game-over', message }`.\n\n---\n\n## Public Exports (`src/index.ts`)\n\n```ts\nexport { GameSession } from '~/core/session';\nexport type { IGameRuleset } from '~/core/types';\nexport type { IActionFeedback, IApplyActionResult, IGameOverResult } from '~/core/result';\nexport { resourceClashRuleset } from '~/rulesets/resource-clash';\nexport type { IResourceClashEvent } from '~/rulesets/resource-clash/events';\nexport type {\n  IEnemyMemory, IFaction, IFactionResources, IOutpost,\n  IResourceClashAction, IResourceClashState, IResourceClashView,\n  ISourceType, IUnit, IUnitType, IVisibleSource,\n} from '~/rulesets/resource-clash/types';\n```\n\nOnly export from `src/index.ts`. Do not import internal modules directly from consuming packages.\n\n---\n\n## Coding Style\n\n- Language: TypeScript strict. Prefer async/await; avoid `void` return annotations.\n- Interfaces and types: MUST be prefixed with `I` (e.g., `IUnit`, `IFaction`).\n- File naming: kebab-case only (`legal-actions.ts`, not `legalActions.ts`).\n- Tests: co-locate as `*.test.ts` alongside source; exercise public APIs; no network fixtures.\n- Class methods: arrow functions unless prototype method is required (no MobX stores in this package).\n\n---\n\n## Adding a New Ruleset\n\n1. Create `src/rulesets/<name>/` with: `index.ts`, `constants.ts`, `types.ts`, `initial-state.ts`, `legal-actions.ts`, `reducer.ts`, `vision.ts`, `scoring.ts`, `events.ts`.\n2. Implement `IGameRuleset<TState, TAction, TView, TEvent>` in `index.ts`.\n3. Export the ruleset object and all public types from `src/index.ts`.\n4. Do not import from other rulesets.\n5. Run `pnpm run check:dep` to verify the dependency graph remains clean.\n","readmeFilename":"README.md","_rev":"1-e7eac6d068ef7c33c1dc2c373fd1ed08"}