From 290259bac25dd3ede1bbaa92366d6e9ae96d366b Mon Sep 17 00:00:00 2001 From: Quintine <15965533+Quintine@users.noreply.github.com> Date: Thu, 10 Sep 2026 12:16:39 +1000 Subject: [PATCH] Implement five CHOAM Nexus Cunning effects with private costs and recovery --- components/choam-baliset.tsx | 33 ++-- components/choam-gamont.tsx | 25 +-- components/choam-power-cost.tsx | 70 ++++++++ components/choam-storm.tsx | 23 +-- components/choam-worthless.tsx | 58 ++++--- components/nexus-cards.tsx | 4 +- docs/COMPONENT_INVENTORY.md | 2 + docs/IMPLEMENTATION_STATUS.md | 2 + docs/NEXUS_CHOAM_RULES.md | 76 ++++++++ docs/NEXUS_CHOAM_RUNTIME.md | 43 +++++ game/bots.ts | 62 +++---- game/choam-power-options.ts | 128 ++++++++++++++ game/engine.ts | 145 ++++++++++++++-- game/nexus-choam.ts | 152 ++++++++++++++++ game/reference.ts | 14 +- tests/fixture-nexus-choam.ts | 228 ++++++++++++++++++++++++ tests/fixture-nexus-traitors.ts | 10 +- tests/nexus-card-controls.test.ts | 4 +- tests/nexus-choam-audit.test.ts | 55 ++++++ tests/nexus-choam-bots.test.ts | 74 ++++++++ tests/nexus-choam-controls.test.ts | 142 +++++++++++++++ tests/nexus-choam-engine.test.ts | 269 +++++++++++++++++++++++++++++ tests/nexus-choam-recovery.test.ts | 229 ++++++++++++++++++++++++ tests/nexus-choam.test.ts | 218 +++++++++++++++++++++++ 24 files changed, 1935 insertions(+), 131 deletions(-) create mode 100644 components/choam-power-cost.tsx create mode 100644 docs/NEXUS_CHOAM_RULES.md create mode 100644 docs/NEXUS_CHOAM_RUNTIME.md create mode 100644 game/choam-power-options.ts create mode 100644 game/nexus-choam.ts create mode 100644 tests/fixture-nexus-choam.ts create mode 100644 tests/nexus-choam-audit.test.ts create mode 100644 tests/nexus-choam-bots.test.ts create mode 100644 tests/nexus-choam-controls.test.ts create mode 100644 tests/nexus-choam-engine.test.ts create mode 100644 tests/nexus-choam-recovery.test.ts create mode 100644 tests/nexus-choam.test.ts diff --git a/components/choam-baliset.tsx b/components/choam-baliset.tsx index a0370731..162a505d 100644 --- a/components/choam-baliset.tsx +++ b/components/choam-baliset.tsx @@ -1,5 +1,7 @@ 'use client'; import { useState } from 'react'; +import { ChoamPowerCost, useChoamPower } from './choam-power-cost'; +import { choamPowerAction } from '@/game/choam-power-options'; import { Button } from '@/components/ui/button'; import { HelpTip } from './help-tip'; import type { Action, GameView } from '@/game/engine'; @@ -14,12 +16,12 @@ export function ChoamBaliset({ act: (a: Action) => void; busy: boolean; }) { + const power = useChoamPower(g, 'baliset'); const [target, setTarget] = useState(''); const [territory, setTerritory] = useState(''); if (!g.choamWorthless || g.phase !== 5) return null; const d = g.decision?.kind === 'choamMovement' ? g.decision : null; - const card = g.choamWorthless.cards.find((c) => c.name === 'Baliset'); - if (!card && !d) return null; + if (!power.play && !d) return null; const players = g.players.filter((p) => p.faction !== 'choam'); const selectedPlayer = d?.mover ?? players.find((p) => p.id === target)?.id ?? players[0]?.id; @@ -50,7 +52,7 @@ export function ChoamBaliset({ forces have moved yet.

) : ( - card && ( + power.play && ( <> - {card ? ( + + {power.play ? ( diff --git a/components/choam-worthless.tsx b/components/choam-worthless.tsx index 14d99b07..727d3c51 100644 --- a/components/choam-worthless.tsx +++ b/components/choam-worthless.tsx @@ -1,5 +1,7 @@ 'use client'; import { useState } from 'react'; +import { ChoamPowerCost, useChoamPower } from './choam-power-cost'; +import { choamPowerAction, choamPowerPlays } from '@/game/choam-power-options'; import { HelpTip } from './help-tip'; import { Button } from '@/components/ui/button'; import type { Action, GameView } from '@/game/engine'; @@ -13,7 +15,7 @@ export function ChoamWorthless({ busy: boolean; }) { const [target, setTarget] = useState(''); - const me = g.players.find((p) => p.id === g.me)!; + const power = useChoamPower(g, g.phase === 4 ? 'laLaLa' : 'kulon'); const reactive = g.decision?.kind === 'choamFreeRevival'; if (!g.choamWorthless || ![4, 5].includes(g.phase)) return null; const selected = g.choamWorthless.targets.includes(target) @@ -53,32 +55,34 @@ export function ChoamWorthless({ force revival.

)} - {g.choamWorthless.cards - .filter((c) => - g.phase === 4 ? c.name === 'La La La' : c.name === 'Kulon', - ) - .map((c) => ( - - ))} + + {power.play && ( + + )} + {choamPowerPlays(g, 'kull').some( + (play) => play.source === 'nexus' && play.effect === 'kull', + ) && ( +

+ Kull Wahad’s Karama prevention effect is not implemented. CHOAM + Cunning cannot use it yet. +

+ )} {reactive && (