# Canopy Refactor And Rules Expansion Plan ## Goals - Convert the game from JavaScript to TypeScript. - Refactor the single-file implementation into small rules and UI modules. - Fix the turn-order leverage problem with a public initiative seat draft. - Add a public weather/effect card draft-ban phase to break symmetry. - Add an alternate per-turn income mode with extra positional costs for balance. ## Recommended Foundation - Use plain TypeScript with typed domain models and pure rules modules. - Do not migrate to a board-game framework during this feature pass. - Keep rendering simple and browser-native for now. - Treat the game as a deterministic rules engine with DOM rendering layered on top. Why this approach: - The game is turn-based and rules-heavy, not entity-heavy. - ECS-style libraries are a poor fit for this problem. - `boardgame.io` is the main browser board-game framework, but it would expand scope too much for this refactor. - If formal phase handling becomes painful later, `XState` is the best follow-up addition. ## Card System Goals The weather/effect cards should: - be readable from a TV screen - be resolved with perfect information - create asymmetric incentives each round - be strong enough to disrupt mirror play - avoid too much arithmetic or long text - mostly modify incentives, not replace the core game ## Card Market Structure Recommended round flow: 1. Determine round initiative order. 2. Reveal `playerCount + 2` public cards. 3. In initiative order, each player takes exactly one action: - draft one card for the round, or - ban one card from the row 4. Drafted cards become active global rules for the round. 5. Banned cards are removed. 6. Undrafted cards are discarded. Recommended limits: - Start with 8 to 14 total cards. - Keep active cards per round low, ideally 1 to 3. - Prefer cards with one sentence and one number. ## Candidate Weather / Effect Cards These are written to fit a large shared screen. The title should be the main thing players read; the rule text should stay short. ### Economy / Position Cards - **Leaf Surge**: Each leaf gains `+1` energy. - **Branching Season**: Each branch beyond your first gives `+1` energy. - **West Light**: Left third columns give `+1` energy. - **East Light**: Right third columns give `+1` energy. - **High Noon**: Center third columns give `+1` energy. - **Sun Ladder**: Straight-up growth costs `-1` once per turn. - **Storehouse**: Banking is enabled this round. - **Shade Cloth**: Your covered nodes still count for `1` energy. ### Conflict / Interaction Cards - **Sap Theft**: When your branch crosses an opponent branch, gain `+1` energy. - **Hedge Trimmer**: Branches above the lowest player height are cut down before scoring. - **Disease Sweep**: Before each turn, that player loses `1` energy. - **Stalemate**: Contested columns give no energy. - **Split Light**: Contested columns give half energy to each tied player. - **Shared Light**: Contested columns give full energy to every tied player. ### Additional Cards Worth Considering - **Root Pulse**: Root shifts cost `0` this round. - **Still Soil**: Root shifts are disabled this round. - **Thin Air**: Diagonal growth costs `+1`. - **Tailwind**: Diagonal growth costs `-1` once per turn. - **Canopy Tax**: The tallest branch of each player costs `+1` to extend. - **Undergrowth**: The first growth each turn costs `0`. - **Dry Season**: Banking loses `1` energy when used. - **Tall Reward**: Your tallest leaf gains `+2` energy. - **Wide Reward**: If you control the most columns, gain `+2` energy. - **Twig Bloom**: New leaves created this round gain `+1` energy when scored. - **Heavy Shade**: Columns with 2 or more players present count as contested. - **Sunbreak**: Empty columns next to your branch give `+1` energy. ## Best Initial Card Set For the first implementation, avoid the cards that require the most new geometry or crossing logic. Start with cards that reuse existing board evaluation. ### Phase 1 card set - **Leaf Surge**: Each leaf gains `+1` energy. - **Branching Season**: Each branch beyond your first gives `+1` energy. - **West Light**: Left third columns give `+1` energy. - **East Light**: Right third columns give `+1` energy. - **High Noon**: Center third columns give `+1` energy. - **Sun Ladder**: Straight-up growth costs `-1` once per turn. - **Storehouse**: Banking is enabled this round. - **Stalemate**: Contested columns give no energy. - **Split Light**: Contested columns give half energy to each tied player. - **Shared Light**: Contested columns give full energy to every tied player. ### Phase 2 card set Add later after the rules core is stable: - **Shade Cloth** - **Sap Theft** - **Hedge Trimmer** - **Disease Sweep** - **Root Pulse** - **Still Soil** Why split it this way: - Phase 1 mostly changes scoring and move costs. - Phase 2 needs new concepts like coverage protection, branch crossing checks, pruning, or per-turn drain. ## Rules Clarifications To Lock Down ### Initiative seat draft - Players use a pure seat-selection token, not growth bidding. - Players choose specific seat numbers. - Seats are drafted publicly in bidding order, so no seat conflict resolution is needed. - Bidding order is determined by setup rule: - rotating from a random starting anchor, or - lowest lifetime growth income first ### Lifetime growth income Track all growth ever gained. This should include: - starting growth at game start - round-end sunlight income - per-turn sunlight income - seat bonus growth - card-generated growth - random event growth like sunbeam bonuses - recovered or protected growth if the game records that as gain This should not double-count: - banked energy carried into a later round after it was already counted when first gained Implementation note: - Add a dedicated `lifetimeGrowthIncome` stat to each player. - Update it only at the moment growth is awarded. ### Banking Current open issue: - Banking should likely become conditional on a round card rather than always-on. Recommended rule: - By default, banking is disabled. - `Storehouse` enables banking for that round. Why: - This helps reduce the economy snowball. - It gives the weather deck a meaningful strategic lever. ### Contested columns The current game reads column ownership by the top intercepting node. Weather cards that talk about contested columns require a clear definition. Recommended definition: - A column is contested if 2 or more players have a node in that column and they are tied under the active contest rule. Open design task: - Decide whether contest is based on equal top row, any shared presence, or special card-defined presence. Recommended default: - Contest means two or more players share the best topmost position for that column. This keeps the rule closest to current sunlight logic. ### Leaves and branches These terms need firm technical definitions before implementation. Recommended definitions: - `leaf`: a node with no children - `branch`: a root-to-leaf path, or more simply for scoring, each leaf represents one branch Recommended implementation simplification: - Use `leaf count` instead of true branch count in the first pass. - Rename card text if needed to match the actual rule. Example: - Instead of `Branching Season: Each branch beyond your first gives +1 energy`, consider `Leaf Burst: Each leaf after your first gives +1 energy`. This is much easier to explain and compute. ### Branch crossing `Sap Theft` needs a precise crossing rule. Risk: - The current data model stores branch segments, but crossing detection between diagonal edges is new logic. Recommendation: - Do not include crossing-based cards in phase 1. - If added later, count only actual geometric edge intersections between different players' segments. ### Hedge Trimmer This card is flavorful but potentially destructive and swingy. Risks: - Requires pruning nodes and edges from the board. - Can invalidate pending move assumptions. - Needs a clear “same level” definition. Recommendation: - Delay to phase 2. - Define it as: `Before scoring, all nodes above the shortest tallest-player height are removed.` - Only apply between turns or at round end, never mid-turn. ### Disease Sweep This can be implemented in more than one way. Choices: - lose growth before each turn - lose sunlight before end-of-round tally - kill twigs as the current disease event does Recommendation: - For a card, use the simple economy version: `Before each turn, that player loses 1 energy.` - Keep the existing random disease event separate. ## Implementation Risks ### Player identity and turn order are currently coupled Current risk: - `player.id` is treated as both a stable identity and an array index. Plan: - Keep `players` in stable id order. - Introduce `turnOrder: PlayerId[]` for seats and active order. - Update turn flow and render helpers to use `turnOrder` where needed. ### The game needs explicit phases Current risk: - The code uses ad hoc flow control plus animation locks. Plan: - Add a typed phase model: - `initiative_bid` - `weather_draft` - `turn` - `round_end` - `game_over` ### The code is currently one large file Current risk: - Rules, state, rendering, and DOM events are tightly mixed. Plan: - Convert to TypeScript first. - Extract pure rules before feature work. ### Per-turn income changes the economy deeply Current risk: - Existing logic assumes income is only awarded between rounds. Plan: - Keep `round_end` income as the stable default. - Add `per_turn` as an alternate rules path after the initiative and weather phases are working. - Add positional surcharges to slow runaway chaining. ### TV readability Current risk: - Too many active modifiers will be hard to track. Plan: - Cap active cards per round. - Keep card titles short. - Show active effects in one compact panel with icons or one-line summaries. ### Banking may conflict with weather cards Current risk: - If banking is always available and also appears on a card, the card has no value. Plan: - Make banking conditional on round effects, or convert the card into a banking bonus instead of availability. ### Randomness vs strategy Current risk: - Too much randomness in the weather row can obscure good play. Plan: - Start with a small curated deck. - Reveal a modest row each round. - Consider a rotating deck instead of full shuffle if needed after testing. ## Proposed TypeScript Module Structure - `src/main.ts` - `src/constants.ts` - `src/state/types.ts` - `src/state/createGame.ts` - `src/state/reducer.ts` - `src/rules/board.ts` - `src/rules/moves.ts` - `src/rules/scoring.ts` - `src/rules/initiative.ts` - `src/rules/weather.ts` - `src/rules/turnFlow.ts` - `src/ui/renderBoard.ts` - `src/ui/renderSidebar.ts` - `src/ui/renderScoreboard.ts` - `src/ui/renderModals.ts` - `src/app/events.ts` ## Proposed Type Model Core types to introduce early: - `PlayerId` - `NodeKey` - `Setup` - `GameState` - `Player` - `Phase` - `TurnMove` - `WeatherCardId` - `WeatherCardDefinition` - `ActiveRoundEffect` New setup fields: - `initiativeMode: "fixed" | "bid"` - `biddingOrderRule: "rotating" | "lowest_growth_income"` - `incomeTiming: "round_end" | "per_turn"` - `weatherDraftEnabled: boolean` New player fields: - `lifetimeGrowthIncome: number` New game state fields: - `phase` - `turnOrder` - `seatChoices` - `initiativeAnchorPlayerId` - `weatherRow` - `draftedWeather` - `bannedWeather` - `activeRoundEffects` ## Execution Order ### Milestone 1: TypeScript conversion and safe refactor 1. Add TypeScript and config. 2. Convert `src/main.js` to `src/main.ts` without changing behavior. 3. Extract constants and types. 4. Extract pure board, move, and scoring helpers. 5. Keep current gameplay working exactly as-is. ### Milestone 2: Turn-order refactor 1. Separate stable player identity from seat order. 2. Introduce `turnOrder`. 3. Update active-player logic to walk `turnOrder`. 4. Add explicit `phase` handling. ### Milestone 3: Initiative draft phase 1. Add setup controls for initiative mode and bidding-order rule. 2. Add lifetime growth income tracking. 3. Implement rotating bid order. 4. Implement lowest-growth-income bid order. 5. Add public seat selection modal. 6. Apply seat growth bonuses. ### Milestone 4: Weather draft-ban phase 1. Define weather card data in `rules/weather.ts`. 2. Add setup toggle for weather phase. 3. Implement weather row generation. 4. Implement draft-or-ban action in seat order. 5. Render active effects clearly. 6. Apply phase 1 weather cards in scoring and move cost logic. ### Milestone 5: Per-turn income mode 1. Add setup control for income timing. 2. Implement payout to the active player immediately after their turn. 3. Keep round-end mode intact. 4. Verify banking and pass logic still make sense. ### Milestone 6: Distance-based balancing 1. Add root-distance surcharge support. 2. Start with a simple formula such as: - `extraCost = floor((abs(dx) + abs(dy)) / 4)` 3. Apply only in `per_turn` mode at first. 4. Tune after playtesting. ### Milestone 7: Phase 2 weather cards 1. Add crossing-based cards if still desired. 2. Add hedge-trimming/pruning cards. 3. Add disease drain cards. 4. Rebalance text and values based on playtests. ## Testing Plan Add rule-level tests as soon as the core logic is modularized. Priority coverage: - legal growth moves - root shift legality - scoring by column - contested column resolution modes - seat draft ordering - lifetime growth income tracking - weather draft resolution - weather effect application - per-turn payout behavior - distance surcharge cost calculation ## Recommended First Shipping Configuration - initiative mode: `bid` - bidding order rule: `rotating` - income timing: `round_end` - weather draft: `enabled` - banking: enabled only through `Storehouse` - weather deck: only phase 1 cards Why this first: - It directly attacks the symmetry problem. - It fixes turn-order leverage without also changing all economy timing at once. - It keeps the new concepts readable for group play. ## Summary The most important design shift is this: - turn order becomes a public seat draft - each round gains a public asymmetric card market - mirror play is no longer automatically safe because players can deny and reshape incentives before growth begins The best implementation strategy is: - TypeScript first - modular rules second - initiative phase third - weather card system fourth - per-turn economy mode after the game flow is stable