feat: honest AI opponent in quick game #68

Merged
developer merged 2 commits from feature/ai-opponent into development 2026-06-15 19:05:23 +00:00
Owner

Adds an explicit 🤖 AI / 👤 Random player choice to New Game's quick game (AI the default). The random path (disguised robot) is unchanged; AI is a robot the player knowingly plays.

Behaviour (vs_ai)

  • Robot is seated and moves at once (event-driven from the game service's after-commit/after-create hook; 30 s driver is the fallback). No sleep, no proactive nudge. Strength logic (playToWin 40% + margin) reused unchanged.
  • No per-move timeout — a 7-day inactivity loss, reusing the turn-timeout sweeper (turn_timeout_secs = AIInactivityTimeout); only the human is ever on the clock.
  • Chat + nudge disabled (word-check kept), add-friend never drawn, opponent shown as 🤖 everywhere, no statistics for either seat.
  • One flag games.vs_ai, set only on AI games, so the disguised path is never revealed (UI derives 🤖/gates from the flag, never the account). Matchmaker.StartVsAI seats the robot directly (never the open pool).

Wire: vs_ai on EnqueueRequest + GameView (FB regen Go+TS), through DTO/transcode/pkg.wire/notify.

Schema/wire change → contour DB wipe needed after merge (DROP SCHEMA backend CASCADE + restart; I'll handle it).

Tests: Go build/vet/gofmt; backend unit; integration on real Postgres (new ai_game_test.go: seated+active+vs_ai+7-day clock, immediate move, stats skipped, 7-day timeout→human loss, chat/nudge rejected); pkg/gateway; UI check + unit (243) + build + bundle gate + e2e (138, incl. a new AI test; quick-match e2e updated to pick Random player).

Bakes back into ARCHITECTURE / FUNCTIONAL(+_ru) / UI_DESIGN / PRERELEASE / backend README.

Adds an explicit **🤖 AI / 👤 Random player** choice to New Game's quick game (AI the default). The random path (disguised robot) is unchanged; AI is a robot the player knowingly plays. **Behaviour (vs_ai)** - Robot is seated and **moves at once** (event-driven from the game service's after-commit/after-create hook; 30 s driver is the fallback). No sleep, no proactive nudge. Strength logic (playToWin 40% + margin) reused unchanged. - **No per-move timeout** — a **7-day inactivity loss**, reusing the turn-timeout sweeper (`turn_timeout_secs = AIInactivityTimeout`); only the human is ever on the clock. - Chat + nudge **disabled** (word-check kept), add-friend never drawn, opponent shown as **🤖** everywhere, **no statistics** for either seat. - One flag `games.vs_ai`, set only on AI games, so the disguised path is never revealed (UI derives 🤖/gates from the flag, never the account). `Matchmaker.StartVsAI` seats the robot directly (never the open pool). **Wire:** `vs_ai` on `EnqueueRequest` + `GameView` (FB regen Go+TS), through DTO/transcode/pkg.wire/notify. **Schema/wire change → contour DB wipe needed after merge** (`DROP SCHEMA backend CASCADE` + restart; I'll handle it). **Tests:** Go build/vet/gofmt; backend unit; integration on real Postgres (new `ai_game_test.go`: seated+active+vs_ai+7-day clock, immediate move, stats skipped, 7-day timeout→human loss, chat/nudge rejected); pkg/gateway; UI check + unit (243) + build + bundle gate + e2e (138, incl. a new AI test; quick-match e2e updated to pick Random player). Bakes back into ARCHITECTURE / FUNCTIONAL(+_ru) / UI_DESIGN / PRERELEASE / backend README.
developer added 1 commit 2026-06-15 18:14:48 +00:00
feat: honest AI opponent in quick game
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Successful in 49s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m12s
aa765a0c06
New Game's quick game gains an explicit opponent selector — 🤖 AI (default)
or 👤 Random player. AI starts a game seated with a pooled robot that joins
and moves at once: no per-move timeout (a 7-day inactivity loss reusing the
turn-timeout sweeper), chat/nudge disabled, no statistics, the opponent shown
as 🤖 everywhere. The random path (disguised robot) is unchanged.

Driven by one game flag (games.vs_ai), set only on AI-started games so the
disguised path is never revealed; Matchmaker.StartVsAI seats the robot
directly (no open pool); the robot replies event-driven via the game service's
after-commit/after-create hook. Wire: vs_ai on EnqueueRequest + GameView.
developer added 1 commit 2026-06-15 18:49:51 +00:00
feat: AI-game refinements (GCG, your_turn, admin, metrics)
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 49s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m7s
d2a9441287
Follow-ups on the honest-AI game, same PR:
- GCG export labels the robot seat "AI" instead of its pool name (ExportGCG
  overrides via accounts.IsRobot); the in-app 🤖 is unchanged.
- vs_ai games emit no your_turn (the robot replies instantly, so it would be
  redundant); opponent_moved still advances the UI.
- Admin console shows the AI flag: a 🤖 column in /games and an "AI game" line
  on the game card (GameRow/GameDetailView gain VsAI).
- games_started_total / games_abandoned_total gain a vs_ai attribute; the
  Grafana Game-domain dashboard splits started/abandoned into human and AI
  panels.

Tests: metrics unit (vs_ai split); integration (no your_turn, GCG "AI").
owner approved these changes 2026-06-15 19:04:26 +00:00
developer merged commit 9e6899bb7d into development 2026-06-15 19:05:23 +00:00
developer deleted branch feature/ai-opponent 2026-06-15 19:05:23 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: developer/scrabble-game#68