Skip to content

Fix: Forfeit works when your deployed AI is playing for you - #137

Open
zachhh13 wants to merge 1 commit into
mainfrom
zach/forfeit-ai-owner
Open

zachhh13 wants to merge 1 commit into
mainfrom
zach/forfeit-ai-owner

Conversation

@zachhh13

Copy link
Copy Markdown
Contributor

The explicitly-required case that the forfeit feature (#132/#136) missed: when your deployed AI plays a game for you (deploy-and-play), the Forfeit button never showed — so you couldn't exit a game your model was driving.

Why

In deploy-and-play the seat is the deployment, not you: game.players holds the deployment id, never the owner's userId (AIParticipant carries no owner field; the only back-pointer is DeploymentRecord.userId). So both gates keyed on the wrong identity:

  • Client Game.tsx: isPlayer = players.some(p => p.username === you) — an AI seat's username is the deployment id, so you're treated as a spectator and the whole toolbar (incl. Forfeit) is hidden.
  • Server forfeitGame: authorized only by players.findIndex(id => id === user.userId) — your userId isn't in players, so it would reject the forfeit anyway.

Fix

  • Client: show Forfeit when you own an AI seat in this game. New pure helper viewerOwnsAiSeat(session, players) matches your tab's deploy-and-play queue session (deploymentId) against the seated AI player (an AI seat's username is its deployment id) — works mid-game, no result event needed. Broadcast/Highlight stay player-only; the owner gets exactly the Forfeit control.
  • Server: forfeitGame resolves the seat by ownership when the requester isn't a direct player — for each AI seat it reads DeploymentRepo[seatId].userId and forfeits the seat the requester owns. gameId alone suffices and the client never names the seat, so a stranger can't forfeit someone else's AI game (same ownership join used at queue time).

Verified live (the required case, screenshots in the run)

Owner deploys a model → it's matchmade vs a human → on the spectated game the Forfeit button now shows even while it's the AI's turn; clicking it ends the game with "Your model lost by forfeit" (rated, −162) and the human wins.

Tests: forfeitGame owner-resolution (owner forfeits their AI's seat; non-owner rejected), viewerOwnsAiSeat unit tests, a Game.tsx integration test (owner sees + emits; bystander doesn't). Server 1038/1038, client 839/839, tsc/lint/prettier clean.

Note: I noticed a final-submission tag landed on the repo while I was working — flagging in case the project is being frozen and you'd rather hold this.

The Forfeit button only appeared for a human seated by username. In the
deploy-and-play flow the seat is the DEPLOYMENT (game.players holds the
deployment id, never the owner's userId), so the owner watching their AI
play was treated as a pure spectator — no toolbar, no Forfeit — even
though exiting that game was the explicit requirement.

- Client: Game.tsx now also shows the Forfeit button when the viewer owns
  an AI seat in this game. New pure helper viewerOwnsAiSeat(session,
  players) matches the tab's deploy-and-play queue session's deploymentId
  against the seated AI player (an AI seat's username IS its deployment
  id) — available mid-game, no result needed. Broadcast/Highlight stay
  human-player-only; the owner gets exactly the Forfeit control.
- Server: forfeitGame resolves the seat by ownership when the user isn't a
  direct player — for each AI seat it reads DeploymentRepo[seatId].userId
  and forfeits the seat the requester owns. gameId alone suffices; the
  client never names the seat, so a stranger can't forfeit someone else's
  AI game (the same userId join used to prove ownership at queue time).

Tested: forfeitGame owner-resolution unit tests (owner forfeits their AI's
seat → opponent wins; a non-owner is rejected), viewerOwnsAiSeat unit
tests, a Game.tsx integration test (owner sees + emits, bystander doesn't),
and a live deploy-and-play run — owner deploys a model, it's matched vs a
human, and on the spectated game the Forfeit button shows (even while it's
the AI's turn) and ends the game 'Your model lost by forfeit'.

Server 1038, client 839, all green.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant