{"_id":"@capylabs-io/todaycode-energy-game","_rev":"5-de944aaa3012f5b7a7c27a4eee0be0b2","name":"@capylabs-io/todaycode-energy-game","dist-tags":{"latest":"0.7.1"},"versions":{"0.1.0":{"name":"@capylabs-io/todaycode-energy-game","version":"0.1.0","keywords":["game-engine","deterministic","codeclash","energy-game"],"license":"UNLICENSED","_id":"@capylabs-io/todaycode-energy-game@0.1.0","maintainers":[{"name":"vuvanhieu","email":"learncnnt@gmail.com"}],"dist":{"shasum":"3451079d509df97929447e040de527201e13530e","tarball":"https://registry.npmjs.org/@capylabs-io/todaycode-energy-game/-/todaycode-energy-game-0.1.0.tgz","fileCount":8,"integrity":"sha512-FoStjKM/JNZRGVt6usdN9mZJT3qZG+mlWY+RreCVwEys6/E9OUxbE4/0zS7fyXu1Qly+qeQDZPOSq1S6wocdYw==","signatures":[{"sig":"MEUCIQDBRKF1GHGy3dl9ts57qcTYqRaRHOzmRxrJg5g1mRHddQIgPrv2PkyHCZdJViyqNGMuLu5R8ZkwpJMjFUP7lxA1bW0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":272123},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"lint":"tsc --noEmit","test":"jest","build":"tsup"},"_npmUser":{"name":"vuvanhieu","email":"learncnnt@gmail.com"},"_npmVersion":"11.4.2","description":"Deterministic engine for the 2-player Energy Game (CodeClash). Pure computation: no I/O, no sandbox, no match loop.","directories":{},"sideEffects":false,"_nodeVersion":"24.4.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","tsup":"^8.0.2","ts-jest":"^29.1.2","typescript":"^5.4.5","@types/jest":"^29.5.12","@capylabs-io/todaycode-base-game-engine":"file:../todaycode-base-game-engine"},"_npmOperationalInternal":{"tmp":"tmp/todaycode-energy-game_0.1.0_1785142913246_0.46899410004636133","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@capylabs-io/todaycode-energy-game","version":"0.4.0","keywords":["game-engine","deterministic","codeclash","energy-game"],"license":"UNLICENSED","_id":"@capylabs-io/todaycode-energy-game@0.4.0","maintainers":[{"name":"vuvanhieu","email":"learncnnt@gmail.com"}],"dist":{"shasum":"9622051b877871e31d1421bab147c363eadc8462","tarball":"https://registry.npmjs.org/@capylabs-io/todaycode-energy-game/-/todaycode-energy-game-0.4.0.tgz","fileCount":8,"integrity":"sha512-RvRxRsfFc3FWg4lAC3yffmSVAtMc2a+5bKgzCuwK/2fzMf3IfYgN3bYcWegiyTrcT4M/5NkZShJ0vO9bFlKdAA==","signatures":[{"sig":"MEYCIQCR1wcablpybeblzMMdmg9osBIN7sy/8sfgxZb1YOPq9gIhALSosDzZxg/J776LZ/m6/TBww6bfpEDZpxEQ34mQRzQR","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":277902},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"bd7d1b94ef8aa286114526b910dd0066fd38267c","scripts":{"lint":"tsc --noEmit","test":"jest","build":"tsup"},"_npmUser":{"name":"vuvanhieu","email":"learncnnt@gmail.com"},"_npmVersion":"11.4.2","description":"Deterministic engine for the 2-player Energy Game (CodeClash). Pure computation: no I/O, no sandbox, no match loop.","directories":{},"sideEffects":false,"_nodeVersion":"24.4.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","tsup":"^8.0.2","ts-jest":"^29.1.2","typescript":"^5.4.5","@types/jest":"^29.5.12","@capylabs-io/todaycode-base-game-engine":"file:../todaycode-base-game-engine"},"_npmOperationalInternal":{"tmp":"tmp/todaycode-energy-game_0.4.0_1785396564321_0.2054023424775422","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@capylabs-io/todaycode-energy-game","version":"0.5.0","keywords":["game-engine","deterministic","codeclash","energy-game"],"license":"UNLICENSED","_id":"@capylabs-io/todaycode-energy-game@0.5.0","maintainers":[{"name":"vuvanhieu","email":"learncnnt@gmail.com"}],"dist":{"shasum":"832c85fba67ac01a3278087f49a4378149d5f2f6","tarball":"https://registry.npmjs.org/@capylabs-io/todaycode-energy-game/-/todaycode-energy-game-0.5.0.tgz","fileCount":8,"integrity":"sha512-cBgRtSbtC0mvaFlLQr8wFX1NoqxmaGxp7cbv7xal8JjtNgn4j0gTaQDDLnz9YR5WOR8DYr7jrYxfxcTJ5azRKg==","signatures":[{"sig":"MEQCIG+efj43wLEaPYFcbIhtlMK9+46hKtp+gcIOVPljE6JeAiByrwxptXse/Bkholxe8jalz+IdigDYMaelELPaeySCbQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":278390},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"bd7d1b94ef8aa286114526b910dd0066fd38267c","scripts":{"lint":"tsc --noEmit","test":"jest","build":"tsup"},"_npmUser":{"name":"vuvanhieu","email":"learncnnt@gmail.com"},"_npmVersion":"11.4.2","description":"Deterministic engine for the 2-player Energy Game (CodeClash). Pure computation: no I/O, no sandbox, no match loop.","directories":{},"sideEffects":false,"_nodeVersion":"24.4.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","tsup":"^8.0.2","ts-jest":"^29.1.2","typescript":"^5.4.5","@types/jest":"^29.5.12","@capylabs-io/todaycode-base-game-engine":"file:../todaycode-base-game-engine"},"_npmOperationalInternal":{"tmp":"tmp/todaycode-energy-game_0.5.0_1786077378945_0.1926728149420216","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"@capylabs-io/todaycode-energy-game","version":"0.6.0","keywords":["game-engine","deterministic","codeclash","energy-game"],"license":"UNLICENSED","_id":"@capylabs-io/todaycode-energy-game@0.6.0","maintainers":[{"name":"vuvanhieu","email":"learncnnt@gmail.com"}],"dist":{"shasum":"dec6f75e74ca3300d5202e05ed599535396a0fea","tarball":"https://registry.npmjs.org/@capylabs-io/todaycode-energy-game/-/todaycode-energy-game-0.6.0.tgz","fileCount":8,"integrity":"sha512-yLp3Wv54O3fsqc4DPPHGLRQ3F57CjuvdxJyz0KrorFC5XEZr35pfhX1dRQdFIIeWJK7f9JxOaz2eHeub5axncA==","signatures":[{"sig":"MEQCIERbS0tFAtwTH2tP5FMxed22UF6g+Ukq61YWSkLEK8+FAiAYu0uNS45V/QWr7skCp5Tnbf124Y8dTyvQYZYsbHaMwg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":346303},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"9aa1c0ffb7e60b3e406628e1662e2188beb81412","scripts":{"lint":"tsc --noEmit","test":"jest","build":"tsup"},"_npmUser":{"name":"vuvanhieu","email":"learncnnt@gmail.com"},"_npmVersion":"11.4.2","description":"Deterministic engine for the 2-player Energy Game (CodeClash). Pure computation: no I/O, no sandbox, no match loop.","directories":{},"sideEffects":false,"_nodeVersion":"24.4.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","tsup":"^8.0.2","ts-jest":"^29.1.2","typescript":"^5.4.5","@types/jest":"^29.5.12","@capylabs-io/todaycode-base-game-engine":"file:../todaycode-base-game-engine"},"_npmOperationalInternal":{"tmp":"tmp/todaycode-energy-game_0.6.0_1786975610555_0.0618962751789911","host":"s3://npm-registry-packages-npm-production"}},"0.7.1":{"name":"@capylabs-io/todaycode-energy-game","version":"0.7.1","description":"Deterministic engine for the 2-player Energy Game (CodeClash). Pure computation: no I/O, no sandbox, no match loop.","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"sideEffects":false,"publishConfig":{"access":"public"},"scripts":{"build":"tsup","test":"jest","lint":"tsc --noEmit"},"keywords":["game-engine","deterministic","codeclash","energy-game"],"license":"UNLICENSED","devDependencies":{"@capylabs-io/todaycode-base-game-engine":"file:../todaycode-base-game-engine","@types/jest":"^29.5.12","jest":"^29.7.0","ts-jest":"^29.1.2","tsup":"^8.0.2","typescript":"^5.4.5"},"_id":"@capylabs-io/todaycode-energy-game@0.7.1","gitHead":"9aa1c0ffb7e60b3e406628e1662e2188beb81412","_nodeVersion":"24.4.1","_npmVersion":"11.4.2","dist":{"integrity":"sha512-mdHlBFfX6OsCvq3gWKmwAjOZ6sfydjmhWl/sX2/jsu2jUpkqfjSrby6TYvCRbWapSbbf2c/U1LbF4BpMZvTDIQ==","shasum":"d77583b2b97226e789368d053e71247fadf2eaad","tarball":"https://registry.npmjs.org/@capylabs-io/todaycode-energy-game/-/todaycode-energy-game-0.7.1.tgz","fileCount":8,"unpackedSize":544757,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDB6Jf3IjX2ZP/dByUaybe3nZiVQJCw2kDwx8O+ABbQ0QIgXCfUtg1WdMySR4Ow5ADe3zL87w4ZT0RcbbqqHYuAoB8="}]},"_npmUser":{"name":"vuvanhieu","email":"learncnnt@gmail.com"},"directories":{},"maintainers":[{"name":"vuvanhieu","email":"learncnnt@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/todaycode-energy-game_0.7.1_1787219100330_0.04477744630955294"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-27T09:01:52.942Z","modified":"2026-08-20T09:45:00.628Z","0.1.0":"2026-07-27T09:01:53.381Z","0.4.0":"2026-07-30T07:29:24.537Z","0.5.0":"2026-08-07T04:36:19.090Z","0.6.0":"2026-08-17T14:06:50.702Z","0.7.1":"2026-08-20T09:45:00.462Z"},"license":"UNLICENSED","keywords":["game-engine","deterministic","codeclash","energy-game"],"description":"Deterministic engine for the 2-player Energy Game (CodeClash). Pure computation: no I/O, no sandbox, no match loop.","maintainers":[{"name":"vuvanhieu","email":"learncnnt@gmail.com"}],"readme":"# @capylabs-io/todaycode-energy-game\n\nEngine deterministic cho **Energy Game** — game đối kháng 2 người, 300 lượt, mỗi bên nộp code điều\nkhiển 1 robot. Engine **chỉ tính toán**: nhận state + action của 2 bên → trả state mới + events + điểm.\n\n- Luật đầy đủ: [GAME_DESIGN.md](./GAME_DESIGN.md)\n- **Toàn bộ số cân bằng: [src/config.ts](./src/config.ts)** — cân bằng game = chỉ sửa file này.\n\n## Ranh giới trách nhiệm\n\n| Engine (package này) | Backend (consumer) |\n|---|---|\n| Luật, tính toán 1 lượt, events, điểm | Sandbox + chạy code người chơi (Pyodide) |\n| Giao thức bot (`serializeForSide` / `parseActions`) | Vòng lặp 300 lượt, timeout, xử lý bot lỗi |\n| Bản đồ mặc định + validate `MapDef` | Lưu replay, chấm điểm hiển thị, render |\n\nEngine không I/O, không `Date.now()`, không `Math.random()`, không chạy code người dùng.\n\n## Cài đặt\n\n```bash\npnpm add @capylabs-io/todaycode-energy-game\n```\n\n## Dùng ở backend\n\n```ts\nimport {\n  createInitialState,\n  serializeForSide,\n  parseActions,\n  applyTick,\n  getFinalScore,\n  defaultConfig,\n  type EnergyEvent,\n} from '@capylabs-io/todaycode-energy-game';\n\nconst state = createInitialState();          // hoặc createInitialState(config, mapDef)\nconst events: EnergyEvent[] = [];\n\nfor (let frame = 0; frame < defaultConfig.match.totalFrames; frame++) {\n  // 1. state mỗi bên nhìn thấy (JSON string) → nạp vào sandbox của người chơi\n  const inputP1 = serializeForSide(state, 'p1');\n  const inputP2 = serializeForSide(state, 'p2');\n\n  // 2. chạy code người chơi (Pyodide/worker) → lấy stdout\n  const orderP1 = parseActions(await runPlayerCode(codeP1, inputP1));\n  const orderP2 = parseActions(await runPlayerCode(codeP2, inputP2));\n\n  // 3. tiến 1 lượt (applyTick MUTATE state — clone nếu cần snapshot từng lượt)\n  events.push(...applyTick(state, frame, orderP1, orderP2).events);\n}\n\nconst result = getFinalScore(state); // { p1, p2, winner }\n```\n\nNếu code người chơi trả về **object** (vd `dict` từ Python) thay vì stdout, dùng `validateActions(obj)`.\nCả `parseActions` và `validateActions` đều **không bao giờ throw** — input rác ⇒ robot đứng yên.\n\n## Giao thức bot\n\n**Input** (`serializeForSide`) — JSON string, xem type `WireState`. Không có sương mù: 2 bên thấy cùng\nthông tin, chỉ đảo góc nhìn `you` / `opponent`; nhà máy/cây của mình có cờ `mine`, nơi tiêu thụ có\n`occupiedBy: 'me' | 'opponent' | null`. Kèm luôn số liệu (`maxPower`, `powerPerTurn`, …) để bot không\nphải hard-code config.\n\n**Output** — mỗi dòng stdout một lệnh, hoặc object `{ move, actions }`:\n\n```\nMOVE right           # up | down | left | right | (STAY = đứng yên)\nHARVEST              # tối đa config.robot.maxActionsPerFrame action mỗi lượt\nDELIVER\nBUILD_PLANT\nPLANT_TREE\nUPGRADE_PLANT        # nâng nhà máy của mình lên 1 cấp, trả upgradeCost vàng\n```\n\nRobot di chuyển **1 ô kề mỗi lượt**, rồi thực thi các action **tại ô sau khi move**.\n\n## API\n\n| Hàm | Mô tả |\n|---|---|\n| `createInitialState(config?, mapDef?)` | Dựng state đầu trận (validate `mapDef`, throw nếu sai) |\n| `applyTick(state, frameIndex, orderP1, orderP2)` | Tiến 1 lượt, **mutate** `state`, trả `{ events }` |\n| `serializeForSide(state, side)` / `toWireState` | State cho code người chơi (string / object) |\n| `parseActions(lines)` / `validateActions(obj)` | Đọc output của bot, tolerant, không throw |\n| `getFinalScore(state)` | `{ p1, p2, winner }`, `finalScore = gold − carbon × penaltyCoefficient` |\n| `runMatch(bots, config?, mapDef?)` | Chạy trọn trận in-memory — **chỉ dùng cho test/cân bằng** |\n| `defaultConfig`, `defaultMapDef`, `validateMapDef` | Config + bản đồ 10×10 đối xứng 180° |\n\nQuery helper: `findPlantAt`, `findConsumerAt`, `findTreeAt`, `terrainAt`, `samePos`, `countTrees`.\n\n## Determinism\n\nCùng `(config, mapDef, action của 2 bên mỗi lượt)` ⇒ **giống hệt** `events`, `state`, `finalScore` trên\nmọi máy. Cách đảm bảo: không RNG (v1 không có luật nào random), chỉ duyệt mảng thứ tự cố định\n(`SIDES`, `state.plants`, `state.consumers`, `state.trees`), xung đột di chuyển xử lý đối xứng\n(cả 2 cùng mất lượt), thứ tự action giữa 2 bên cố định p1 → p2.\n`src/__tests__/determinism.spec.ts` chạy trọn 1 trận 2 lần và deep-compare.\n\n## Phát triển\n\n```bash\npnpm install\npnpm test              # jest, 14 suite / 114 test\npnpm test -- movement  # 1 file\npnpm lint              # tsc --noEmit (strict)\npnpm build             # tsup → dist (ESM + CJS + .d.ts)\n```\n\nRelease: sửa `src/` → `pnpm build` (**không được bỏ bước này**) → commit cả `src/` và `dist/` →\nbump `version` → `npm publish`.\n\n## Trạng thái cân bằng\n\nToàn bộ số liệu trong `src/config.ts` đã khớp **thể lệ v1.3**\n(`the-le-cuoc-thi-nang-luong.pdf`, Phụ lục A–E) — không còn `// TODO: balance` nào là số giả.\nNhưng **chưa số nào được validate bằng simulation** `runMatch`.\n\nCòn ngỏ: `config.match.tieBreak` vẫn là `'draw'`, trong khi thể lệ Điều 19 chốt **không có hòa** với\n4 tiêu chí phụ; 2 tiêu chí cuối (số dòng mã nguồn, thời điểm nộp) là dữ liệu của backend nên cần\nchốt cách chia việc engine ↔ backend trước khi implement.\n","readmeFilename":"README.md"}