npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@amas.nghia/todaycode-farmwars

v0.3.1

Published

Farm Wars rulebook — implements the todaycode-game-engine Engine contract; the single source of Farm Wars rules for todaycode-be and CodeClash-fe.

Readme

todaycode-farmwars

Quyển luật duy nhất của Farm Wars — game đấu bot 1v1 (trồng cây, thu hoạch, phá hoại trên grid 12×9, 200 turn) — implement Engine contract của todaycode-game-engine. Luật tính ở hai nơi (browser chạy thử + server trận chính thức) và phải ra kết quả giống nhau bit-for-bit, nên viết đúng một lần ở đây.

Chỉ chứa luật thuần: không chạy bot, không render, không I/O, không Math.random().

Bạn cần gì? Nhảy thẳng tới đó

| Bạn là | Đọc | |---|---| | Dev tích hợp (gọi luật từ app) | Dùng thế nào | | Dev sửa luật / thêm cơ chế | Sửa luật thế nào + bảng module | | Người viết bot | Contract với bot | | Game designer đổi chỉ số | Config — không cần đọc code |

Module nào làm gì — cho ai

| Module | Làm gì | Ai đang dùng thật | |---|---|---| | 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 | | 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 | | initial-state.ts | createInitialState(seed, config) — state turn-0 | Cả hai simulator | | 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 | | 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 | | 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 | | replay.ts | Reducer applyFrame(state, events) — phát lại trận đã tính, không mutate | FE trang /replays/[id]/watch |

Dùng thế nào

1. Chạy một trận từ đầu đến cuối

import { createInitialState, applyTick, defaultConfig, type Actions } from 'todaycode-farmwars'

const config = defaultConfig()
let state = createInitialState(/* seed */ 42, config)

for (let turn = 1; turn <= config.maxTicks; turn++) {
  // Actions của mỗi đội cho turn này — thực tế lấy từ bot (xem contract dưới);
  // ở đây hardcode để minh hoạ. baseSetup chỉ có tác dụng ở turn 1.
  const a1: Actions = { commands: ['right', 'right', 'stay'], giftChoice: 0,
                        ...(turn === 1 ? { baseSetup: [1, 1, 2] as [number,number,number] } : {}) }
  const a2: Actions = { commands: ['stay', 'stay', 'stay'], giftChoice: 0 }

  // 1 turn = 3 frame (actor speed 1/2/3 di chuyển ở các frame khác nhau).
  // applyTick MUTATE state truyền vào — muốn giữ frame bất biến cho scrubber
  // thì structuredClone trước (FE làm vậy trong engine/run-turn.ts).
  for (let f = 0; f < 3; f++) {
    const { events } = applyTick(state, (turn - 1) * 3 + f, a1, a2)
    // events → đẩy cho renderer / battle log / stats
  }
}
console.log(state.team1.score, state.team2.score)

Cùng (seed, config, chuỗi Actions) → cùng kết quả trên mọi máy. Bản chạy được đầy đủ (kèm bot Python thật): CodeClash-fe/scripts/farm-wars-headless.mjs.

2. Đổi chỉ số không cần release

import { resolveConfig, createInitialState } from 'todaycode-farmwars'

// overrides lấy từ DB (app_config row `farmwars.config`) hoặc file JSON.
// Sai kiểu/thiếu field → tự fallback default, không bao giờ NaN.
const config = resolveConfig({ treeMaxHp: 3, actorStats: { worm: { atk: 4 } } })
const state = createInitialState(42, config)

Qua contract generic thì nhét vào level: engine.init({ ...level, definition: { config: overrides } }, seed). Bắt buộc: lưu snapshot config vào match/replay — replay phát lại bằng config của NÓ, không phải config mới nhất.

3. Phát lại trận đã tính (trang xem replay)

import { applyFrame, type FarmWarsState } from 'todaycode-farmwars'

let state: FarmWarsState = resultData.keyframe
for (const f of resultData.frames) {
  state = applyFrame(state, f.events) // trả state MỚI, không mô phỏng lại luật
}

Contract với bot

Mỗi turn bot nhận wire state (JSON) qua stdin và in ra stdout:

<lệnh actor 0: planter>      up | down | left | right | stay | attack <x> <y>
<lệnh actor 1: harvester>
<lệnh actor 2: worm>
gift <0-3>                   luôn có; 0 = bỏ qua
base <i> <j> <k>             CHỈ turn 1 — chọn base cho từng actor (được đứng chung)

Lệnh sai/thiếu tự thành stay. Wire state: my_team (đủ thông tin) + enemy_team (giấu inventory harvester) + grid {width, height, trees} + gifts. Luật đáng nhớ: attack tầm Manhattan ≤ 1, cooldown 1 tick; worm cắn được mọi cây địch (quả hay không); planter tự trồng khi đứng ngoài base; harvester tự hái cây mình có quả; deposit khi về base.

Sửa luật thế nào

| Muốn | Sửa ở đâu | Sau đó | |---|---|---| | Đổ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 | | 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ẽ) | | Đổ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 | | Đổ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) |

Release checklist (mọi thay đổi src/): pnpm lint && pnpm test → pnpm build → commit cả dist/ → tag v* → CI tự publish GitHub Packages → bump version ở todaycode-be + CodeClash-fe. Dist lệch src từng gây crash thật ở FE (v0.1.1, lỗi "reading '0'") — đừng bỏ bước build.

Cài & phát triển

"todaycode-farmwars": "npm:@amas-nghia/todaycode-farmwars@^0.2.1",
"todaycode-game-engine": "npm:@amas-nghia/todaycode-game-engine@^0.2.1"

Cần GITHUB_TOKEN (read:packages) + .npmrc scope @amas-nghia.

pnpm install && pnpm test && pnpm build && pnpm lint