Decision: build a browser game with Phaser, TypeScript, and Vite.
Why: the Game Week spec allowed a browser path, Phaser is a focused 2D game framework, and Vite keeps iteration fast. TypeScript made the simulation state and move catalog easier to keep coherent while learning the game stack.
Tradeoff: the spec's browser path calls out Phaser 3 and Socket.io. This project uses Phaser 4 and plain WebSockets via ws.
Decision: put combat rules in BrawlSimulation instead of directly inside Phaser rendering code.
Why: the simulation owns fighters, moves, rounds, pickups, and events. The Phaser scene can focus on drawing and sound. This made it possible to move the authoritative loop into the Node server without rewriting rendering.
Tradeoff: multiplayer now runs the simulation on the server, but local Vite fallback still runs it in the browser.
Decision: player text prompts are interpreted into a literal command plan (up to five move/pickup/attack/retreat/wait steps, with emergency interrupts and a clear-memory intent) on a hidden fighter, with a finisher compiler that guarantees a named signature move lands, plus soft pressure as a secondary bias lane.
Why: the original pressure-only model felt unresponsive — shouts often produced flavor without the action the player asked for. Command plans make "hit blue with the chair" reliably happen, while the finisher compiler back-solves prerequisites so signature moves do not degrade into a punch. Soft pressure remains for vague shouts and keeps the simulation in final control of balance and timing.
Tradeoff: the agent contract and the simulation's command executor are more complex than a single pressure value, and the model still keeps probabilistic edges so prompts are not perfectly deterministic remote control.
Decision: include ?agent=mock as a first-class path.
Why: the game remains playable without secrets, network access, rate-limit risk, or a deployed AI proxy. It also makes demos safer. The Node server also falls back to mock prompt responses if no GEMINI_API_KEY is present.
Tradeoff: mock responses cover only a small set of prompt intents, so they are less flexible than Gemini.
Decision: deploy the app as one Node web service on Render.
Why: the product has exactly one public lobby. A single in-memory server can own queue order, hidden fighter assignments, prompt privacy, bot seats, champion retention, and the authoritative fight state without Redis or a database.
Tradeoff: deploys and service restarts reset the lobby. Running more than one instance would create multiple lobbies unless state is externalized later.
Decision: implement progression as a three-round series with crown counts, distinct room arenas, and a final parking-lot arena.
Why: the spec requires levels or character progression. A round series fits a fighting game and gives the player visible escalation without needing persistent accounts or saved XP.
Tradeoff: this is session progression, not long-term character progression.
Decision: apply one hidden fatigue damage per accumulated active second.
Why: multiplayer rounds can otherwise stall when fighters crowd, recover, or fail to close distance. Fatigue preserves the last-fighter-standing objective without adding a visible timer.
Tradeoff: very long rounds become more deterministic because low-health fighters are forced toward resolution.