Files

3.3 KiB

Super Auto Pets: The Board Game

Go backend (pure rules engine in internal/game, WebSocket rooms in internal/server, computer opponent in internal/ai) + React frontend in web/. See README.md for the full layout and rules summary.

  • mise run test — Go tests · mise run check — go vet + frontend tsc

Seats, sides, and packs

Three distinctions are easy to conflate and worth keeping straight:

  • Seat vs. side. A game seats 2, 4, or 6 players, and each round pairs them off into simultaneous battles (schedule.go). Inside a BattleResult everything is indexed by side — 0 or 1 within that one battle — including BattleEvent.Seat/Target, Lineups, Survivors, and ManaAfter. Seats maps side → seat, and Side(seat) maps back. WinnerSeat is the deliberate exception: it's a real seat, because it's the only field that means anything outside the battle. When adding a per-side field to a battle, index it by side and say so.
  • One battle vs. the round. resolveBattles loops the round's pairings; runBattle(first, second) resolves one of them and mutates no persistent state. Anything that should happen once per round rather than once per battle (clearing per-round banks, say) belongs in resolveBattles, not finalizeBattle.
  • Packs are plural. Game.Packs is a list whose tier decks shuffle together. Anything reading printed card data must go through TierContentsForPacks / CatalogForPacks / g.packList(), never a single pack — a game can hold Trumpets, Mana, and Ailments at once.

Keep the AI in sync with game behavior

Whenever game behavior changes — rules, cards, effects, phases, log entries, or views — make sure the computer opponent accounts for it. The AI plays from the same information a human sees, so changes ripple into it in specific ways:

  • Battle rules: internal/ai evaluates moves by running the real resolver (game.SimulateBattle), so battle changes are picked up automatically — but re-check the hand-written heuristics that summarize battle wisdom: leadScore, keepValue, deckValue (eval.go) and the food-placement strategies / synergy pet list (arrange.go).
  • New or changed cards/effects: shop-time deck effects are mirrored in applyTemplateShopEffects (shop.go); a new shop-time trigger or action must be added there or the bot will misvalue it.
  • New public actions or log changes: the bot tracks the opponent via structured tags on public log entries (LogBuy, LogSell, LogTrade, LogTradePick, spawn counts — see log.go). New public actions need tags plus handling in Observe (memory.go). Tags must only ever duplicate facts the entry's text already states publicly.
  • View changes: the AI decides from game.View only — never hand it the Game. If a field is added to the view, confirm it doesn't leak hidden information (deck order, trade options, shop decks), because the AI (and any client) would legitimately see it.
  • Phase/flow changes: the server's bot driver (internal/server/bots.go) must know when a bot owes an action (ai.Pending) and have a legal fallback (botFallback) for any new phase or forced decision.

After any such change, run go test ./internal/ai/ — it plays complete bot-vs-bot games and fails on any illegal or missing bot action.