{"_id":"@amas.nghia/todaycode-farmwars","_rev":"2-276bfe893b045a951c58e04166509dee","name":"@amas.nghia/todaycode-farmwars","dist-tags":{"latest":"0.3.1"},"versions":{"0.3.0":{"name":"@amas.nghia/todaycode-farmwars","version":"0.3.0","license":"UNLICENSED","_id":"@amas.nghia/todaycode-farmwars@0.3.0","maintainers":[{"name":"amas.nghia","email":"amasnghia@gmail.com"}],"dist":{"shasum":"04b94c7cc3a867589d25c536614f0d21f7cbb03a","tarball":"https://registry.npmjs.org/@amas.nghia/todaycode-farmwars/-/todaycode-farmwars-0.3.0.tgz","fileCount":8,"integrity":"sha512-00GtYh9uxBIMRmJpIfYtH2u73Y2qU52Ro8XuPc/vQ+Umao4lcOJlozG8K3/VvrrRY145cM6PuUcVWes2oSnFEA==","signatures":[{"sig":"MEYCIQDGMYT9fYA990SSkd4IoRPIDhYVVGAJSAwfqNvOcEWm/QIhAO2Yiqt9o/WcthWiwNZyTku5TE9tPnPpGTb8+ismB+UN","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":233149},"jest":{"preset":"ts-jest","testMatch":["**/__tests__/**/*.spec.ts"],"testEnvironment":"node","moduleFileExtensions":["ts","js","json"]},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.cts","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":"5d955aa7d32ee5f0e16025627700cf450431b9bf","scripts":{"lint":"tsc -b --noEmit","test":"jest","build":"tsup"},"_npmUser":{"name":"amas.nghia","email":"amasnghia@gmail.com"},"_npmVersion":"11.12.1","description":"Farm Wars rulebook — implements the todaycode-game-engine Engine contract; the single source of Farm Wars rules for todaycode-be and CodeClash-fe.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"@amas.nghia/todaycode-game-engine":"^0.2.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.0.0","tsup":"^8.0.0","ts-jest":"^29.0.0","typescript":"^5.6.0","@types/jest":"^30.0.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/todaycode-farmwars_0.3.0_1784029027008_0.28133059403695326","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@amas.nghia/todaycode-farmwars","version":"0.3.1","description":"Farm Wars rulebook — implements the todaycode-game-engine Engine contract; the single source of Farm Wars rules for todaycode-be and CodeClash-fe.","license":"UNLICENSED","publishConfig":{"access":"public"},"type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.cts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"build":"tsup","test":"jest","lint":"tsc -b --noEmit"},"dependencies":{"@amas.nghia/todaycode-game-engine":"^0.2.3"},"devDependencies":{"@types/jest":"^30.0.0","@types/node":"^22.0.0","jest":"^30.0.0","ts-jest":"^29.0.0","tsup":"^8.0.0","typescript":"^5.6.0"},"jest":{"preset":"ts-jest","testEnvironment":"node","testMatch":["**/__tests__/**/*.spec.ts"],"moduleFileExtensions":["ts","js","json"]},"gitHead":"5d955aa7d32ee5f0e16025627700cf450431b9bf","_id":"@amas.nghia/todaycode-farmwars@0.3.1","_nodeVersion":"24.15.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-X75Oy8NqUKOET2dLeHqr+D72mgjILk1T9EP/wR1ZTBqiyg31NPkhb+JmXPNSf43cAGOXq4SpTSX0L1JhIyH21A==","shasum":"76fe62f0b73d112f7d7fd88399833115ba455f00","tarball":"https://registry.npmjs.org/@amas.nghia/todaycode-farmwars/-/todaycode-farmwars-0.3.1.tgz","fileCount":8,"unpackedSize":233149,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIBPum9ZWme3k3RtyEZeTT/mVl7ug+q7FxcPbGZw0GsZOAiBSwYpDDHEXjVUscNOFM7i/gxfgejjoEEhdj/pKVLKlRA=="}]},"_npmUser":{"name":"amas.nghia","email":"amasnghia@gmail.com"},"directories":{},"maintainers":[{"name":"amas.nghia","email":"amasnghia@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/todaycode-farmwars_0.3.1_1784029469979_0.6215728880172475"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-14T11:37:06.828Z","modified":"2026-07-14T11:44:30.210Z","0.3.0":"2026-07-14T11:37:07.182Z","0.3.1":"2026-07-14T11:44:30.112Z"},"license":"UNLICENSED","description":"Farm Wars rulebook — implements the todaycode-game-engine Engine contract; the single source of Farm Wars rules for todaycode-be and CodeClash-fe.","maintainers":[{"name":"amas.nghia","email":"amasnghia@gmail.com"}],"readme":"# todaycode-farmwars\n\n**Quyển luật duy nhất của Farm Wars** — game đấu bot 1v1 (trồng cây, thu hoạch,\nphá hoại trên grid 12×9, 200 turn) — implement `Engine` contract của\n[todaycode-game-engine](https://github.com/amas-nghia/todaycode-game-engine).\nLuật tính ở **hai nơi** (browser chạy thử + server trận chính thức) và phải ra\nkết quả giống nhau bit-for-bit, nên viết đúng một lần ở đây.\n\n**Chỉ chứa luật thuần**: không chạy bot, không render, không I/O, không\n`Math.random()`.\n\n## Bạn cần gì? Nhảy thẳng tới đó\n\n| Bạn là | Đọc |\n|---|---|\n| Dev tích hợp (gọi luật từ app) | [Dùng thế nào](#dùng-thế-nào) |\n| Dev sửa luật / thêm cơ chế | [Sửa luật thế nào](#sửa-luật-thế-nào) + bảng module |\n| Người viết bot | [Contract với bot](#contract-với-bot) |\n| Game designer đổi chỉ số | [Config](#2-đổi-chỉ-số-không-cần-release) — không cần đọc code |\n\n## Module nào làm gì — cho ai\n\n| Module | Làm gì | Ai đang dùng thật |\n|---|---|---|\n| `tick.ts` (+`combat/scoring/gift-rng`) | **Trái tim luật**: `applyTick(state, frame, a1, a2)` — 1 bước mô phỏng (3 frame = 1 turn; di chuyển theo speed, `attack x y` tường minh, cây HP/ra quả, deposit, gift) | FE `match-simulator` + BE `simulator.ts` gọi mỗi frame |\n| `config.ts` | `FarmWarsConfig` + `defaultConfig()` + `resolveConfig(overrides)` — mọi chỉ số balance là **dữ liệu của trận**, validate input rác | App truyền config từ DB/JSON vào mỗi trận |\n| `initial-state.ts` | `createInitialState(seed, config)` — state turn-0 | Cả hai simulator |\n| `farmwars.engine.ts` | `FarmWarsEngine implements Engine` — cổng generic; `init` đọc `level.definition.config` | Hạ tầng nào muốn chạy game không cần biết luật |\n| `orders.ts` / `wire.ts` | Parse stdout bot → `Actions`; serialize state cho bot (giấu inventory harvester địch — tường chống gian lận) | Runner BE; FE có bản cùng semantics |\n| `events.ts` | 20+ loại event (`move`, `targeted_attack`, `tree_damaged`, `tree_destroy`…) — client vẽ animation từ đây, không chạy lại luật | FE `effect-descriptors` + `battle-log`; BE lưu vào replay |\n| `replay.ts` | Reducer `applyFrame(state, events)` — phát lại trận đã tính, không mutate | FE trang `/replays/[id]/watch` |\n\n## Dùng thế nào\n\n### 1. Chạy một trận từ đầu đến cuối\n\n```ts\nimport { createInitialState, applyTick, defaultConfig, type Actions } from 'todaycode-farmwars'\n\nconst config = defaultConfig()\nlet state = createInitialState(/* seed */ 42, config)\n\nfor (let turn = 1; turn <= config.maxTicks; turn++) {\n  // Actions của mỗi đội cho turn này — thực tế lấy từ bot (xem contract dưới);\n  // ở đây hardcode để minh hoạ. baseSetup chỉ có tác dụng ở turn 1.\n  const a1: Actions = { commands: ['right', 'right', 'stay'], giftChoice: 0,\n                        ...(turn === 1 ? { baseSetup: [1, 1, 2] as [number,number,number] } : {}) }\n  const a2: Actions = { commands: ['stay', 'stay', 'stay'], giftChoice: 0 }\n\n  // 1 turn = 3 frame (actor speed 1/2/3 di chuyển ở các frame khác nhau).\n  // applyTick MUTATE state truyền vào — muốn giữ frame bất biến cho scrubber\n  // thì structuredClone trước (FE làm vậy trong engine/run-turn.ts).\n  for (let f = 0; f < 3; f++) {\n    const { events } = applyTick(state, (turn - 1) * 3 + f, a1, a2)\n    // events → đẩy cho renderer / battle log / stats\n  }\n}\nconsole.log(state.team1.score, state.team2.score)\n```\n\nCùng `(seed, config, chuỗi Actions)` → cùng kết quả trên mọi máy. Bản chạy được\nđầy đủ (kèm bot Python thật): `CodeClash-fe/scripts/farm-wars-headless.mjs`.\n\n### 2. Đổi chỉ số không cần release\n\n```ts\nimport { resolveConfig, createInitialState } from 'todaycode-farmwars'\n\n// overrides lấy từ DB (app_config row `farmwars.config`) hoặc file JSON.\n// Sai kiểu/thiếu field → tự fallback default, không bao giờ NaN.\nconst config = resolveConfig({ treeMaxHp: 3, actorStats: { worm: { atk: 4 } } })\nconst state = createInitialState(42, config)\n```\n\nQua contract generic thì nhét vào level: `engine.init({ ...level,\ndefinition: { config: overrides } }, seed)`. **Bắt buộc**: lưu snapshot config\nvào match/replay — replay phát lại bằng config của NÓ, không phải config mới nhất.\n\n### 3. Phát lại trận đã tính (trang xem replay)\n\n```ts\nimport { applyFrame, type FarmWarsState } from 'todaycode-farmwars'\n\nlet state: FarmWarsState = resultData.keyframe\nfor (const f of resultData.frames) {\n  state = applyFrame(state, f.events) // trả state MỚI, không mô phỏng lại luật\n}\n```\n\n## Contract với bot\n\nMỗi turn bot nhận wire state (JSON) qua stdin và in ra stdout:\n\n```\n<lệnh actor 0: planter>      up | down | left | right | stay | attack <x> <y>\n<lệnh actor 1: harvester>\n<lệnh actor 2: worm>\ngift <0-3>                   luôn có; 0 = bỏ qua\nbase <i> <j> <k>             CHỈ turn 1 — chọn base cho từng actor (được đứng chung)\n```\n\nLệnh sai/thiếu tự thành `stay`. Wire state: `my_team` (đủ thông tin) +\n`enemy_team` (giấu inventory harvester) + `grid {width, height, trees}` + `gifts`.\nLuật đáng nhớ: attack tầm Manhattan ≤ 1, cooldown 1 tick; worm cắn được **mọi**\ncây địch (quả hay không); planter tự trồng khi đứng ngoài base; harvester tự hái\ncây mình có quả; deposit khi về base.\n\n## Sửa luật thế nào\n\n| Muốn | Sửa ở đâu | Sau đó |\n|---|---|---|\n| Đổi chỉ số balance (HP, atk, thời gian cây…) | **Không sửa code** — đổi config của trận (mục 2 trên). Đổi *default*: `constants.ts` | test + release |\n| Thêm loại event | `events.ts` (thêm variant) → phát trong `tick.ts` | **Checklist consumer**: FE `effect-descriptors.ts` (hiệu ứng) + `battle-log.ts` (log) + reducer `replay.ts` nếu event làm đổi state — event không ai vẽ = tàng hình (đã có bug thật: `tree_damaged` từng không được vẽ) |\n| Đổi cơ chế (movement, combat, gift…) | `tick.ts` / `combat.ts`… | golden fixtures sẽ đỏ — cập nhật có chủ đích, đừng sửa số cho test xanh |\n| Đổi giao thức bot (thêm lệnh) | `orders.ts` + `wire.ts` | đồng bộ FE: worker parse (`public/farm-wars-pyodide-worker.js`), `helpers.py`, 2 bot mẫu — **lệch harness là bot chết lặng lẽ cả trận** (đã xảy ra) |\n\n**Release checklist** (mọi thay đổi `src/`):\n`pnpm lint && pnpm test` → `pnpm build` → **commit cả `dist/`** → tag `v*` → CI\ntự publish GitHub Packages → bump version ở `todaycode-be` + `CodeClash-fe`.\nDist lệch src từng gây crash thật ở FE (`v0.1.1`, lỗi \"reading '0'\") — đừng bỏ bước build.\n\n## Cài & phát triển\n\n```json\n\"todaycode-farmwars\": \"npm:@amas-nghia/todaycode-farmwars@^0.2.1\",\n\"todaycode-game-engine\": \"npm:@amas-nghia/todaycode-game-engine@^0.2.1\"\n```\n\nCần `GITHUB_TOKEN` (read:packages) + `.npmrc` scope `@amas-nghia`.\n\n```bash\npnpm install && pnpm test && pnpm build && pnpm lint\n```\n","readmeFilename":"README.md"}